0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Inline Hook Syscall详解

Linux阅码场 来源:Linux阅码场 作者:伟林 2022-06-16 10:32 次阅读

1. hook一般syscall

在安全、性能分析等领域,经常会需要对系统调用syscall进行hook。有些模块在kernel代码中已经预先hook,例如syscall trace event。

通常syscall使用sys_call_table[]数组来间接调用:


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

sys_call_table[]数组中保存的是所有系统调用的函数指针:


#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)
    ...};

对于其他没有预置代码的模块来说,需要在运行的时候动态hook,通常我们使用inline hook。inline hook的好处是hook完以后,运行时零开销。

实例代码:

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_syscall_table[]数组中的函数指针 */  orig_syscallxxx = (void *)g_syscall_table[__NR_syscallxxx];    /* (2) 把g_syscall_table[]数组值改为新的函数指针 */  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) 调用原有的syscall处理 */  return orig_syscallxxx(....); }

这种hook方式在大部分情况下工作正常,但是某些特殊的系统调用会工作异常。

2. hook stub syscall

2.1 stub_xxx 原理

在4.5版本及以下的内核中,x86架构对某些系统调用有特殊处理。我们可以在sys_call_table[]数组中看到的函数不是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)    ...};

这有点出乎我们的意料,字面上理解是一些桩函数,我们看看其具体做了些什么:

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) 调用实际的系统调用sys_xxx()函数 */  jmp ptregscall_common  CFI_ENDPROCEND(label)  .endm
    /* (1) stub_clone/fork/vfork/sigaltstack/iopl 函数的定义 */  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函数的定义 */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) 调用实际的系统调用sys_execve()函数 */  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函数的定义 */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) 调用实际的系统调用sys_rt_sigreturn()函数 */  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)

为什么系统要对这几个系统调用做stub_xxx的特殊处理?

注释中的一段话说明了大概原因:

/* * Certain special system calls that need to save a complete full stack frame. * 某些特殊的系统调用需要保存完整的完整堆栈帧。*/

针对这类特殊的系统调用,我们有两种方法来进行hook。

2.2 方法1:hook stub_xxx

第一种方法我们还是继续替换sys_call_table[]数组中函数指针,但是要自己处理hook函数的栈平衡。

写一段自己的stub_new_syscallxxx函数来替换原有的stub_syscallxxx函数:

stub_new_syscallxxx:    /**     * (1.1) 保存寄存器状态, 保证之后调用原来的stub_syscallxxx的时候CPU执行环境一致     * 其中rdi,rsi,rdx,rcx,rax,r8,r9,r10,r11保存sysenter的参数,rbx作为临时变量     */    pushq   %rbx    pushq   %rdi    pushq   %rsi    pushq   %rdx    pushq   %rcx    pushq   %rax    pushq   %r8    pushq   %r9    pushq   %r10    pushq   %r11   /* (1.2) 调用自己的hook函数 */    call    new_syscallxxx    test    %rax, %rax    movq    %rax, %rbx     /* (1.3) 恢复寄存器状态 */    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时 */    movq    %rbx, %rax    pop     %rbx    ret   /* 这里不一定要jmp int_ret_from_sys_call,反正syscallxxx已经被我们拦截了 */        /* (2.2) new_syscallxxx返回值为0时 */new_syscallxxx_done:    pop     %rbxjmp*orig_sys_call_table(,%rax,8)/*调用原始的stub_syscallxxx*/

这种方法要小心处理调用堆栈,在我们hook函数运行之前要小心的保护堆栈,在hook函数运行完成后要完全恢复堆栈。而且不方便实现post hook。

2.3 方法2:hook call sys_xxx

另一种方法我们替换stub_syscallxxx函数中的call sys_syscallxxx语句。例如:

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语句中的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对应的命令码为e8 74 b2 b9 ff,其中:

  • e8对应call指令。

  • ffb9b274表示被调用函数和当前pc的偏移:

call函数地址 - 当前地址 - 当前指令长度 = offset0xffffffff8100aac0-0xffffffff8146f847-5=0xFFFFFFFFFFB9B274&0xFFFFFFFF=0xFFB9B274

所以我们只要定义个参数完全一致的新函数new_sys_execve(),把sys_execve()的对应偏移ffb9b274替换成new_sys_execve()的相对偏移即可。

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 点 */
  /* Finally, call the original sys_execve */    /* (2) 调用原始系统调用 */  ret = orig_sys_execve_fn(filename, argv, envp, regs);
    /* (3) post hook 点 */    printk("orig_sys_execve_fn ret = %d
", ret);
    return ret;}

具体代码放在inlinehook_syscall_example。

参考文档:

1.x86平台inline hook原理和实现

2.execmon

3.Linux x64下hook系统调用execve的正确方法

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 模块
    +关注

    关注

    7

    文章

    2707

    浏览量

    47476
  • 代码
    +关注

    关注

    30

    文章

    4788

    浏览量

    68616
  • inline
    +关注

    关注

    0

    文章

    4

    浏览量

    1641

