0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Inline Hook Syscall詳解

Linux閱碼場(chǎng) ? 來(lái)源:Linux閱碼場(chǎng) ? 作者:偉林 ? 2022-06-16 10:32 ? 次閱讀

1. hook一般syscall

在安全、性能分析等領(lǐng)域,經(jīng)常會(huì)需要對(duì)系統(tǒng)調(diào)用syscall進(jìn)行hook。有些模塊在kernel代碼中已經(jīng)預(yù)先hook,例如syscall trace event。

通常syscall使用sys_call_table[]數(shù)組來(lái)間接調(diào)用:


kernelarchx86kernelentry_64.S:
ENTRY(system_call)
  call *sys_call_table(,%rax,8)  # XXX:   rip relative

sys_call_table[]數(shù)組中保存的是所有系統(tǒng)調(diào)用的函數(shù)指針:


#define __SYSCALL(nr, sym) [nr] = sym,
const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {    #define __NR_read        0    __SYSCALL(__NR_read, sys_read)    #define __NR_write        1    __SYSCALL(__NR_write, sys_write)    #define __NR_open        2    __SYSCALL(__NR_open, sys_open)    #define __NR_close        3    __SYSCALL(__NR_close, sys_close)
    ...};

對(duì)于其他沒有預(yù)置代碼的模塊來(lái)說(shuō),需要在運(yùn)行的時(shí)候動(dòng)態(tài)hook,通常我們使用inline hook。inline hook的好處是hook完以后,運(yùn)行時(shí)零開銷。

實(shí)例代碼:

void syscallxxx_hook_init(void){  unsigned long *sct;    void ** g_syscall_table;
    g_syscall_table = (void **)kallsyms_lookup_name("sys_call_table");      make_kernel_page_readwrite();  preempt_disable();      /* (1) 備份原有g(shù)_syscall_table[]數(shù)組中的函數(shù)指針 */  orig_syscallxxx = (void *)g_syscall_table[__NR_syscallxxx];    /* (2) 把g_syscall_table[]數(shù)組值改為新的函數(shù)指針 */  sct[__NR_syscallxxx] = (unsigned long)new_syscallxxx;    preempt_enable();  make_kernel_page_readonly();}

asmlinkage long new_syscallxxx(...){  long rc;      /* (2.1) 做一些hook增加的事情 */  rc = do_something(...);  if (0 != rc)      return rc;       /* (2.2) 調(diào)用原有的syscall處理 */  return orig_syscallxxx(....); }

這種hook方式在大部分情況下工作正常,但是某些特殊的系統(tǒng)調(diào)用會(huì)工作異常。

2. hook stub syscall

2.1 stub_xxx 原理

在4.5版本及以下的內(nèi)核中,x86架構(gòu)對(duì)某些系統(tǒng)調(diào)用有特殊處理。我們可以在sys_call_table[]數(shù)組中看到的函數(shù)不是sys_xxx而是stub_xxx:

const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
    #define __NR_rt_sigreturn      15    __SYSCALL(__NR_rt_sigreturn, stub_rt_sigreturn)
    #define __NR_clone        56    __SYSCALL(__NR_clone, stub_clone)    #define __NR_fork        57    __SYSCALL(__NR_fork, stub_fork)    #define __NR_vfork        58    __SYSCALL(__NR_vfork, stub_vfork)    #define __NR_execve        59    __SYSCALL(__NR_execve, stub_execve)
    #define __NR_sigaltstack      131    __SYSCALL(__NR_sigaltstack, stub_sigaltstack)
    #define __NR_iopl        172    __SYSCALL(__NR_iopl, stub_iopl)    ...};

這有點(diǎn)出乎我們的意料,字面上理解是一些樁函數(shù),我們看看其具體做了些什么:

