如c程序的代码:
test.c:
1
2 #include <stdio.h>
3
4 int main(void)
5 {
6 int buf[10];
7
8 printf("end\n"); //用于让编译器对lr寄存器压栈
9 return 0;
10 }
反汇编后得到的代码:
000083cc <main>:
83cc: e92d4800 push {fp, lr}
83d0: e28db004 add fp, sp, #4
83d4: e24dd028 sub sp, sp, #40 // int buf[10]的空间
83d8: e59f0010 ldr r0, [pc, #16] ; 83f0 <main+0x24>
83dc: ebffffb7 bl 82c0 <_init+0x20>
83e0: e3a03000 mov r3, #0
83e4: e1a00003 mov r0, r3
83e8: e24bd004 sub sp, fp, #4
83ec: e8bd8800 pop {fp, pc}
main函数执行时栈里的内容:
[lr寄存器原内容]
[fp寄存器原内容]
[ buf[9] ] // &buf[9], 栈的地址从高往低,数组元素的地址是从低往高.
...
[ buf[0] ] // &buf[0]
通过数组超界访问,可以改变栈里存放的lr寄存器原内容,可以改为想执行的代码的地址.
实验代码:
1
2 #include <stdio.h>
3
4 void func()
5 {
6 printf("in func ...\n");
7 }
8 int main(void)
9 {
10 int buf[10];
11 int i = 11;
12
13 buf[i] = (int)func;
14 buf[i+1] = (int)func; //这里不是执行,只是把func函数的地址存放在栈里的lr寄存器原内容的位置
15
16 printf("end\n"); //用于让编译器对lr寄存器压栈
17 return 0; //当函数执行结束,pop {fp, pc}; 从栈里的lr寄存器内容的位置上读出内容,并根据读出的返回地址来跳转, 所以func函数就有机会执行了
18 }
编译后执行的输出:
^_^ /mnt/codes/03asm_deep # ./a.out
end
in func ... //func函数执行的输出
Segmentation fault
溢出攻击实现原理:
如上面的代码, buf数组足够大, 接收用户输入时如果不判断长度, 就可以把要执行的指令存入buf数组, 最后通过数组的越界访问,把栈里存放的原lr寄存器的内容改成buf数组的首地址, 那么buf数组里存放的指令就可以执行了.
而已当main函数是管理员执行时, 那么在buf数组里存放的指令执行就具有管理员的权限了, 手机的root用的就是这种方法
防止溢出攻击的方法:
1). 接用户输时判断数据长度, 防止数组越界访问
2). 尽量在堆里分配空间
3). gcc 编译时加上-fstack-protection选项(man gcc里查看)