天天看點

系統調用捕獲和分析—Ring0層kprobe劫持系統調用

本文為畢業設計過程中學習相關知識、動手實踐記錄下來的完整筆記,通過閱讀本系列文章,您可以從零基礎了解系統調用的底層原理并對系統調用進行攔截。由于本人能力有限,文章中可能會出現部分錯誤資訊,如有錯誤歡迎指正。

完整系列文章清單

系統調用捕獲和分析—通過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      

繼續閱讀