12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 178|回复: 0

x64平台下inline hook的原理、实现与防御

[复制链接]

282

主题

282

帖子

574

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
574
发表于 2022-1-28 19:34:40 | 显示全部楼层 |阅读模式
0x01 概述
inline hook是一项改变函数调用控制流的技术。与传统的PLT(或IAT)表挟制,库打桩等技术不同,inline hook会修改函数自己(通常是头部的数个字节)来举行函数的hook。inline hook较PLT挟制更复杂,但是不会受到PLT挟制技术的诸多限定。例如PLT挟制/库打桩只能控制ELF导入的外部动态库函数,对于库内函数调用和自己程序内部函数调用无法hook。而inline hook几乎能顺应所有情况。
本文通过运行时库打桩引入,主要介绍Linux X64平台下inline hook的原理和实现,windows平台的相关实现其实异曲同工。

0x02 前排说明
这应该是高考前发的最后一篇文章了,主要为了给高考攒点品德
因为精力有限,时间急忙,文章着重讲述实现原理和边界细节,给出的代码片段主要是为了概念演示而不能直接运行。如果文章内容有疏漏,还请您多多指教。


0x03 情境导入
X同学硕招简历填了一个自己的github项目,项目主要是通过hook来给某闭源的软件扩展功能和修bug。硕招面试时,导师对他的项目非常感兴趣,问他hook的具体实现。X同学懵了,他用的就是一个现成的库啊,应该关注的重点岂非不是如何靠逆向来修复软件bug吗。
一阵沉默...X同学只想着:hook是什么?能吃吗?好吃吗?回家后,X同学打开了电脑,搜索了自己用的库的介绍,发现它用了一个叫做inline hook的神奇玩意。用某搜索软件搜一下,发现第一页全部都是复读机(一个"转载"一个),而且都是描述x86下实现的远古文章了。于是X同学决定自(qiu)己(zhu)动(wang)手(you)研究。


0x04 大道至简——从库打桩开始

(限于篇幅原因,这里不介绍库打桩的背景知识了,因为库打桩和inline hook也没有关系。)X同学非常庆幸自己曾经读过CSAPP,内里有一章就是介绍库打桩技术的,看起来也和hook相关。他马上动手尝试,搞出来一个测试环境。
目标就是挟制rand函数,让他输出自己的幸运数字

根据书上的指示,他又写了一个假的rand函数,编译到a.so内里


最后通过LD_PRELOAD=./a.so ./a.out 成功挟制了a.out内里临rand()的调用。

"这就是hook吗?爱了爱了" X同学以为自己已经掌握了核心技术。然而当他尝试用同样的方法改写a.out内里的其他函数时却失败了。
颠末查(zi)阅(xun)资(da)料(lao),他得到了一个非常普通的原理描述。最开始的a.out是动态链接的产物,有的必要用的函数其实都没有编译进去只有程序装载时(RTLD_NOW/RELRO)大概真正用到这个函数时(lazy binding),才会由动态链接器去探求这个函数并且提供给a.out
这就是为什么rand可以偷梁换柱,而main不能了。因为rand是一个外部动态链接库提供的,可以通过哄骗动态链接器加载自己的实现。而main函数是被编译到a.out内里的,不涉及动态链接,以是无法用这种方法hook。



0x05 狸猫换太子——实现inline hook

为了避免第二天面试的尴尬,X同学只能继续硬杠这个问题。他突然想到,为什么不能直接修改被调用的函数呢?翻了翻指令手册,他发现,其实把函数头部改成jmp ->到自己的函数就可以了。他直接手写汇编,把函数头部覆盖成



jmp *0(%rip) 跳转到64位地址.... (指令ff 25 00 00 00 00)
.dq 0x7ffffff123456 跳转地址


memcpy(&victim,"\xff\x25\x00\x00\x00\x00......",14)一共花了14个字节,就完成了任务。当他兴奋地直接覆盖时,喜提段错误。


!上图为32位体系的示意图,和64位体系不同,仅供类比明白



原来他把一个只能读和执行的text段修改了,以是会段错误。
可以通过mprotect来修改指定内存页的掩护属性,先答应写,写完之后再禁止。

