注:本位为简译文章,原文见最后的reference
进程的内存布局
程序载入内存会分成多个段,我们关心下面三个
- 程序空间
- 堆
- 栈
一般情况下布局如下
我们可以通过gef看到内存布局及权限
1 | azeria@labs:~/exp $ gdb program |
栈溢出
如图,就是程序对用户输入的长度做限制,导致可以覆盖LR寄存器的味道,通过精心地控制,即可达到任意代码执行的目的
我们用如下程序测试
1 | /*azeria@labs:~/exp $ gcc stack.c -o stack*/ |
汇编代码如下:
我们输入7个A,可以看到 R11(即FP)和LR储存的位置还没被覆盖
假如我们输入16个A,可以看到都覆盖了,之后的pop {r11, pc}就会让攻击者劫持控制流了
继续运行,确实被控制了,程序也蹦了
堆溢出
堆相对复杂,我们malloc一次,就会得到一个chunk,这个chunk有header和user data,堆溢出有下面图中的两种:一个是chunk内部,一个是溢出到另一个chunk了
chunk内部溢出
示例代码
1 |
|
可以看到,假如我们分配一个结构体的内存,假如对name的输入没有限制,那么number也是我们可以控制的
汇编如下:
输入7个A
堆从0x00021000开始,结构如下,number还没被覆盖
但是我们输入8个A,那么number的最低位就被00覆盖了
那就可以改变程序的执行流程
chunk外部溢出
示例代码如下:
1 | /*azeria@labs:~/exp $ gcc inter_chunk.c -o inter_chunk*/ |
差不多,我们也先输入7个A,可以看到堆的结构如下
16个A,header和some_number都被覆盖了
也同样可以改变执行流程
Reference
https://azeria-labs.com/process-memory-and-memory-corruption/