国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

深入探究Linux中的Kprobe機制

Linux閱碼場 ? 來源:csdn ? 作者:liuhangtiant ? 2021-01-02 11:53 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

概述

kprobe機制用于在內核中動態添加一些探測點,可以滿足一些調試需求。本文主要探尋kprobe的執行路徑,也就是說如何trap到kprobe,以及如何回到原路徑繼續執行。

實例

先通過一個實例來感受下kprobe,linux中有一個現成的實例: samples/kprobes/kprobe_example.c 由于當前驗證環境是基于qemu+arm64,我刪除了其他架構的代碼,并稍稍做了一下改動:

/* * NOTE: This example is works on x86 and powerpc. * Here‘s a sample kernel module showing the use of kprobes to dump a * stack trace and selected registers when _do_fork() is called. * * For more information on theory of operation of kprobes, see * Documentation/kprobes.txt * * You will see the trace data in /var/log/messages and on the console * whenever _do_fork() is invoked to create a new process. */ #include 《linux/kernel.h》#include 《linux/module.h》#include 《linux/kprobes.h》 #define MAX_SYMBOL_LEN 64static char symbol[MAX_SYMBOL_LEN] = “_do_fork”;module_param_string(symbol, symbol, sizeof(symbol), 0644); /* For each probe you need to allocate a kprobe structure */static struct kprobe kp = { .symbol_name = symbol,}; /* kprobe pre_handler: called just before the probed instruction is executed */static int handler_pre(struct kprobe *p, struct pt_regs *regs){ pr_info(“《%s》 pre_handler: p-》addr = 0x%p, pc = 0x%lx,” “ pstate = 0x%lx ”, p-》symbol_name, p-》addr, (long)regs-》pc, (long)regs-》pstate); dump_stack(); /* A dump_stack() here will give a stack backtrace */ return 0;} /* kprobe post_handler: called after the probed instruction is executed */static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags){ pr_info(“《%s》 post_handler: p-》addr = 0x%p, pstate = 0x%lx ”, p-》symbol_name, p-》addr, (long)regs-》pstate); dump_stack();} /* * fault_handler: this is called if an exception is generated for any * instruction within the pre- or post-handler, or when Kprobes * single-steps the probed instruction. */static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr){ pr_info(“fault_handler: p-》addr = 0x%p, trap #%dn”, p-》addr, trapnr); /* Return 0 because we don’t handle the fault. */ 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 ”, ret); return ret; } pr_info(“Planted kprobe at %p ”, kp.addr); return 0;} static void __exit kprobe_exit(void){ unregister_kprobe(&kp); pr_info(“kprobe at %p unregistered ”, kp.addr);} module_init(kprobe_init)module_exit(kprobe_exit)MODULE_LICENSE(“GPL”);

這段代碼很簡單,默認情況下,kprobe做了3個鉤子,分別在_do_fork對應位置的指令執行之前,執行之后,以及出異常的時候。

插入該內核模塊之后,隨便輸入一條命令,可看到下面的打印:

[ 19.882832] kprobe_example: loading out-of-tree module taints kernel.[ 19.900442] Planted kprobe at (____ptrval____)[ 19.908571] 《_do_fork》 pre_handler: p-》addr = 0x(____ptrval____), pc = 0xffff0000080d2c98, pstate = 0x80000005[ 19.913657] CPU: 0 PID: 1358 Comm: udevd Tainted: G O 4.18.0 #7[ 19.916239] Hardware name: linux,dummy-virt (DT)[ 19.918400] Call trace:[ 19.919373] dump_backtrace+0x0/0x180[ 19.920681] show_stack+0x14/0x20[ 19.921817] dump_stack+0x90/0xb4[ 19.923678] handler_pre+0x24/0x68 [kprobe_example][ 19.926357] kprobe_breakpoint_handler+0xbc/0x160[ 19.926627] brk_handler+0x70/0x88[ 19.926802] do_debug_exception+0x94/0x160[ 19.927102] el1_dbg+0x18/0x78[ 19.927299] _do_fork+0x0/0x358[ 19.927465] el0_svc_naked+0x30/0x34[ 19.928973] 《_do_fork》 post_handler: p-》addr = 0x(____ptrval____), pstate = 0x80000005[ 19.929361] CPU: 0 PID: 1358 Comm: udevd Tainted: G O 4.18.0 #7[ 19.929693] Hardware name: linux,dummy-virt (DT)[ 19.929962] Call trace:[ 19.930102] dump_backtrace+0x0/0x180[ 19.930289] show_stack+0x14/0x20[ 19.930461] dump_stack+0x90/0xb4[ 19.934684] handler_post+0x24/0x30 [kprobe_example][ 19.934968] post_kprobe_handler+0x54/0x98[ 19.935234] kprobe_single_step_handler+0x74/0xa8[ 19.935389] single_step_handler+0x3c/0xb0[ 19.935516] do_debug_exception+0x94/0x160[ 19.935642] el1_dbg+0x18/0x78[ 19.935965] 0xffff000000ac8004[ 19.936067] el0_svc_naked+0x30/0x34

