一道破解练习题

下面是来自alert7 前辈博客http://hi.baidu.com/weiwang_blog/blog/item/ee4704decce0ba5cccbf1ad1.html 的一道破解题:

 

“The following binary code executes and gives correct results only when
the Key_file is present in the same directory as the code. Your goal is
to use whatever techniques you can to modify the binary so that the new
code produces the correct results without the Key_file.”
文件在这
elfhack.whitecell.org/oOo/bin_challenge1.zip

所需的一些技能
1:英语能力
2:汇编技术
3:linux调试技术
4:一些linux的系统知识

上面的链接坏了,自己编译生成了一个可执行文件。源文件如下:

编译生成可执行文件exercise:

$gcc -o exercise exercise.c

下面分析exercise反汇编后的代码。

(1)读取section表

 (2)反汇编.text section,分析main函数

 

0x8048554~0x8048555:保存旧ebp,ebp指向当前栈顶;

 0x8048557:栈顶以16对齐;

 0x804855a:为main函数中的局部变量、中间变量等分配0x70字节的空间;

 0x804855d~0x8048560:???

 0x8048564~0x804856a:GCC中SSP 堆栈保护技术,通过这两句代码就在函数栈框中插入了一个 Canary,并实现了通过这个 canary 来检测函数栈是否被破坏。%gs:0x14 中保存是一个随机数, 这两 条语句将这个随机数放入了栈中 [ESP+0x6c] 的位置。函数返回前 0x08048669 到 0x08048676 处的 4 条语句则将栈中 [ESP+0x6c] 处保存的 Canary 取出并与 %gs:0x14 中的随机数作比较。若不等,则说明函数执行过程中发生了溢出,函数栈框已经被破坏,此时程序会跳转到 __stack_chk_fail 输出错误消息然后中止运行。若相等,则函数正常返回。

 0x804856e~0x804857f:[ESP+0x64]保存filename变量的值0x80487a4,0x80487a4(位于.rodata section中)保存字符串“keyfile/0”;

[ESP+68]保存值0x80487a8,0x80487a8(位于.rodata section)保存字符串“ile/0”。???

0x8048583~0x8048588:[ESP+0x40]保存key变量的值0x80487ac,0x80487ac(位于.rodata section中)保存字符串“hyh/0”;这样[ESP+0x44]到[ESP+0x64]共32字节的空间用于line变量。

0x80485ac~0x804859c:从右到左依次压入fopen函数的参数.rodata section中保存“r/0”字符串的值0x8048740、filename变量的地址;然后调用fopen。

......

......

......

(3).rodata section中字符串常量

 

 (4)libc.so库中fopen函数的调用过程

GCC为了提高加载动态库的速度,在加载动态库时没有进行链接动态库中的外部函数,而是在运行到该函数时才进行链接,即所谓的延迟绑定。下面分析这一过程。

 

查看.plt section中fopen对应的项,$objdump -d -j .plt exercise

fopen@plt的第一条指令是一条通过GOT间接跳转的指令。0x804a00c表示GOT中保存fopen这个函数相应的项,下面查看.got.plt section中该地址处的值,$objdump -d -j .got.plt exercise

 

 如果链接器在初始化时已经初始化该项(0x804a00c处的值),并且将fopen的地址填入该项,那么这个跳转指令的结果就是我们所期望的,跳转到fopen,实现函数正确调用。但是为了实现延迟绑定,

链接器在初始化阶段并没有将fopen的地址填入到该项,而是将上面代码中的第二条指令“push $0x18”的地址填入到0x804a00c中。所以,第一条指令的效果是跳转到第二条指令,相当于没有进行任

何操作。第二条指令将一个数字0x18压入堆栈中,这个数字是fopen这个符号引用在.got.plt中的下标。然后跳转至8048408处,实际上是.plt section中的第一项,下面查看该项,

$objdump -d -j .plt exercise

 

 在上述第一项中,再将模块ID压入堆栈,然后调用动态链接器的_dl_runtime_resolve()函数来完成符号解析和重定位工作。_dl_runtime_resolve()在进行一系列工作以后将fopen()的真正地址填入到.got.plt

section中fopen项对应的0x804a00c处。一旦fopen函数被解析完毕,当我们再次跳转到fopen@plt时,第一条jmp指令就能够跳转至真正的fopen函数中。

注:.got用来保存全局变量引用的地址,.got.plt用来保存外部函数引用的地址,即所有对于外部函数的引用全部被分离放到了.got.plt中,另外,.got.plt的前三项是有特殊意义的,分别含义如下:第一项保存的是

.dynamic段的地址,这个段描述了本模块动态链接相关的信息;第二项保存的是本模块的ID;第三项保存的是_dl_runtime_resolve()的地址;其中第二项和第三项由动态链接器在装载共享模块时负责将它们初始

化。.got.plt中其余项分别对应每个外部函数的引用。

(5)动态链接重定位相关结构

动态链接文件中,有两种重定位表分别叫做“.rel.dyn”和“rel.plt”。.rel.dyn修正的位置位于.got以及数据段;而.rel.plt是对外部函数引用的修正,它所修正的位置位于.got.plt section中。下面查看exercise的

重定位表,$readelf -r exercise

 

 其中,offset一列是该项外部符号在.got或.got.plt中的偏移量。

(6)解题过程

说了这么多,解答这道题不需要全部了解上述知识,权且是我这段时间学习ELF文件以及动态链接机制的一个总结。下面是解答此题的思路。

a.修改判断keyfile文件打开失败的指令0x80485ac,让其直接跳转至0x8048658指令处。JMP指令的格式:e9 <offset>,其中e9是操作码,offset是欲跳转到的指令距离当前指令的下一跳指令的偏移。

offset=0x8048658-0x80485ac=0xa8。0x80485ac处修改之后的指令为,e9a8000000。

b.获得与修改的指令在exercise二进制文件中的偏移,使用二进制编辑工具,例如vim,修改之。欲修改指令在.text section中的偏移:0x80485ac-0x80484a0=0x10c,而.text在exercise中的偏移

为0x4a0(section 表中指明),所以修改处位于exercise中的0x4a0+0x10c=0x5ac处。O(∩_∩)O~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值