目录
1. Chkrootkit Introduce 2. Source Code Frame 3. chklastlog.c 4. chkwtmp.c 5. ifpromisc.c 6. chkproc.c 7. chkdirs.c 8. check_wtmpx.c 9. strings.c 10. chkrootkit的使用场景及其局限
1. Chkrootkit Introduce
chkrootkit是一个Linux系统下的查找检测rootkit后门的工具,需要明白的是,chkrootkit是一款ring3级别的rootkit检测工具,所以从某种程序上来说,chkrootkit能做的事也很有限,但是我们也必须明白,攻防对抗中并不是一味的追求底层kernel的hacking技术,往往多种技术结合(ring3、ring0)能够获得更好的效果
关于ring3、ring0下的rootkit攻防的平衡取舍,请参阅另一篇文章
http://www.cnblogs.com/LittleHann/p/3910696.htmlRelevant Link:
http://www.centospub.com/make/chkrootkit.html http://www.bootf.com/556.html http://www.chkrootkit.org/
2. Source Code Frame
chkrootkit的总体代码功能框架如下
1. 系统日志审计检查 1.1 chklastlog.c 1) 根据"/var/log/wtmp"和"/var/log/lastlog"进行交叉比较,检查当前系统是否存在新增帐号异常登录 2) 检测本次登录的用户是否有在/etc/passwd中出现 3) 检查/etc/passwd中是否有白名单之外的"超级用户(uid)",这是一种异常现象 1.2 chkwtmp.c 1) 对登录日志进行清除,造成日志文件中存在一段的"登录日志空档期",chkwtmp.c的目的就是发现这个"空档期",从而发现可疑的入侵现象 2. 网络状态审计检测 2.1 ifpromisc.c 1) 检测当前系统的网卡接口是否正在进行"raw packet"的处理(sniffer的特征) 检测当前系统中是否有sniffer程序在进行嗅探操作,"/proc/net/packet"这个虚拟目录保存了那些需要处理"raw network packets"的进程、及相关网络信息,正常情况下,一般的进程是不需要收发、处理"raw network packets"的,如果发现这类进程,则说明这是一个可疑sniffer进程(有可能是rootkit在进行嗅探操作) 2) 检测当前系统的网卡是否处于"PROMISC(混杂模式)" 3. 进程隐藏状态检测 3.1 chkproc.c 1) 针对readdir句柄劫持的检测 很多rootkit常常会对"/proc/的readdir句柄"句柄(而内部还是调用的Sys_getdents64)进行劫持从而进行进程隐藏,针对这个现象,chkrootkit采取了2种策略 1.1) 采用read原生系统调用来对/proc进行读写(绕过rootkit对Sys_getdents64系统调用的劫持) 1.2) 将/proc/number/(进程列表的枚举结果和"ps -edf、ps auxw、ps mauxw 2>&1、ps auxw -T | tr -s ' '| cut -d' ' -f2-"的结果进行对比,因为ps这类进程枚举命令内部调用的系统调用是/proc/的readdir句柄(),可以发现进程隐藏的迹象 2) 针对进程枚举指令劫持的检测 进程隐藏是LKM Rootkit常用的功能,rootkit常常会通过替换ps等指令程序为恶意程序(会自动过滤掉对rootkit自身的枚举)
3. chklastlog.c
1) 根据"/var/log/wtmp"和"/var/log/lastlog"进行交叉比较,检查当前系统是否存在新增帐号异常登录 2) 检测本次登录的用户是否有在/etc/passwd中出现 3) 检查/etc/passwd中是否有白名单之外的"超级用户(uid)",这是一种异常现象code
#if defined(SOLARIS2) || defined(__linux__) #define HAVE_LASTLOG_H 1 #else #undef HAVE_LASTLOG_H #endif #if __FreeBSD__ > 9 int main () { return 0; } #else #include <stdio.h> #ifdef __linux__ #include <stdlib.h> #endif #include <sys/stat.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <pwd.h> #include <sys/types.h> #include <utmp.h> #if (HAVE_LASTLOG_H) #include <lastlog.h> #endif #include <sys/file.h> #ifdef SOLARIS2 #include <fcntl.h> #endif #ifdef __FreeBSD__ #define WTMP_FILENAME "/var/log/wtmp" #define LASTLOG_FILENAME "/var/log/lastlog" #endif #ifdef __OpenBSD__ #include <stdlib.h> #define WTMP_FILENAME "/var/log/wtmp" #define LASTLOG_FILENAME "/var/log/lastlog" #endif #ifndef WTMP_FILENAME #define WTMP_FILENAME "/var/adm/wtmp" #endif #ifndef LASTLOG_FILENAME #define LASTLOG_FILENAME "/var/adm/lastlog" #endif #define TRUE 1L #define FALSE 0L long total_wtmp_bytes_read = 0; size_t wtmp_file_size; uid_t *uid; void read_status(); struct s_localpwd { int numentries; uid_t *uid; char **uname; }; #ifndef SOLARIS2 int nonuser(struct utmp utmp_ent); #endif struct s_localpwd *read_pwd(); void free_results(struct s_localpwd *); uid_t *localgetpwnam(struct s_localpwd *, char *); int getslot(struct s_localpwd *, uid_t); #define MAX_ID 99999 int main(int argc, char*argv[]) { int fh_wtmp; int fh_lastlog; /* struct lastlog { int32_t ll_time; // When user logged in char ll_line[UT_LINESIZE]; // Terminal line name char ll_host[UT_HOSTSIZE]; // Host user came from }; 用于查看那所用账号的最后登录时间 */ struct lastlog lastlog_ent; /* struct utmp { char ut_line[UT_LINESIZE]; // Terminal line name char ut_name[UT_NAMESIZE]; // User’s login name char ut_host[UT_HOSTSIZE]; // Host user came from int32_t ut_time; // When user logged in }; /var/log/wtmp 记录当前和历史上登录到系统的用户的登录tty、登录用户名、来源和时间等信息。和/var/log/lastlog一样,这个文件是一个二进制文件,需要用last命令查看 last -f /var/log/wtmp */ struct utmp utmp_ent; long userid[MAX_ID]; long i, slot; int status = 0; long wtmp_bytes_read; /* struct stat { dev_t st_dev; // ID of device containing file -文件所在设备的ID ino_t st_ino; // inode number -inode节点号 mode_t st_mode; // protection -保护模式 nlink_t st_nlink; // number of hard links -链向此文件的连接数(硬连接) uid_t st_uid; // user ID of owner -user id gid_t st_gid; // group ID of owner - group id dev_t st_rdev; // device ID (if special file) -设备号,针对设备文件 off_t st_size; // total size, in bytes -文件大小,字节为单位 blksize_t st_blksize; // blocksize for filesystem I/O -系统块的大小 blkcnt_t st_blocks; // number of blocks allocated -文件所占块数 time_t st_atime; // time of last access - 最近存取时间 time_t st_mtime; // time of last modification - 最近修改时间 time_t st_ctime; // time of last status change - 最近创建时间 }; */ struct stat wtmp_stat; struct s_localpwd *localpwd; struct passwd *user; uid_t *uid; char wtmpfile[128], lastlogfile[128]; memcpy(wtmpfile, WTMP_FILENAME, 127); memcpy(lastlogfile, LASTLOG_FILENAME, 127); while (--argc && ++argv) /* poor man getopt */ { if (!memcmp("-f", *argv, 2)) { if (!--argc) { break; } ++argv; memcpy(wtmpfile, *argv, 127); } else if (!memcmp("-l", *argv, 2)) { if (!--argc) { break; } ++argv; memcpy(lastlogfile, *argv, 127); } } //信号的安装(确定要接收和处理的信号),指定read_status()作为信号处理函数 signal(SIGALRM, read_status); //信号的发送,专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号,又称为闹钟时间 alarm(5); for (i=0; i < MAX_ID; i++) { userid[i]=FALSE; } //打开"/var/log/lastlog"文件 if ((fh_lastlog = open(lastlogfile, O_RDONLY)) < 0) { fprintf(stderr, "unable to open lastlog-file %s\n", lastlogfile); return(1); } //打开"/var/log/wtmp"文件 if ((fh_wtmp = open(wtmpfile, O_RDONLY)) < 0) { fprintf(stderr, "unable to open wtmp-file %s\n", wtmpfile); close(fh_lastlog); return(2); } //获取/var/log/wtmp的文件属性: struct stat,保存在wtmp_stat结构体中 if (fstat(fh_wtmp, &wtmp_stat)) { perror("chklastlog::main: "); close(fh_lastlog); close(fh_wtmp); return(3); } wtmp_file_size = wtmp_stat.st_size; //获取"/etc/passwd"文件结构化内容 localpwd = read_pwd(); /* 3. 检查/etc/passwd中是否有白名单之外的"超级用户(uid)",这是一种异常现象 */ while((user = getpwent())!=0) { //默认白名单只有root用户 if ((user->pw_uid == 0 || user->pw_gid == 0) && user->pw_name != "root") { printf("dedect Suspicious Account\n"); printf("\n%s:%d:%d:%s:%s:%s\n",user->pw_name, user->pw_uid, user->pw_gid, user->pw_gecos,user->pw_dir,user->pw_shell); } } endpwent(); /* "/var/log/wtmp"是一个记录用户登录信息的列表,每一行都记录了一次用户的登录信息,接下来的代码对其进行逐行遍历 */ while ((wtmp_bytes_read = read(fh_wtmp, &utmp_ent, sizeof (struct utmp))) >0) { if (wtmp_bytes_read < sizeof(struct utmp)) { fprintf(stderr, "wtmp entry may be corrupted"); break; } total_wtmp_bytes_read += wtmp_bytes_read; /* 对当前遍历中的"struct utmp"进行过滤检查 1. 是否是"shutdonw"用户 2. 当前登录用户账户命是否在"/etc/passwd"中存在 */ if ( !nonuser(utmp_ent) && /*strncmp(utmp_ent.ut_line, "ftp", 3)*/ && (uid = localgetpwnam(localpwd, utmp_ent.ut_name)) != NULL ) { if (*uid > MAX_ID) { fprintf(stderr, "MAX_ID is %ld and current uid is %ld, please check\n\r", MAX_ID, *uid ); exit (1); } if (!userid[*uid]) { lseek(fh_lastlog, (long)*uid * sizeof (struct lastlog), 0); if ((wtmp_bytes_read = read(fh_lastlog, &lastlog_ent, sizeof (struct lastlog))) > 0) { if (wtmp_bytes_read < sizeof(struct lastlog)) { fprintf(stderr, "lastlog entry may be corrupted"); break; } if (lastlog_ent.ll_time == 0) { if (-1 != (slot = getslot(localpwd, *uid))) { //1. 如果本次登录的用户在lastlog中的没有对应的登录记录(即这是一个突然新增的新用户登录),则表明是一个可疑用户登录行为 printf("user %s deleted or never logged from lastlog!\n", NULL != localpwd->uname[slot] ? (char*)localpwd->uname[slot] : "(null)"); } else { //2. 检测本次登录的用户是否有在/etc/passwd中出现 printf("deleted user uid(%d) not in passwd\n", *uid); } ++status; } userid[*uid]=TRUE; } } } } #if 0 printf("\n"); #endif free_results(localpwd); close(fh_wtmp); close(fh_lastlog); return(status); } #ifndef SOLARIS2 /* minimal funcionality of nonuser() */ int nonuser(struct utmp utmp_ent) { return (!memcmp(utmp_ent.ut_name, "shutdown", sizeof ("shutdown"))); } #endif void read_status() { double remaining_time; static long last_total_bytes_read=0; int diff; diff = total_wtmp_bytes_read-last_total_bytes_read; if (diff == 0) diff = 1; remaining_time=(wtmp_file_size-total_wtmp_bytes_read)*5/(diff); last_total_bytes_read=total_wtmp_bytes_read; printf("Remaining time: %6.2f seconds\n", remaining_time); /* signal(SIGALRM,read_status); alarm(5); */ } struct s_localpwd *read_pwd() { /* struct passwd { char * pw_name; // Username, POSIX.1 char * pw_passwd; //Password __uid_t pw_uid; // User ID, POSIX.1 __gid_t pw_gid; // Group ID, POSIX.1 char * pw_gecos; // Real Name or Comment field char * pw_dir; // Home directory, POSIX.1 char * pw_shell; // Shell Program, POSIX.1 }; */ struct passwd *pwdent; int numentries=0,i=0; struct s_localpwd *localpwd; //setpwent()用来将getpwent()的读写地址指回密码文件开头 setpwent(); /* 获取"/etc/passw"文件的信息,getpwent()用来从密码文件(/etc/passwd)中读取一项用户数据,该用户的数据以passwd 结构返回。第一次调用时会取得第一位 用户数据,之后每调用一次就会返回下一项数据,直到已无任何数据时返回NULL */ while ((pwdent = getpwent())) { numentries++; } endpwent(); localpwd = (struct s_localpwd *)malloc((size_t)sizeof(struct s_localpwd)); localpwd->numentries=numentries; localpwd->uid = (uid_t *)malloc((size_t)numentries*sizeof(uid_t)); localpwd->uname = (char **)malloc((size_t)numentries*sizeof(char *)); for (i=0;i<numentries;i++) { localpwd->uname[i] = (char *)malloc((size_t)30*sizeof(char)); } i=0; setpwent(); while ((pwdent = getpwent()) && (i<numentries)) { localpwd->uid[i] = pwdent->pw_uid; memcpy(localpwd->uname[i], pwdent->pw_name, (strlen(pwdent->pw_name)>29)?29:strlen(pwdent->pw_name)+1); i++; } endpwent(); return(localpwd); } void free_results(struct s_localpwd *localpwd) { int i; free(localpwd->uid); for (i=0;i<(localpwd->numentries);i++) { free(localpwd->uname[i]); } free(localpwd->uname); free(localpwd); } uid_t *localgetpwnam(struct s_localpwd *localpwd, char *username) { int i; size_t len; for (i=0; i<(localpwd->numentries);i++) { len = (strlen(username) > 9) ? 30 : strlen(username) + 1; if (!memcmp(username, localpwd->uname[i],len)) { return &(localpwd->uid[i]); } } return NULL; } int getslot(struct s_localpwd *localpwd, uid_t uid) { int i; for (i=0; i<(localpwd->numentries);i++) { if (localpwd->uid[i] == uid) { return i; } } return -1; } #endifRelevant Link:
https://www.mirbsd.org/htman/i386/man5/lastlog.htm
4. chkwtmp.c
"/var/log/wtmp"记录了当前和历史上登录到系统的用户的登录tty、登录用户名、来源和时间等信息,黑客在入侵了主机后,会对登录日志进行清除,造成日志文件中存在一段的"登录日志空档期",chkwtmp.c的目的就是发现这个"空档期",从而发现可疑的入侵现象
#if __FreeBSD__ > 9 int main () { return 0; } #else #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <utmp.h> #include <time.h> #include <sys/time.h> #include <sys/file.h> #ifdef SOLARIS2 #include <fcntl.h> #endif #ifdef __FreeBSD__ #define WTMP_FILENAME "/var/log/wtmp" #else #ifndef WTMP_FILENAME #define WTMP_FILENAME "/var/adm/wtmp" #endif #endif void printit(counter, start, end) int counter; long start,end; { char buffer[30]; printf("%d deletion(s) between ", counter); strncpy(buffer, ctime( (time_t *) &start), 30); buffer[24]='\0'; printf("%s and %s", buffer, ctime( (time_t *) &end)); } int main(int argc, char*argv[]) { int filehandle; struct utmp utmp_ent; struct timeval mytime; struct timezone dummy; long start_time, act_time; int del_counter, t_del; char wtmpfile[128]; del_counter=t_del=0; start_time=0; gettimeofday(&mytime, &dummy); act_time=mytime.tv_sec; wtmpfile[127]='\0'; memcpy(wtmpfile, WTMP_FILENAME, 127); if ( argc == 3 && !memcmp("-f", argv[1], 2) && *argv[2]) { memcpy(wtmpfile, argv[2], 127); } if ((filehandle = open(wtmpfile,O_RDONLY)) < 0) { fprintf(stderr, "unable to open wtmp-file %s\n", wtmpfile); return(2); } while (read (filehandle, (char *) &utmp_ent, sizeof (struct utmp)) > 0) { if (utmp_ent.ut_time == 0) { del_counter++; } else { if (del_counter) { printit(del_counter, start_time, utmp_ent.ut_time); t_del++; del_counter=0; } start_time=utmp_ent.ut_time; } } close(filehandle); if (del_counter) { printit(del_counter, start_time, act_time); } exit((int) t_del+del_counter); } #endif
5. ifpromisc.c
#include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #ifdef __linux__ #include <linux/if.h> #include <linux/if_arp.h> #include <linux/if_ether.h> #include <dirent.h> #include <sys/stat.h> #else #include <net/if.h> #ifndef __OpenBSD__ #include <net/if_arp.h> #endif #endif #ifdef SOLARIS2 #include <sys/sockio.h> #endif #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #include <unistd.h> struct interface { char name[IFNAMSIZ]; // interface name short type; // if type short flags; // various flags #ifdef __linux__ int index; //interface index #endif }; char *Release = "chkrootkit package", *Version = "@(#) ifpromisc)"; int skfd = -1; /* AF_INET or AF_PACKET raw socket desc. */ int q = 0; /* Quiet mode on or off */ struct packet_info { int index; int type; int proto; int inode; char *cmd; struct packet_info *next; }; #ifdef __linux__ /* * the contents of /proc/net/packet */ static struct packet_info *proc_net_packet = 0; /* * read the entries from /proc/net/packet */ static void read_proc_net_packet() { FILE *proc; char buf[80]; proc = fopen("/proc/net/packet", "r"); if (!proc) { if (errno != ENOENT) { perror("opening /proc/net/packet"); } return; } /* skip the header */ fgets(buf, 80, proc); while (fgets(buf, 80, proc)) { int type = 0; unsigned int proto = 0; int index = 0; unsigned int inode = 0; if (sscanf(buf, "%*p %*d %d %x %d %*d %*u %*u %u", &type, &proto, &index, &inode) == 4) { struct packet_info *pi; pi = (struct packet_info *)malloc(sizeof(struct packet_info)); pi->type = type; pi->proto = proto; pi->index = index; pi->inode = inode; pi->cmd = 0; pi->next = proc_net_packet; proc_net_packet = pi; } else { fprintf(stderr, "cannot grok /proc/net/packet: %s", buf); } } fclose(proc); } /* look up an entry from /proc/net/packet by inode */ static struct packet_info *find_packet_info(int inode) { struct packet_info *p; for (p = proc_net_packet; p; p = p->next) { if (p->inode == inode) { return p; } } return NULL; } /* walk a processes fd dir looking for sockets with inodes that match the inodes from /proc/net/packet, when a match is found, the processes exe is stored 获取正在进行处理"raw network packets"的进程(疑似sniffer进程)的进程列表 */ static void walk_process(char *process) { DIR *dir; struct dirent *ent; char path[1024]; if (snprintf(path, sizeof(path), "/proc/%s/fd", process) == -1) { fprintf(stderr, "giant process name! %s\n", process); return; } if ((dir = opendir(path)) == NULL) { if (errno != ENOENT) { perror(path); } return; } while ((ent = readdir(dir))) { struct stat statbuf; struct packet_info *info; if (snprintf(path, sizeof(path), "/proc/%s/fd/%s", process, ent->d_name) == -1) { fprintf(stderr, "giant fd name /proc/%s/fd/%s\n", process, ent->d_name); continue; } if (stat(path, &statbuf) == -1) { perror(path); continue; } if (S_ISSOCK(statbuf.st_mode) && (info = find_packet_info(statbuf.st_ino))) { char link[1024]; memset(link, 0, sizeof(link)); /* no need to check rv since it has to be long enough, * otherwise, one of the ones above will have failed */ snprintf(path, sizeof(path), "/proc/%s/exe", process); readlink(path, link, sizeof(link) - 1); info->cmd = strdup(link); } } closedir(dir); } /* walk the proc file system looking for processes, call walk_proc on each * process */ static void walk_processes() { DIR *dir; struct dirent *ent; if ((dir = opendir("/proc")) == NULL) { perror("/proc"); return; } while ((ent = readdir(dir))) { /* we only care about dirs that look like processes */ if (strspn(ent->d_name, "0123456789") == strlen(ent->d_name)) { walk_process(ent->d_name); } } closedir(dir); } /* return 1 if index is a member of pcap_session_list, 0 otherwise. */ static int has_packet_socket(int index) { struct packet_info *p; for (p = proc_net_packet; p; p = p->next) { if (p->index == index) { return 1; } } return 0; } #endif /* __linux__ */ static void ife_print(struct interface *ptr) { #ifdef __linux__ //检测当前网卡接口是否处于"PROMISC(混杂模式)" int promisc = ptr->flags & IFF_PROMISC; //检测当前网卡接口是否正在进行"raw packet"的处理(同样也是sniffer的特征) int has_packet = has_packet_socket(ptr->index); if (promisc || has_packet) { printf("%s:", ptr->name); if (promisc) printf(" PROMISC"); if (has_packet) { struct packet_info *p; printf(" PF_PACKET("); p = proc_net_packet; if (p) { printf("%s", p->cmd); for (p = p->next; p; p = p->next) { if (p->index == ptr->index) { printf(", %s", p->cmd); } } } printf(")"); } printf("\n"); } else { if (!q) printf("%s: not promisc and no PF_PACKET sockets\n", ptr->name); } #else if (ptr->flags & IFF_PROMISC) printf("%s is %s", ptr->name, "PROMISC"); else { if (!q) printf("%s is %s", ptr->name, "not promisc"); } putchar('\n'); #endif } /* Fetch the inteface configuration from the kernel. */ static int if_fetch(char *ifname, struct interface *ife) { struct ifreq ifr; memset((char *) ife, 0, sizeof(struct interface)); strncpy(ife->name, ifname, sizeof(ife->name)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); //获得接口标志 if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) return(-1); ife->flags = ifr.ifr_flags; #ifdef __linux__ /* store the device index */ if (ioctl(skfd, SIOCGIFINDEX, &ifr) < 0) return(-1); ife->index = ifr.ifr_ifindex; #endif return(0); } static void if_print() { char buff[1024]; /* struct interface { char name[IFNAMSIZ]; // interface name short type; // if type short flags; // various flags #ifdef __linux__ int index; //interface index #endif }; */ struct interface ife; struct ifconf ifc; struct ifreq *ifr; int i; ifc.ifc_len = sizeof(buff); ifc.ifc_buf = buff; //获取所有网卡接口的信息 if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { fprintf(stderr, "SIOCGIFCONF: %s\n", strerror(errno)); return; } ifr = ifc.ifc_req; for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { if (if_fetch(ifr->ifr_name, &ife) < 0) { #ifdef __linux__ fprintf(stderr, "%s: unknown interface.\n", ifr->ifr_name); #endif continue; } if (!memcmp(ifr->ifr_name, "lo", 2)) continue; ife_print(&ife); } } int main(int argc, char **argv) { if (argc == 2 && !memcmp(argv[1], "-q", 2)) { q++; } /* Create a channel to the NET kernel. */ if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(-1); } #ifdef __linux__ read_proc_net_packet(); walk_processes(); #endif if_print(); (void) close(skfd); exit(0); }Relevant Link:
http://blog.cloudpassage.com/2012/09/05/warn-packet-sniffer-running/
6. chkproc.c
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__sun) int main (){ return 0; } #else #include <stdio.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <dirent.h> #include <ctype.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/stat.h> #if defined(__sun) #include <procfs.h> #include <fcntl.h> #endif #include <sys/resource.h> #define PS_SUN 0 #define PS_LOL 1 #define PS_COM 2 #define PS_LNX 3 #define PS_MAX 3 #define ENYELKM "/proc/12345" // #define ENYELKM "/tmp/12345" #if defined(__sun) #define FIRST_PROCESS 0 #else #define FIRST_PROCESS 1 #endif #define MAX_PROCESSES 99999 #define MAX_BUF 1024 #if !defined (SIGXFSZ) #define SIGXFSZ 25 #endif static char *ps_cmds[] = { "ps -edf", "ps auxw", "ps mauxw 2>&1 ", "ps auxw -T | tr -s ' '| cut -d' ' -f2-", }; int psproc [MAX_PROCESSES+1]; int dirproc[MAX_PROCESSES+1]; #if defined(__linux__) int isathread[MAX_PROCESSES+1]; #endif /* * read at most the first (size-1) chars into s and terminate with a '\0'. * stops reading after a newline or EOF. if a newline is read, it will be * the last char in the string. if no newline is found in the first * (size-1) chars, then keep reading and discarding chars until a newline * is found or EOF. */ char *readline(char *s, int size, FILE *stream) { char *rv = fgets(s, size, stream); if (strlen(s) == (size-1) && s[size-1] != '\n') { char buf[MAX_BUF]; fgets(buf, MAX_BUF, stream); while (strlen(buf) == (MAX_BUF-1) && buf[MAX_BUF-1] != '\n') { fgets(buf, MAX_BUF, stream); } } return rv; } int main(int argc, char **argv) { char buf[MAX_BUF], *p, path[MAX_BUF]; char *pscmd = (char *)0; FILE *ps; //打开/proc虚拟目录 DIR *proc = opendir("/proc"); struct dirent *dir; struct stat sb; int i, j, retps, retdir, pv, verbose; long ret = 0L; char * tmp_d_name; #if defined(__linux__) int maybeathread; #endif #if defined(__sun) psinfo_t psbuf; #endif pv = verbose = 0; if (!proc) { perror("proc"); exit (1); } for (i = 1; i < argc; i++) { if (!memcmp(argv[i], "-v", 2)) { verbose++; } else if (!memcmp(argv[i], "-?", 2)) { printf("Usage: %s [-v] [-v] -p <num>\n", argv[0]); return 0; } #if defined(__linux__) else if (!memcmp(argv[i], "-p", 2)) { if (i+1 < argc) { pv = atoi(argv[++i]); } else { printf("Usage: %s [-v] [-v] [-p procps version]\n", argv[0]); return 0; } } #endif } #if defined(__sun) pscmd = ps_cmds[PS_SUN]; #elif !defined (__linux__) pscmd = ps_cmds[PS_COM]; #endif #if defined(__linux__) if (pv < 1 || pv > PS_MAX) pv = 1; pscmd = ps_cmds[pv]; /* printf("pv = %d\n\r", pv); /* -- DEBUG */ #endif /* printf("pscmd = %s\n\r", pscmd); /* -- DEBUG */ if (!(ps = popen(pscmd, "r"))) { perror("ps"); exit(errno); } *buf = 0; readline(buf, MAX_BUF, ps); /* Skip header */ #if defined(__sun) if (!isspace(*buf)) #else if (!isalpha(*buf)) #endif { readline(buf, MAX_BUF, ps); /* Skip header */ if (!isalpha(*buf) && pv != PS_LNX) { if (pv != PS_LOL) execlp(argv[0], argv[0], "-p 1", NULL); fprintf(stderr, "OooPS!\n"); exit(2); } } if (!memcmp(buf, "ps:", 3) && (pv != PS_LOL)) execlp(argv[0], argv[0], "-p 1", NULL); for (i = FIRST_PROCESS; i <= MAX_PROCESSES; i++) { /* Init matrix */ psproc[i] = dirproc[i] = 0; #if defined(__linux__) isathread[i] = 0; #endif } while (readline(buf, MAX_BUF, ps)) { p = buf; #if defined(__sun) while (isspace(*p)) /* Skip spaces */ p++; #endif while (!isspace(*p)) /* Skip User */ p++; while (isspace(*p)) /* Skip spaces */ p++; /* printf(">>%s<<\n", p); /* -- DEBUG */ ret = atol(p); if ( ret < 0 || ret > MAX_PROCESSES ) { fprintf (stderr, " OooPS, not expected %ld value\n", ret); exit (2); } psproc[ret] = 1; } pclose(ps); while ((dir = readdir(proc))) { #if defined(__linux__) maybeathread = 0; #endif tmp_d_name = dir->d_name; if (!strcmp(tmp_d_name, ".") || !strcmp(tmp_d_name, "..")) continue; #if defined(__linux__) if (*tmp_d_name == '.') { /* here we catch the new NTPL threads in linux. They are listed in /proc as PIDs with a period prepended */ tmp_d_name++; maybeathread = 1; } #endif if(!isdigit(*tmp_d_name)) continue; #if defined(__linux__) else if (maybeathread) { isathread[atol(tmp_d_name)] = 1; /* mark it as a linux NTPL thread if it's in the form of "\.[0-9]*" */ if (verbose) printf("%ld is a Linux Thread, marking as such...\n", atol(tmp_d_name)); } #endif /* printf("%s\n", tmp_d_name); /* -- DEBUG */ dirproc[atol(tmp_d_name)] = 1; } closedir(proc); /* Brute force */ strcpy(buf, "/proc/"); retps = retdir = 0; for (i = FIRST_PROCESS; i <= MAX_PROCESSES; i++) { snprintf(&buf[6], 6, "%d", i); if (!chdir(buf)) { if (!dirproc[i] && !psproc[i]) { #if defined(__linux__) if (!isathread[i]) { #endif retdir++; if (verbose) printf ("PID ](%s): not in readdir output\n", i, buf); #if defined(__linux__) } #endif } if (!psproc[i] ) /* && !kill(i, 0)) */ { #if defined(__linux__) if(!isathread[i]) { #endif retps++; if (verbose) printf ("PID ]: not in ps output\n", i); #if defined(__linux__) } #endif } #if defined(__linux__) if(!isathread[i]) { #endif /* if ((!dirproc[i] || !psproc[i]) && !kill(i, 0) && (verbose > 1)) */ if ((!dirproc[i] || !psproc[i]) && (verbose > 1)) { #if defined(__linux__) j = readlink ("./cwd", path, sizeof(path)); path[(j < sizeof(path)) ? j : sizeof(path) - 1] = 0; printf ("CWD ]: %s\n", i, path); j = readlink ("./exe", path, sizeof(path)); path[(j < sizeof(path)) ? j : sizeof(path) - 1] = 0; printf ("EXE ]: %s\n", i, path); #elif defined(__FreeBSD__) j = readlink ("./file", path, sizeof(path)); path[(j < sizeof(path)) ? j : sizeof(path) - 1] = 0; printf ("FILE ]: %s\n", i, path); #elif defined(__sun) if ((j = open("./psinfo", O_RDONLY)) != -1) { if (read(j, &psbuf, sizeof(psbuf)) == sizeof(psbuf)) printf ("PSINFO ]: %s\n", i, psbuf.pr_psargs); else printf ("PSINFO ]: unknown\n", i); close(j); } else printf ("PSINFO ]: unknown\n", i); #endif } #if defined(__linux__) } #endif } #ifndef __FreeBSD__ else { errno = 0; getpriority(PRIO_PROCESS, i); if (!errno) { retdir++; if (verbose) printf ("PID ](%s): not in getpriority readdir output\n", i, buf); } } #endif } if (retdir) printf("You have % 5d process hidden for readdir command\n", retdir); if (retps) printf("You have % 5d process hidden for ps command\n", retps); #if defined(__linux__) kill(1, 100); /* Check for SIGINVISIBLE Adore signal */ if (kill (1, SIGXFSZ) < 0 && errno == 3) { printf("SIGINVISIBLE Adore found\n"); retdir+= errno; } /* Check for Enye LKM */ if (stat(ENYELKM, &sb) && kill (12345, 58) >= 0) { printf("Enye LKM found\n"); retdir+= errno; } #endif return (retdir+retps); } #endif
7. chkdirs.c
#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__sun) || defined (hpux) || defined (__bsdi__) || defined (bsdi) || defined (__APPLE__) #include <limits.h> #elif defined(__APPLE__) && defined(__MACH__) #include <sys/syslimits.h> #endif #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <dirent.h> #include <string.h> #include <errno.h> #ifndef NAME_MAX #define NAME_MAX PATH_MAX #endif struct dirinfolist { char dil_name[NAME_MAX+1]; int dil_lc; struct dirinfolist *dil_next; }; void usage () { fprintf(stderr, "chkdirs [-n] dir ...\n"); exit(255); } char *make_pathname (char *path, char *dir, char **buffer) { int plen, pathname_len, bufsize, offs; bufsize = 0; plen = strlen(path); pathname_len = plen + strlen(dir) + 2; if (!(*buffer) || (sizeof(*buffer) < pathname_len)) { if (buffer) free((void *)*buffer); bufsize = (pathname_len > PATH_MAX) ? pathname_len : PATH_MAX; if (!(*buffer = (char *)malloc(bufsize))) { return((char *)NULL); } } if (dir[0] == '/') { /* "dir" is absolute pathname, don't prepend "path" */ offs = 0; } else { strncpy(*buffer, path, bufsize); if ((*buffer)[plen-1] == '/') { /* "path" ends in "/", don't add extra */ offs = plen; } else { (*buffer)[plen] = '/'; offs = plen + 1; } } strncpy((*buffer)+offs, dir, bufsize - offs); return((*buffer)); } int check_dir (char *dir, char *path, int linkcount, int norecurse) { int diff = -1; int plen, buflen, numdirs; char *curpath, *fullpath; DIR *dirhandle; struct dirent *finfo; struct dirinfolist *dl, *dptr; struct stat statinfo; /* When called recursively, "path" will be the full path of the cwd, but when called from main() "path" is empty. We need the cwd path so we can chdir() back at the end of this routine, as well as when printing errors and other output. */ if (!path || !(plen = strlen(path))) { buflen = PATH_MAX; retry: if (!(curpath = (char *)malloc(buflen))) { fprintf(stderr, "malloc() failed: %s\n", strerror(errno)); return(-1); } if (!getcwd(curpath, buflen)) { if (errno == ERANGE) { free((void *)curpath); buflen = buflen * 2; goto retry; } else { fprintf(stderr, "getcwd() failed: %s\n", strerror(errno)); return(-1); } } } else { /* "path" is set, so just copy it into "curpath" */ if (!(curpath = (char *)malloc(plen+1))) { fprintf(stderr, "malloc() failed: %s\n", strerror(errno)); return(-1); } strncpy(curpath, path, plen+1); } /* Now set "fullpath" to be the absolute path name of the directory we will be checking (prepend "curpath" if "dir" is not already an absolute pathname). */ fullpath = (char *)NULL; if (!make_pathname(curpath, dir, &fullpath)) { fprintf(stderr, "make_pathname() failed: %s\n", strerror(errno)); free((void *)curpath); return(-1); } if (chdir(dir)) { fprintf(stderr, "chdir(%s): %s\n", fullpath, strerror(errno)); free((void *)curpath); free((void *)fullpath); return(-1); } /* Again, "linkcount" (the link count of the current directory) is set only if check_dir() is called recursively. Otherwise, we need to stat the directory ourselves. */ if (!linkcount) { if (lstat(".", &statinfo)) { fprintf(stderr, "lstat(%s): %s\n", fullpath, strerror(errno)); goto abort; } linkcount = statinfo.st_nlink; } if (!(dirhandle = opendir("."))) { fprintf(stderr, "opendir(%s): %s\n", fullpath, strerror(errno)); goto abort; } numdirs = 0; dl = (struct dirinfolist *)NULL; while ((finfo = readdir(dirhandle))) { if (!strcmp(finfo->d_name, ".") || !strcmp(finfo->d_name, "..")) continue; if (lstat(finfo->d_name, &statinfo)) { fprintf(stderr, "lstat(%s/%s): %s\n", fullpath, finfo->d_name, strerror(errno)); closedir(dirhandle); goto abort; } if (S_ISDIR(statinfo.st_mode)) { numdirs++; if (norecurse) continue; /* just count subdirs if "-n" */ /* Otherwise, keep a list of all directories found that have link count > 2 (indicating directory contains subdirectories). We'll call check_dir() on each of these subdirectories in a moment... */ if (statinfo.st_nlink > 2) { dptr = dl; if (!(dl = (struct dirinfolist *)malloc(sizeof(struct dirinfolist)))) { fprintf(stderr, "malloc() failed: %s\n", strerror(errno)); norecurse = 1; while (dptr) { dl = dptr->dil_next; free((void *)dptr); dptr = dl; } continue; } strncpy(dl->dil_name, finfo->d_name, sizeof(dl->dil_name)); dl->dil_lc = statinfo.st_nlink; dl->dil_next = dptr; } } } closedir(dirhandle); /* Parent directory link count had better equal #subdirs+2... */ diff = linkcount - numdirs - 2; if (diff) printf("%d\t%s\n", diff, fullpath); /* Now check all subdirectories in turn... */ while (dl) { check_dir(dl->dil_name, fullpath, dl->dil_lc, norecurse); dptr = dl->dil_next; free((void *)dl); dl = dptr; } abort: if (chdir(curpath)) { fprintf(stderr, "Final chdir(%s) failed (%s) -- EXIT!\n", curpath, strerror(errno)); exit(255); } free((void *)fullpath); free((void *)curpath); return(diff); } int main (int argc, char **argv) { int norecurse = 0; int i, retval; int c; opterr = 0; while ((c = getopt(argc, argv, "n")) > 0) { switch (c) { case 'n': norecurse = 1; break; default: usage(); } } if (argc <= optind) usage(); { for (i = optind; i < argc; i++) { retval = check_dir(argv[i], (char *)NULL, 0, norecurse); } } exit(retval); }
8. check_wtmpx.c
#if !defined(__SunOS__) && !defined(SOLARIS2) int main () { return 0; } #else #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/file.h> #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <pwd.h> #include <time.h> #include <utmp.h> #include <utmpx.h> #include <lastlog.h> #include <fcntl.h> #include <unistd.h> #define WTMP_FILENAME "/var/adm/wtmp" #define WTMPX_FILENAME "/var/adm/wtmpx" struct file_utmp_entry { char ut_user[8]; /* User login name */ char ut_id[4]; /* /etc/inittab id */ char ut_line[12]; /* device name (console, lnxx) */ int16_t ut_pid; /* process id */ int16_t ut_type; /* type of entry */ struct { int16_t e_termination; /* Process termination status */ int16_t e_exit; /* Process exit status */ } ut_exit; /* The exit status of a process */ uint32_t ut_time; /* time entry was made */ }; struct timeval_32 { uint32_t tv_sec; /* seconds */ int32_t tv_usec; /* and microseconds */ }; /* * This data structure describes the utmp *file* contents using * fixed-width data types. It should only be used by the implementation. * * Applications should use the getutxent(3c) family of routines to interact * with this database. */ struct file_utmpx_entry { char ut_user[32]; /* user login name */ char ut_id[4]; /* inittab id */ char ut_line[32]; /* device name (console, lnxx) */ uint32_t ut_pid; /* process id */ int16_t ut_type; /* type of entry */ struct { int16_t e_termination; /* process termination status */ int16_t e_exit; /* process exit status */ } ut_exit; /* exit status of a process */ struct timeval_32 ut_tv; /* time entry was made */ int32_t ut_session; /* session ID, user for windowing */ int32_t pad[5]; /* reserved for future use */ int16_t ut_syslen; /* significant length of ut_host */ char ut_host[257]; /* remote host name */ }; static void usage ( char * arg ) { fprintf( stderr, " Usage: %s [-h] [-w wtmp] [-x wtmpx]\n", arg ); exit( EXIT_FAILURE ); } /* end of usage */ int main ( int argc, char * argv[] ) { int fd_wtmp, fd_wtmpx; char filename_wtmp[128] = WTMP_FILENAME; char filename_wtmpx[128] = WTMPX_FILENAME; ssize_t wtmp_bytes_read; ssize_t wtmpx_bytes_read; uint32_t wtmp_read_counter = 0; uint32_t wtmpx_read_counter = 0; int c; struct file_utmp_entry utmp_entry; struct file_utmpx_entry utmpx_entry; opterr = 0; /* Don't want getopt() writing to stderr */ while ( ( c = getopt( argc, argv, "hw:x:" ) ) != EOF ) { switch ( c ) { case 'w': strncpy( filename_wtmp, optarg, 128 ); filename_wtmp[127] = '\0'; break; case 'x': strncpy( filename_wtmpx, optarg, 128 ); filename_wtmpx[127] = '\0'; break; case 'h': case '?': usage( argv[0] ); break; } /* end of switch */ } /* end of while */ fd_wtmp = open( filename_wtmp, O_RDONLY ); if ( fd_wtmp < 0 ) { fprintf( stderr, "Unable to open %s\n", filename_wtmp ); return( EXIT_FAILURE ); } fd_wtmpx = open( filename_wtmpx, O_RDONLY ); if ( fd_wtmpx < 0 ) { fprintf( stderr, "Unable to open %s\n", filename_wtmpx ); close( fd_wtmp ); return( EXIT_FAILURE ); } while ( 1 ) { wtmpx_bytes_read = read( fd_wtmpx, &utmpx_entry, sizeof( struct file_utmpx_entry ) ); if ( wtmpx_bytes_read > 0 ) { if ( wtmpx_bytes_read < sizeof( struct file_utmpx_entry ) ) { fprintf( stderr, "wtmpx entry may be corrupted\n" ); break; } wtmpx_read_counter++; } wtmp_bytes_read = read( fd_wtmp, &utmp_entry, sizeof( struct file_utmp_entry ) ); if ( wtmp_bytes_read > 0 ) { if ( wtmp_bytes_read < sizeof( struct file_utmp_entry ) ) { fprintf( stderr, "wtmp entry may be corrupted\n" ); break; } wtmp_read_counter++; } if ( ( wtmpx_bytes_read <= 0 ) || ( wtmp_bytes_read <= 0 ) ) { break; } if ( strncmp( utmp_entry.ut_user, utmpx_entry.ut_user, 8 ) != 0 ) { fprintf( stderr, "[ %u ] ut_user %s <-> %s\n", wtmp_read_counter, utmp_entry.ut_user, utmpx_entry.ut_user ); break; } if ( memcmp( utmp_entry.ut_id, utmpx_entry.ut_id, 4 ) != 0 ) { fprintf( stderr, "[ %u ] utmp_entry.ut_id != utmpx_entry.ut_id\n", wtmp_read_counter ); break; } if ( strcmp( utmp_entry.ut_line, utmpx_entry.ut_line ) != 0 ) { fprintf( stderr, "[ %u ] ut_line %s <-> %s\n", wtmp_read_counter, utmp_entry.ut_line, utmpx_entry.ut_line ); break; } if ( utmp_entry.ut_pid != utmpx_entry.ut_pid ) { fprintf( stderr, "[ %u ] ut_pid %d <-> %d\n", wtmp_read_counter, utmp_entry.ut_pid, utmpx_entry.ut_pid ); break; } if ( utmp_entry.ut_type != utmpx_entry.ut_type ) { fprintf( stderr, "[ %u ] ut_type %d <-> %d\n", wtmp_read_counter, utmp_entry.ut_type, utmpx_entry.ut_type ); break; } if ( utmp_entry.ut_time != utmpx_entry.ut_tv.tv_sec ) { fprintf( stderr, "[ %u ] ut_time X <-> X\n", wtmp_read_counter, utmp_entry.ut_time, utmpx_entry.ut_tv.tv_sec ); break; } } /* end of while */ if ( wtmpx_read_counter != wtmp_read_counter ) { fprintf( stderr, "wtmpx or wtmp entry may be deleted\n" ); } close( fd_wtmpx ); close( fd_wtmp ); return( EXIT_SUCCESS ); } /* end of main */ #endif
9. strings.c
#include <stdio.h> #include <strings.h> #include <sys/types.h> #include <sys/stat.h> #include <ctype.h> #include <stdlib.h> #ifdef __FreeBSD__ #include <string.h> #endif #define MAXFILESIZE (4 * 1024 * 1024) /* * Many options here. The current choice produces a little more output * than gnu strings */ /* * Try to get the filesize via stat, and get a buffer to match * Naievely allocate a 4MB buffer if we can't * Fails badly if it allocate a buffer big enough to load a file */ unsigned char *filebuf(FILE *pf, int *sz) { struct stat buf; unsigned char *cdata; if (fstat(fileno(pf), &buf) < 0) { perror("fstat"); exit (1); } *sz = buf.st_size; if(*sz == 0) *sz = MAXFILESIZE; if ((cdata = malloc(*sz+1)) == NULL) { perror("malloc"); exit (1); } return cdata; } /* * Find printable strings of 4 or more characters * Always scans entire file (-a option of gnu strings) */ void strings(FILE *pf) { static char printme[1024]; int sz; unsigned char *cdata; int nread; int printmeindex; cdata = filebuf(pf,&sz); nread = fread(cdata, 1, sz, pf); printmeindex = 0; if (nread > 0) { int i; unsigned char c; int isprintable; int iseol; for (i = 0; i < nread; i++) { c = cdata[i]; isprintable = isprint(c); iseol = 0; if (c == 0 || c == '\n' || printmeindex >= sizeof(printme)-1) iseol = 1; if (iseol || !isprintable) { if (printmeindex > 3 && iseol) { printme[printmeindex++] = 0; printf("%s\n", printme); printmeindex = 0; } } else if (isprintable) { printme[printmeindex++] = c; } } } if (printmeindex > 3) { printme[printmeindex++] = 0; printf("%s\n", printme); printmeindex = 0; } free(cdata); } /* * Silently accepts the -a option */ int main(int argc, char **argv) { if (argc > 1) { int i; for (i = 1; i < argc; i++) { FILE *pf; if (strcmp(argv[i], "-a") == 0) continue; if ((pf = fopen(argv[i],"rb")) == NULL) { perror("fopen"); return(1); } strings(pf); } } else { strings(stdin); } return(0); }
10. chkrootkit的使用场景及其局限
Relevant Link:
http://xteam.baidu.com/?p=237&qq-pf-to=pcqq.group
Copyright (c) 2014 LittleHann All rights reserved
转载于:https://www.cnblogs.com/LittleHann/p/3918030.html
相关资源:垃圾分类数据集及代码