1,系统调用日志收集系统的意义。
系统调用是用户获取系统服务的唯一入口,因此对系统调用的安全调用直接关系到系统
的安全,假如有用户恶意不断地调用系统调用,将会导致系统负载增加,所以如果能收集到时谁
调用了一些危险的系统调用,以及调用系统调用的时间和其他信息,将有助于系统管理员进行事
后追踪,从而提高系统的安全性。
--------------------------------------------------------------
2,系统调用日志收集系统总体框架图。
-----------------------------------------------------------
3,系统调用日志收集系统的实现。(以linux-2.6..39.4为例)
3.1,添加系统调用号.
位置:arch/x86/include/asm/unistd_32.h
#define __NR_syscall_audit 345
#define __NR_myaudit 346
#ifdef __KERNEL__
#define NR_syscalls
347
----------------------------------------------------------
3.2,在系统调用表中添加相应的表项。
.long sys_syscall_audit
.long sys_myaudit
-----------------------------------------------------------
3.3,修改系统调用入口。(有两个入口,syscall_call和sysenter)
位置:arch/x86/kernel/entry_32.S
主要是在系统调用的两个入口进行监控,发现调用了如下四个系统调用(getpid,sysinfo,fork,execv)
将会执行 sys_syscall_audit函数。
--------------------------------------------------------------------------------------------
3.4,添加系统调用的服务例程。
在arch/x86/kernel/下面添加文件myaudit.c
该文件中实现了系统调用的服务例程,但这儿实现的服务例程都只有一个接口,
真正的实现将在模块中实现,这样是为了方便调试,避免了多次编译内核。这儿主要
是通过两个全局变量( my_audit 和my_sysaudit )与内核模块之间建立了联系。
----------------------------------------------------------------------------------------------
3.5,修改makefile。位置:arch/x86/kernel/Makefile
----------------------------------------------------------------------------------------------
3.6,导出全局变量。(不导出的话,内核模块将对其不可见)
修改arch/x86/kernel/i386_ksyms_32.c文件,在其末尾追加:
----------------------------------------------------------------------------------------------
3.7,声明全局变量。
修改:include/linux/module.h 添加:
------------------------------------------------------------------------------------------------------
3.10,启动用户测试程序。
--------------------------------------------------------------------------------------------------------
3.11,其中用户触发程序。(向内核不断地申请系统调用)
----------------------------------------------------------------------------------------------------------------
感想:
经过几天的时间调试这个程序,最终还是调试通过了,收获还是蛮大的,这个程序中
既要内核编程,还有编写内核模块,还要编写用户态程序。内核态和用户态协调合作。当然
在其中也出现了不少问题,开始我们就没有考虑到sysenter,结果导致使用库函数,对内核
就没有触发,只能使用int 0x80,直接在用户态使用汇编或嵌入汇编才能对其触发。后来把
sysenter这条路“堵死”就好了。
------------------------------------------------------------------------------------------------------------------
主要是在系统调用的两个入口进行监控,发现调用了如下四个系统调用(getpid,sysinfo,fork,execv)
将会执行 sys_syscall_audit函数。
- syscall_call:
- call *sys_call_table(,%eax,4)
- movl %eax,PT_EAX(%esp) # store the return value
- #如下位添加代码
- #----------------------------------------------------
- cmpl $20,0x2c(%esp) #getpid()
- je myauditsys
- cmpl $116,0x2c(%esp) #sysinfo()
- je myauditsys
- cmpl $2,0x2c(%esp) #fork
- je myauditsys
- cmpl $11,0x2c(%esp) #execv
- je myauditsys
- #-----------------------------------------------
- syscall_exit:
- LOCKDEP_SYS_EXIT
- ...
- jne syscall_exit_work
- #如下位添加代码
- #------------------------------------------------
- jmp restore_all
- myauditsys:
- pushl %eax
- pushl 0x30(%esp)
- call sys_syscall_audit
- popl %eax
- popl %eax
- jmp syscall_exit
- #------------------------------------------------
- restore_all:
- TRACE_IRQS_IRET
- sysenter_do_call:
- ...
- movl %eax,PT_EAX(%esp)
- #如下为添加代码
- /*--------------------------------------------------------*/
- cmpl $20,0x2c(%esp) #getpid()
- je enter_audit
- cmpl $116,0x2c(%esp) #sysinfo()
- je enter_audit
- cmpl $2,0x2c(%esp) #fork
- je enter_audit
- cmpl $11,0x2c(%esp) #execv
- je enter_audit
- come_back:
- /*--------------------------------------------------------*/
- sysenter_exit:
- ....
- ENABLE_INTERRUPTS_SY***IT
- /*--------------------------------------------------------*/
- jmp sysenter_audit
- enter_audit:
- pushl %eax
- pushl 0x30(%esp)
- call sys_syscall_audit
- popl %eax
- popl %eax
- jmp come_back
- /*--------------------------------------------------------*/
3.4,添加系统调用的服务例程。
在arch/x86/kernel/下面添加文件myaudit.c
该文件中实现了系统调用的服务例程,但这儿实现的服务例程都只有一个接口,
真正的实现将在模块中实现,这样是为了方便调试,避免了多次编译内核。这儿主要
是通过两个全局变量( my_audit 和my_sysaudit )与内核模块之间建立了联系。
- /**
- * myaudit.c
- */
-
- #include <linux/proc_fs.h>
- #include <linux/init.h>
- #include <linux/types.h>
- #include <linux/sched.h>
- #include <asm/current.h>
-
- void (*my_audit)(int, int) = 0;
-
- asmlinkage void sys_syscall_audit(int syscall_number, int return_value)
- {
- if (my_audit) {
- return (*my_audit)(syscall_number, return_value);
- printk("IN KERNEL: %s(%d), syscall: %d, return: %d\n", current->comm, (int)current->pid, syscall_number, return_value);
- //printk("IN KERNEL: %s\n", current);
- } else {
- printk("my_audit is empty!\n");
- }
- return ;
- }
-
- int (*my_sysaudit)(u8, u8*, u16, u8) = 0;
-
- asmlinkage int sys_myaudit(u8 type, u8 *us_buf, u16 us_buf_size, u8 reset)
- {
- if (my_sysaudit) {
- return (*my_sysaudit)(type, us_buf, us_buf_size, reset);
- printk("IN KERNEL: my system call sys_myaudit() working...\n");
- } else {
- printk("my_sysaudit is empty!\n");
- }
- return 0;
- }
3.5,修改makefile。位置:arch/x86/kernel/Makefile
- obj-y := process_$(BITS).o signal.o entry_$(BITS).o myaudit.o
3.6,导出全局变量。(不导出的话,内核模块将对其不可见)
修改arch/x86/kernel/i386_ksyms_32.c文件,在其末尾追加:
- EXPORT_SYMBOL(my_audit);
- EXPORT_SYMBOL(my_sysaudit);
3.7,声明全局变量。
修改:include/linux/module.h 添加:
- extern void (*my_audit)(int,int);
- extern int (*my_sysaudit)(u8,u8*,u16,u8);
接系统调用日志收集系统 (1)
---------------------------------------------------------------
3.8重新编译内核。
如何编译内核就不再说了。
----------------------------------------------------------------
3.9插入模块。
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/types.h>
- #include <asm/current.h>
- #include <linux/sched.h>
- #include <asm/uaccess.h>
-
- #define COMM_SIZE 16
-
- struct syscall_buf {
- u32 serial;
- u32 ts_sec;
- u32 syscall;
- u32 status;
- pid_t pid;
- uid_t uid;
- u8 comm[COMM_SIZE];
- };
-
- //初始化一个队列buffer_wait
- DECLARE_WAIT_QUEUE_HEAD(buffer_wait);
-
- #define AUDIT_BUF_SIZE 100
- //长度为100的缓冲区。
- static struct syscall_buf audit_buf[AUDIT_BUF_SIZE];
- static int current_pos = 0;//缓冲区中的位置.
- static u32 serial = 0;//序列号
-
- void write_buf_audit(int syscall,int return_value)
- {
- struct syscall_buf *ppb_tmp;
- printk("write_buf_audit is execing!\n");
- if (current_pos < AUDIT_BUF_SIZE) {
- ppb_tmp = &audit_buf[current_pos];
- ppb_tmp->serial = serial++;
- ppb_tmp->ts_sec = 1;
- ppb_tmp->syscall = syscall;
- ppb_tmp->status = return_value;
- ppb_tmp->pid = current->pid;
- ppb_tmp->uid = current->tgid;
- memcpy(ppb_tmp->comm,current->comm,COMM_SIZE);
-
- if (++current_pos == AUDIT_BUF_SIZE *1/10) {
- printk("in syscall_audit,it near full!\n");
- wake_up_interruptible(&buffer_wait);
- }
- }
- return ;
- }
-
- int read_buf_audit(u8 type,u8 *us_buf,u16 us_buf_size,u8 reset)
- {
- int ret = 0;
- printk("read_buf_audit is execving!\n");
- if (!type) {
- if (clear_user((void *)us_buf, (unsigned long)us_buf_size)) {
- printk("error:clear_user!\n");
- return 0;
- }
- ret= wait_event_interruptible(buffer_wait,current_pos >= AUDIT_BUF_SIZE*1/10);
- if (copy_to_user((void *)us_buf,audit_buf,(current_pos)*sizeof(struct syscall_buf))) {
- printk("error:copy error!\n");
- return 0;
- }
- ret = current_pos;
- current_pos = 0;
- }
- return ret;
- }
-
- static int __init audit_init(void)
- {
- my_audit = write_buf_audit;
- my_sysaudit = read_buf_audit;
- printk("starting syscall audit!\n");
- return 0;
- }
-
- static void __exit audit_exit(void)
- {
- my_audit = NULL;
- my_sysaudit = NULL;
- printk("exiting syscall audit!\n");
- return ;
- }
-
- module_init(audit_init);
- module_exit(audit_exit);
- MODULE_LICENSE("GPL");
3.10,启动用户测试程序。
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <signal.h>
- #include <unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
-
- typedef unsigned char u8;
- typedef unsigned int u32;
-
- #define COMM_SIZE 16
-
- struct syscall_buf {
- u32 serial;
- u32 ts_sec;
- u32 syscall;
- u32 status;
- pid_t pid;
- uid_t uid;
- u8 comm[COMM_SIZE];
- };
-
- #define AUDIT_BUF_SIZE 100*sizeof(struct syscall_buf)
-
- int main(int argc, char *argv[])
- {
- u8 col_buf[AUDIT_BUF_SIZE];
- unsigned char reset = 1;
- int num = 0;
- struct syscall_buf *p = NULL;
- u8 j = 0;
- int i;
-
- while (1) {
- num = syscall(346, 0, col_buf, AUDIT_BUF_SIZE, reset);
- printf("num is: %d\n", num);
- p = (struct syscall_buf *)col_buf;
- for (i = 0; i < num; i++) {
- printf("serial: %d ", p[i].serial);
- printf("syscall: %d ", p[i].syscall);
- printf("ts_sec: %d ", ((struct syscall_buf *)col_buf)[i].ts_sec);
- printf("status: %d ", p[i].status);
- printf("pid: %d ", ((struct syscall_buf *)col_buf)[i].pid);
- printf("uid: %d ", ((struct syscall_buf *)col_buf)[i].uid);
- printf("comm: %s\n", ((struct syscall_buf *)col_buf)[i].comm);
- }
-
- putchar('\n');
- }
-
- return 0;
- }
3.11,其中用户触发程序。(向内核不断地申请系统调用)
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/sysinfo.h>
-
- int main(void)
- {
- struct sysinfo info;
- unsigned long value = 0;
- int i = 0;
-
- while (1) {
- sysinfo(&info);
- printf("sysinfo is execving!\n");
- value = (unsigned long)getpid();
- printf("pid = %lu\n",value);
- sleep(1);
- }
- return 0;
- }
感想:
经过几天的时间调试这个程序,最终还是调试通过了,收获还是蛮大的,这个程序中
既要内核编程,还有编写内核模块,还要编写用户态程序。内核态和用户态协调合作。当然
在其中也出现了不少问题,开始我们就没有考虑到sysenter,结果导致使用库函数,对内核
就没有触发,只能使用int 0x80,直接在用户态使用汇编或嵌入汇编才能对其触发。后来把
sysenter这条路“堵死”就好了。
------------------------------------------------------------------------------------------------------------------