很轻松,成功了。然而最关键的问题是,hook的精髓不是直接暴力修改函数,而是修改后还能调用原函数。他都把原函数前14字节覆盖了,还怎么调用?

聪明的他想到了:直接把前14个字节内包罗的指令复制出来,然后在最背面跳回原函数,不就做成了一个original (下文称为shadow function)
任务完成,全文完。



0x06 知识回敲——为什么我的inline hook挂掉了

如果文章只在0x05就完了,那么和网上的绝大多数介绍就基本没啥区别了。"如果hook这么简单,为什么还有那么多人用现成的库呢?"X同学嘀咕。
实际上,很多文章/很多实现很粗暴的库都先做了一个假定,x86函数的开头都是由
mov %edi,%edi
push %ebp
mov %esp,%ebp
构成的,恰好足够放一个32位相对地址jmp。
而在64位平台下一样平常是
push %rbp
mov %rbp,%rsp


一共四个字节,还必要再改一个函数指令才华放得下一个32位相对地址jmp。
在这种情况下,直接把开头的指令复制到shadow function依然能执行。因为开头的指令是rip地址无关,且不会引用函数体。
感性地明白,开头的指令无论放到哪里,放到哪个程序,他都执行了同一件变乱。

但是实际情况真的这么好吗?众所周知,push rbp,rbp=rsp其实是带有frame pointer的函数的经典入口操作,gcc只必要开到O2及以上就会打开-fomit-frame-pointer,函数开头的这几个指令就被删除了

没有了这4字节的固定指令,意味着我们必须得处置处罚函数的前5字节指令来放置自己的jmp。如果你以为问题依然和上面一样简单的话你就大错特错了。
以此函数为例说明


纵然还存在push rbp,rbp=rsp的固定结构,第三条指令却是一个rip地址相关指令(rip相对寻址),无法继续复制到shadow function执行



下面的描述大概有点难懂,建议搭配文末的图片食用。
起首必要考虑rip相关指令
例如 lea 0x4(%rip),%rax就是一个rip相关指令。因为他的操作数的计算用到了rip寄存器的值。也就是说,这个指令在0x100000和0x700000处执行的结果是不同的。如果还按照上面的方法暴力复制出来,很显然要挂掉
0x10000 lea 0x4(%rip),%rax   === rax=0x4+当前指令之后的rip =0x10000+4+指令长度
0x70000 lea 0x4(%rip),%rax   === rax=0x4+当前指令之后的rip =0x70000+4+指令长度

如果你愿意翻手册,会发现能把rip当作寻址基址寄存器的指令大把大把....
但没有关系,因为大多数指令的寻址偏移量都是32位的,留给了我们足够的空间去修复
比如把上面0x10000的指令移动到0x70000的时间,就可以改成
0x70000 lea (0x4+0x10000-0x70000)(%rip),%rax   === rax=0x4+当前指令之后的rip-(0x70000-0x10000) =0x10000+4+指令长度
针对这种偏移量的修改,必要用到一些反汇编库,这里推荐ZyDis,这个库可以直接给出操作数对应偏移量在指令内里的具体位置,修改很方便。



再考虑一下rip相关指令的特例
如果开头是一个跳转指令,情况会变得复杂,因为很多函数内跳转指令都是short jmp。也就是说jmp的相对偏移量是一个8位数字比如
0x1000 jmp
.....
0x1009 ret


这里的jmp指令就没办法用上面的办法patch了。因为偏移空间只有8位,是没办法从0x70000一下子跳回去的。一部分hook库遇到了这种情况选择直接拒绝hook。
下面给出一种处置处罚这种指令的方法。只管这样的short jmp指令无法修改偏移,但我们可以通过jmpbed来破解限定。

如今直接从shadow function开始修改

0x700000 jmp +9   //假设这里对应0x1009,忽略指令长度
.....             //其他shadow function指令
0x700009 jmp test+n //跳回原函数执行




第一行的jmp +9显然必要修改,他原本要跳到0x1009。我们考虑给shadow function背面追加一个far jmp,来跳到0x1009


0x70000e jmp test+9 //跳回原地址 (这里是jmpbed)


