0
点赞
收藏
分享

微信扫一扫

系统调用捕获和分析完结篇制作系统调用日志收集系统


本文为毕业设计过程中学习相关知识、动手实践记录下来的完整笔记,通过阅读本系列文章,您可以从零基础了解系统调用的底层原理并对系统调用进行拦截。由于本人能力有限,文章中可能会出现部分错误信息,如有错误欢迎指正。另外,本系列所有内容仅作为个人学习研究的笔记,转载请标明出处。感谢您的关注!这也是作者极力推荐的一个系列文章!

完整系列文章列表

系统调用捕获和分析—通过ptrace获取系统调用信息

系统调用捕获和分析—通过strace获取系统调用信息

系统调用捕获和分析—必备的系统安全的知识点

系统调用捕获和分析—使用LKM方法添加系统调用

系统调用捕获和分析—修改内核方法添加系统调用

系统调用捕获和分—Ring3层LD_PRELOAD机制进行库函数劫持

系统调用捕获和分析—Ring0层kprobe劫持系统调用

文章目录

  • ​​前言​​
  • ​​项目架构图​​
  • ​​项目实现​​
  • ​​第一步,添加系统调用、做拦截​​
  • ​​第二步,添加一个内核模块​​
  • ​​第三步,编写日志收集程序​​

前言

注意:此项目环境为ubuntu16.04,源内核版本4.15.x,编译内核版本4.13.10。
另外,实验开始前需要先下载4.13.10的源码!!

项目架构图

系统调用捕获和分析完结篇制作系统调用日志收集系统_网络

项目实现

第一步,添加系统调用、做拦截

修改​​/usr/src/linux-4.13.10/arch/x86/entry/syscalls/syscall_64.tbl​​,添加系统调用号。

系统调用捕获和分析完结篇制作系统调用日志收集系统_linux_02


在目录​​/usr/src/linux-4.13.10/arch/x86/kernel/​​下添加一个文件myaudit_sys.c,其中是添加的系统调用,并且要把其中的几个变量符号导出来。

#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/types.h>
#include <asm/current.h>
#include <linux/sched.h>

/*
添加一个系统调用,用于日志记录程序调用,从buf中获取日志信息;
同时向外导出两个函数指针,用于LKM方法定义函数体和在文件外部调用。
*/

/*
u8 -> unsigned byte (8 bits)
u16 -> unsigned byte (16 bits)
*/

void (*audit_to_buf) (int, int) = NULL;
int (*audit_from_buf)(u8, u8*, u16) = NULL;

asmlinkage long sys_myaudit(u8 type, u8* us_buf, u16 us_buf_size)
{
if (audit_from_buf) {
printk("oh~ audit_from_buf() found !!\n");
return (*audit_from_buf)(type, us_buf, us_buf_size); //执行audit_from_buf()函数
} else
printk("oh~ audit_from_buf() not found !!\n");
return 1;
}

EXPORT_SYMBOL(audit_to_buf);
EXPORT_SYMBOL(audit_from_buf);

修改​​/usr/src/linux-4.13.10/arch/x86/kernel/Makefile​​文件,添加myaudit_sys.c的编译。

系统调用捕获和分析完结篇制作系统调用日志收集系统_#include_03


增加函数声明,编辑​​/usr/src/linux-4.13.10/include/linux/syscalls.h​​文件,在最下面添加

系统调用捕获和分析完结篇制作系统调用日志收集系统_网络_04


拦截系统调用

修改​​/usr/src/linux-4.13.10/arch/x86/entry/common.c​​​中的​​do_syscall_64(struct pt_regs *regs)​​函数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9XBw7R9B-1658646740076)(images/a5625a1009a947eda0bdb8f3eaaba006.png)]

引用外部变量

系统调用捕获和分析完结篇制作系统调用日志收集系统_linux_05


配置编译选项

先删除之前编译所生成的文件和配置文件
sudo make mrproper
sudo make clean

配置-->生成.config文件
sudo make menuconfig

sudo cp /boot/config-xxx -r .config 这两行命令是使用现有的配置编译设置,
sudo make oldconfig 可以先不加,如果报错再执行

编译安装

sudo make -j5  5个线程快一些

sudo make modules_install

sudo make install

重启时按esc键,选择ubuntu高级,然后选择刚才编译的内核进入。
通过命令dmesg或者syscall(系统调用号) 来检查是否成功。

#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>

int main(){
long int res = syscall(334); 333就是刚才添加的系统调用号
printf("res = %d\n", res);
}

第二步,添加一个内核模块

编写模块myaudit_module.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/time.h>
#include <linux/cred.h>
#include <asm/current.h>
#include <linux/sched.h>
#include <linux/uaccess.h>