kernelarchx86kernelentry_64.S:
/* * Certain special system calls that need to save a complete full stack frame. */  .macro PTREGSCALL label,func,argENTRY(label)  PARTIAL_FRAME 1 8    /* offset 8: return address */  subq $REST_SKIP, %rsp  CFI_ADJUST_CFA_OFFSET REST_SKIP  call save_rest  DEFAULT_FRAME -2 8    /* offset 8: return address */  leaq 8(%rsp), arg  /* pt_regs pointer */  call func              /* (1.1) 調(diào)用實(shí)際的系統(tǒng)調(diào)用sys_xxx()函數(shù) */  jmp ptregscall_common  CFI_ENDPROCEND(label)  .endm
    /* (1) stub_clone/fork/vfork/sigaltstack/iopl 函數(shù)的定義 */  PTREGSCALL stub_clone, sys_clone, %r8  PTREGSCALL stub_fork, sys_fork, %rdi  PTREGSCALL stub_vfork, sys_vfork, %rdi  PTREGSCALL stub_sigaltstack, sys_sigaltstack, %rdx  PTREGSCALL stub_iopl, sys_iopl, %rsi
ENTRY(ptregscall_common)  DEFAULT_FRAME 1 8  /* offset 8: return address */  RESTORE_TOP_OF_STACK %r11, 8  movq_cfi_restore R15+8, r15  movq_cfi_restore R14+8, r14  movq_cfi_restore R13+8, r13  movq_cfi_restore R12+8, r12  movq_cfi_restore RBP+8, rbp  movq_cfi_restore RBX+8, rbx  ret $REST_SKIP    /* pop extended registers */  CFI_ENDPROCEND(ptregscall_common)
    /* (2) stub_execve函數(shù)的定義 */ENTRY(stub_execve)  CFI_STARTPROC  addq $8, %rsp  PARTIAL_FRAME 0  SAVE_REST  FIXUP_TOP_OF_STACK %r11  movq %rsp, %rcx  call sys_execve             /* (2.1) 調(diào)用實(shí)際的系統(tǒng)調(diào)用sys_execve()函數(shù) */  RESTORE_TOP_OF_STACK %r11  movq %rax,RAX(%rsp)  RESTORE_REST  jmp int_ret_from_sys_call  CFI_ENDPROCEND(stub_execve)
/* * sigreturn is special because it needs to restore all registers on return. * This cannot be done with SYSRET, so use the IRET return path instead. */    /* (3) stub_rt_sigreturn函數(shù)的定義 */ENTRY(stub_rt_sigreturn)  CFI_STARTPROC  addq $8, %rsp  PARTIAL_FRAME 0  SAVE_REST  movq %rsp,%rdi  FIXUP_TOP_OF_STACK %r11  call sys_rt_sigreturn       /* (3.1) 調(diào)用實(shí)際的系統(tǒng)調(diào)用sys_rt_sigreturn()函數(shù) */  movq %rax,RAX(%rsp) # fixme, this could be done at the higher layer  RESTORE_REST  jmp int_ret_from_sys_call  CFI_ENDPROCEND(stub_rt_sigreturn)

為什么系統(tǒng)要對(duì)這幾個(gè)系統(tǒng)調(diào)用做stub_xxx的特殊處理?

注釋中的一段話說(shuō)明了大概原因:

/* * Certain special system calls that need to save a complete full stack frame. * 某些特殊的系統(tǒng)調(diào)用需要保存完整的完整堆棧幀。*/

針對(duì)這類特殊的系統(tǒng)調(diào)用,我們有兩種方法來(lái)進(jìn)行hook。

2.2 方法1:hook stub_xxx

第一種方法我們還是繼續(xù)替換sys_call_table[]數(shù)組中函數(shù)指針,但是要自己處理hook函數(shù)的棧平衡。

寫一段自己的stub_new_syscallxxx函數(shù)來(lái)替換原有的stub_syscallxxx函數(shù):