再修改jmp +9的偏移,让他跳到+e处的jmpbed,就完成了short jmp的patch


0x07 以子之矛,陷子之盾——inline hook的防范

上面已经讨论了inline hook能遇到的绝大多数情况,然而inline hook不是万能的,仍然可以举行反hook。一种直接想到的办法就是检测函数的开头数个字节,如果被修改那么就被hook了,但是这种方法容易被patch(因为有明显的特征),再者每次调用都要检测,非常麻烦



下面介绍一种比较巧妙的方法,来导致hook后无法调用original从而反hook这牵扯到inline hook的第二种边界情况——回跳(第一种即上文描述的rip相关指令)。
我们已经明白了,inlinehook的原理就是覆盖目标函数的前几个字节。但是我们无法包管前几个字节没有被函数内部引用

比如下面的例子


LOC_ENTRY:
        xor %rax,%rax; #48 31 c0,用来初始化循环计数,并且使第二条指令处在jmp HOOKED指令的中间
LOC_ANTIHOOK:
        add $1,%rax; #回跳,若被hook此处会被覆盖从而引发段错误
LOC_CONFUSE_CF:        jmp LOC_ANTIHOOK_LP; #控制流肴杂,骗过hook库对于回跳的分析,认为此处函数结束

        ret;
LOC_ANTIHOOK_LP:        cmp $2,%rax;
        jne LOC_ANTIHOOK;  #回跳
LOC_REAL:  #真实函数体
        mov $1,%rax;
        ret;


由于函数体引用了LOC_ANTIHOOK处的指令,而我们的inline hook又恰好覆盖了这里,以是调用original会导致指令错误,程序停止。



这里是hook前后的对比


后:(可以看到ANTIHOOK被覆盖了)



只管有的hook库做了回跳检测,但是由于是纯裸的反汇编而不是控制流分析,以是通过上面的LOC_CONFUSE_CF就能导致hook库误认为函数结束而忽略下面的回跳

测试一下,会发现original调用后段错误。成功反hook。
反hook实行的源代码见附件,必要linux平台。在funchook 1.1.0下测试通过。

0x08 简略实现&FAQ
为了便于读者明白本文,我写了一个简单的实现(当然也涵盖了文中所描述的全部状态)。实现稍长,已放至附件。



Q:为什么必要辅助长跳。
A:linux平台下主文件和动态库文件装入地址差异在2GB以上,以是不能直接用32位相对跳实现hook。可以用32位indirect jmp来跳到64位任意地址(必要6字节),也可以先32位相对jmp(5字节)跳到一个64位indirect jmp。一样平常来说覆盖字节越少hook遇到复杂情况的概率越小。本实现采用后者5字节的实现
(如果你极端寻求性能的话,也可以用一个indirect jmp+64位地址,一共14字节)

Q:inline hook好像不但仅这一种
A:是的。比如还有分析整个文件,对目标函数做xref然后修改跳到目标函数的指令的。但是这个方法一方面过于依赖静态分析,一方面无法处置处罚indirect call。本文介绍的方法是最流行的且成功率最高的。

Q:大多数情况下并没有遇到文中写到的RIP相关指令的特别情况
A:一些windows的dll为了方便hook,会在函数头放一个空指令便于替换(不相识windows机制,个人推测)。比如上文的mov %edi,%edi,就可以替换成一个short jmp而不影响原函数逻辑。
但是inline hook如今更多的被用在动态调试辅助和二进制热修改上,实境遇到的很多函数都不会有那么优越的条件供hook使用。比如libc的一些函数开头就是lea,必须要举行32位相对patch。



0x09 结语
如王安石在《游褒禅山记》中所言:"而世之奇伟、瑰怪,非常之观,常在于险远,而人之所罕至焉,故非有志者不能至也。"
做学问更要懂得深入的探究,知其然,也要知其以是然。在此分享一些技术上的探究过程,愿与诸君共勉。inline_hook.7z
303.01 KB, 阅读权限: 10, 下载次数: 41, 下载积分: 吾爱币 -1 CB

linux下测试

来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-3-29 03:57 , Processed in 0.109375 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表