【发布时间】:2018-12-03 01:03:24
【问题描述】:
我正在尝试从函数 foo() 重写 ret 地址,以在离开函数 foo() 后跳过第一条指令。我正在关注 Smash stack 的乐趣或利润,以及 Jon Erickson 的 Hacking。这是我想跳过“b = 2;”的简单代码指令,所以在 b 中应该存储值 1。
在 gcc 中编译,设置:-fno-stack-protector -z execstack -g
我也关闭了 ASLR。
#include <stdio.h>
int foo() {
int c = 0;
int *ptr;
ptr = (int*) 0x00007fffffffdf38;
// Second instruction after leaving from foo(). I also tried to skip all instructions before call printf function.
*ptr = 0x0000000000400577;
return 0;
}
int main()
{
int b = 1;
foo();
b = 2;
printf("b = %d\n", b);
return 0;
}
这个程序的输出是:“b = 2”。
我如何假设 RET 指令存储在哪里:
(gdb) disass main
...
0x000000000040056b <+20>: call 0x40052d <foo>
0x0000000000400570 <+25>: mov DWORD PTR [rbp-0x4],0x2
0x0000000000400577 <+32>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040057a <+35>: mov esi,eax
...
(gdb) break 4 // set breakpoint 1 in function foo before int *ptr;
(gdb) break 7 // set breakpoint 2 in function foo after rewriting RET for checking
(gdb) run
(gdb) x/8x &c // "x/8x $esp" doesn't work, it says: "Cannot access memory at address 0xffffffffffffdf30", so I'm looking into memory from &c
0x7fffffffdf2c: 0x00000000 0xffffdf50 0x00007fff 0x00400570
0x7fffffffdf3c: 0x00000000 0xffffe030 0x00007fff 0x00000000
// simple math tell me, it should be:
(gdb) x/8xb 0x7fffffffdf38
0x7fffffffdf38: 0x70 0x05 0x40 0x00 0x00 0x00 0x00 0x00
这就是我找到 RET 地址的方式,它位于 0x7fffffffdf38。到这个地方我把下一条指令的地址 - 0x0000000000400577。 但是,即使 RET 被成功改写,计算机仍然没有跳过指令 b = 2。 我检查了它是否真的替换了 RET 地址:
(gdb) c
(gdb) x/8xb 0x7fffffffdf38
0x7fffffffdf38: 0x77 0x05 0x40 0x00 0x00 0x00 0x00 0x00
所以 RET 地址确实被重写了,但是当程序从函数 foo() 离开时,它会跳转到我想跳过的原始地址 0x0000000000400570...
应该很简单,找到存储ret地址的地方,然后把其他地址放到这个地方。我究竟做错了什么? 感谢您的每一个回答。
为了指定任务,我添加了反汇编函数:
Dump of assembler code for function foo:
0x000000000040052d <+0>: push rbp
0x000000000040052e <+1>: mov rbp,rsp
0x0000000000400531 <+4>: mov DWORD PTR [rbp-0x4],0x0
0x0000000000400538 <+11>: movabs rax,0x7fffffffdf38
0x0000000000400542 <+21>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400546 <+25>: mov rax,QWORD PTR [rbp-0x10]
0x000000000040054a <+29>: mov DWORD PTR [rax],0x400577
0x0000000000400550 <+35>: mov eax,0x0
0x0000000000400555 <+40>: pop rbp
0x0000000000400556 <+41>: ret
End of assembler dump.
(gdb) disass main
Dump of assembler code for function main:
0x0000000000400557 <+0>: push rbp
0x0000000000400558 <+1>: mov rbp,rsp
0x000000000040055b <+4>: sub rsp,0x10
0x000000000040055f <+8>: mov DWORD PTR [rbp-0x4],0x1
0x0000000000400566 <+15>: mov eax,0x0
0x000000000040056b <+20>: call 0x40052d <foo>
0x0000000000400570 <+25>: mov DWORD PTR [rbp-0x4],0x2
0x0000000000400577 <+32>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040057a <+35>: mov esi,eax
0x000000000040057c <+37>: mov edi,0x400624
0x0000000000400581 <+42>: mov eax,0x0
0x0000000000400586 <+47>: call 0x400410 <printf@plt>
0x000000000040058b <+52>: mov eax,0x0
0x0000000000400590 <+57>: leave
0x0000000000400591 <+58>: ret
【问题讨论】:
-
我不是 100% 清楚你在做什么,但是调用指令将返回指令推入堆栈。 RET 指令的位置(您的 0x7fffffffdf38 值)似乎无关紧要。您应该在当前堆栈帧中找到返回地址,该地址由
call推送并修改。 -
我发现(从 dbg,break 4 - inside foo)在 0x7fffffffdf38 上存储了 0x0000000000400570,这是下一条指令。所以 0x7ffffffffdf38 是地方,返回地址存放在哪里,不是吗?
-
如果您希望我进一步评论,请发布
main和foo的整个反汇编。 -
在
foo内,您的退货地址应位于[rbp+8]。 -
对我来说似乎是对的,但您的问题措辞不当,因为您试图更改 返回地址 而不是 ret 指令。不过,只要两次运行之间的堆栈指针相同,我就没有看到您所做的任何问题。也许更稳定的程序会使用
p = &c + 3;。
标签: c buffer-overflow