#ifndef WIN32 #include #include #include #include #include #include #include #include #include #include "os_crypto/md5/md5_op.h" #include "external/procps/readproc.h" #include "proc_collector.h" static struct { int queue_fd; int proc_fd; } proc_monitor = { .queue_fd = -1, .proc_fd = -1 }; typedef struct __attribute__((aligned(NLMSG_ALIGNTO))) { struct nlmsghdr nl_hdr; struct __attribute__((__packed__)) { struct cn_msg cn_msg; enum proc_cn_mcast_op cn_mcast; }; } register_msg_t; typedef struct __attribute__((aligned(NLMSG_ALIGNTO))) { struct nlmsghdr nl_hdr; struct __attribute__((__packed__)) { struct cn_msg cn_msg; struct proc_event proc_ev; }; } event_msg_t; #define MAX_TEMP_PROCESS 5000 #define TEMP_WAIT_TIME 1 #define PROCESS_SLOT_EMPTY 0 #define MAX_PATH_NAME 4096 #define MAX_PIPE_WAIT_USECOND 100 typedef struct { pid_t pid; pid_t ppid; time_t fork_time; int status; char path[MAX_PATH_NAME]; os_md5 md5; char parent_path[MAX_PATH_NAME]; os_md5 parent_md5; } process_parent_child_pair_t; static process_parent_child_pair_t temp_processes[ MAX_TEMP_PROCESS ]; /* typedef struct { pid_t pid; // process id char *name; // process name char *state; //program running state pid_t ppid; // parent process id char *pname; // parent process name ulong utime; // process time in user mode ulong stime; // process time in kernel mode char **cmd_line; // command line char *real_user; // char *effective_user; char *saved_user; char *fs_user; char *real_group; char *effective_group; char *saved_group; char *fs_group; int priority; // process priority int nice; // nice value long size; // the toal memory long vm_size; // virtual memory long resident; // occupied physical memory long share; // shared memory time_t start_time; // process startup time int pgrp; /// process group int session; // session id long nlwp; // number of thread int tgid; // task group id int tty; // control terminal int processor; // cpu number last executed on } proc_info_t; */ static void wm_proc_cleanup() { if ( proc_monitor.proc_fd > 0 ) { close( proc_monitor.proc_fd ); proc_monitor.proc_fd = -1; } if ( proc_monitor.queue_fd > 0 ) { close( proc_monitor.queue_fd ); proc_monitor.queue_fd = -1; } } static int wm_setup_netlink( ) { int rc; int nl_sock; struct sockaddr_nl sa_nl; register_msg_t nlcn_msg; nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); if (nl_sock == -1) { mterror( WM_PROC_LOGTAG, "Can't open netlink socket"); return -1; } sa_nl.nl_family = AF_NETLINK; sa_nl.nl_groups = CN_IDX_PROC; sa_nl.nl_pid = getpid(); // 向内核注册 rc = bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl)); if (rc == -1) { mterror( WM_PROC_LOGTAG, "Can't bind netlink socket"); close(nl_sock); return -1; } // create listener memset(&nlcn_msg, 0, sizeof(nlcn_msg)); nlcn_msg.nl_hdr.nlmsg_len = sizeof(nlcn_msg); nlcn_msg.nl_hdr.nlmsg_pid = getpid(); nlcn_msg.nl_hdr.nlmsg_type = NLMSG_DONE; nlcn_msg.cn_msg.id.idx = CN_IDX_PROC; nlcn_msg.cn_msg.id.val = CN_VAL_PROC; nlcn_msg.cn_msg.len = sizeof(enum proc_cn_mcast_op); nlcn_msg.cn_mcast = PROC_CN_MCAST_LISTEN ; rc = send(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0); if (rc == -1) { mterror(WM_PROC_LOGTAG, "can't register to netlink"); close( nl_sock ); return -1; } proc_monitor.proc_fd = nl_sock; return 0; } static int wm_setup_mq( wm_proc_t *proc ) { unsigned int indx; // Connect to socket for (indx = 0; indx < WM_MAX_ATTEMPTS; indx++) { proc_monitor.queue_fd = StartMQ(DEFAULTQPATH, WRITE); if ( proc_monitor.queue_fd > 0 ) { break; } wm_delay(1000 * WM_MAX_WAIT); } if (indx == WM_MAX_ATTEMPTS) { mterror(WM_PROC_LOGTAG, "Can't connect to queue."); pthread_exit(NULL); } // Cleanup exiting atexit(wm_proc_cleanup); return 0; } int wm_proc_linux_setup( wm_proc_t *proc ) { memset( temp_processes, 0, sizeof( temp_processes ) ); if ( wm_setup_netlink() < 0 ) { return -1; } return wm_setup_mq(proc); } static void get_path_and_md5( pid_t pid, char *path, char *md5 ) { char pid_path[MAX_PATH_NAME]; struct stat dir_stat; memset( pid_path, 0, MAX_PATH_NAME); snprintf( pid_path, MAX_PATH_NAME-1, "/proc/%d", pid ); if ( stat( pid_path, &dir_stat ) < 0 ) { //mterror(WM_PROC_LOGTAG, "failed to open directory %s, errno=(%d:%m)", pid_path, errno ); return; } memset( pid_path, 0, MAX_PATH_NAME); snprintf( pid_path, MAX_PATH_NAME-1, "/proc/%d/exe", pid ); memset( path, 0, MAX_PATH_NAME ); memset( md5, 0, sizeof(os_md5)); if ( readlink( pid_path, path, MAX_PATH_NAME ) < 0 ) { memset(pid_path, 0, MAX_PATH_NAME); snprintf(pid_path, MAX_PATH_NAME - 1, "/proc/%d/comm", pid); memset(path, 0, MAX_PATH_NAME); FILE* fp = fopen(pid_path, "rb"); if ( NULL == fp ) { return; } fread( path, MAX_PATH_NAME, 1, fp ); if ( strlen( path ) > 0 ) { path[strlen(path)-1] = '\0'; } fclose( fp ); return; } OS_MD5_File(path, md5, OS_BINARY); return; } static void get_process_baseinfo( const process_parent_child_pair_t *pair, const char *location ) { proc_t curr_proc; memset( &curr_proc, 0, sizeof( proc_t )); cJSON *process = cJSON_CreateObject(); if( NULL == process) { return; } cJSON_AddStringToObject(process, "type", "new process"); cJSON_AddNumberToObject(process, "pid", pair->pid); cJSON_AddNumberToObject(process, "eventTime", pair->fork_time); cJSON_AddNumberToObject(process, "ppid", pair->ppid); if (strlen(pair->path) > 0) { cJSON_AddStringToObject(process, "exe", pair->path); } if (strlen(pair->md5) > 0) { cJSON_AddStringToObject(process, "md5", pair->md5); } if (strlen(pair->parent_path) > 0) { cJSON_AddStringToObject(process, "parent_exe", pair->parent_path); } if (strlen(pair->parent_md5) > 0) { cJSON_AddStringToObject(process, "parent_md5", pair->parent_md5); } // get process information if (NULL != get_proc_stats(pair->pid, &curr_proc)) { cJSON_AddStringToObject(process, "state", &curr_proc.state); cJSON_AddNumberToObject(process, "utime", curr_proc.utime); cJSON_AddNumberToObject(process, "stime", curr_proc.stime); if (curr_proc.cmdline && curr_proc.cmdline[0]) { cJSON *argvs = cJSON_CreateArray(); cJSON_AddStringToObject(process, "cmd", curr_proc.cmdline[0]); for (int i = 1; curr_proc.cmdline[i]; i++) { if (!strlen(curr_proc.cmdline[i]) == 0) { cJSON_AddItemToArray(argvs, cJSON_CreateString(curr_proc.cmdline[i])); } } if (cJSON_GetArraySize(argvs) > 0) { cJSON_AddItemToObject(process, "argvs", argvs); } else { cJSON_Delete(argvs); } } cJSON_AddStringToObject(process, "euser", curr_proc.euser); cJSON_AddStringToObject(process, "egroup", curr_proc.egroup); cJSON_AddNumberToObject(process, "resident", curr_proc.resident); cJSON_AddNumberToObject(process, "nlwp", curr_proc.nlwp); cJSON_AddNumberToObject(process, "tty", curr_proc.tty); cJSON_AddNumberToObject(process, "processor", curr_proc.processor); /* cJSON_AddStringToObject(process, "ruser", curr_proc.ruser); cJSON_AddStringToObject(process, "suser", curr_proc.suser); cJSON_AddStringToObject(process, "rgroup", curr_proc.rgroup); cJSON_AddStringToObject(process, "sgroup", curr_proc.sgroup); cJSON_AddStringToObject(process, "fgroup", curr_proc.fgroup); cJSON_AddNumberToObject(process, "priority", curr_proc.priority); cJSON_AddNumberToObject(process, "nice", curr_proc.nice); cJSON_AddNumberToObject(process, "size", curr_proc.size); cJSON_AddNumberToObject(process, "vm_size", curr_proc.vm_size); cJSON_AddNumberToObject(process, "share", curr_proc.share); cJSON_AddNumberToObject(process, "pgrp", curr_proc.pgrp); cJSON_AddNumberToObject(process, "session", curr_proc.session); cJSON_AddNumberToObject(process, "tgid", curr_proc.tgid); */ } char *msg = cJSON_PrintUnformatted(process); wm_sendmsg( MAX_PIPE_WAIT_USECOND, proc_monitor.queue_fd, msg, location, PROC_COLLECTOR_MQ ); free(msg); cJSON_Delete( process ); return; } static void get_process_info( const process_parent_child_pair_t *pair, const char *location ) { // get process base info get_process_baseinfo( pair, location ); // get process library info // find rootkit in user mode // check process is a malware } static void handle_temp_process( const char *location ) { int i = 0; for ( i = 0; i < MAX_TEMP_PROCESS; i++ ) { if ( ( temp_processes[i].status != PROCESS_SLOT_EMPTY ) && ( temp_processes[i].ppid != 0 ) ) { mtdebug1(WM_PROC_LOGTAG, "handle process: %d, parent process:%d", temp_processes[i].pid, temp_processes[i].ppid ); get_process_info( &temp_processes[i], location ); temp_processes[i].status = PROCESS_SLOT_EMPTY; } } } static void handle_process_fork( const event_msg_t *proc_msg ) { int i = 0; for ( i = 0; i < MAX_TEMP_PROCESS; i++ ) { if ( temp_processes[i].status == PROCESS_SLOT_EMPTY ) { get_path_and_md5( proc_msg->proc_ev.event_data.fork.child_pid, temp_processes[i].path, temp_processes[i].md5 ); get_path_and_md5( proc_msg->proc_ev.event_data.fork.parent_pid, temp_processes[i].parent_path, temp_processes[i].parent_md5); temp_processes[i].pid = proc_msg->proc_ev.event_data.fork.child_pid; temp_processes[i].ppid = proc_msg->proc_ev.event_data.fork.parent_pid; temp_processes[i].fork_time = time(0); temp_processes[i].status = !PROCESS_SLOT_EMPTY; mtdebug1(WM_PROC_LOGTAG, "fork process: %d, parent process:%d", temp_processes[i].pid, temp_processes[i].ppid ); break; } } } static void handle_process_exec( const event_msg_t *proc_msg, const char *location ) { int i = 0; for ( i = 0; i < MAX_TEMP_PROCESS; i++ ) { if (temp_processes[i].pid == proc_msg->proc_ev.event_data.exec.process_pid && temp_processes[i].status != PROCESS_SLOT_EMPTY) { get_path_and_md5( proc_msg->proc_ev.event_data.exec.process_pid, temp_processes[i].path, temp_processes[i].md5 ); mtdebug1(WM_PROC_LOGTAG, "exec process: %d, parent process:%d", temp_processes[i].pid, temp_processes[i].ppid ); get_process_info( &temp_processes[i], location ); temp_processes[i].status = PROCESS_SLOT_EMPTY; break; } } } static void handle_process_exit( const event_msg_t *proc_msg, const char* location ) { int i = 0; for ( i = 0; i < MAX_TEMP_PROCESS; i++ ) { if ( ( temp_processes[i].status != PROCESS_SLOT_EMPTY ) && ( proc_msg->proc_ev.event_data.exit.process_pid == temp_processes[i].pid ) && ( temp_processes[i].ppid == 0 ) ) { temp_processes[i].status = PROCESS_SLOT_EMPTY; return; } } cJSON *process = cJSON_CreateObject(); cJSON_AddStringToObject(process, "type", "exit"); cJSON_AddNumberToObject( process, "pid", proc_msg->proc_ev.event_data.exit.process_pid ); cJSON_AddNumberToObject( process, "eventTime", time(0) ); cJSON_AddNumberToObject(process, "exitCode", proc_msg->proc_ev.event_data.exit.exit_code); cJSON_AddNumberToObject(process, "exitSignal", (int)proc_msg->proc_ev.event_data.exit.exit_signal); char *exit_msg = cJSON_PrintUnformatted( process ); wm_sendmsg( MAX_PIPE_WAIT_USECOND, proc_monitor.queue_fd, exit_msg, location, PROC_COLLECTOR_MQ ); free( exit_msg ); cJSON_Delete( process ); } static time_t get_process_start_time( pid_t pid ) { char path[MAX_PATH_NAME]; memset( path, 0, MAX_PATH_NAME); snprintf( path, MAX_PATH_NAME-1, "/proc/%d", pid ); struct stat dir_stat; if ( stat( path, &dir_stat) < 0 ) { return 0; } return dir_stat.st_mtime; } static void get_inventory_processes(const char *location) { DIR *proc_dir = opendir( "/proc" ); if ( NULL == proc_dir ) { mterror( WM_PROC_LOGTAG, "failed to open /proc, errno=(%d:%m)", errno ); return; } struct dirent *entry = NULL; while ( NULL != ( entry = readdir( proc_dir ))) { if ( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) { continue; } int pid = atoi( entry->d_name ); if ( 0 == pid ) { continue; } char pid_path[MAX_PATH_NAME]; os_md5 md5; memset( pid_path, 0, sizeof( MAX_PATH_NAME)); memset( md5, 0, sizeof( os_md5 ) ); get_path_and_md5( pid, pid_path, md5 ); cJSON *process = cJSON_CreateObject(); cJSON_AddStringToObject(process, "type", "new process"); cJSON_AddNumberToObject(process, "eventTime", time(0)); cJSON_AddNumberToObject(process, "pid", pid); cJSON_AddNumberToObject(process, "startTime", get_process_start_time(pid)); if (strlen(pid_path) > 0) { cJSON_AddStringToObject(process, "exe", pid_path); } if (strlen(md5) > 0) { cJSON_AddStringToObject(process, "md5", md5); } proc_t curr_proc; memset( &curr_proc, 0, sizeof( proc_t )); if ( NULL != get_proc_stats(pid, &curr_proc )) { cJSON_AddStringToObject(process, "state", &curr_proc.state); cJSON_AddNumberToObject(process, "utime", curr_proc.utime); cJSON_AddNumberToObject(process, "stime", curr_proc.stime); cJSON_AddStringToObject(process, "euser", curr_proc.euser); cJSON_AddStringToObject(process, "egroup", curr_proc.egroup); cJSON_AddNumberToObject(process, "resident", curr_proc.resident); cJSON_AddNumberToObject(process, "nlwp", curr_proc.nlwp); cJSON_AddNumberToObject(process, "tty", curr_proc.tty); cJSON_AddNumberToObject(process, "processor", curr_proc.processor); /* cJSON_AddStringToObject(process, "ruser", curr_proc.ruser); cJSON_AddStringToObject(process, "suser", curr_proc.suser); cJSON_AddStringToObject(process, "rgroup", curr_proc.rgroup); cJSON_AddStringToObject(process, "sgroup", curr_proc.sgroup); cJSON_AddStringToObject(process, "fgroup", curr_proc.fgroup); cJSON_AddNumberToObject(process, "priority", curr_proc.priority); cJSON_AddNumberToObject(process, "nice", curr_proc.nice); cJSON_AddNumberToObject(process, "size", curr_proc.size); cJSON_AddNumberToObject(process, "vm_size", curr_proc.vm_size); cJSON_AddNumberToObject(process, "share", curr_proc.share); cJSON_AddNumberToObject(process, "pgrp", curr_proc.pgrp); cJSON_AddNumberToObject(process, "session", curr_proc.session); cJSON_AddNumberToObject(process, "tgid", curr_proc.tgid); */ memset(pid_path, 0, sizeof(MAX_PATH_NAME)); memset(md5, 0, sizeof(os_md5)); get_path_and_md5(curr_proc.ppid, pid_path, md5); if (strlen(pid_path) > 0) { cJSON_AddStringToObject(process, "parent_exe", pid_path); } if (strlen(md5) > 0) { cJSON_AddStringToObject(process, "parent_md5", md5); } } char *msg = cJSON_PrintUnformatted(process); wm_sendmsg(MAX_PIPE_WAIT_USECOND, proc_monitor.queue_fd, msg, location, PROC_COLLECTOR_MQ); free(msg); cJSON_Delete(process); } closedir( proc_dir); } int wm_proc_linux_process(wm_proc_t *proc, const char *location) { int rc; event_msg_t proc_msg; fd_set readfds; int max_fd = proc_monitor.proc_fd + 1; struct timeval tv; if (proc->flags.enabled == 0) { return 0; merror("process collector disabled, let us rest."); } merror("process collector enabled, let us work."); get_inventory_processes(location); tv.tv_sec = 5; tv.tv_usec = 0; while (1) { FD_ZERO(&readfds); FD_SET(proc_monitor.proc_fd, &readfds); rc = select(max_fd, &readfds, NULL, NULL, &tv); if (0 == rc) { handle_temp_process(location); tv.tv_sec = 5; tv.tv_usec = 0; continue; } if (-1 == rc) { if (errno == EINTR) { continue; } mterror(WM_PROC_LOGTAG, "failed to listen to netlink socket, errno=(%d:%m)", errno); return rc; } if (FD_ISSET(proc_monitor.proc_fd, &readfds)) { rc = recv(proc_monitor.proc_fd, &proc_msg, sizeof(proc_msg), 0); if (rc > 0) { switch (proc_msg.proc_ev.what) { case proc_event::PROC_EVENT_FORK: handle_process_fork(&proc_msg); tv.tv_sec = 1; tv.tv_usec = 1000; break; case proc_event::PROC_EVENT_EXEC: handle_process_exec(&proc_msg, location); break; case proc_event::PROC_EVENT_COMM: break; case proc_event::PROC_EVENT_EXIT: handle_process_exit(&proc_msg, location); break; default: break; } } else if (rc == -1) { if (errno == EINTR) { continue; } mterror(WM_PROC_LOGTAG, "failed to received from netlink socket, errno=(%d:%m)", errno); } } } return 0; } #endif