独自快乐|怎样Hack Linux的内核符号?( 二 )
普通需求到这里就完事了 , 但是针对客户的特殊场景 , 稍微思考一下就会发现有很大缺陷 。 假如修复补丁中一共涉及到了数百个未导出的函数 , 我们则要在修复代码中把所有使用到这些函数的地方全部修改成函数指针调用的形式 , 工作量增加了不少 。 最简单的解决办法是内核加载修复模块时 , 单独走Kallsyms解析模块符号 , 而绕过export_symbols这个符号子集(前提是不引入新的内核安全风险) 。
【独自快乐|怎样Hack Linux的内核符号?】Linux内核模块的加载过程其实跟可执行程序加载动态链接库的过程是一样的 。 举个简单例子 , 在printf(“hello world”)中 , 我们其实并没有实现printf(由puts函数封装而来) 。 它实际是由Libc库实现 。 当我们运行HelloWorld程序的时候 , 操作系统会解析程序符号 , 载入依赖的动态链接库(每次加载的基址可能不同) , 计算重定位符号地址 , 并把地址填回HelloWorld程序中 。 我们可以通过下图过程来验证:
对于Linux内核模块而言 , 它本质上也是动态链接库 , 因此加载模块时必然存在解析符号地址的函数 。 于是我们的思路是 , 动态拦截该函数 , 重定向到我们的替换函数中 , 并在替换函数中添加Kallsyms查找符号地址的逻辑即可:左图为我们的替换函数 , 右图为内核原始函数 。 这样达到的效果是 , 我们可以在CVE修复代码中直接使用诸如d_absolute_path这样的未导出函数 , 而不用做任何函数指针形式的改造 , 便于漏洞修复过程的自动化 。
可能会有同学感兴趣我们是如何实现内核函数拦截的 , 即如何从find_symbol_in_section跳转到hook_find_symbol_in_section , 这里以ARM64架构CPU为例简单说明 。 我们在内核的find_symbol_in_section函数开头插入了下图所示的汇编指令(以二进制形式修改内核代码区) 。 这里借用了x0寄存器作远距离跳转(从内核跳转到内核模块) 。 由于无条件跳转不应该产生任何副作用(即栈帧和寄存器不能改变) , 因此我们需要先保存x0的值到栈上 , 远跳转后再恢复x0内容 。
ldr指令从.addr(low)和.addr(high)中把跳板函数地址装载进x0 , 注意到ARM64的地址长度为64位 , 而ARM64的指令长度为32位 , 因此跳板函数地址被折成低32位和高32位 。 进入跳板函数后先恢复x0寄存器值 , 再做近距离跳转(内核模块内部跳转) , 注意前图hook_find_symbol_in_section函数末尾有一行HOOK_FUNC_TEMPLATE(find_symbol_in_section) , 即为宏定义的find_symbol_in_section的跳板函数 。 这样经过连续无条件跳转后 , 执行流被拦截到我们的HOOK函数中 。
此外顺便多提一下 , 上述使用Inline Hook技术的拦截方式跟CPU架构是强相关的 , 如果想实现ARM32或x86架构的函数拦截 , 则需要分别单独实现 。
文/ThoughtWorks刘涛
推荐阅读
- 董明珠|当年“骗”董明珠26个亿的魏银仓,带着钱逃往美国后,结果怎样了?
- 郑强|当初那个认为科学有国界,要取消英语高考的郑强,现再怎样了
- 津东小霸王|王者荣耀:深度剖析eStar诺言猫神赛后言行!传递怎样的信息?
- 虚拟偶像密集进入大众视线 折射出怎样的文化心态?
- 半月谈|虚拟偶像密集进入大众视线 折射出怎样的文化心态?
- 文化关注|哭着求原谅的刘露,今怎样了,她认为自己很红?辱骂民警大闹车站
- 刘芯小虞姬|喊出这么多鱼来,搞笑GIF:怎样才能像你一样
- 梅花之快乐|上层阶级如何产生?耶鲁大学老鼠阶级实验告诉你
- 喵家影视|就能够体验到它的快乐,路亚只要有一定的资源
- 逍遥网游代练工作室|在被刀之后怎样发挥他最大的效用?,王者荣耀姜子牙S20玩法教学