stub_new_syscallxxx:    /**     * (1.1) 保存寄存器狀態(tài), 保證之后調(diào)用原來(lái)的stub_syscallxxx的時(shí)候CPU執(zhí)行環(huán)境一致     * 其中rdi,rsi,rdx,rcx,rax,r8,r9,r10,r11保存sysenter的參數(shù),rbx作為臨時(shí)變量     */    pushq   %rbx    pushq   %rdi    pushq   %rsi    pushq   %rdx    pushq   %rcx    pushq   %rax    pushq   %r8    pushq   %r9    pushq   %r10    pushq   %r11   /* (1.2) 調(diào)用自己的hook函數(shù) */    call    new_syscallxxx    test    %rax, %rax    movq    %rax, %rbx     /* (1.3) 恢復(fù)寄存器狀態(tài) */    pop     %r11    pop     %r10    pop     %r9    pop     %r8    pop     %rax    pop     %rcx    pop     %rdx    pop     %rsi    pop     %rdi     jz      new_syscallxxx_done        /* (2.1) new_syscallxxx返回值為非0時(shí) */    movq    %rbx, %rax    pop     %rbx    ret   /* 這里不一定要jmp int_ret_from_sys_call,反正syscallxxx已經(jīng)被我們攔截了 */        /* (2.2) new_syscallxxx返回值為0時(shí) */new_syscallxxx_done:    pop     %rbxjmp*orig_sys_call_table(,%rax,8)/*調(diào)用原始的stub_syscallxxx*/

這種方法要小心處理調(diào)用堆棧,在我們hook函數(shù)運(yùn)行之前要小心的保護(hù)堆棧,在hook函數(shù)運(yùn)行完成后要完全恢復(fù)堆棧。而且不方便實(shí)現(xiàn)post hook。

2.3 方法2:hook call sys_xxx

另一種方法我們替換stub_syscallxxx函數(shù)中的call sys_syscallxxx語(yǔ)句。例如:

ENTRY(stub_execve)  CFI_STARTPROC  addq $8, %rsp  PARTIAL_FRAME 0  SAVE_REST  FIXUP_TOP_OF_STACK %r11  movq %rsp, %rcx  call sys_execve             // 替換call語(yǔ)句中的sys_execve為new_sys_execve  RESTORE_TOP_OF_STACK %r11  movq %rax,RAX(%rsp)  RESTORE_REST  jmp int_ret_from_sys_call  CFI_ENDPROCEND(stub_execve)

查看原始指令碼:

(gdb) disassemble /r stub_execveDump of assembler code for function stub_execve:   0xffffffff8146f7e0 <+0>:     48 83 c4 08     add    $0x8,%rsp   ...   0xffffffff8146f847 <+103>:   e8 74 b2 b9 ff  callq  0xffffffff8100aac0   // call sys_execve   ...   0xffffffff8146f890 <+176>:   e9 77 fd ff ff  jmpq   0xffffffff8146f60c End of assembler dump.(gdb) p sys_execve$2={long(constchar*,constchar*const*,constchar*const*,structpt_regs*)}0xffffffff8100aac0

我們可以看到call sys_execve對(duì)應(yīng)的命令碼為e8 74 b2 b9 ff,其中:

  • e8對(duì)應(yīng)call指令。

  • ffb9b274表示被調(diào)用函數(shù)和當(dāng)前pc的偏移:

call函數(shù)地址 - 當(dāng)前地址 - 當(dāng)前指令長(zhǎng)度 = offset0xffffffff8100aac0-0xffffffff8146f847-5=0xFFFFFFFFFFB9B274&0xFFFFFFFF=0xFFB9B274

所以我們只要定義個(gè)參數(shù)完全一致的新函數(shù)new_sys_execve(),把sys_execve()的對(duì)應(yīng)偏移ffb9b274替換成new_sys_execve()的相對(duì)偏移即可。

