首页 > 其他分享 >系统调用捕获和分析—Ring0层kprobe劫持系统调用

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

时间:2022-11-08 20:10:17浏览次数:36  
标签:调用 struct jprobe Ring0 kprobe handler include

本文为毕业设计过程中学习相关知识、动手实践记录下来的完整笔记,通过阅读本系列文章,您可以从零基础了解系统调用的底层原理并对系统调用进行拦截。由于本人能力有限,文章中可能会出现部分错误信息,如有错误欢迎指正。

完整系列文章列表
系统调用捕获和分析—通过ptrace获取系统调用信息
系统调用捕获和分析—通过strace获取系统调用信息
系统调用捕获和分析—必备的系统安全的知识点
系统调用捕获和分析—使用LKM方法添加系统调用
系统调用捕获和分析—修改内核方法添加系统调用
系统调用捕获和分—Ring3层LD_PRELOAD机制进行库函数劫持


kprobe轻量级内核调试机制

利用kprobe技术,用户可以自定义自己的回调函数,可以在几乎所有的函数中动态插入探测点。

当内核执行流程执行到指定的探测函数时,会调用该回调函数,用户即可收集所需的信息了,同时内核最后还会回到原本的正常执行流程。如果用户已经收集足够的信息,不再需要继续探测,则同样可以动态的移除探测点。

kprobes技术包括的3种探测手段分别是kprobe、jprobe和kretprobe。

首先kprobe是最基本的探测方式,是实现后两种的基础,它可以在任意的位置放置探测点(就连函数内部的某条指令处也可以),它提供了探测点的调用前、调用后和内存访问出错3种回调方式,分别是pre_handler、post_handler和fault_handler,其中pre_handler函数将在被探测指令被执行前回调,post_handler会在被探测指令执行完毕后回调(注意不是被探测函数),fault_handler会在内存访问出错时被调用;

jprobe基于kprobe实现,它用于获取被探测函数的入参值;

kretprobe同样基于kprobe实现,用于获取被探测函数的返回值。


kprobe机制劫持系统调用实验

内核源码​​/linux-4.13.10/samples/kprobes/​​中有示例程序。

注意在linux-4.15.0中register_jprobe函数失败返回-38,网上说4.15.0以后废除。

4.15.5版本内核下实验krpobe

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/cred.h>
#include <asm/current.h>

//定义要hook的函数
static struct kprobe kp = {
.symbol_name = "_do_fork",
};

static int handler_pre(struct kprobe *p, struct pt_regs *regs){
printk(KERN_INFO "<%s> pre_handler: p->addr = 0x%p, ip = %lx, flags = 0x%lx, proc_name = %s, pid = %d\n",
p->symbol_name, p->addr, regs->ip, regs->flags, current->comm, current->pid);
return 0;
}

static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags){
printk(KERN_INFO "<%s> post_handler: p->addr = 0x%p, flags = 0x%lx, proc_name = %s, pid = %d\n",
p->symbol_name, p->addr, regs->flags, current->comm, current->pid);
}

static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr){
printk(KERN_INFO "fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr);
return 0;
}

static int __init kprobe_init(void)
{
int ret;
kp.pre_handler = handler_pre;
kp.post_handler = handler_post;
kp.fault_handler = handler_fault;

ret = register_kprobe(&kp);
if (ret < 0) {
pr_err("register_kprobe failed, returned %d\n", ret);
return ret;
}
pr_info("Planted kprobe at %p\n", kp.addr);
return 0;
}

static void __exit kprobe_exit(void)
{
unregister_kprobe(&kp);
pr_info("kprobe at %p unregistered\n", kp.addr);
}

module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

Makefile

obj-m := hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

编译,插入模块,查看内核信息

make
sudo insmod hello.ko
dmesg


附4.13.10可运行成功jprobs机制代码

准备代码,拦截write系统调用hello.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kprobes.h>

//同linux-4.14.15/include/linux/syscalls.h#603下sys_write定义
long my_sys_write(unsigned int fd, const char __user *buf, size_t count){
pr_info("fd=%u, buf=%lx, count=%lu\n", fd, buf, count);
/* Always end with a call to jprobe_return(). */
jprobe_return(); //官方规定在回调函数执行完毕以后,必须调用jprobe_return函数
return 0;
}

/*
struct jprobe {
struct kprobe kp;
void *entry; // probe handling code to jump to
};

struct kprobe定义位置/linux-4.14.15/include/linux/kprobes.h#74
*/

struct jprobe jprobe_write = {
.entry= my_sys_write,
.kp = {
.symbol_name = "sys_write",
},
};

static int __init mymodule_init(void){
//挂载hook
int ret;
ret = register_jprobe(&jprobe_write); //向内核注册jprobe探测点
if (ret < 0) {
printk("register_jprobe failed, ret=%d\n", ret);
return -1;
}
printk("register_jprobe success, syscall: write\n");
return 0;
}

static void __exit mymodule_exit(void){
unregister_jprobe(&jprobe_write); //卸载jprobe探测点
}

module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");

Makefile文件

obj-m := hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

编译,插入模块,查看内核信息

make
sudo insmod hello.ko
dmesg


注意

如果拦截sys_write函数,%s字符型时输出参数buf时会导致死机,根据函数调用栈查看其具体使用buf参数的情况无果。但看到有人打印do_execve参数成功,

标签:调用,struct,jprobe,Ring0,kprobe,handler,include
From: https://blog.51cto.com/u_15457669/5834739

相关文章