首先,您的代码不会产生分段错误。相反,它将调出“Stack Smashing”并在输出控制台中的 libc_message 下方抛出。
*** stack smashing detected ***: _executable-name-with-path_ terminated.
当程序向位于堆栈上的缓冲区写入的数据多于为该缓冲区实际分配的数据时,会导致堆栈缓冲区溢出错误。
Stack Smashing Protector (SSP) 是一个 GCC 扩展,用于保护应用程序免受此类堆栈粉碎攻击。
而且,正如其他答案中所说,您的问题通过递增(strcat()
函数的第一个参数)得到解决。从
char line[100]
至
char line[130]; // size of line must be atleast `strlen(line) + strlen(mod) + 1`. Though 130 is not perfect, it is safer
让我们看看问题在代码中的确切位置:
为此,我提出了你的 main 的反汇编代码。
(gdb) disas main
Dump of assembler code for function main:
0x0804857c <+0>: push %ebp
0x0804857d <+1>: mov %esp,%ebp
0x0804857f <+3>: and $0xfffffff0,%esp
0x08048582 <+6>: sub $0xb0,%esp
0x08048588 <+12>: mov %gs:0x14,%eax
0x0804858e <+18>: mov %eax,0xac(%esp)
..... //Leaving out Code after 0x0804858e till 0x08048671
0x08048671 <+245>: call 0x8048430 <strcat@plt>
0x08048676 <+250>: movl $0x80487d5,0x4(%esp)
.... //Leaving out Code after 0x08048676 till 0x08048704
0x08048704 <+392>: mov 0xac(%esp),%edx
0x0804870b <+399>: xor %gs:0x14,%edx
0x08048712 <+406>: je 0x8048719 <main+413>
0x08048714 <+408>: call 0x8048420 <__stack_chk_fail@plt>
0x08048719 <+413>: leave
0x0804871a <+414>: ret
按照通常的汇编语言序言,
指令 at 0x08048582
: 堆栈增长 b0(十进制 176)字节,以允许主函数存储堆栈内容。
%gs:0x14 提供用于堆栈保护的随机金丝雀值。
指令 at 0x08048588
:将上述值存储到 eax 寄存器中。
指令 at 0x0804858e
: eax
content(canary value) 被推送到 $esp 处的堆栈,偏移量为 172
在 处保留断点 (1) 0x0804858e
。
(gdb) break *0x0804858e
Breakpoint 1 at 0x804858e: file program_name.c, line 6.
运行程序:
(gdb) run
Starting program: /path-to-executable/executable-name
Breakpoint 1, 0x0804858e in main () at program_name.c:6
6 {
一旦程序在断点(1)处暂停,通过打印寄存器“eax”的内容来检索随机金丝雀值
(gdb) i r eax
eax 0xa3d24300 -1546501376
0x08048671
在: 恰好在 call 之前保留断点(2)strcat()
。
(gdb) break *0x08048671
Breakpoint 2 at 0x8048671: file program_name.c, line 33.
继续程序执行到达断点(2)
(gdb) continue
Continuing.
Breakpoint 2, 0x08048671 in main () at program_name.c:33
通过在 gdb 中执行以下命令,打印出我们存储随机金丝雀值的第二个顶部堆栈内容,以确保它在strcat()
调用之前是相同的。
(gdb) p *(int*)($esp + 172)
$1 = -1546501376
将断点 (3) 保留在0x08048676
: 从调用返回后立即strcat()
(gdb) break *0x08048676
Breakpoint 3 at 0x8048676: file program_name.c, line 36.
继续程序执行到达断点(3)
(gdb) continue
Continuing.
Breakpoint 3, main () at program_name.c:36
通过在 gdb 中执行以下命令打印出我们存储随机金丝雀值的第二个顶部堆栈内容,以确保它不会被调用损坏strcat()
(gdb) p *(int*)($esp + 172)
$2 = 1869111673
但是它被调用破坏了strcat()
。你可以看到$1
和$2
不一样。让我们看看由于破坏了随机金丝雀值会发生什么。
指令在0x08048704
:提取损坏的随机金丝雀值并存储在“edx”寄存器中
指令在0x0804870b
:xor 实际随机金丝雀值和“edx”寄存器的内容
指令 at 0x08048712
:如果它们相同,则直接跳转到 main 的末尾并安全返回。在我们的例子中,随机金丝雀值已损坏,“edx”寄存器内容与实际随机金丝雀值不同。因此跳转条件失败并调用 __stack_chk_fail ,这会抛出答案顶部提到的 libc_message 并中止应用程序。
有用的链接:
IBM SSP 页面
有趣的阅读 SSP - 警告 pdf。