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



我正在尝试修改下面的C程序,以便主函数跳过printf("x是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指针需要首先设置为函数的返回地址。然后我需要添加一个偏移量,将它推到我想要跳过的行上。我在Linux系统上运行这段代码,该系统使用2 x Intel(R)Xeon(TM)CPU 3.20GHz。我使用gcc版本4.7.2(Debian 4.7.2-5)进行编译。我也试着用这个例子中的3.c(http://insecure.org/stf/smashstack.html)链接以供参考。以下是使用gdb:对主要函数的反汇编

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个字节长,我应该为SFP大小添加额外的4个字节。这意味着我添加28个字节以获得<41>。然后看起来我想跳到<73>。这应该是32的偏移量。然而,当我执行代码时,仍然会打印"x是1"。我似乎不知道为什么。我的数学或假设有问题吗?

你也应该反汇编函数func(),以便更好地了解事情的进展。此外,我不理解你对strcpy()的调用的作用,这只是我出现分段错误的原因。我对它进行了注释,以使你的代码正常工作。

不要忘记,代码中的大小是以十六进制打印的,而代码中的缓冲区偏移是以十进制输入的。所以,当你读到以下内容时:

mov    %rdi,-0x28(%rbp)

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

上面的代码实际上是从func()函数反汇编中提取的。正如@cybermike所提到的,别忘了,虽然Alpeh1的文本仍然是关于这个主题的参考,但它现在已经很古老了:直到现在,架构和保护系统已经广泛发展。

如前所述,在x64体系结构上,编译器现在将尝试将堆栈地址与16字节的边界对齐,因此要为24个字符的数组分配大小,它实际上将保留32个字节,即最近的边界。

再加上为你的"rest"指针分配的8个字节,你就知道你的返回地址正好位于40个字节之外。

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

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

我们希望它是:

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

因此,我们必须将收益增加63-41=22。

总之,我让您的练习按照预期使用以下函数():

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

最新更新