我一直在阅读“Shellcoders手册”,并提到了这个链接,用于实现堆栈溢出。 但似乎Linux内核开发者已经使内核非常安全。 这是我的问题。
1)这个代码
void function(int a, int b, int c) { char buffer1[8]; char buffer2[10]; int* ret; ret = buffer1 + 6; *ret+=8; } void main() { int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); }
给出输出
$ cc smash.c smash.c: In function 'function': smash.c:7:8: warning: assignment from incompatible pointer type $ ./a.out 1
但用*ret+=8
replace行*ret+=8
*ret=8
给出以下输出
*** stack smashing detected ***: ./a.out terminated ======= Backtrace: ========= /lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x50)[0xa86df0] /lib/i386-linux-gnu/libc.so.6(+0xe5d9a)[0xa86d9a] ./a.out[0x8048448] ./a.out[0x8048477] /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x9b7e37] ./a.out[0x8048381] ======= Memory map: ======== 003df000-003e0000 r-xp 00000000 00:00 0 [vdso] 009a1000-00afb000 r-xp 00000000 08:01 3277633 /lib/i386-linux-gnu/libc-2.13.so 00afb000-00afc000 ---p 0015a000 08:01 3277633 /lib/i386-linux-gnu/libc-2.13.so 00afc000-00afe000 r--p 0015a000 08:01 3277633 /lib/i386-linux-gnu/libc-2.13.so ... ...
如果用ret = buffer1 + 7
replaceret = buffer1 + 6
,结果与上面相同。 如果我用ret=buffer1+8
(或任何更大的值)replaceret = buffer1 +
6,那么对于上述情况(即,将值*ret
增加8还是将其更改为8),都会有堆栈被捣毁。
请告诉我这是怎么发生的。 有用的链接也将不胜感激。 最重要的是,如何禁用Linux内核的这个安全function,以便我可以使用这本书?
平台:i386内核:2.6.38
要禁用堆栈粉碎检测,请在编译时使用-fno-stack-protector 。 在使用“Shellcoders手册”时,您可能还想使用-ggdb和-mpreferred-stack-boundary = 4来启用GDB符号并标准化堆栈。
编辑:当我编译你提供的代码( gcc -fno-stack-protector -ggdb -mpreferred-stack-boundary=4 -o sc in.c
)时,编译器重新排列function
局部变量的顺序。 我使用GDB发现了这个:
willi@ubuntu:~/testing$ gdb sc (gdb) set disassembly-flavor intel (gdb) disassemble function Dump of assembler code for function function: 0x080483c4 <+0>: push ebp 0x080483c5 <+1>: mov ebp,esp 0x080483c7 <+3>: sub esp,0x20 0x080483ca <+6>: lea eax,[ebp-0xc] 0x080483cd <+9>: add eax,0x6 0x080483d0 <+12>: mov DWORD PTR [ebp-0x4],eax 0x080483d3 <+15>: mov eax,DWORD PTR [ebp-0x4] 0x080483d6 <+18>: mov eax,DWORD PTR [eax] 0x080483d8 <+20>: lea edx,[eax+0x8] 0x080483db <+23>: mov eax,DWORD PTR [ebp-0x4] 0x080483de <+26>: mov DWORD PTR [eax],edx 0x080483e0 <+28>: leave 0x080483e1 <+29>: ret End of assembler dump.
0x080483ca告诉我, ebp - 0xC
是buffer1,0x080483d0告诉我ebp - 0x4
是ret。 所以,这些变量并不存在于堆栈中,因为它们存在于我们的C代码中。 鉴于ret
是我们最重要的局部变量,我们可以直接使用它。 不过,让我们来处理你的代码吧。
要修改返回指针,我们需要改变保存在ebp下面的地址,所以ebp + 0x4
。 所以,要从我们的变量buffer1得到返回指针,我们必须添加0xC(到达ebp
),然后0x4(返回指针在ebp
下是0x4)。 现在我们可以进行修改。
我从你的C代码中取出你想跳过x = 1
的赋值并直接返回到printf
。 我反汇编main
找到适当的修改返回指针:
(gdb) disassemble main Dump of assembler code for function main: 0x080483e2 <+0>: push ebp 0x080483e3 <+1>: mov ebp,esp 0x080483e5 <+3>: and esp,0xfffffff0 0x080483e8 <+6>: sub esp,0x20 0x080483eb <+9>: mov DWORD PTR [esp+0x1c],0x0 0x080483f3 <+17>: mov DWORD PTR [esp+0x8],0x3 0x080483fb <+25>: mov DWORD PTR [esp+0x4],0x2 0x08048403 <+33>: mov DWORD PTR [esp],0x1 0x0804840a <+40>: call 0x80483c4 <function> 0x0804840f <+45>: mov DWORD PTR [esp+0x1c],0x1 0x08048417 <+53>: mov eax,DWORD PTR [esp+0x1c] 0x0804841b <+57>: mov DWORD PTR [esp+0x4],eax 0x0804841f <+61>: mov DWORD PTR [esp],0x80484f0 0x08048426 <+68>: call 0x80482f4 <printf@plt> 0x0804842b <+73>: leave 0x0804842c <+74>: ret End of assembler dump.
在不修改返回指针的情况下,0x0804840a处的function
调用返回0x0804840f。 但是我们想跳过这个并返回到下一条指令。 下一条指令从0x08048417开始,沿着0x8字节。 所以我们对返回指针的修改必须增加0x8值。
考虑到这些因素,我使用下面的代码打印“0”而不是“1”:
void function(int a, int b, int c) { char buffer1[8]; char buffer2[10]; int* ret; ret = buffer1 + 0x10; *ret+=8; } void main() { int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); }
堆栈粉碎保护可以像这样禁用:
$ gcc -ggdb -m32 -o buffer1 -fno-stack-protector -mpreferred-stack-boundary=4 buffer1.c
该链接提供了有关Linux安全功能的更多信息