static asmlinkage long new_sys_execve(const char __user * filename,        const char __user * const __user * argv,        const char __user * const __user * envp, struct pt_regs *regs) {  size_t exec_line_size;  char * exec_str = NULL;  char ** p_argv = (char **) argv;    long ret = 0;
    /* (1) pre hook 點(diǎn) */
  /* Finally, call the original sys_execve */    /* (2) 調(diào)用原始系統(tǒng)調(diào)用 */  ret = orig_sys_execve_fn(filename, argv, envp, regs);
    /* (3) post hook 點(diǎn) */    printk("orig_sys_execve_fn ret = %d
", ret);
    return ret;}

具體代碼放在inlinehook_syscall_example。

參考文檔:

1.x86平臺(tái)inline hook原理和實(shí)現(xiàn)

2.execmon

3.Linux x64下hook系統(tǒng)調(diào)用execve的正確方法

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 模塊
    +關(guān)注

    關(guān)注

    7

    文章

    2713

    瀏覽量

    47485
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68625
  • inline
    +關(guān)注

    關(guān)注

    0

    文章

    4

    瀏覽量

    1641

原文標(biāo)題:Inline Hook Syscall 詳解

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    什么是HOOK函數(shù)?HOOK函數(shù)的用途和用法

    市面上絕大多數(shù)操作系統(tǒng),都存在這樣一類API函數(shù)接口:HOOK函數(shù)(也叫鉤子函數(shù))。
    發(fā)表于 09-13 09:31 ?2319次閱讀
    什么是<b class='flag-5'>HOOK</b>函數(shù)?<b class='flag-5'>HOOK</b>函數(shù)的用途和用法

    請(qǐng)問(wèn)static inline有什么作用?

    弱弱的問(wèn)一下 static inline 有什么作用?1. uboot的arch\arm\include\asm\arch下的s3c2400.h中:static inline struct
    發(fā)表于 04-28 06:56

    linux的類似hook函數(shù)

    hook在windows下可以說(shuō)是知名度相當(dāng)高的一種"高級(jí)“技術(shù)想在linux下面實(shí)現(xiàn)像windows下的那種hook的功能,不過(guò)網(wǎng)上的資料很少(LD_PRELOAD 也可以做類似的事)
    發(fā)表于 07-25 07:48

    怎樣去使用RT-Thread系統(tǒng)中的Hook功能呢

    1、綜述? 從4.1.0版本開始,RT-Thread在保證向前兼容的基礎(chǔ)上對(duì)原有的HOOK方式進(jìn)行了改進(jìn),實(shí)現(xiàn)了如下的效果:與原有使用函數(shù)指針進(jìn)行“運(yùn)行時(shí)刻”HOOK的方式兼容,依賴原有方式進(jìn)行代碼
    發(fā)表于 06-14 10:34

    講講Hook技術(shù)的攻防對(duì)抗思路

    ,一類是針對(duì)Native層,即系統(tǒng)層的Hook框架,使用PLT/GOTHook或inline-Hook的框架,如bhook、xhook、yahfa等,一類是針對(duì)Java層的Hook框架,如Xposed
    發(fā)表于 09-28 11:12

    利用Hook技術(shù)實(shí)現(xiàn)進(jìn)程控制

    Windows 系統(tǒng)是基于消息,建立在事件驅(qū)動(dòng)基礎(chǔ)上的操作系統(tǒng)。Hook 是Windows 系統(tǒng)消息處理機(jī)制中的一個(gè)監(jiān)視點(diǎn)。Hook 機(jī)制允許應(yīng)用程序截獲消息并進(jìn)行處理,它為我們實(shí)現(xiàn)進(jìn)程的控制
    發(fā)表于 09-12 16:08 ?10次下載

    Linux下的網(wǎng)絡(luò)HOOK實(shí)現(xiàn)

    最近瘋狂的研究Linux的種種功能,也頗有心得,這里講述一下Linux下的Net的Hook,使用net的Hook可以實(shí)現(xiàn)很多很多非常底層的功能
    發(fā)表于 05-14 10:27 ?5148次閱讀

    基于NDIS-HOOK的個(gè)人防水墻設(shè)計(jì)

    為了解決網(wǎng)絡(luò)數(shù)據(jù)泄露的問(wèn)題,討論了防水墻的概念和NDIS概念結(jié)構(gòu),研究了NDIS數(shù)據(jù)包發(fā)送流程和NDIS-HOOK數(shù)據(jù)包發(fā)送流程,并簡(jiǎn)要分析了NDIS-HOOK技術(shù)在防水墻數(shù)據(jù)包處理中的應(yīng)用,討論
    發(fā)表于 07-06 15:34 ?0次下載
    基于NDIS-<b class='flag-5'>HOOK</b>的個(gè)人防水墻設(shè)計(jì)

    在嵌入式設(shè)備中使用Malloc Hook的試驗(yàn)

    在嵌入式設(shè)備中,計(jì)劃使用malloc hook來(lái)進(jìn)行內(nèi)存跟蹤,以便測(cè)試程序的內(nèi)存使用。 試驗(yàn)1: 在程序開始,增加了mtrace函數(shù),定義環(huán)境變量MALLOC_TRACE。 發(fā)現(xiàn)了
    發(fā)表于 04-02 14:37 ?697次閱讀

    內(nèi)核級(jí)HOOK的幾種實(shí)現(xiàn)方法與應(yīng)用說(shuō)明

    實(shí)現(xiàn)內(nèi)核級(jí)HOOK 對(duì)于攔截、分析、跟蹤系統(tǒng)內(nèi)核起著致關(guān)重要的作用。實(shí)現(xiàn)的方法不同意味著應(yīng)用側(cè)重點(diǎn)的不同。如想要攔截NATIVE API 那么可能常用的就是HOOKSERVICE TABLE 的方法
    發(fā)表于 11-10 17:35 ?5次下載

    使用內(nèi)核三步實(shí)現(xiàn)InlineHook的詳細(xì)分析

    Inline hook.通俗的說(shuō)就是對(duì)函數(shù)執(zhí)行流程進(jìn)行修改。達(dá)到控制函數(shù)過(guò)濾操作的目的。理論上我們可以在函數(shù)任何地方把原來(lái)指令替換成我們的跳轉(zhuǎn)指令,也確實(shí)有些人在inline Hook
    發(fā)表于 02-01 09:58 ?5次下載
    使用內(nèi)核三步實(shí)現(xiàn)InlineHook的詳細(xì)分析

    RTOS操作系統(tǒng)中HOOK函數(shù)有什么用途?

    在很多操作系統(tǒng)中,都存在這樣一類API(函數(shù)接口):HOOK函數(shù),也叫鉤子函數(shù)。 比如:Windows桌面系統(tǒng)、μC/OS、 FreeRTOS等RTOS中,都可以看見HOOK函數(shù)的存在。 下面結(jié)合
    的頭像 發(fā)表于 03-29 11:28 ?1828次閱讀
    RTOS操作系統(tǒng)中<b class='flag-5'>HOOK</b>函數(shù)有什么用途?

    C++基礎(chǔ)語(yǔ)法之inline 內(nèi)聯(lián)函數(shù)

    上節(jié)我們分析了C++基礎(chǔ)語(yǔ)法的const,static以及 this 指針,那么這節(jié)內(nèi)容我們來(lái)看一下 inline 內(nèi)聯(lián)函數(shù)吧! inline 內(nèi)聯(lián)函數(shù) 特征 相當(dāng)于把內(nèi)聯(lián)函數(shù)里面的內(nèi)容寫在調(diào)用內(nèi)聯(lián)
    的頭像 發(fā)表于 09-09 09:38 ?2151次閱讀

    C++語(yǔ)法中的inline內(nèi)聯(lián)函數(shù)詳解

    上節(jié)我們分析了C++基礎(chǔ)語(yǔ)法的const,static以及 this 指針,那么這節(jié)內(nèi)容我們來(lái)看一下 inline 內(nèi)聯(lián)函數(shù)吧! inline 內(nèi)聯(lián)函數(shù) 特征 相當(dāng)于把內(nèi)聯(lián)函數(shù)里面的內(nèi)容寫在調(diào)用內(nèi)聯(lián)
    的頭像 發(fā)表于 09-09 09:33 ?3364次閱讀

    HOOK函數(shù)概述、用途和用法

      在很多操作系統(tǒng)中,都存在這樣一類API函數(shù)接口:HOOK函數(shù)(也叫鉤子函數(shù))。
    的頭像 發(fā)表于 04-12 20:08 ?1.4w次閱讀