首页
社区
课程
招聘
[原创] ELF Hook总结与代码实现
发表于: 2019-11-17 16:14 12330

[原创] ELF Hook总结与代码实现

2019-11-17 16:14
12330

博客:b4cK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4N6A6M7X3g2Y4K9r3!0K6N6q4)9J5k6h3y4F1

Hook,中文又译为“挂钩”或“钩子”。这里可以首先从字面上做了解,钩子是干什么的呢?日常生活中,我们的钩子是用来钩住某种东西的,比如说,鱼钩是用来钓鱼的,一旦鱼咬了钩,钩子就一直钩住鱼了,任凭鱼在水里怎么游,也逃不出鱼钩的控制。同样的,Android、IOS中的钩子Hook也是用来钩东西的,比较抽象的是它是用来钩函数或者变量的。举个例子,Hook钩子钩住键盘事件相关的函数,那么当有任何相应的键盘操作时,通过Hook就能知道用户都输入了些什么,多么形象啊,把老鼠Mouse钩住了,不管你干什么,都逃不过我钩子Hook的手掌心。

掌握ELF Hook,有必要先了解下ELF文件格式,特别是对于符号表、GOT(全局偏移表)、PLT(过程链接表)、重定位表需要有个清晰的认识。至于这部分在之前的博文中就已做了详细介绍。。

So的Hook手法大体来说,一共有3种方式:导入表、导出表及Inline Hook。

前面已经提到过“.rel.dyn”和“.rel.plt”两个段中分别保存了该模块所需要导入的变量、函数符号以及其所在模块等信息,而“.got”和“.got.plt”则是保存着这些变量和函数的真正地址。所以,导入表hook的原理主要就是替换.GOT表中外部函数地址(还有一部分被保存在data数据段,比如使用全局函数指针调用外部函数时)

具体思路为:当用dlopen打开一个so时,实际返回的是一个soinfo结构体,这里已经包含了so的相关信息。之后,直接根据“.rel.plt”重定位表中的offset定位到“.got.plt”,进行修改替换即可。
如果细心观察的话,就会注意到上面的代码是有所遗漏的。它仅针对了直接调用外部函数的情况,当使用全局函数指针调用外部函数时(重定位类型为R_ARM_ABS32),用的是".rel.dyn"重定位表,其下的offset会定位到data段,数据段的该地址下保存着实际外部函数的真正地址。对应实现上,只需补个重定位类型的判断,其余代码都是类似的,将".rel.plt"换成".rel.dyn"即可,有兴趣的同学可以自己动手试试。

导入表 HOOK的本质其实就是"改自身",即修改当前应用进程下的Got表中外部符号地址。所以它的功能是很有限的,不会影响到其他进程。此外,对于通过dlopen 动态获得并调用外部符号这种情形是无效的。
特点:即时生效。因为是直接改的Got表、修改了外部函数的调用地址,所以在进行测试时是hook后可直接看到效果的。。

ELF文件中的符号表有说明该符号为导入符号还是导出符号,这点在linker.c源码中可以看到。linker将so加载到内存后,在最后阶段是有对符号进行重定位的。在重定位过程中,如果发现符号为外部符号,会去解析相应的依赖库,获取外部符号的地址。所以,导出表Hook的原理就是修改要hook的函数所在so中的符号表中的值,并对其进行替换。

具体思路为:用dlopen打开一个so,根据返回的soinfo结构体定位到符号表。在符号表中查找要hook的函数名,并修改该符号的st_value值为NewFunc – BaseAddr(st_value保存的是偏移地址)

导出表Hook和导入表Hook的代码实现其实比较类似,本质都是修改函数地址,但却能起到较好的效果。因为它是直接改的要hook的函数所在so中的符号表,其他库要调用该so中的方法,都会从它的符号表下进行引用,并填充到自身的Got表下,所以能够起到一次Hook,永久替换的效果。当然,其拦截效果还是不如inline这种方式。不过由于inline hook会受到函数字节数的轻微限制,导出表hook也可视为一种对Inline的补充。此外,导出表Hook只能Hook导出的符号,对偏移函数没有办法。
特点:不能即时生效。因为改的是符号表,当前运行的程序已经加载完成,调用外部函数时,是直接走的Got。所以进行测试时,在hook完成后,还需要再loadlibrary一次,让修改过的符号重新导入到Got表。说到这里,其实也能dlopen、dlsym来进行测试,让dlsym来检验是否已修改目标的st_value。。