probe和post鉤子得到執行,這對查看內核的調用棧非常有幫助。

深入探究

是否只能基于symbol_name做kprobe?

顯然不太可能,struct kprobe中有一個addr成員,很明顯是可以直接基于地址做kprobe的。

把這段代碼:

#define MAX_SYMBOL_LEN 64static char symbol[MAX_SYMBOL_LEN] = “_do_fork”;module_param_string(symbol, symbol, sizeof(symbol), 0644); /* For each probe you need to allocate a kprobe structure */static struct kprobe kp = { .symbol_name = symbol,};

修改為:

/* For each probe you need to allocate a kprobe structure */static struct kprobe kp = { .addr= (kprobe_opcode_t *)0xffff0000080d2c98,};

效果是一樣的。

kprobe是如何動態添加探針的?

這個肯定要分析代碼了,好在代碼相當簡單:

register_kprobe |------arm_kprobe | |------__arm_kprobe | | |------arch_arm_kprobe /* arm kprobe: install breakpoint in text */void __kprobes arch_arm_kprobe(struct kprobe *p){ patch_text(p-》addr, BRK64_OPCODE_KPROBES);}

從注釋就可以很明顯看出來,是把addr對應位置的指令修改為brk指令,當然這里說的是ARM64架構。那么一旦CPU執行到addr,就會觸發異常,trap到kprobe注冊的鉤子上。

post鉤子為什么會用到single step

從上面的調用棧可以看到,post鉤子實際上是通過單步斷點trap過來的?為什么需要用到單步斷點呢?這個其實很好解釋。我們先來理一下kprobe的過程:

把addr位置的指令修改為brk指令

CPU執行到addr處trap到pre執行

pre執行完畢后需要把addr處的指令恢復

CPU繼續執行addr處的指令

CPU執行post

那么CPU如何才能執行到post,很簡單,使能單步執行就可以了。肯定有人會說,可以把addr+4的指令也替換成brk,這個肯定是不行的,因為ARM64可能是32位/16位指令混編的,即便是固定32位指令,CPU下一條要執行的指令也不一定是addr+4,比如當前addr是一條跳轉指令。

fault_handler 鉤子什么時候會用到

通過分析代碼可知,當發生page fault的時候,會調用當前正在running的kprobe的fault_handler鉤子,所以這里發生page fault的代碼并不一定是addr處的指令,也可能是pre或者post中的指令。我在pre中注入一段訪問0地址的邏輯:

static void * g_addr=0;static int handler_pre(struct kprobe *p, struct pt_regs *regs) __attribute__((optimize(“O0”)));static int handler_pre(struct kprobe *p, struct pt_regs *regs){ pr_info(“《%s》 pre_handler: p-》addr = 0x%p, pc = 0x%lx,” “ pstate = 0x%lx ”, p-》symbol_name, p-》addr, (long)regs-》pc, (long)regs-》pstate); printk(“%d ”, *(char *)g_addr); /* A dump_stack() here will give a stack backtrace */ return 0;}

經驗證確實調用到了fault_handler鉤子:

