1. 内存布局
Kernel space 0xffffffff
操作系统挪用出来的kernel 空间
Stack 0xc0000000
Dynamic libraries 0x40000000
Heap
Read/write sections(.data .bss)
Readonly sections(.init, .rodata, .text)
Reserved 0x08048000
( 保留区 是对内存中受到保护而禁止访问的内存区域,如NULL)
一般的非法操作内存, 就是访问到了保留区的内存了。
2. 栈
栈总是向下增长的, 栈顶由称为esp 的寄存器进行定位.
栈保存了一个函数调用所需要维护的信息, 常常被称为 堆栈帧:
包括了:
1. 函数的返回地址和参数
2. 临时变量: 包括函数的非静态局部变量以及编译器自动生成的临时变量
3. 保存上下文: 包括在函数条用前后需要保持不变的寄存器。
汇编代码解析:
Push ebp
Move ebp, esp ( 保存ebp ,让ebp 指向目前的栈顶)
Sub esp, 0C0h ( 在栈上开辟一块空间)
Push ebx
Push esi
Push edi ( 保存ebx,esi,edi 寄存器)
Xxxxxx
xxxxxx
mov eax, 7bh ( 返回值通过eax 寄存器传递的)
pop edi
pop esi
pop ebx ( 恢复edi,esi,ebx 寄存器)
push esp,ebp
pop ebp ( 恢复esp,ebp)
hook 函数的实现原理, 在实际函数调用前加入的函数, 可以得函数执行的信息或者参数,甚至可以改变某些数据,控制函数的执行。
函数返回值的传递
1. 首先main 函数在栈上额外开辟了一片空间,并将这块空间的一部分作为传递返回值的临时对象。
2. 将临时对象的地址作为隐藏参数传递给调用函数,
3. 调用函数将数据拷贝给临时对象,并将临时对象的地址用eax 传出。
4. Main 函数将eax 指向的临时对象的内容拷贝出来。
书上的例子很说明问题。
4. 堆
栈上面分配的变量, 尽尽只能在这个函数里使用,出了这个函数就没有了( 因为函数已经被pop 出栈了) , 所以我们需要在堆上进行内存分配, 使变量能贯穿整个工程。
堆分配的过程
1. 运行库向操作系统“批发”一块比较大的堆空间
Mmap ()函数来分配大块空间,一般大于128K 的内存,调用malloc 函数 其实就是在调用mmap();
3. 运行库在大块内存分配给我们程序员使用
3 种算法:
1. 空间链表, 实现简单.
2. 位图, 速度快,但是容易产生碎片。
3. 对象池。