/*
pid_t -> int
uid_t -> int
u8 -> unsigned byte (8 bits)
u32 -> unsigned byte (32 bits)
*/
#define COMM_LENGTH 16//每条命令最多16个字节
struct Audit_buf {
u32 serial; //记录第几条日志
u32 syscall; //系统调用号
u32 status; //系统调用返回值
pid_t pid; //进程id
uid_t uid; //用户id
u8 comm[COMM_LENGTH]; //命令
};

//创建一个等待队列头
DECLARE_WAIT_QUEUE_HEAD(buffer_wait);

//audit信息缓冲区定义
#define AUDIT_BUF_SIZE 20
static struct Audit_buf audit_buf[AUDIT_BUF_SIZE]; //audit信息缓冲区
static int current_pos = 0;
static u32 serial = 0; //记录的第几条数据

//引入系统调用函数指针
extern void (*audit_to_buf)(int, int);
extern int (*audit_from_buf)(unsigned char, unsigned char *, unsigned short);


void audit_to_buf_body(int syscall, int return_status)
{
struct Audit_buf * ppb_temp; //遍历audit_buf数组的指针

if(current_pos < AUDIT_BUF_SIZE) {
ppb_temp = &audit_buf[current_pos];
ppb_temp->serial = serial++;
ppb_temp->syscall = syscall;
ppb_temp->status = return_status;
ppb_temp->pid = current->pid;
ppb_temp->uid = current_uid().val;
memcpy(ppb_temp->comm, current->comm, COMM_LENGTH);
if(current_pos++ == AUDIT_BUF_SIZE*8/10) { //队列快满,激活正在阻塞的等待
printk("oh~ audit_buf is nearly full !!\n");
wake_up_interruptible(&buffer_wait);
}
}
}

int audit_from_buf_body(u8 type, u8 * usr_buf, u16 usr_buf_size)
{
int ret = 0;
if(!type) {
if(__clear_user(usr_buf, usr_buf_size)) { //清空用户空间内存,返回不能清0的字节数
printk("oh~ Error: can not zero all buf !!\n");
return 0;
}
printk("oh~ in kernel module audit_from_buf_body starting !!\n");
ret = wait_event_interruptible(buffer_wait, current_pos >= AUDIT_BUF_SIZE*8/10); //阻塞
//printk("oh~ in kernel module current_pos is %d !!\n", current_pos);
if(__copy_to_user(usr_buf, audit_buf, (current_pos)*sizeof(struct Audit_buf))) { //从audit_buf一次全部拿出
printk("oh~ Error: copy error !!\n");
return 0;
}
ret = current_pos - 1;
current_pos = 0;
}
return ret;
}
static int __init audit_init(void)
{
audit_from_buf = audit_from_buf_body;
audit_to_buf = audit_to_buf_body;
printk("oh~ Starting System Call Auditing !!\n");
return 0;
}
static void __exit audit_exit(void)
{
audit_to_buf = NULL;
audit_from_buf = NULL;
printk("oh~ Exiting System Call Auditing !!\n");
return;
}

MODULE_LICENSE("Dual BSD/GPL");
module_init(audit_init);
module_exit(audit_exit);

Makefile文件

obj-m += myaudit_module.o
CONFIG_MODULE_SIG=n
LINUX_KERNEL_PATH := /lib/modules/$(shell uname -r)/build

all:
make -C $(LINUX_KERNEL_PATH) M=$(CURDIR) modules
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURDIR)

编译并加载模块

sudo make
sudo insmod hello.ko
lsmod |

系统调用捕获和分析完结篇制作系统调用日志收集系统_#include_06

第三步,编写日志收集程序

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdbool.h>

typedef unsigned char u8;
typedef unsigned int u32;

#define COMM_LENGTH 16
struct Audit_buf {
u32 serial;
u32 syscall;
u32 status;
pid_t pid;
uid_t uid;
u8 comm[COMM_LENGTH];
};

#define AUDIT_BUF_SIZE 20
#define USR_AUDIT_BUF_SIZE AUDIT_BUF_SIZE*sizeof(struct Audit_buf)

int main(int argc, char *argv[])
{
u8 my_audit_buf[USR_AUDIT_BUF_SIZE];
struct Audit_buf *p;
while(true) {
//334是自定义的系统调用的调用号
int log_cnt = syscall(334, 0, my_audit_buf, USR_AUDIT_BUF_SIZE);
p = (struct Audit_buf *)my_audit_buf;
int i;
for(i=0; i<log_cnt; i++) {
printf("serial:%d, ", p[i].serial);
printf("syscall_number:%d, ", p[i].syscall);
printf("ret_status:%d, ", p[i].status);
printf("pid:%d, ", p[i].pid);
printf("uid:%d, ", p[i].uid);
printf("commond:%s.\n", p[i].comm);
}
}
return 0;
}

gcc -o myaudit_program myaudit_program.c
./myaudit_program

系统调用捕获和分析完结篇制作系统调用日志收集系统_#include_07

linux kernel api
​​​ https://www.kernel.org/doc/htmldocs/kernel-api/index.html​​


举报

相关推荐

0 条评论