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/hello.c | 162 ++++++++++ connector/proc_collector_linux.cpp | 618 +++++++++++++++++++++++++++++++++++++ connector/proc_mon_linux.cpp | 487 +++++++++++++++++++++++++++++ connector/test.c | 64 ++++ src/deal.go | 57 +++- src/global.go | 48 ++- 6 files changed, 1417 insertions(+), 19 deletions(-) create mode 100644 connector/hello.c create mode 100755 connector/proc_collector_linux.cpp create mode 100755 connector/proc_mon_linux.cpp create mode 100644 connector/test.c diff --git a/connector/hello.c b/connector/hello.c new file mode 100644 index 0000000..5240c15 --- /dev/null +++ b/connector/hello.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include +#include +#include +#include "cn_proc.h" + +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; + +event_msg_t proc_msg; + +void Now() +{ + struct timespec ts; + struct tm *tm_info; + char buffer[64]; + + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) + { + perror("clock_gettime"); + return; + } + + tm_info = localtime(&ts.tv_sec); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info); + printf("Localtime %s.%03ld ", buffer, ts.tv_nsec / 1000000); +} + +void printEvent() +{ + union unnamed *procEvent = &proc_msg.proc_ev.event_data; + switch (proc_msg.proc_ev.what) + { + case PROC_EVENT_FORK: + Now(); + printf("Fork\t%6d\t%6d\t%6d\t%6d\n", procEvent->fork.parent_pid, procEvent->fork.parent_tgid, procEvent->fork.child_pid, procEvent->fork.child_tgid); + break; + case PROC_EVENT_EXIT: + Now(); + printf("Exit\t%6d\t%6d\t%6d\t%6d\n", procEvent->exit.process_pid, procEvent->exit.process_tgid, procEvent->exit.exit_code, procEvent->exit.exit_signal); + break; + case PROC_EVENT_EXEC: + default: + break; + } +} + +int main() +{ + int s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + + register_msg_t nlcn_msg; + struct sockaddr_nl l_local; + l_local.nl_family = AF_NETLINK; + l_local.nl_groups = 12345; + l_local.nl_pid = 0; + + if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) + { + perror(bind); + close(s); + return -1; + } + + // int on = l_local.nl_groups; + // setsockopt(s,270,1,&on,sizeof(on)); + 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; + + if (send(s, &nlcn_msg, sizeof(nlcn_msg), 0) == -1) + { + perror("can't register to netlink"); + close(s); + return -1; + } + + // 震惊,拿到socket了,开听! + printf("Hello, kernel-connector!\n"); + // fd_set readfds; + // struct timeval tv = { + // .tv_sec = 5, + // .tv_usec = 0}; + struct pollfd fds; + + fds.fd = s; + fds.events = POLLIN; + int rc; + + while (1) + { + // FD_ZERO(&readfds); + // FD_SET(s, &readfds); + + // int rc = select(s + 1, &readfds, NULL, NULL, &tv); + rc = poll(&fds, 1, 5000); + + if (rc == -1) + { + if (errno == EINTR) + { + continue; + } + fprintf(stderr, "Failed to listen to netlink socket: %s\n", strerror(errno)); + return -1; + } + else if (rc == 0) + { + printf("No message in 5s...\n"); + } + else + { + rc = recv(s, &proc_msg, sizeof(proc_msg), 0); + if (rc == -1) + { + if (errno == EINTR) + { + continue; + } + fprintf(stderr, "Failed to listen to netlink socket: %s\n", strerror(errno)); + } + else + { + printEvent(); + } + } + } +} \ No newline at end of file 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 diff --git a/connector/proc_mon_linux.cpp b/connector/proc_mon_linux.cpp new file mode 100755 index 0000000..16dd51c --- /dev/null +++ b/connector/proc_mon_linux.cpp @@ -0,0 +1,487 @@ +#ifndef WIN32 +#include +#include +#include +#include "shared.h" +#include "proc_mon_linux.h" +#include +#include "linux_proc_baseline.h" + + +#define MAX_PIPE_WAIT_USECOND 100 +#define PROCESS_SCAN_ITERTIME 10 +const char *WM_PROC_LOCATION = "proc_collector"; + +ProcMon* ProcMon::m_instance = NULL; + +ProcMon& ProcMon::instance() +{ + if ( m_instance == NULL ) + { + m_instance = new ProcMon; + } + + return *m_instance; +} + +void ProcMon::destroy() +{ + delete m_instance; + m_instance = NULL; +} + +int ProcMon::init( const wm_proc_t* config ) +{ + pthread_mutex_init(&m_mutex, NULL); + setConfig( config ); + + int ret = setupNetlink(); + if (ret != 0 ) + { + fini(); + return -1; + } + + ret = setupMQ(); + if (ret != 0 ) + { + fini(); + return -1; + } + + return 0; +} + +int ProcMon::working() +{ + int rc; + event_msg_t procMsg; + fd_set readfds; + int maxfd = m_procFd + 1; + struct timeval tv; + time_t last = time(0); + + if ( m_config.flags.enabled == 0 ) + { + return 0; + } + + getInventoryProcesses(); + + + while ( 1 ) + { + FD_ZERO(&readfds); + FD_SET( m_procFd, &readfds ); + tv.tv_sec = 1; + tv.tv_usec = 0; + + if ( time(0) - last > PROCESS_SCAN_ITERTIME ) + { + // scan processes + scanProcesses(last); + last = time(0); + } + + rc = select( maxfd, &readfds, NULL, NULL, &tv ); + if ( 0 == rc ) + { + 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( m_procFd, &readfds ) ) + { + rc = recv( m_procFd, &procMsg, sizeof(procMsg), 0 ); + if( rc > 0 ) + { + switch (procMsg.proc_ev.what) + { + case proc_event::PROC_EVENT_FORK: + handleForkMsg(procMsg); + break; + case proc_event::PROC_EVENT_EXEC: + handleExecMsg(procMsg); + break; + case proc_event::PROC_EVENT_COMM: + break; + case proc_event::PROC_EVENT_EXIT: + handleExitMsg(procMsg); + 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); + } + } + + malloc_trim(0); + } +} + +ProcMon::ProcMon() +{ + m_mutex = PTHREAD_MUTEX_INITIALIZER; + m_processes.clear(); + m_pidCache.clear(); + m_procFd = -1; + m_queueFd = -1; +} + +ProcMon::~ProcMon() +{ + fini(); +} + +void ProcMon::setConfig( const wm_proc_t* config ) +{ + m_config.flags.enabled = config->flags.enabled; + m_config.flags.rootcheck = config->flags.rootcheck; + m_config.flags.limitcheck = config->flags.limitcheck; + m_config.flags.environcheck = config->flags.environcheck; + m_config.flags.statuscheck = config->flags.statuscheck; + m_config.flags.threadcheck = config->flags.threadcheck; + m_config.flags.cgroupcheck = config->flags.cgroupcheck; + m_config.flags.libcheck = config->flags.libcheck; + m_config.flags.malwarecheck = config->flags.malwarecheck; + m_config.interval = config->interval; +} + +int ProcMon::setupNetlink() +{ + 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; + } + + m_procFd = nl_sock; + return 0; +} + +int ProcMon::setupMQ() +{ + unsigned int indx; + // Connect to socket + for (indx = 0; indx < WM_MAX_ATTEMPTS; indx++) + { + m_queueFd = StartMQ(DEFAULTQPATH, WRITE); + if (m_queueFd > 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); + } + + return 0; +} + + +void ProcMon::fini() +{ + m_processes.clear(); + m_pidCache.clear(); + if (m_procFd != -1 ) + { + close( m_procFd); + m_procFd = -1; + } + + if (m_queueFd != -1 ) + { + close( m_queueFd); + m_queueFd = -1; + } +} + +void ProcMon::getInventoryProcesses() +{ + DIR *procDir = opendir( "/proc" ); + if ( NULL == procDir ) + { + mterror(WM_PROC_LOGTAG, "failed to open /proc, errno=(%d:%m)", errno); + return; + } + + struct dirent *entry = NULL; + while (NULL != (entry = readdir(procDir))) + { + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + { + continue; + } + int pid = atoi(entry->d_name); + if (0 == pid) + { + continue; + } + + Process *proc = new Process(pid); + if ( NULL == proc ) + { + continue; + } + if(0 != proc->init()) + { + delete proc; + proc = NULL; + continue; + } + std::pair::iterator,bool> ret = m_processes.insert(std::make_pair(pid, proc)); + if(!ret.second) + { + delete proc; + proc = NULL; + continue; + } + std::vector events = proc->getEvents(); + if ( !events.empty() ) + { + reportEvents(events); + } + usleep( 500 ); + } + + return; +} + +void ProcMon::handleForkMsg( const event_msg_t& procMsg ) +{ + pid_t pid = procMsg.proc_ev.event_data.fork.child_pid; + auto iter = m_pidCache.find( pid ); + if ( iter != m_pidCache.end() ) + { + m_pidCache.erase(iter); + return; + } + + PidRelation relation; + relation.pid = pid; + relation.ppid = procMsg.proc_ev.event_data.fork.parent_pid; + relation.pidEvent = PidRelation::FORKEVENT; + relation.born = time(0); + + m_pidCache.insert( std::make_pair(relation.pid, relation )); +} + +void ProcMon::handleExecMsg( const event_msg_t& procMsg ) +{ + pid_t pid = procMsg.proc_ev.event_data.exec.process_pid; + auto iter = m_pidCache.find( pid ); + if ( iter == m_pidCache.end() ) + { + return; + } + + //mterror(WM_PROC_LOGTAG, "cache size:%lu", m_pidCache.size()); + Process *proc = new Process( pid, iter->second.ppid, iter->second.born ); + if ( NULL == proc ) + { + return; + } + // check hidden process + if ( 0 != proc->init() ) + { + return; + } + m_processes.insert( std::make_pair(pid, proc )); + std::vector events = proc->getEvents(); + if (!events.empty()) + { + reportEvents(events); + } +// m_pidCache.erase(iter); + //mterror(WM_PROC_LOGTAG, "exec process:%d exe:%s", pid, proc.exe().c_str()); +} + +void ProcMon::handleExitMsg( const event_msg_t& procMsg ) +{ + pid_t pid = procMsg.proc_ev.event_data.exit.process_pid; + auto iter = m_pidCache.find( pid ); + if ( iter != m_pidCache.end() ) + { + m_pidCache.erase(iter); + } + + auto procIter = m_processes.find(pid); + if ( procIter == m_processes.end() ) + { + return; + } + + std::vector signals{ 3, 9, 10, 12, 15,16,17,30,31}; + + //if ( signals.end() == std::find( signals.begin(), signals.end(), procMsg.proc_ev.event_data.exit.exit_signal ) + //|| (procMsg.proc_ev.event_data.exit.exit_code != 0)) + { + // report abnormal events + cJSON *obj= cJSON_CreateObject(); + cJSON_AddStringToObject(obj, "type", "exit"); + cJSON_AddStringToObject(obj, "exe", procIter->second->exe().c_str()); + cJSON_AddNumberToObject(obj, "pid", procIter->first); + cJSON_AddNumberToObject(obj, "event_time", time(0)); + cJSON_AddNumberToObject(obj, "exit_signal", procMsg.proc_ev.event_data.exit.exit_signal); + cJSON_AddNumberToObject(obj, "exit_code", procMsg.proc_ev.event_data.exit.exit_code); + char *data = cJSON_PrintUnformatted( obj ); + std::string msg(data); + free(data); + reportEvent( msg ); + cJSON_Delete(obj); + //mterror(WM_PROC_LOGTAG, "%s", msg.c_str()); + } + + delete procIter->second; + m_processes.erase(procIter); + //mterror(WM_PROC_LOGTAG, "process collection:%d", m_processes.size()); +} + +void ProcMon::scanProcesses(time_t last) +{ + time_t now = time(0); +// mterror(WM_PROC_LOGTAG, "pid cache size: %d , process collection size : %d", m_pidCache.size(), m_processes.size()); + for (auto iter = m_pidCache.begin(); iter != m_pidCache.end(); ) + { + if ( now - iter->second.born < 1 || m_processes.find(iter->second.pid) != m_processes.end()) + { + iter++; + } + else + { + Process *proc = new Process( iter->second.pid, iter->second.ppid, iter->second.born); + if (NULL != proc ) + { + if(0 != proc->init()) + { + delete proc; + proc = NULL; + continue; + } + std::pair::iterator, bool> ret = m_processes.insert(std::make_pair(iter->first, proc)); + if(!ret.second) + { + delete proc; + proc = NULL; + continue; + } + std::vector events = proc->getEvents(); + if (!events.empty()) + { + reportEvents(events); + } + + } + else + { + // hidden process found + cJSON *obj = cJSON_CreateObject(); + cJSON_AddStringToObject(obj, "type", "rootkit"); + cJSON_AddNumberToObject(obj, "pid", iter->first); + cJSON_AddNumberToObject(obj, "event_time", iter->second.born); + cJSON_AddNumberToObject(obj, "ppid", iter->second.ppid); + auto parent = m_processes.find(iter->second.ppid); + if (parent != m_processes.end()) + { + cJSON_AddStringToObject(obj, "parent exe", parent->second->exe().c_str()); + } + char *data = cJSON_PrintUnformatted(obj); + std::string msg(data); + free(data); + reportEvent(msg); + cJSON_Delete(obj); + } + iter = m_pidCache.erase(iter); + } + usleep( 500 ); + } +/* + if ( now - last < 60 ) + { + return; + } +*/ + for (auto& procIter : m_processes ) + { + procIter.second->scan(); + std::vector events = procIter.second->getEvents(); + if (!events.empty()) + { + reportEvents(events); + } + usleep( 5000 ); + } +} + +void ProcMon::reportEvent( const std::string& event ) +{ + wm_sendmsg(MAX_PIPE_WAIT_USECOND, m_queueFd, event.c_str(), WM_PROC_LOCATION, PROC_COLLECTOR_MQ); +} + +void ProcMon::reportEvents(const std::vector &events) +{ + for (auto it : events) + { + wm_sendmsg(MAX_PIPE_WAIT_USECOND, m_queueFd, it.c_str(), WM_PROC_LOCATION, PROC_COLLECTOR_MQ); + } +} +#endif diff --git a/connector/test.c b/connector/test.c new file mode 100644 index 0000000..c12ad10 --- /dev/null +++ b/connector/test.c @@ -0,0 +1,64 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#define STACK_SIZE (1024 * 1024) // 定义栈大小 + +// 子任务的入口函数 +void *child_func(void *arg) +{ + printf("子任务: PID=%ld, PPID=%ld, TID=%ld\n", (long)getpid(), + (long)getppid(), (long)syscall(SYS_gettid)); + sleep(3); +} + +int main() +{ + char *stack; // 栈的指针 + char *stack_top; // 栈顶的指针 + pid_t child_tid; + pid_t forkPid; + pid_t threadPid; + + printf("主任务:我是%d\n", getpid()); + if ((forkPid = fork()) == 0) + { + sleep(3); + exit(0); + } + printf("主任务:fork子任务 %d\n", forkPid); + + pthread_create(&threadPid, NULL, child_func, NULL); + printf("主任务:create新线程%d\n", threadPid); + + // 使用 clone 创建子任务 + child_tid = clone(child_func, stack_top, SIGCHLD, NULL); + if (child_tid == -1) + { + perror("clone"); + free(stack); + exit(EXIT_FAILURE); + } + + printf("主任务: 创建了子任务, TID=%ld\n", (long)child_tid); + + // 等待子任务结束 + if (waitpid(child_tid, NULL, 0) == -1) + { + perror("waitpid"); + free(stack); + exit(EXIT_FAILURE); + } + + // 释放分配的栈 + // free(stack); + + printf("主任务: 子任务结束\n"); + return 0; +} diff --git a/src/deal.go b/src/deal.go index aaac8c5..717344c 100644 --- a/src/deal.go +++ b/src/deal.go @@ -55,18 +55,25 @@ func deal() { break } + // fmt.Printf("%v\n", cooked) + switch cooked.tag { case NEWPID: dealNewPid(cooked) case EXECVE: + check(cooked) dealExecve(cooked) case PIDEXIT: + check(cooked) deletePid(cooked) case FILEOPEN: + check(cooked) fileOpen(cooked) case FILEWRITE: + check(cooked) fileWrite(cooked) case FILECLOSE: + check(cooked) fileClose(cooked) } } @@ -168,19 +175,19 @@ func dealExecve(cooked Event) { }, }, }) - } else { - // 先fork抵达,插入 - pidCol.InsertOne(bson.M{ - "ppid": cooked.ppid, - "pid": cooked.pid, - "children": []bson.M{}, - "execve": []bson.M{ - { - "timestamp": cooked.timestamp, - "execArgs": cooked.argv, - }, - }, - }) + // } else { + // // 先fork抵达,插入 + // pidCol.InsertOne(bson.M{ + // "ppid": cooked.ppid, + // "pid": cooked.pid, + // "children": []bson.M{}, + // "execve": []bson.M{ + // { + // "timestamp": cooked.timestamp, + // "execArgs": cooked.argv, + // }, + // }, + // }) } mongoMutex.Unlock() } @@ -255,3 +262,27 @@ func fileWrite(cooked Event) { "close_timestamp": bson.M{"$exists": false}, }, bson.M{"$push": bson.M{"written": cooked.timestamp}}) } + +func check(cooked Event) { + // 检查进程是否需要记录 + // 有无父进程在观察中 + docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.ppid}) + if err != nil || len(docRes) != 1 { + return + } + + // 自身是否已经记录 + docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.pid}) + if err != nil { + fmt.Printf("Err finding: %v\n", err) + return + } + if len(docRes) == 0 { + pidCol.InsertOne(bson.M{ + "ppid": cooked.ppid, + "pid": cooked.pid, + "children": []bson.M{}, + "start_timestamp": cooked.timestamp, + }) + } +} diff --git a/src/global.go b/src/global.go index d1c5c0f..f0f909c 100644 --- a/src/global.go +++ b/src/global.go @@ -1,8 +1,11 @@ package main import ( + "fmt" "sync" "time" + + "go.mongodb.org/mongo-driver/bson/primitive" ) type eventType int @@ -12,11 +15,19 @@ const ( PIDEXIT EXECVE FILEOPEN - FILEWRITE FILECLOSE + FILEWRITE TYPENUM ) +func (et eventType) String() string { + names := []string{"NEWPID", "PIDEXIT", "EXECVE", "FILEOPEN", "FILECLOSE", "FILEWRITE", "TYPENUM"} + if et < NEWPID || et > TYPENUM { + return "Unknown" + } + return names[et] +} + type Event struct { tag eventType timestamp time.Time @@ -30,12 +41,37 @@ type Event struct { pathName string } -func (et eventType) String() string { - names := []string{"NEWPID", "PIDEXIT", "EXECVE", "FILEOPEN", "FILEWRITE", "TYPENUM"} - if et < NEWPID || et > TYPENUM { - return "Unknown" +func (event Event) String() string { + var res string + res = fmt.Sprintf("tag: %v\ntimestamp: %v\nppid: %d\npid: %d\n", event.tag, event.timestamp.Local(), event.ppid, event.pid) + res += fmt.Sprintf("syscall: %s\nexit_code: %d\nargs: \n", syscallTable[event.syscall], event.exit_code) + for i := 0; i < len(event.argv); i++ { + res += fmt.Sprintf("\t\"%s\"\n", event.argv[i]) } - return names[et] + res += "syscallParam: " + for i := 0; i < len(event.syscallParam); i++ { + res += fmt.Sprintf("\t\"%d\"\n", event.syscallParam[i]) + } + res += "pathName: \"" + event.pathName + "\"\n------\n" + return res +} + +type pidExec struct { + timestamp time.Time `bson:"timestamp"` + execArgs []string `bson:"execArgs"` +} + +type pid struct { + ID primitive.ObjectID `bson:"_id,ometempty"` + start_timestamp time.Time `bson:"start_timestamp"` + ppid int `bson:"ppid"` + pid int `bson:"pid"` + cwd string `bson:"cwd"` + args []string `bson:"args"` + execve []pidExec `bson:"execve"` + children []int `bson:"children"` + exit_timestamp time.Time `bson:"exit_timestamp"` + exit_code uint64 `bson:"exit_code"` } var wg sync.WaitGroup // 掌管协程 -- cgit v1.2.3-70-g09d2