Suterusu源码阅读和分析(2)
suterusu的系统调用函数hook部分代码解析,位于util.c
系统函数hook
0x01 获取全局符号表
想要hook内核函数,我们必须先得到syscall表,通过syscall表找到各个函数。这里使用暴搜(获取sys_call_table还有很多别的手段 此处占坑),从sys_close
向下查找,直到syscall_table[__NR_close] == sys_close
,这时候的syscall_table即为全局符号表。
1 |
|
- 用此简单方法找到了sys_call_table,如下图
找到syscall表后我们就可以用宏来查找对应的函数指针,比如syscall_table[__NR_write]
,其中__NR_write
就是系统写函数对应的下标。
0x02 hook函数
hook函数即是将函数指针指向的内容进行改变,同时新函数与原始函数保持相同的参数列表。系统依然会调用函数指针,但每次都会执行的都是我们替换上去的新函数,在新函数内完成自己想要的动作之后,再回调原始函数来完成系统原本的操作。
这里hook函数使用了函数蹦床技术,因为如果直接通过修改函数指针,比如symbol_table[__NR_write] = our_new_pointer
,这样直接将原来的系统函数指针替换成我们的新函数指针很容易被检测到。于是采用更改系统函数指针所指向内存的前几个字节,跳转到我们的新函数上来达到劫持效果。
- hook操作伪代码
1 |
|
- new_func伪代码
1 |
|
写保护的开启关闭
理清了上述hook的流程后,其实还少了几步关键细节没说。如果你想直接对sys_read赋值,sorry 系统有对应的保护机制,防止你做此高危操作,这时候需要关闭写保护。
cr0
- control register,Linux中存在一类称为控制寄存器的register,每一位均代表不同的含义,其中cr0的
WP位
是控制只读区域是否可写。置1时,只读区域只能被读出;置0时,则CPU可以在任意位置进行写入 - 所以我们想将
new_func
写入sys_read
指向的只读区,必须先将cr0
的WP
置0
Linux提供了读写
cr0
的接口unsigned long read_cr0()
write_cr0(unsigned long)
1
2
3
4
5
6
7
8
9unsigned long o_cr0 = read_cr0();
write_cr0(o_cr0 & (~0x10000)); // 将WP位 置0
/*
your codes
*/
write_cr0(o_cr0); // 将原始cr0写入,返回原状态
- control register,Linux中存在一类称为控制寄存器的register,每一位均代表不同的含义,其中cr0的
preempt_disable关闭抢占
barrier内存屏障
- 内存屏障本身不具有运算功能。因此在阅读代码时可以忽略
- 含义:指令实际执行时确保存屏障后代码产生的指令开始执行前,内存屏障之前代码产生的指令一定已经执行结束
- 作用:内存屏障保证编译器优化指令时,barrier前后的指令顺序不变
将上述几个关键点写成代码
1 |
|
0x03 hook管理
找到符号表后想要完成hook动作,同时在后期还要实现对系统函数的恢复、重置、清除,那么我们还需要用一个结构体来保存系统函数位置,以及位置里面的值,为了将此结构体以链表的形式进行管理,我们还需要添加侵入式链表元素,结构体如下。
1 |
|
- 上述结构体配合系统链表的api管理,很常用,这里不赘述了
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!