原文标题:Inline Hook Syscall 详解

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    什么是HOOK函数?HOOK函数的用途和用法

    市面上绝大多数操作系统,都存在这样一类API函数接口:HOOK函数(也叫钩子函数)。
    发表于 09-13 09:31 2314次阅读
    什么是<b class='flag-5'>HOOK</b>函数?<b class='flag-5'>HOOK</b>函数的用途和用法

    请问static inline有什么作用?

    弱弱的问一下 static inline 有什么作用?1. uboot的arch\arm\include\asm\arch下的s3c2400.h中:static inline struct
    发表于 04-28 06:56

    linux的类似hook函数

    hook在windows下可以说是知名度相当高的一种"高级“技术想在linux下面实现像windows下的那种hook的功能,不过网上的资料很少(LD_PRELOAD 也可以做类似的事)
    发表于 07-25 07:48

    怎样去使用RT-Thread系统中的Hook功能呢

    1、综述​ 从4.1.0版本开始,RT-Thread在保证向前兼容的基础上对原有的HOOK方式进行了改进,实现了如下的效果:与原有使用函数指针进行“运行时刻”HOOK的方式兼容,依赖原有方式进行代码
    发表于 06-14 10:34

    讲讲Hook技术的攻防对抗思路

    ,一类是针对Native层,即系统层的Hook框架,使用PLT/GOTHook或inline-Hook的框架,如bhook、xhook、yahfa等,一类是针对Java层的Hook框架,如Xposed
    发表于 09-28 11:12

    利用Hook技术实现进程控制

    Windows 系统是基于消息,建立在事件驱动基础上的操作系统。Hook 是Windows 系统消息处理机制中的一个监视点。Hook 机制允许应用程序截获消息并进行处理,它为我们实现进程的控制
    发表于 09-12 16:08 10次下载

    Linux下的网络HOOK实现

    最近疯狂的研究Linux的种种功能,也颇有心得,这里讲述一下Linux下的Net的Hook,使用net的Hook可以实现很多很多非常底层的功能
    发表于 05-14 10:27 5146次阅读

    基于NDIS-HOOK的个人防水墙设计

    为了解决网络数据泄露的问题,讨论了防水墙的概念和NDIS概念结构,研究了NDIS数据包发送流程和NDIS-HOOK数据包发送流程,并简要分析了NDIS-HOOK技术在防水墙数据包处理中的应用,讨论
    发表于 07-06 15:34 0次下载
    基于NDIS-<b class='flag-5'>HOOK</b>的个人防水墙设计

    在嵌入式设备中使用Malloc Hook的试验

    在嵌入式设备中,计划使用malloc hook来进行内存跟踪,以便测试程序的内存使用。 试验1: 在程序开始,增加了mtrace函数,定义环境变量MALLOC_TRACE。 发现了
    发表于 04-02 14:37 697次阅读

    内核级HOOK的几种实现方法与应用说明

    实现内核级HOOK 对于拦截、分析、跟踪系统内核起着致关重要的作用。实现的方法不同意味着应用侧重点的不同。如想要拦截NATIVE API 那么可能常用的就是HOOKSERVICE TABLE 的方法
    发表于 11-10 17:35 5次下载

    使用内核三步实现InlineHook的详细分析

    Inline hook.通俗的说就是对函数执行流程进行修改。达到控制函数过滤操作的目的。理论上我们可以在函数任何地方把原来指令替换成我们的跳转指令,也确实有些人在inline Hook
    发表于 02-01 09:58 5次下载
    使用内核三步实现InlineHook的详细分析

    RTOS操作系统中HOOK函数有什么用途?

    在很多操作系统中,都存在这样一类API(函数接口):HOOK函数,也叫钩子函数。 比如:Windows桌面系统、µC/OS、 FreeRTOS等RTOS中,都可以看见HOOK函数的存在。 下面结合
    的头像 发表于 03-29 11:28 1828次阅读
    RTOS操作系统中<b class='flag-5'>HOOK</b>函数有什么用途?

    C++基础语法之inline 内联函数

    上节我们分析了C++基础语法的const,static以及 this 指针,那么这节内容我们来看一下 inline 内联函数吧! inline 内联函数 特征 相当于把内联函数里面的内容写在调用内联
    的头像 发表于 09-09 09:38 2150次阅读

    C++语法中的inline内联函数详解

    上节我们分析了C++基础语法的const,static以及 this 指针,那么这节内容我们来看一下 inline 内联函数吧! inline 内联函数 特征 相当于把内联函数里面的内容写在调用内联
    的头像 发表于 09-09 09:33 3363次阅读

    HOOK函数概述、用途和用法

      在很多操作系统中,都存在这样一类API函数接口:HOOK函数(也叫钩子函数)。
    的头像 发表于 04-12 20:08 1.4w次阅读