成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux 內核動態追蹤技術的實現

系統 Linux
kprobe 是內核提供的動態追蹤技術機制,它允許動態安裝內核模塊的方式安裝系統鉤子,非常強大。下面先看一個內核中的例子。

[[434928]]

之前的文章介紹了基于 tracepoint 靜態追蹤技術的實現,本文再介紹基于 kprobe 的動態追蹤即使的實現。同樣,動態追蹤也是排查問題的利器。

kprobe 是內核提供的動態追蹤技術機制,它允許動態安裝內核模塊的方式安裝系統鉤子,非常強大。下面先看一個內核中的例子。

  1. #include <linux/kernel.h> 
  2. #include <linux/module.h> 
  3. #include <linux/kprobes.h> 
  4.  
  5. #define MAX_SYMBOL_LEN  64 
  6. // 要 hanck 的內核函數名 
  7. static char symbol[MAX_SYMBOL_LEN] = "_do_fork"
  8. module_param_string(symbol, symbol, sizeof(symbol), 0644); 
  9. static struct kprobe kp = { 
  10.     .symbol_name    = symbol, 
  11. }; 
  12.  
  13. // 執行系統函數前被執行的鉤子 
  14. static int __kprobes handler_pre(struct kprobe *p, struct pt_regs *regs){ 
  15.     // ... 
  16.  
  17. // 執行系統函數的單條指令后執行的鉤子(不是執行完系統函數) 
  18. static void __kprobes handler_post(struct kprobe *p, struct pt_regs *regs, 
  19.                 unsigned long flags){ 
  20.     // ... 
  21.  
  22. // 鉤子執行出錯或者單條執行執行出錯時被執行函數static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr){ 
  23.     // ... 
  24.  
  25. static int __init kprobe_init(void){ 
  26.     int ret; 
  27.     // 設置鉤子 
  28.     kp.pre_handler = handler_pre; 
  29.     kp.post_handler = handler_post; 
  30.     kp.fault_handler = handler_fault; 
  31.     // 安裝鉤子 
  32.     register_kprobe(&kp); 
  33.     return 0; 
  34.  
  35. static void __exit kprobe_exit(void){ 
  36.     unregister_kprobe(&kp); 
  37.     pr_info("kprobe at %p unregistered\n", kp.addr); 
  38.  
  39. // 安裝進內核后的初始化和注銷函數 
  40. module_init(kprobe_init) 
  41. module_exit(kprobe_exit) 
  42. MODULE_LICENSE("GPL"); 

設置完 kprobe 后,通過 register_kprobe 注冊到內核。

  1. int register_kprobe(struct kprobe *p){ 
  2.     int ret; 
  3.     struct kprobe *old_p; 
  4.     struct module *probed_mod; 
  5.     kprobe_opcode_t *addr; 
  6.  
  7.     // 通過系統函數名找到對應的地址,內核維護了這個數據 
  8.     addr = kprobe_addr(p); 
  9.     // 記錄這個地址 
  10.     p->addr = addr; 
  11.     p->flags &= KPROBE_FLAG_DISABLED; 
  12.     p->nmissed = 0; 
  13.     INIT_LIST_HEAD(&p->list); 
  14.     // 之前是否已經存在鉤子,是的話就插入存在的列表,否則插入一個新的記錄 
  15.     old_p = get_kprobe(p->addr); 
  16.     if (old_p) { 
  17.         /* Since this may unoptimize old_p, locking text_mutex. */ 
  18.         ret = register_aggr_kprobe(old_p, p); 
  19.         goto out
  20.     } 
  21.     // 把被 hack 的系統函數的指令保存到 probe 結構體,因為下面要覆蓋這塊內存 
  22.     /* 
  23.         prepare_kprobe => 
  24.             unsigned long addr = (unsigned long) p->addr; 
  25.             unsigned long *kprobe_addr = (unsigned long *)(addr & ~0xFULL); 
  26.             memcpy(&p->opcode, kprobe_addr, sizeof(kprobe_opcode_t)); 
  27.             memcpy(p->ainsn.insn, kprobe_addr, sizeof(kprobe_opcode_t)); 
  28.     */ 
  29.     ret = prepare_kprobe(p); 
  30.  
  31.     INIT_HLIST_NODE(&p->hlist); 
  32.     // 插入內核維護的哈希表 
  33.     hlist_add_head_rcu(&p->hlist, 
  34.                &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]); 
  35.     // hack 掉系統函數所在內存的內容 
  36.     arm_kprobe(p); 

注冊一個 probe,首先是通過被 hack 的函數名找到對應的地址,然后保存這個地址對應內存的信息,接著把 probe 插入哈希表,最后調用 arm_kprobe 函數 hack 掉系統函數所在內存的內容。看一下 arm_kprobe。

  1. void arch_arm_kprobe(struct kprobe *p){ 
  2.     // #define INT3_INSN_OPCODE 0xCC 
  3.     u8 int3 = INT3_INSN_OPCODE; 
  4.     // 把 int3 的內存復制到 addr 
  5.     text_poke(p->addr, &int3, 1); 
  6.     text_poke_sync(); 
  7.     perf_event_text_poke(p->addr, &p->opcode, 1, &int3, 1); 

0xCC 是 intel 架構下 int3 對應的指令。所以這里就是把被 hack 函數對應指令的前面部分改成 int3。完成 hack。當執行到系統函數的時候,就會執行 int3,從而觸發 trap,并執行對應的處理函數 do_int3(這里比較復雜,我也沒有深入分析,大概是這個流程)。

  1. static bool do_int3(struct pt_regs *regs){ 
  2.     kprobe_int3_handler(regs);}int kprobe_int3_handler(struct pt_regs *regs){ 
  3.     kprobe_opcode_t *addr; 
  4.     struct kprobe *p; 
  5.     struct kprobe_ctlblk *kcb; 
  6.     addr = (kprobe_opcode_t *)(regs->ip - sizeof(kprobe_opcode_t)); 
  7.  
  8.     kcb = get_kprobe_ctlblk(); 
  9.     // 通過地址從 probe  哈希表拿到對應的 probe 結構體 
  10.     p = get_kprobe(addr); 
  11.  
  12.     set_current_kprobe(p, regs, kcb); 
  13.     kcb->kprobe_status = KPROBE_HIT_ACTIVE; 
  14.  
  15.     // 執行 pre_handler 鉤子  
  16.     if (!p->pre_handler || !p->pre_handler(p, regs)) 
  17.         setup_singlestep(p, regs, kcb, 0); 

執行完。pre_handler 鉤子后,會通過 setup_singlestep 設置單步執行 flag。

  1. static void setup_singlestep(struct kprobe *p, struct pt_regs *regs, 
  2.                  struct kprobe_ctlblk *kcb, int reenter){ 
  3.     // 修改寄存器的值 
  4.     // 設置 eflags 寄存器的 tf 位,允許單步調試 
  5.     regs->flags |= X86_EFLAGS_TF; 
  6.     regs->flags &= ~X86_EFLAGS_IF; 
  7.     // 設置下一條指令為系統函數的指令 
  8.     if (p->opcode == INT3_INSN_OPCODE) 
  9.         regs->ip = (unsigned long)p->addr; 
  10.     else 
  11.         regs->ip = (unsigned long)p->ainsn.insn; 

setup_singlestep 首先設置了允許單步調試,也就是說執行下一條指令后會觸發一個 trap,從而執行一個處理函數。并設置了下一條指令為被 hack 函數對應的指令,這是在注冊 probe 時保存下來的。觸發單步調試的 trap 后,最終會執行到 kprobe_debug_handler

  1. int kprobe_debug_handler(struct pt_regs *regs){ 
  2.     struct kprobe *cur = kprobe_running(); 
  3.     struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); 
  4.     // 恢復指令為系統函數的指令 
  5.     resume_execution(cur, regs, kcb); 
  6.     regs->flags |= kcb->kprobe_saved_flags; 
  7.     // 執行 post 鉤子 
  8.     if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) { 
  9.         kcb->kprobe_status = KPROBE_HIT_SSDONE; 
  10.         cur->post_handler(cur, regs, 0); 
  11.     } 

在單步調試的 trap 處理函數中,會執行 post 鉤子,并恢復真正的系統函數執行。這就完成了整個過程。

我們可以看到 kprobe 可以在系統函數執行前執行我們的鉤子,另外內核還提供了另外一個機制 kretprobe 用于在系統函數執行后返回前安裝鉤子。下面通過一個例子大致看一下 kretprobe。

  1. struct my_data { 
  2.     ktime_t entry_stamp; 
  3. }; 
  4.  
  5. // 記錄函數執行開始時間 
  6. static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs){ 
  7.     struct my_data *data; 
  8.     data = (struct my_data *)ri->data; 
  9.     data->entry_stamp = ktime_get(); 
  10.     return 0; 
  11.  
  12. // 記錄函數執行結束時間 
  13. static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs){ 
  14.     unsigned long retval = regs_return_value(regs); 
  15.     struct my_data *data = (struct my_data *)ri->data; 
  16.     s64 delta; 
  17.     ktime_t now; 
  18.  
  19.     now = ktime_get(); 
  20.     delta = ktime_to_ns(ktime_sub(now, data->entry_stamp)); 
  21.     return 0; 
  22.  
  23. static struct kretprobe my_kretprobe = { 
  24.     // 函數返回前執行 
  25.     .handler        = ret_handler, 
  26.     // 函數開始前執行 
  27.     .entry_handler      = entry_handler, 
  28.     .data_size      = sizeof(struct my_data), 
  29.     /* Probe up to 20 instances concurrently. */ 
  30.     .maxactive      = 20, 
  31. }; 
  32.  
  33. static char func_name[NAME_MAX] = "_do_fork"
  34. module_param_string(func, func_name, NAME_MAX, S_IRUGO); 
  35. my_kretprobe.kp.symbol_name = func_name; 
  36. // 注冊 
  37. register_kretprobe(&my_kretprobe); 

我們可以看到可以通過 kretprobe 計算系統函數的耗時。kretprobe 是基于 kprobe 實現的,主要邏輯是通過通過 kprobe 注冊一個 pre_handler,在 pre_handler 中 hack 掉函數的棧,因為函數執行時,返回地址是存在棧中的,把這個內存改成一段內核的代碼,等到函數執行完后,彈出返回地址時,就會執行內核 hack 的代碼,從而執行我們的鉤子,執行完后再跳回到真正的返回地址繼續執行。

 

總結:內核通過劫持的方式實現了 kprobe,基于 kprobe 的動態追蹤技術可謂是非常復雜而強大,我們可以利用這個機制,動態修改邏輯,收集信息。不過實現過于復雜,涉及到對 CPU 架構和內存模型的了解,本文也是大致分析了一下流程,有興趣的同學可以自行查看源碼。

 

責任編輯:武曉燕 來源: 編程雜技
相關推薦

2021-11-14 07:29:55

Linux 內核靜態追蹤Linux 系統

2016-12-08 09:57:09

LinuxDTrace技術

2025-04-01 02:00:22

2025-04-02 00:33:00

2025-06-09 02:10:00

2022-03-03 18:18:53

BPF解釋器系統

2021-10-06 09:46:17

trace-cmd追蹤內核Linux

2025-05-15 09:12:27

2025-03-07 08:30:00

pwruLinux網絡包追蹤

2025-01-02 11:06:22

2023-02-28 09:47:42

2023-03-01 23:56:11

2023-03-10 14:56:37

Linuxconnect系統

2023-03-01 23:53:30

Linuxshutdown進程

2014-04-01 16:52:10

SUSEkGraftLinux內核

2024-04-15 11:24:32

庫存跟蹤技術NFC藍牙

2017-01-12 19:15:03

Linux內核調試自構proc

2021-09-30 09:43:11

Linux內核Zstd補丁

2023-11-24 11:24:16

Linux系統

2022-05-24 12:34:32

Docker容器Linux容器進程
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线一区视频 | 搞黄视频免费看 | 天堂一区在线 | 九九久久精品 | 久久久精品综合 | 日韩精品在线观看视频 | 精品亚洲一区二区三区四区五区 | 成人av一区| 国产精品久久久久不卡 | 欧美精品欧美精品系列 | 国产精品久久 | 欧美激情一区二区三级高清视频 | 亚洲一区不卡在线 | 久久a久久 | 99re热精品视频 | 亚洲国产精品一区二区三区 | 在线观看午夜视频 | 九九综合| 天久久 | 成人精品视频在线观看 | 欧美一区二区三区视频 | 午夜一区 | 欧美不卡一区 | 亚洲一区二区三区免费观看 | 免费日韩av网站 | 亚洲高清成人 | www精品美女久久久tv | 国产精品自拍视频网站 | 亚洲日韩欧美一区二区在线 | 精品一区二区久久 | 成人网视频 | 精品欧美视频 | 国产精品极品美女在线观看免费 | 中文字幕在线不卡播放 | 99精品视频在线观看免费播放 | 99热这里都是精品 | 国产精品视频免费播放 | 国产精品九九九 | 91网站在线观看视频 | 99久久婷婷国产综合精品电影 | 日本黄色大片免费 |