From d6c6e13796435f9e1e59fec891aa53680748a2d7 Mon Sep 17 00:00:00 2001 From: We-unite <3205135446@qq.com> Date: Tue, 30 Jul 2024 19:37:48 +0800 Subject: Try to use kernel connector --- connector/proc_collector_linux.cpp | 618 +++++++++++++++++++++++++++++++++++++ 1 file changed, 618 insertions(+) create mode 100755 connector/proc_collector_linux.cpp (limited to 'connector/proc_collector_linux.cpp') diff --git a/connector/proc_collector_linux.cpp b/connector/proc_collector_linux.cpp new file mode 100755 index 0000000..478ac2b --- /dev/null +++ b/connector/proc_collector_linux.cpp @@ -0,0 +1,618 @@ +#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 \ No newline at end of file -- cgit v1.2.3-70-g09d2