网站搭建团队,陕西建设监理证书查询网站,旅行社建设网站,wordpress0day在上一篇文章《系统调用分析(2)》中介绍和分析了32位和64位的快速系统调用指令——sysenter/sysexit和syscall/sysret#xff0c;以及内核对快速系统调用部分的相关代码#xff0c;并追踪了一个用户态下的系统调用程序运行过程。本篇中将基于最新的Linux-5.0内核#xff0c;… 在上一篇文章《系统调用分析(2)》中介绍和分析了32位和64位的快速系统调用指令——sysenter/sysexit和syscall/sysret以及内核对快速系统调用部分的相关代码并追踪了一个用户态下的系统调用程序运行过程。 本篇中将基于最新的Linux-5.0内核添加一个系统调用完成一个“系统调用日志收集系统” 并对这三篇文章进行一个总结。 加前两篇文章在这里https://mp.weixin.qq.com/s/3Dvd2dy0l6OYFVGzfEvOcghttps://mp.weixin.qq.com/s/7uXVXXqzN8wMqgxqrN_5og加一个系统调用// 单纯添加一个系统调用会显得有些单调出于既是作业又是学习角度将系统调用、工作队列、修改内核、内核编译和内核模块编写插入等结合起来通过完成一个系统调用日志收集系统。1系统调用日志收集系统的目的 系统调用是用户程序与系统打交道的入口系统调用的安全直接关系到系统的安全如果一个用户恶意地不断调用fork()将导致系统负载增加所以如果能收集到是谁调用了一些有危险的系统调用以及系统调用的时间和其他信息将有助于系统管理员进行事后追踪从而提高系统的安全性。2系统调用日志收集系统概述图3-1 系统调用日志收集系统示意图 根据示意图系统调用日志收集系统所做工作为当用户进程执行系统调用运行到内核函数do_syscall_64时进行判断是否为我们需要记录的系统调用如果是我们需要记录的系统调用则通过my_audit这一函数将记录内容写入内核中的buffer里同时编写用户态测试程序调用335号系统调用(myaudit)这一系统调用调用my_sysaudit这一函数将内核buffer中数据copy到用户buffer中并显示日志内容其中我们调用的my_audit和my_sysaudit都是钩子函数具体实现使用内核模块完成并插入方便调试。3系统调用日志收集系统实现 (1)增加系统调用表的表项 打开arch/x86/entry/syscalls/syscall_64.tbl添加一个系统调用表项335 common myaudit __x64_sys_myaudit (2)添加系统调用函数 在arch/x86/kernel/目录下添加myaudit.c文件完成系统调用函数#include #include #include #include #include #include #include #include void (*my_audit) (int, int) 0;int (*my_sysaudit)(u8, u8 *, u16, u8) 0;SYSCALL_DEFINE4(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_sysadit is not exist\n); return 1;}EXPORT_SYMBOL(my_audit);EXPORT_SYMBOL(my_sysaudit); 这里可以看到实际上定义两个钩子函数在我们系统调用里去调用这两个钩子函数这样可以以模块的方式添加这两个函数的具体内容方便调试。 (3)修改Makefile 修改arch/x86/kernel/Makefile将myaduit.c文件加入内核编译obj-y myaudit.o (4)增加函数声明 在include/linux/syscalls.h最后的endif前添加函数声明 asmlink long sys_myaudit(u8, u8 *, u16, u8);extern void (*my_audit)(int, int); (5)拦截相关系统调用正如前面对syscall执行的分析修改do_syscall_64()函数(在/arch/x86/entry/common.c中)对系统调用号nr进行判断如果是我们日志收集系统需要记录的系统调用就调用我们的记录函数进行记录__visible void do_syscall_64(unsigned long nr, struct pt_regs *regs){ ... nr syscall_trace_enter(regs); nr __SYSCALL_MASK; if (likely(nr NR_syscalls)) { nr array_index_nospec(nr, NR_syscalls); regs-ax sys_call_table[nr](regs); if (nr 57 || nr 2 || nr 3 || nr 59 || nr 39 || nr 56) { if (my_audit) (*my_audit)(nr, regs-ax); else printk(my_audit is not exist.\n); } } syscall_return_slowpath(regs); } 可以看到要记录的系统调用有2open3close39getpid56clone57fork59execve。 (6)重新编译内核#提前把原来内核版本的.config拷贝到5.0内核源码根目录下cd linux-5.0的路径sudo cp /usr/src/内核版本/.config ./ #进入menuconfig后按照 load-OK-save-OK-exit-exit执行sudo make menuconfig sudo make olddefconfig#编译内核sudo make bzImage -j2sudo make modules#安装内核修改引导sudo make modules_install sudo make installsudo update-grub2#重启sudo reboot (7)添加实现钩子函数的内核模块 my_audit.c:#include #include #include #include #include #define COMM_SIZE 16#define AUDIT_BUF_SIZE 100MODULE_LICENSE(GPL v2);struct syscall_buf { u32 serial; u64 ts_sec; u64 ts_micro; u32 syscall; u32 status; pid_t pid; uid_t uid; u8 comm[COMM_SIZE]; };DECLARE_WAIT_QUEUE_HEAD(buffer_wait);static struct syscall_buf audit_buf[AUDIT_BUF_SIZE];static int current_pos 0;static u32 serial 0;void syscall_audit(int syscall, int return_status){ struct syscall_buf *ppb_temp; struct timespec64 nowtime; ktime_get_real_ts64(nowtime); if (current_pos AUDIT_BUF_SIZE) { ppb_temp audit_buf[current_pos]; ppb_temp-serial serial; ppb_temp-ts_sec nowtime.tv_sec; ppb_temp-ts_micro nowtime.tv_nsec; ppb_temp-syscall syscall; ppb_temp-status return_status; ppb_temp-pid current-pid; ppb_temp-uid current-tgid; memcpy(ppb_temp-comm, current-comm, COMM_SIZE); if (current_pos AUDIT_BUF_SIZE * 8 / 10) { printk(IN MODULE_audit: yes, it near full\n); wake_up_interruptible(buffer_wait); } }}int sys_audit(u8 type, u8 *us_buf, u16 us_buf_size, u8 reset){ int ret 0; if (!type) { if (clear_user(us_buf, us_buf_size)) { printk(Error:clear_user\n); return 0; } printk(IN MODULE_systemcall:starting...\n); ret wait_event_interruptible(buffer_wait, current_pos AUDIT_BUF_SIZE * 8 / 10); printk(IN MODULE_systemcall:over, current_pos is %d\n, current_pos); if(copy_to_user(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_sysaudit sys_audit; my_audit syscall_audit; printk(Starting System Call Auditing\n); return 0;}module_init(audit_init);static void __exit audit_exit(void){ my_audit NULL; my_sysaudit NULL; printk(Exiting System Call Auditing\n); return ;} module_exit(audit_exit); (8)实现用户空间收集日志进程程序 test_syscall.c#include #include #include #include #include #include #include #include #define COMM_SIZE 16typedef unsigned char u8;typedef unsigned int u32;typedef unsigned long long u64;struct syscall_buf { u32 serial; u64 ts_sec; u64 ts_micro; u32 syscall; u32 status; pid_t pid; uid_t uid; u8 comm[COMM_SIZE];};#define AUDIT_BUF_SIZE (20 * sizeof(struct syscall_buf))int main(int argc, char *argv[]){ u8 col_buf[AUDIT_BUF_SIZE]; unsigned char reset 1; int num 0; int i, j; struct syscall_buf *p; while(1) { num syscall(335, 0, col_buf, AUDIT_BUF_SIZE, reset); printf(num: %d\n, num); p (struct syscall_buf *)col_buf; for(i 0; i num; i) { printf(num [%d], serial: %d,\t syscall: %d,\t pid: %d,\t comm: %s,\t ts_sec: %ld\n, i, p[i].serial, p[i].syscall, p[i].pid, p[i].comm, ctime(p[i].ts_sec)); } } return 1; } (9)测试系统 运行用户空间收集日志进程程序随着OS系统的运行不断从内核里记录相关系统调用日志的buffer中取出打印在屏幕上//图3-2 系统测试截图总结图 4-1 系统调用总结图《系统调用分析》一共三篇文章先从最早的系统调用方法——(int 80)开始基于Linux-2.6.39内核开始分析对用软中断系统调用的初始化、处理流程和系统调用表进行了学习探究。随后基于Linux-4.20内核分析学习了从机制上对系统调用进行优化的方法——vsyscalls和vDSO。之后对32位下的快速系统调用指令——sysenter/sysexit进行指令学习和对相关Linux源码分析。然后在Linux-4.20内核下编写调用系统调用的程序使用gdb进行调试跟踪并分析出最后使用syscall指令执行系统调用再对64位下的快速系统调用指令syscall/sysret进行指令学习和对相关Linux源码分析。最后在Linux-5.0内核上完成一个系统调用日志收集系统其中包含着添加系统调用编译内核修改内核代码添加内核模块编写用户态程序测试。参考文献[1] 英特尔®64和IA-32架构软件开发人员手册合并卷. https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf[2] The Definitive Guide to Linux System Calls. https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/#32-bit-fast-system-calls[3] Linux 2.6 对新型 CPU 快速系统调用的支持. https://www.ibm.com/developerworks/cn/linux/kernel/l-k26ncpu/index.html[4] vsyscalls and vDSO. https://0xax.gitbooks.io/linux-insides/content/SysCall/linux-syscall-3.html[5] Linux系统调用过程分析. https://www.binss.me/blog/the-analysis-of-linux-system-call/[6] Fix-Mapped Addresses and ioremap. https://0xax.gitbooks.io/linux-insides/content/MM/linux-mm-2.html[7] 王宗涛. Linux快速系统调用实现机制分析. TP316.81[8] linux下系统调用的实现. https://www.pagefault.info/2010/10/09/implementation-of-system-call-under-linux.html