Inline hook其实就是直接修改函数指令,对代码的调用流程进行替换,它的基本流程如下所示:

这部分的代码实现,个人是直接参考的d15K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6U0M7X3#2#2L8r3I4A6L8X3g2J5i4K6u0r3j5h3c8T1K9g2!0q4z5q4!0n7c8W2)9&6z5g2!0q4y4q4!0n7z5q4!0m8b7g2!0q4y4g2!0n7b7#2)9^5x3q4!0q4y4W2!0n7b7g2)9&6x3q4!0q4y4#2)9&6b7g2)9^5y4r3S2G2L8$3D9`. 框架。不过,它有个bug,注意到这个问题是在9f1K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3c8J5L8%4m8K6i4K6u0W2N6$3!0G2P5i4g2F1i4K6u0W2L8%4u0Y4i4K6u0r3N6r3W2H3M7#2)9J5c8U0f1@1x3o6W2Q4c8e0k6Q4z5e0c8Q4b7V1g2Q4c8e0g2Q4z5o6N6Q4b7V1q4Q4c8e0W2Q4z5e0S2Q4b7V1k6Q4c8e0W2Q4z5o6N6Q4z5p5y4Q4c8e0c8Q4b7U0S2Q4b7V1g2Q4c8e0g2Q4z5p5q4Q4z5f1g2Q4c8e0N6Q4z5f1q4Q4z5o6c8Q4c8e0g2Q4b7f1g2Q4z5o6W2Q4c8e0g2Q4z5o6g2Q4b7e0S2Q4c8e0k6Q4z5p5y4Q4z5e0q4Q4c8e0k6Q4z5o6S2Q4z5e0S2Q4c8e0S2Q4b7U0g2Q4z5f1u0Q4c8e0N6Q4z5f1q4Q4z5o6c8Q4c8e0S2Q4b7e0N6Q4b7e0y4Q4c8e0W2Q4b7e0u0Q4z5e0S2Q4c8e0k6Q4z5o6m8Q4z5f1c8Q4c8e0S2Q4b7U0N6Q4b7f1k6Q4c8e0k6Q4z5e0N6Q4b7U0k6Q4c8f1k6Q4b7V1y4Q4z5o6S2Q4c8e0g2Q4z5o6g2Q4b7U0k6Q4c8e0g2Q4b7f1g2Q4z5f1g2Q4c8e0c8Q4b7V1q4Q4z5o6k6Q4c8e0S2Q4b7e0N6Q4b7e0y4Q4c8e0g2Q4z5o6S2Q4b7U0m8S2k6r3u0A6 hook也是这个时间)。

对adbi hook源码中的这一部分进行修改后,再来分析下它的实现原理。先看看hook.h头文件中定义的hook_t结构体,具体结构如下:


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 9
支持
分享
最新回复 (6)
雪    币: 758
活跃值: (78)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
2019-11-19 15:05
0
雪    币: 15922
活跃值: (7158)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
导出表Hook,还需要再loadlibrary一次就有效吗?以前测试没通过后来就改用inline hook。
如果真的可以的话,导出表hook更简单省事。
2019-11-19 16:01
0
雪    币: 3196
活跃值: (3712)
能力值: ( LV8,RANK:147 )
在线值:
发帖
回帖
粉丝
4
tDasm 导出表Hook,还需要再loadlibrary一次就有效吗?以前测试没通过后来就改用inline hook。 如果真的可以的话,导出表hook更简单省事。
我感觉Inline hook更灵活一些,我平时用的比较多·
2019-11-30 17:51
0
雪    币: 20
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
楼主写得很详细,学习了。有个小疑问: 会自动将该函数的符号地址设置为“真正映射地址 -1”   中符号地址应该=真正映射地址+1吧? 
2019-12-9 11:32
0
雪    币: 129
活跃值: (511)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
大佬牛逼
2023-6-16 12:07
0
雪    币: 1922
活跃值: (4221)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
m
2023-6-16 14:49
0
游客
登录 | 注册 方可回帖
返回