[ 17.272594] kprobe_example: loading out-of-tree module taints kernel.[ 17.294266] Planted kprobe at (____ptrval____)# # ls[ 19.072586] 《(null)》 pre_handler: p-》addr = 0x(____ptrval____), pc = 0xffff0000080d2c98, pstate = 0x80000005[ 19.073189] fault_handler: p-》addr = 0x(____ptrval____), trap #-1778384890n[ 19.073568] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000[ 19.074271] Mem abort info:[ 19.074393] ESR = 0x96000006[ 19.074641] Exception class = DABT (current EL), IL = 32 bits[ 19.074887] SET = 0, FnV = 0[ 19.075014] EA = 0, S1PTW = 0[ 19.075174] Data abort info:[ 19.075324] ISV = 0, ISS = 0x00000006[ 19.075455] CM = 0, WnR = 0[ 19.075774] user pgtable: 4k pages, 48-bit VAs, pgdp = (____ptrval____)[ 19.076005] [0000000000000000] pgd=00000000485c6003, pud=00000000bb2f4003, pmd=0000000000000000[ 19.076596] Internal error: Oops: 96000006 [#1] PREEMPT SMP[ 19.076924] Modules linked in: kprobe_example(O)[ 19.077693] CPU: 0 PID: 1387 Comm: sh Tainted: G O 4.18.0 #7[ 19.077927] Hardware name: linux,dummy-virt (DT)[ 19.078298] pstate: 400003c5 (nZcv DAIF -PAN -UAO)[ 19.078962] pc : handler_pre+0x50/0x70 [kprobe_example][ 19.079149] lr : handler_pre+0x44/0x70 [kprobe_example][ 19.079359] sp : ffff00000ac63c00[ 19.079565] x29: ffff00000ac63c00 x28: ffff80007a3c9a80 [ 19.079821] x27: ffff000008ac1000 x26: 00000000000000dc [ 19.080047] x25: ffff80007dfb7788 x24: 0000000000000000 [ 19.080363] x23: ffff0000080d2c98 x22: ffff00000ac63d70 [ 19.080621] x21: ffff000000ac2000 x20: 0000800074f02000 [ 19.080863] x19: ffff0000090b5788 x18: ffffffffffffffff [ 19.081197] x17: 0000000000000000 x16: 0000000000000000 [ 19.081501] x15: ffff0000090d96c8 x14: 3030303030666666 [ 19.081720] x13: 667830203d206370 x12: ffff0000090d9940 [ 19.081933] x11: ffff0000085dd8d8 x10: 5f287830203d2072 [ 19.082189] x9 : 0000000000000017 x8 : 2065746174737020 [ 19.082455] x7 : 2c38396332643038 x6 : ffff80007dfb8240 [ 19.082660] x5 : ffff80007dfb8240 x4 : 0000000000000000 [ 19.082871] x3 : ffff80007dfbf030 x2 : 793b575e486def00 [ 19.083068] x1 : 0000000000000000 x0 : 0000000000000000 [ 19.083390] Process sh (pid: 1387, stack limit = 0x(____ptrval____))[ 19.083783] Call trace:[ 19.084020] handler_pre+0x50/0x70 [kprobe_example][ 19.084470] kprobe_breakpoint_handler+0xbc/0x160[ 19.084693] brk_handler+0x70/0x88[ 19.084839] do_debug_exception+0x94/0x160[ 19.085132] el1_dbg+0x18/0x78[ 19.085259] _do_fork+0x0/0x358[ 19.085443] el0_svc_naked+0x30/0x34[ 19.085939] Code: 95d9a53f b0000000 9101c000 f9400000 (39400000) [ 19.086713] ---[ end trace 3bb11c402bc37363 ]---

但由于fault_handler中沒有對該異常做處理,所以依然掛死了。

fault_handler可以用于報錯或者糾錯,報錯可以自定義一些錯誤信息給用戶,以便分析錯誤;糾錯用于修改錯誤,那么針對當前這個錯誤應該怎么做糾錯呢?

在fault_handler中為g_addr分配空間?,這顯然不行,g_addr肯定已經被載入寄存器了,此時修改已經太遲。唯一的方法就是修改寄存器的值,而寄存器此時肯定已經入棧了,所以必須修改寄存器在棧里面的內容。

下面我們來fixup這個掛死問題:

