Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 堆栈溢出-奇数返回地址_Linux_Gcc_Stack - Fatal编程技术网

Linux 堆栈溢出-奇数返回地址

Linux 堆栈溢出-奇数返回地址,linux,gcc,stack,Linux,Gcc,Stack,我正在通过“外壳编码器手册”中的一个例子进行研究。然而,事情并不是那么顺利。我正在运行Debian2.6.32-5-686内核i386 以下演练将引导读者了解发生缓冲区溢出时发生的情况 该方案: include <stdio.h> include <string.h> void return_input(void) { char array[30]; gets (array); printf("%s\n", array); } int main

我正在通过“外壳编码器手册”中的一个例子进行研究。然而,事情并不是那么顺利。我正在运行Debian2.6.32-5-686内核i386

以下演练将引导读者了解发生缓冲区溢出时发生的情况

该方案:

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

void return_input(void)
{
    char array[30];
    gets (array);
    printf("%s\n", array);
}

int main ()
{
    return_input();
    return 0;
}
我运行
gdb测试
并开始调查:

(gdb) disas return_input 
Dump of assembler code for function return_input:
0x080483f4 <return_input+0>:    push   %ebp
0x080483f5 <return_input+1>:    mov    %esp,%ebp
0x080483f7 <return_input+3>:    sub    $0x24,%esp
0x080483fa <return_input+6>:    lea    -0x1e(%ebp),%eax
0x080483fd <return_input+9>:    mov    %eax,(%esp)
0x08048400 <return_input+12>:   call   0x804830c <gets@plt>
0x08048405 <return_input+17>:   lea    -0x1e(%ebp),%eax
0x08048408 <return_input+20>:   mov    %eax,(%esp)
0x0804840b <return_input+23>:   call   0x804832c <puts@plt>
0x08048410 <return_input+28>:   leave  
0x08048411 <return_input+29>:   ret    
End of assembler dump.
(gdb) break *0x08048400
Breakpoint 1 at 0x8048400: file test.c, line 7.
(gdb) break *0x08048411
Breakpoint 2 at 0x8048411: file test.c, line 9.
这就是堆栈在调用
之前的样子。我已经用星号(
0x0804841a
)标记了回信地址。让我们覆盖以下内容:

(gdb) continue
Continuing.
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD

Breakpoint 2, 0x08048411 in return_input () at test.c:9
9   }
(gdb) x/20x 0xbffff3ac
0xbffff3ac: 0xbffff3b2  0x4141a304  0x41414141  0x41414141
0xbffff3bc: 0x42424242  0x42424242  0x43434242  0x43434343
0xbffff3cc: 0x43434343  0x44444444  *0x44444444*    0xbf004444
0xbffff3dc: 0xb7e9ec76  0x00000001  0xbffff484  0xbffff48c
0xbffff3ec: 0xb7fe18c8  0xbffff440  0xffffffff  0xb7ffeff4
上面是堆栈在从函数返回之前的样子。正如你所看到的,我们已经用那些多余的“D”覆盖了返回地址。后果让我们结束:

(gdb) x/li $eip
0x8048411 <return_input+29>:    ret    
(gdb) stepi
Cannot access memory at address 0x44444448
(gdb)x/li$eip
0x8048411:ret
(gdb)stepi
无法访问地址为0x4448的内存
嗯,嗯??这个
0x4448
不知从何而来。不知何故,gcc在我们返回之前修改了返回地址。谢谢

有什么想法吗?我是否正确地假设gcc已经做了自己的内部检查返回地址是否有效。如果不是的话,它会在里面放一些垃圾来阻止我们设计一个肮脏的回信地址


有办法吗?我在这里什么都试过了。相同的结果。

这是预期的结果—页面错误。您的程序被操作系统停止,因为您正在访问未分配给任何物理内存的虚拟内存


您看到的消息只是调试器通知您这一事实。

这是预期的结果—页面错误。您的程序被操作系统停止,因为您正在访问未分配给任何物理内存的虚拟内存


