修改具有缓冲区溢出漏洞的C函数的返回地址

我试图修改下面的C程序,以便主函数将跳过printf(“x is 1”)行,只打印“x是0”。

void func(char *str) { char buffer[24]; int *ret; ret = buffer + 28; // Supposed to set ret to the return address of func (*ret) += 32; // Add the offset needed so that func will skip over printf("x is 1") strcpy(buffer, str); } int main(int argc, char **argv) { int x; x = 0; func(argv[1]); x = 1; printf("x is 1"); printf("x is 0"); getchar(); } 

正如注释所暗示的,ret指针需要先被设置为函数的返回地址。 然后我需要添加一个偏移量,将它推过我想跳过的一行。 我使用2个Intel(R)Xeon(TM)CPU 3.20GHz的Linux系统运行这个代码。 我正在使用gcc版本4.7.2(Debian 4.7.2-5)进行编译。 我也想从这个( http://insecure.org/stf/smashstack.html )链接中使用example3.c作为参考。 这里是使用gdb的主要function的反汇编:

 Dump of assembler code for function main: 0x0000000000400641 <+0>: push %rbp 0x0000000000400642 <+1>: mov %rsp,%rbp 0x0000000000400645 <+4>: sub $0x20,%rsp 0x0000000000400649 <+8>: mov %edi,-0x14(%rbp) 0x000000000040064c <+11>: mov %rsi,-0x20(%rbp) 0x0000000000400650 <+15>: movl $0x0,-0x4(%rbp) 0x0000000000400657 <+22>: mov -0x20(%rbp),%rax 0x000000000040065b <+26>: add $0x8,%rax 0x000000000040065f <+30>: mov (%rax),%rax 0x0000000000400662 <+33>: mov %rax,%rdi 0x0000000000400665 <+36>: callq 0x4005ac <func> 0x000000000040066a <+41>: movl $0x1,-0x4(%rbp) 0x0000000000400671 <+48>: mov $0x40075b,%edi 0x0000000000400676 <+53>: mov $0x0,%eax 0x000000000040067b <+58>: callq 0x400470 <printf@plt> 0x0000000000400680 <+63>: mov $0x400762,%edi 0x0000000000400685 <+68>: mov $0x0,%eax 0x000000000040068a <+73>: callq 0x400470 <printf@plt> 0x000000000040068f <+78>: callq 0x400490 <getchar@plt> 0x0000000000400694 <+83>: leaveq 0x0000000000400695 <+84>: retq End of assembler dump. 

使用我已经读过的例子,我的缓冲区是24个字节长,我应该添加一个额外的4个字节的SFP大小。 这意味着我添加了28个字节到达<+41>的返回地址。 然后,它看起来像我想跳到最后一次printf调用<+73>。 这应该是32的偏移量。但是,当我执行代码时,“x是1”仍然打印。 我似乎无法找出原因。 我的math或假设有什么问题吗?

你也应该反编译函数func(),以便更好地了解事情的进展情况。 此外,我没有理解你对strcpy()的调用的角色,这只是我分割错误的一个原因,我为了让你的代码正常工作而评论了它。

不要忘记,在代码中可以看到的大小以十六进制打印,而输入缓冲区时则以十进制的形式移动。 所以,当你读到的东西是:

 mov %rdi,-0x28(%rbp) 

您必须考虑40个字节(0x28 hexa = 40十进制),而不是28个字节。

上面的代码实际上是从func()函数反汇编中提取的。 正如@cybermike所提到的,不要忘记,虽然Alpeh1的文本仍然是一个关于这个问题的参考文献,但现在已经变得相当古老了:建筑和保护系统到现在已经有了广泛的发展。

正如在这里提到的,在x64架构上,编译器现在会尝试将堆栈地址与16个字节的边界对齐,所以要为24个字符的数组分配大小,实际上会保留32个字节。 最近的边界。

再加上为你的“休息”指针分配的8个字节,那么你知道你的返回地址完全位于40个字节之外。

然后,看main()反汇编,正常的返回地址是:

 0x00000000004005fe <+41>: movl $0x1,-0x4(%rbp) 

我们希望它是:

 0x0000000000400614 <+63>: mov $0x4006bb,%edi 

所以我们必须增加63 – 41 = 22的回报。

所以,总结一下,我有你的锻炼按照预期与以下func()函数工作:

 void func(char *str) { char buffer[24]; int *ret; ret = buffer + 40; // Supposed to set ret to the return address of func (*ret) += 22; // Add the offset needed so that func will skip over printf("x is 1") //strcpy(buffer, str); 

}

执行结果:

 $ ./se x is 0