根據掛死信息

[ 19.084020] handler_pre+0x50/0x70 [kprobe_example]

是在handler_pre+0x50這個位置出異常的,通過反匯編得知這個位置對應的指令是:

50: 39400000 ldrb w0, [x0]

x0的內容是0,所以這里是讀0地址,很明顯,g_addr被載入到了x0中,所以只要修改x0就可以了。

fixup實現

修改fault_handler函數:

static int g_addr1=0x5a;static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr){ pr_info(“fault_handler: p-》addr = 0x%p, trap #%dn”, p-》addr, trapnr); regs-》regs[0] = (unsigned long)&g_addr1; /* Return 0 because we don‘t handle the fault. */ return 1;}

驗證

[ 58.882059] 《(null)》 pre_handler: p-》addr = 0x(____ptrval____), pc = 0xffff0000080d2c98, pstate = 0x80000005[ 58.882393] fault_handler: p-》addr = 0x(____ptrval____), trap #-1778384890n[ 58.882411] 90[ 58.882658] 《(null)》 post_handler: p-》addr = 0x(____ptrval____), pstate = 0x80000005[ 58.882960] CPU: 1 PID: 1388 Comm: sh Tainted: G O 4.18.0 #7

fault_handler之后,pre_handler打印了g_addr對應地址的內容是90,也就是0x5a。

大功告成,我們成功的讓內核訪問了0地址,并且返回了0x5a。

當然,這一切都是假的!

原文標題:深入探究Linux Kprobe機制

文章出處:【微信公眾號:Linuxer】歡迎添加關注!文章轉載請注明出處。

責任編輯:haq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內核
    +關注

    關注

    4

    文章

    1467

    瀏覽量

    42873
  • Linux
    +關注

    關注

    88

    文章

    11758

    瀏覽量

    219009

原文標題:深入探究Linux Kprobe機制

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    深入解析Rockchip SFC驅動:SPI Flash傳輸流程與問題排查指南

    Controller)驅動 (spi-rockchip-sfc.c),用于高效管理SPI Flash的讀寫傳輸。本文基于Linux內核驅動代碼與Rockchip官方開發指南,深入拆解SFC驅動的核心流程
    的頭像 發表于 02-04 07:13 ?426次閱讀
    <b class='flag-5'>深入</b>解析Rockchip SFC驅動:SPI Flash傳輸流程與問題排查指南

    【「Linux 設備驅動開發(第 2 版)」閱讀體驗】+讀深入理解Linux內核內存分配

    最近這段時間內存條、硬盤的價格飛漲,隨著AI產品的興起,大數據模型的應用,對硬件存儲要求的更高。這節閱讀有關Linux內存管理方面的知識。 Linux系統使用了“虛擬內存”機制,虛擬內存機制
    發表于 01-16 20:05

    【「Linux 設備驅動開發(第 2 版)」閱讀體驗】+讀內核處理的核心輔助函數

    “處理內核的核心輔助函數”進行學習。 第3章又是以5個主題展開討論學習,①、Linux內核加鎖機制和共享資源;②、處理內核等待、睡眠和延遲機制;③、深入理解
    發表于 01-10 22:08

    深入探究 SN65LVELT23:一款高性能的電平轉換器

    深入探究 SN65LVELT23:一款高性能的電平轉換器 作為一名電子工程師,在日常的硬件設計,電平轉換是一個常見且關鍵的環節。今天,咱們就來深入聊聊德州儀器(TI)的 SN65LV
    的頭像 發表于 12-25 09:40 ?295次閱讀

    深入Linux內核:進程調度的核心邏輯與實現細節

    Linux系統,進程調度就像一位精明的“CPU管理員”——它決定著哪個進程能優先使用CPU,多久切換一次進程,如何平衡系統響應速度與資源利用率。小到桌面應用的流暢點擊,大到服務器的多任務并發
    的頭像 發表于 12-24 07:05 ?4299次閱讀
    <b class='flag-5'>深入</b><b class='flag-5'>Linux</b>內核:進程調度的核心邏輯與實現細節

    飛凌嵌入式ElfBoard-文件I/O的了解探究Linux系統的文件管理機制

    的靜態文件讀取到內存中進行緩存,讀寫操作都是針對這份動態文件。,在Linux系統,內核會為每個進程設置一個專門的數據結構用于管理該進程,用于記錄進程的狀態信息、運行特征等,這個被稱為進程控制塊
    發表于 11-18 08:50

    【書籍評測活動NO.67】成為硬核Linux開發者:《Linux 設備驅動開發(第 2 版)》

    )。成為硬核Linux開發者Linux系統的設備驅動開發,一直給人門檻較高的印象,主要因內核機制抽象、需深度理解硬件原理、開發調試難度大所致。2021年,一本講解驅動開發的專著問世即獲市場青睞,暢銷近萬冊
    發表于 11-17 17:52

    教程來啦!LuatOS的消息通信機制詳解及其應用場景

    在資源受限的嵌入式環境,LuatOS采用消息機制實現模塊間解耦與高效通信。通過預定義消息名稱(如“new_msg”),開發者可輕松構建響應式程序結構。接下來我們將深入剖析其實現原理與典型使用方法
    的頭像 發表于 09-26 18:59 ?424次閱讀
    教程來啦!LuatOS<b class='flag-5'>中</b>的消息通信<b class='flag-5'>機制</b>詳解及其應用場景

    華納云服務器Linux系統電源管理與節能優化配置方法

    在云計算時代,Linux系統的電源管理優化成為提升云服務器能效的關鍵環節。本文將深入解析Linux內核的電源管理機制,從CPU調頻策略到磁盤休眠設置,提供一套完整的節能配置方案。通過調
    的頭像 發表于 08-21 15:09 ?916次閱讀

    Linux權限體系解析

    你真的了解Linux權限嗎?大多數人只知道rwx,但Linux的權限體系遠比你想象的復雜和強大。今天我們深入探討Linux的12位權限體系,這是每個運維工程師都應該掌握的核心知識。
    的頭像 發表于 07-23 16:57 ?858次閱讀

    Linux系統中網絡配置詳解

    網絡配置是Linux系統運維的核心技能之一。正確理解和配置子網掩碼、網關等網絡參數,直接影響系統的網絡連通性和性能。本文將深入探討Linux系統中網絡配置的方方面面,為運維工程師提供
    的頭像 發表于 07-17 11:01 ?1195次閱讀

    淺切多道切割工藝對晶圓 TTV 厚度均勻性的提升機制與參數優化

    TTV 厚度均勻性欠佳。淺切多道切割工藝作為一種創新加工方式,為提升晶圓 TTV 厚度均勻性提供了新方向,深入探究其提升機制與參數優化方法具有重要的現實意義。 二
    的頭像 發表于 07-11 09:59 ?603次閱讀
    淺切多道切割工藝對晶圓 TTV 厚度均勻性的提升<b class='flag-5'>機制</b>與參數優化

    MICRO OLED 金屬陽極像素制作工藝對晶圓 TTV 厚度的影響機制及測量優化

    與良品率,因此深入探究二者關系并優化測量方法意義重大。 影響機制 工藝應力引發變形 在金屬陽極像素制作時,諸如光刻、蝕刻、金屬沉積等步驟會引入工藝應力。光刻,光刻膠的涂覆與曝光過程會
    的頭像 發表于 05-29 09:43 ?741次閱讀
    MICRO OLED 金屬陽極像素制作工藝對晶圓 TTV 厚度的影響<b class='flag-5'>機制</b>及測量優化

    Linux系統管理的核心概念

    在前一篇文章,我們深入探討了Linux的文件操作命令,如cp、mv、rm,以及文本處理命令grep、wc和管道符。本文將繼續深入
    的頭像 發表于 05-15 17:05 ?661次閱讀

    Linux權限管理基礎入門

    Linux的廣闊天空中,權限管理猶如一只翱翔的雄鷹,掌控著系統的安全與秩序。掌握Linux權限,不僅能讓你的系統管理更加得心應手,還能有效防止未授權訪問和數據泄露。本文將帶你深入探索Linu
    的頭像 發表于 05-06 13:44 ?756次閱讀
    <b class='flag-5'>Linux</b>權限管理基礎入門