mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 03:01:15 +07:00
update memory
This commit is contained in:
parent
d167525354
commit
79f10279ca
@ -185,6 +185,8 @@ fastcall | 函数本身 | 都两个 DWORD(4 字节)类型或者占更少字
|
||||
|
||||
## 堆与内存管理
|
||||
#### 堆
|
||||
![](../pic/1.5.7_spacelayout.png)
|
||||
|
||||
堆是用于存放除了栈里的东西之外所有其他东西的内存区域,有动态内存分配器负责维护。分配器将堆视为一组不同大小的块(block)的集合来维护,每个块就是一个连续的虚拟内存器片(chunk)。当使用 `malloc()` 和 `free()` 时就是在操作堆中的内存。对于堆来说,释放工作由程序员控制,容易产生内存泄露。
|
||||
|
||||
堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
|
||||
@ -202,7 +204,132 @@ int brk(void *addr);
|
||||
|
||||
void *sbrk(intptr_t increment);
|
||||
```
|
||||
参数 `*addr` 是进程数据段的结束地址,`brk()` 通过改变该地址来改变数据段的大小,当结束地址向高地址移动,进程内存空间增大,当结束地址向低地址移动,进程内存空间减小。`brk()`调用成功时返回 0,失败时返回 -1。 `sbrk()` 与 `brk()` 类似,但是参数 `increment` 表示增量,即增加或减少的空间大小,调用成功时返回增加后减小后数据段的结束地址,失败时返回 -1。
|
||||
参数 `*addr` 是进程数据段的结束地址,`brk()` 通过改变该地址来改变数据段的大小,当结束地址向高地址移动,进程内存空间增大,当结束地址向低地址移动,进程内存空间减小。`brk()`调用成功时返回 0,失败时返回 -1。 `sbrk()` 与 `brk()` 类似,但是参数 `increment` 表示增量,即增加或减少的空间大小,调用成功时返回增加后减小前数据段的结束地址,失败时返回 -1。
|
||||
|
||||
在上图中我们看到 brk 指示堆结束地址,start_brk 指示堆开始地址。BSS segment 和 heap 之间有一段 Random brk offset,这是由于 ASLR 的作用,如果关闭了 ASLR,则 Random brk offset 为 0,堆结束地址和数据段开始地址重合。
|
||||
|
||||
例子:[源码](../src/Others/1.5.7_brk.c)
|
||||
```
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
void main() {
|
||||
void *curr_brk, *tmp_brk, *pre_brk;
|
||||
|
||||
printf("当前进程 PID:%d\n", getpid());
|
||||
|
||||
tmp_brk = curr_brk = sbrk(0);
|
||||
printf("初始化后的结束地址:%p\n", curr_brk);
|
||||
getchar();
|
||||
|
||||
brk(curr_brk+4096);
|
||||
curr_brk = sbrk(0);
|
||||
printf("brk 之后的结束地址:%p\n", curr_brk);
|
||||
getchar();
|
||||
|
||||
pre_brk = sbrk(4096);
|
||||
curr_brk = sbrk(0);
|
||||
printf("sbrk 返回值(即之前的结束地址):%p\n", pre_brk);
|
||||
printf("sbrk 之后的结束地址:%p\n", curr_brk);
|
||||
getchar();
|
||||
|
||||
brk(tmp_brk);
|
||||
curr_brk = sbrk(0);
|
||||
printf("恢复到初始化时的结束地址:%p\n", curr_brk);
|
||||
getchar();
|
||||
}
|
||||
```
|
||||
开启两个终端,一个用于执行程序,另一个用于观察内存地址。首先我们看关闭了 ASLR 的情况。第一步初始化:
|
||||
```text
|
||||
# echo 0 > /proc/sys/kernel/randomize_va_space
|
||||
```
|
||||
```text
|
||||
$ ./a.out
|
||||
当前进程 PID:27759
|
||||
初始化后的结束地址:0x56579000
|
||||
```
|
||||
```text
|
||||
# cat /proc/27759/maps
|
||||
...
|
||||
56557000-56558000 rw-p 00001000 08:01 28587506 /home/a.out
|
||||
56558000-56579000 rw-p 00000000 00:00 0 [heap]
|
||||
...
|
||||
```
|
||||
数据段结束地址和堆开始地址同为 `0x56558000`,堆结束地址为 `0x56579000`。
|
||||
|
||||
第二步使用 `brk()` 增加堆空间:
|
||||
```text
|
||||
$ ./a.out
|
||||
当前进程 PID:27759
|
||||
初始化后的结束地址:0x56579000
|
||||
|
||||
brk 之后的结束地址:0x5657a000
|
||||
```
|
||||
```text
|
||||
# cat /proc/27759/maps
|
||||
...
|
||||
56557000-56558000 rw-p 00001000 08:01 28587506 /home/a.out
|
||||
56558000-5657a000 rw-p 00000000 00:00 0 [heap]
|
||||
...
|
||||
```
|
||||
堆开始地址不变,结束地址增加为 `0x5657a000`。
|
||||
|
||||
第三步使用 `sbrk()` 增加堆空间:
|
||||
```text
|
||||
$ ./a.out
|
||||
当前进程 PID:27759
|
||||
初始化后的结束地址:0x56579000
|
||||
|
||||
brk 之后的结束地址:0x5657a000
|
||||
|
||||
sbrk 返回值(即之前的结束地址):0x5657a000
|
||||
sbrk 之后的结束地址:0x5657b000
|
||||
```
|
||||
```text
|
||||
# cat /proc/27759/maps
|
||||
...
|
||||
56557000-56558000 rw-p 00001000 08:01 28587506 /home/a.out
|
||||
56558000-5657b000 rw-p 00000000 00:00 0 [heap]
|
||||
...
|
||||
```
|
||||
|
||||
第四步减小堆空间:
|
||||
```text
|
||||
]$ ./a.out
|
||||
当前进程 PID:27759
|
||||
初始化后的结束地址:0x56579000
|
||||
|
||||
brk 之后的结束地址:0x5657a000
|
||||
|
||||
sbrk 返回值(即之前的结束地址):0x5657a000
|
||||
sbrk 之后的结束地址:0x5657b000
|
||||
|
||||
恢复到初始化时的结束地址:0x56579000
|
||||
```
|
||||
```text
|
||||
# cat /proc/27759/maps
|
||||
...
|
||||
56557000-56558000 rw-p 00001000 08:01 28587506 /home/a.out
|
||||
56558000-56579000 rw-p 00000000 00:00 0 [heap]
|
||||
...
|
||||
```
|
||||
|
||||
再来看一下开启了 ASLR 的情况:
|
||||
```text
|
||||
# echo 2 > /proc/sys/kernel/randomize_va_space
|
||||
```
|
||||
```text
|
||||
]$ ./a.out
|
||||
当前进程 PID:28025
|
||||
初始化后的结束地址:0x578ad000
|
||||
```
|
||||
```text
|
||||
# cat /proc/28025/maps
|
||||
...
|
||||
5663f000-56640000 rw-p 00001000 08:01 28587506 /home/a.out
|
||||
5788c000-578ad000 rw-p 00000000 00:00 0 [heap]
|
||||
...
|
||||
```
|
||||
可以看到这时数据段的结束地址 `0x56640000` 不等于堆的开始地址 `0x5788c000`。
|
||||
|
||||
`mmap()` 的声明如下:
|
||||
```c
|
||||
@ -220,7 +347,73 @@ void *mmap(void *addr, size_t len, int prot, int flags,
|
||||
int munmap(void *addr, size_t len);
|
||||
```
|
||||
|
||||
C 标准库提供了一个叫做 `malloc` 的分配器,程序通过调用 `malloc()` 函数来从堆中分配块,声明如下:
|
||||
例子:[源码](../src/Others/1.5.7_mmap.c)
|
||||
```
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
void main() {
|
||||
void *curr_brk;
|
||||
|
||||
printf("当前进程 PID:%d\n", getpid());
|
||||
printf("初始化后\n");
|
||||
getchar();
|
||||
|
||||
char *addr;
|
||||
addr = mmap(NULL, (size_t)4096, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
|
||||
printf("mmap 完成\n");
|
||||
getchar();
|
||||
|
||||
munmap(addr, (size_t)4096);
|
||||
printf("munmap 完成\n");
|
||||
getchar();
|
||||
}
|
||||
```
|
||||
第一步初始化:
|
||||
```text
|
||||
$ ./a.out
|
||||
当前进程 PID:28652
|
||||
初始化后
|
||||
```
|
||||
```text
|
||||
# cat /proc/28652/maps
|
||||
...
|
||||
f76b2000-f76b5000 rw-p 00000000 00:00 0
|
||||
f76ef000-f76f1000 rw-p 00000000 00:00 0
|
||||
...
|
||||
```
|
||||
第二步 mmap:
|
||||
```text
|
||||
]$ ./a.out
|
||||
当前进程 PID:28652
|
||||
初始化后
|
||||
mmap 完成
|
||||
```
|
||||
```text
|
||||
# cat /proc/28652/maps
|
||||
...
|
||||
f76b2000-f76b5000 rw-p 00000000 00:00 0
|
||||
f76ee000-f76f1000 rw-p 00000000 00:00 0
|
||||
...
|
||||
```
|
||||
第三步 munmap:
|
||||
```text
|
||||
$ ./a.out
|
||||
当前进程 PID:28652
|
||||
初始化后
|
||||
mmap 完成
|
||||
munmap 完成
|
||||
```
|
||||
```text
|
||||
# cat /proc/28652/maps
|
||||
...
|
||||
f76b2000-f76b5000 rw-p 00000000 00:00 0
|
||||
f76ef000-f76f1000 rw-p 00000000 00:00 0
|
||||
...
|
||||
```
|
||||
可以看到第二行第一列地址从 `f76ef000`->`f76ee000`->`f76ef000` 变化。`0xf76ee000-0xf76ef000=0x1000=4096`。
|
||||
|
||||
通常情况下,我们不会直接使用 `brk()` 和 `mmap()` 来分配堆空间,C 标准库提供了一个叫做 `malloc` 的分配器,程序通过调用 `malloc()` 函数来从堆中分配块,声明如下:
|
||||
```c
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -46,7 +46,7 @@ Partial RELRO Canary found NX enabled PIE enabled No RPATH No RU
|
||||
No-eXecute,表示不可执行,其原理是将数据所在的内存页标识为不可执行,如果程序产生溢出转入执行 shellcode 时,CPU 会抛出异常。其绕过方法是 ret2libc。
|
||||
|
||||
#### PIE
|
||||
PIE(Position Independent Executable)需要配合 ASLR 来使用,以达到可执行文件的加载时地址随机化。简单来说,PIE 是编译时随机化,由编译器完成;ASLR 是加载时随机化,有操作系统完成。
|
||||
PIE(Position Independent Executable)需要配合 ASLR 来使用,以达到可执行文件的加载时地址随机化。简单来说,PIE 是编译时随机化,由编译器完成;ASLR 是加载时随机化,由操作系统完成。
|
||||
|
||||
我们通过实际例子来探索一下 PIE 和 ASLR:
|
||||
```c
|
||||
@ -146,8 +146,8 @@ Partial RELRO No canary found NX enabled No PIE No RPATH No RU
|
||||
>
|
||||
>完全开启(在部分开启的基础上增加 heap的随机化:`# echo 2 > /proc/sys/kernel/randomize_va_space`
|
||||
|
||||
#### RELOAD
|
||||
RELRO 设置符号重定向表为只读或在程序启动时就解析并绑定所有动态符号,从而减少对 GOT(Global Offset Table)的攻击。
|
||||
#### RELRO
|
||||
RELRO(ReLocation Read-Only)设置符号重定向表为只读或在程序启动时就解析并绑定所有动态符号,从而减少对 GOT(Global Offset Table)的攻击。
|
||||
|
||||
|
||||
## 编译参数
|
||||
@ -171,7 +171,7 @@ gcc hello.c -o hello-S -fstack-protector-all -z noexecstack -pie -z now
|
||||
|
||||
- FORTIFY
|
||||
- `-D_FORTIFY_SOURCE=1`:仅在编译时检测溢出
|
||||
- `-D_FORTIFY_SOURCE=1`:在编译时和运行时检测溢出
|
||||
- `-D_FORTIFY_SOURCE=2`:在编译时和运行时检测溢出
|
||||
|
||||
|
||||
## 保护机制检测
|
||||
|
BIN
pic/1.5.7_spacelayout.png
Normal file
BIN
pic/1.5.7_spacelayout.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
27
src/Others/1.5.7_brk.c
Normal file
27
src/Others/1.5.7_brk.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
void main() {
|
||||
void *curr_brk, *tmp_brk, *pre_brk;
|
||||
|
||||
printf("当前进程 PID:%d\n", getpid());
|
||||
|
||||
tmp_brk = curr_brk = sbrk(0);
|
||||
printf("初始化后的结束地址:%p\n", curr_brk);
|
||||
getchar();
|
||||
|
||||
brk(curr_brk+4096);
|
||||
curr_brk = sbrk(0);
|
||||
printf("brk 之后的结束地址:%p\n", curr_brk);
|
||||
getchar();
|
||||
|
||||
pre_brk = sbrk(4096);
|
||||
curr_brk = sbrk(0);
|
||||
printf("sbrk 返回值(即之前的结束地址):%p\n", pre_brk);
|
||||
printf("sbrk 之后的结束地址:%p\n", curr_brk);
|
||||
getchar();
|
||||
|
||||
brk(tmp_brk);
|
||||
curr_brk = sbrk(0);
|
||||
printf("恢复到初始化时的结束地址:%p\n", curr_brk);
|
||||
getchar();
|
||||
}
|
19
src/Others/1.5.7_mmap.c
Normal file
19
src/Others/1.5.7_mmap.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
void main() {
|
||||
void *curr_brk;
|
||||
|
||||
printf("当前进程 PID:%d\n", getpid());
|
||||
printf("初始化后\n");
|
||||
getchar();
|
||||
|
||||
char *addr;
|
||||
addr = mmap(NULL, (size_t)4096, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
|
||||
printf("mmap 完成\n");
|
||||
getchar();
|
||||
|
||||
munmap(addr, (size_t)4096);
|
||||
printf("munmap 完成\n");
|
||||
getchar();
|
||||
}
|
Loading…
Reference in New Issue
Block a user