ARM中的进程内存和内存破坏

注:本位为简译文章,原文见最后的reference

进程的内存布局

程序载入内存会分成多个段,我们关心下面三个

  1. 程序空间

一般情况下布局如下

我们可以通过gef看到内存布局及权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
azeria@labs:~/exp $ gdb program
...
gef> gef config context.layout "code"
gef> break main
Breakpoint 1 at 0x104c4: file program.c, line 6.
gef> run
...
gef> nexti 2
-----------------------------------------------------------------------------------------[ code:arm ]----
...
0x104c4 <main+20> mov r0, #8
0x104c8 <main+24> bl 0x1034c <malloc@plt>
-> 0x104cc <main+28> mov r3, r0
0x104d0 <main+32> str r3, [r11, #-8]
...
gef> vmmap
Start End Offset Perm Path
0x00010000 0x00011000 0x00000000 r-x /home/azeria/exp/program <---- Program Image
0x00020000 0x00021000 0x00000000 rw- /home/azeria/exp/program <---- Program Image continues...
0x00021000 0x00042000 0x00000000 rw- [heap] <---- HEAP
0xb6e74000 0xb6f9f000 0x00000000 r-x /lib/arm-linux-gnueabihf/libc-2.19.so <---- Shared library (libc)
0xb6f9f000 0xb6faf000 0x0012b000 --- /lib/arm-linux-gnueabihf/libc-2.19.so <---- libc continues...
0xb6faf000 0xb6fb1000 0x0012b000 r-- /lib/arm-linux-gnueabihf/libc-2.19.so <---- libc continues...
0xb6fb1000 0xb6fb2000 0x0012d000 rw- /lib/arm-linux-gnueabihf/libc-2.19.so <---- libc continues...
0xb6fb2000 0xb6fb5000 0x00000000 rw-
0xb6fcc000 0xb6fec000 0x00000000 r-x /lib/arm-linux-gnueabihf/ld-2.19.so <---- Shared library (ld)
0xb6ffa000 0xb6ffb000 0x00000000 rw-
0xb6ffb000 0xb6ffc000 0x0001f000 r-- /lib/arm-linux-gnueabihf/ld-2.19.so <---- ld continues...
0xb6ffc000 0xb6ffd000 0x00020000 rw- /lib/arm-linux-gnueabihf/ld-2.19.so <---- ld continues...
0xb6ffd000 0xb6fff000 0x00000000 rw-
0xb6fff000 0xb7000000 0x00000000 r-x [sigpage]
0xbefdf000 0xbf000000 0x00000000 rw- [stack] <---- STACK
0xffff0000 0xffff1000 0x00000000 r-x [vectors]

栈溢出

如图,就是程序对用户输入的长度做限制,导致可以覆盖LR寄存器的味道,通过精心地控制,即可达到任意代码执行的目的

我们用如下程序测试

1
2
3
4
5
6
7
8
/*azeria@labs:~/exp $ gcc stack.c -o stack*/
#include "stdio.h"

int main(int argc, char **argv)
{
char buffer[8];
gets(buffer);
}

汇编代码如下:

我们输入7个A,可以看到 R11(即FP)和LR储存的位置还没被覆盖

假如我们输入16个A,可以看到都覆盖了,之后的pop {r11, pc}就会让攻击者劫持控制流了

继续运行,确实被控制了,程序也蹦了

堆溢出

堆相对复杂,我们malloc一次,就会得到一个chunk,这个chunk有header和user data,堆溢出有下面图中的两种:一个是chunk内部,一个是溢出到另一个chunk了

chunk内部溢出

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

/*azeria@labs:~/exp $ gcc intra_chunk.c -o intra_chunk -O*/
#include "stdlib.h"
#include "stdio.h"

struct u_data //object model: 8 bytes for name, 4 bytes for number
{
char name[8];
int number;
};

int main ( int argc, char* argv[] )
{
struct u_data* objA = malloc(sizeof(struct u_data)); //create object in Heap

objA->number = 1234; //set the number of our object to a static value
gets(objA->name); //set name of our object according to user's input

if(objA->number == 1234) //check if static value is intact
{
puts("Memory valid");
}
else //proceed here in case the static value gets corrupted
{
puts("Memory corrupted");
}
}

可以看到,假如我们分配一个结构体的内存,假如对name的输入没有限制,那么number也是我们可以控制的

汇编如下:

输入7个A

堆从0x00021000开始,结构如下,number还没被覆盖

但是我们输入8个A,那么number的最低位就被00覆盖了

那就可以改变程序的执行流程

chunk外部溢出

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*azeria@labs:~/exp $ gcc inter_chunk.c -o inter_chunk*/
#include "stdlib.h"
#include "stdio.h"

int main ( int argc, char* argv[] )
{
char *some_string = malloc(8); //create some_string "object" in Heap
int *some_number = malloc(4); //create some_number "object" in Heap

*some_number = 1234; //assign some_number a static value
gets(some_string); //ask user for input for some_string

if(*some_number == 1234) //check if static value (of some_number) is in tact
{
puts("Memory valid");
}
else //proceed here in case the static some_number gets corrupted
{
puts("Memory corrupted");
}
}

差不多,我们也先输入7个A,可以看到堆的结构如下

16个A,header和some_number都被覆盖了

也同样可以改变执行流程

Reference

https://azeria-labs.com/process-memory-and-memory-corruption/

自愿打赏专区