您看到的消息只是调试器通知您这一事实。

在调试gcc时,gcc什么都不做。在调试gcc时,gcc什么都不做。
call
eip
推到堆栈上,以便在调用
ret
时,它知道返回到哪里。在此示例中,
eip
已被
0x4444
覆盖。这是书中明确指出的。公平地说,这是一个页面错误,但是在
eip
中添加4与文本相矛盾(或者至少看起来是矛盾的)。事实上,文本的下一节假设了这一点,并继续将返回地址修改为上面的
return\u input
函数的返回地址。这样:
printf“aaaaaaaaaaaaaabbbbbbbbbbccccccccccccccccccddd\xed\x83\x04\x08”|./test
其中函数的地址为
0x080483ed
。这会导致函数运行两次。但当然,这在机器上不起作用。@tbh1:您是对的,
ret
将直接跳转到堆栈上的地址(在这种情况下,
0x4444
)。所以我给了你错误的线索。页面错误可能不是由程序直接引起的,而是由调试器引起的。调试器修改程序,使其在每条指令后停止。它通过在每条指令后插入中断指令来实现。因此,在执行
ret
之前,它会尝试访问地址
0x4448
,以插入中断指令。如果你在没有调试器的情况下运行你的程序,它会在正确的地址出现页面错误。啊哈,你说得对!非常微妙,谢谢你,麦基。我让程序调用
return\u input
函数两次。在文本中,它使用了我在上述评论中给出的
printf
。然而,这对我来说不起作用。工作是
printf“aaaaaaaaaaaaaabbbbbbbbccccccccccccccdd\xed\x83\x04\x08DD”|/测试
。结果。
call
eip
推到堆栈上,以便在调用
ret
时,它知道返回到哪里。在此示例中,
eip
已被
0x4444
覆盖。这是书中明确指出的。公平地说,这是一个页面错误,但是在
eip
中添加4与文本相矛盾(或者至少看起来是矛盾的)。事实上,文本的下一节假设了这一点,并继续将返回地址修改为上面的
return\u input
函数的返回地址。这样:
printf“aaaaaaaaaaaaaabbbbbbbbbbccccccccccccccccccddd\xed\x83\x04\x08”|./test
其中函数的地址为
0x080483ed
。这会导致函数运行两次。但当然,这在机器上不起作用。@tbh1:您是对的,
ret
将直接跳转到堆栈上的地址(在这种情况下,
0x4444
)。所以我给了你错误的线索。页面错误可能不是由程序直接引起的,而是由调试器引起的。调试器修改程序,使其在每条指令后停止。它通过在每条指令后插入中断指令来实现。因此,在执行
ret
之前,它会尝试访问地址
0x4448
,以插入中断指令。如果你在没有调试器的情况下运行你的程序,它会在正确的地址出现页面错误。啊哈,你说得对!非常微妙,谢谢你,麦基。我让程序调用
return\u input
函数两次。在文本中,它使用了我在上述评论中给出的
printf
。然而,这对我来说不起作用。工作是
printf“aaaaaaaaaaaaaabbbbbbbbccccccccccccccdd\xed\x83\x04\x08DD”|/测试
。后果
(gdb) continue
Continuing.
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD

Breakpoint 2, 0x08048411 in return_input () at test.c:9
9   }
(gdb) x/20x 0xbffff3ac
0xbffff3ac: 0xbffff3b2  0x4141a304  0x41414141  0x41414141
0xbffff3bc: 0x42424242  0x42424242  0x43434242  0x43434343
0xbffff3cc: 0x43434343  0x44444444  *0x44444444*    0xbf004444
0xbffff3dc: 0xb7e9ec76  0x00000001  0xbffff484  0xbffff48c
0xbffff3ec: 0xb7fe18c8  0xbffff440  0xffffffff  0xb7ffeff4
(gdb) x/li $eip
0x8048411 <return_input+29>:    ret    
(gdb) stepi
Cannot access memory at address 0x44444448