ret2_dl_resolve
GOT & PLT
动态链接需要考虑的两点
- 需要存放外部函数的数据段(GOT)
- 获取数据段存放函数地址的一小段额外代码(PLT)
PLT
PLT
内容是一段代码,所以PLT
位于代码段,不可更改
GOT
GOT
是一个表,表里面存的是函数的地址。
GOT
表项中还保存了三个公共表项
got[0]
: 本ELF动态段.dynamic
的装载地址
got[1]
: 本ELF的link_map
数据结构描述符地址
got[2]
: dl_rumtime_resolve
函数的地址
流程
延迟重定向
在函数第一次调用前,got
表项的内容都是链接器生成的,他的值指向对应的plt
中jmp
代码的下一条指令。
1 |
|
最后都会跳到<common@plt>
【也就是plt[0]】中去执行代码。
这是动态链接做符号解析和重定位的公共入口。
common@plt
内容
1 |
|
所有动态链接库函数在第一次调用时都会通过
xxx@plt
===> common@plt
===> dl_runtime_resolve()
完成调用。
dl_runtime_resolve
调用流程
两个参数
link_map
的指针
包含了.dynamic
的指针,使dl_runtime_resolve
可以访问到这个段。
- offset
1 |
|
这里push
的数字其实是函数的ID,这个ID就是offset。
这也就解释了,dl_runtime_resolve
如何知道是查找哪个函数。
攻击手段
改写.dynstr
节的指针
让这个指针指向我们需要的函数名字符串,例如system
字符串
- 缺陷
只有在NO RELRO时候才有权限更改
.dynstr
。
伪造结构体
通过栈溢出,让函数返回到<common@plt>
中,利用伪造的参数达到调用函数的目的。
因为函数的执行过程就是不断地寻找结构体,从结构体中拿数据,而在函数源码中,并不会检查offset是否会过界,所以我们可以在可控制的地方伪造结构体来达到目的。
-
计算fake_rel和
.rel.plt
的距离偏移量作为offset,注意64位需要除以sizeof(ELF32_REL)
。 -
伪造一
fake_rel
,使他的rel_info字段成为0x******07
,这个07是导入函数的参数。暂时不知道干嘛的,但最好不要改。 -
******
是指fake_sym距离.dynsym
的偏移/sizeof(ELF32_SYM)
。 -
fake_sym
中st_name
是fake_string距离.dynstr
的偏移量。