1071 字
3 分钟
cpp(九)——内存序,堆栈
cpp(九)—关于堆栈、字节序
一、类型与读取
在ida里,我们经常能看到类型转换比如*(_DWORD *),括号内是指针类型,括号前的星号是用于解引用,这里也就是读取内存的方式,如下
IDA 伪代码 含义*(_DWORD *)a1 从 a1 地址读取 4 字节*(_QWORD *)a1 从 a1 地址读取 8 字节*(_DWORD *)(a1 + 8) 从 a1+8 读取 4 字节*(_QWORD *)(a1 + 0x10) 从 a1+16 读取 8 字节a1 + 0x20 地址增加 32 字节++a1 地址增加一个所指类型的大小(取决于 a1 类型)二、栈与堆
栈
栈主要用于保存函数执行过程中需要的数据,例如:
- 局部变量
- 函数调用时需要保存的信息
- 部分函数参数
- 返回地址和寄存器保存现场(具体位置取决于 ABI 和编译器)
void test() { int a = 10; int b = 20; int c = a + b;}函数执行的时候,编译器会安排空间
比如下面的汇编
SUB SP, SP, #0x30这通常表示:
将栈指针 SP 减少 0x30,为当前函数分配 48 字节的栈空间。
函数结束时可能看到:
ADD SP, SP, #0x30RET这里表示释放空间。
栈在操作系统里布局通常是处在在上方,会往低地址生长。
堆
堆用于程序运行过程中动态申请和释放内存。
例如:
int* p = new int(100);这里发生了什么?
- 程序向动态内存分配器申请空间。
- 分配器返回一块可以存放
int的内存地址。 - 指针变量
p保存这个地址。 *p访问这块内存中的整数。
这里隐藏着一个堆栈关系:位于栈上的指针p里保存着堆上new出来的int100的地址。
int* p = new int(100);
std::cout << *p << std::endl;
delete p;p = nullptr;这里 delete p 释放的是堆上的对象,不是指针变量 p 本身。
从这里就能看出野指针的来源了,不过之前咱们看了RAll,只能说cpp牛逼吧。
栈与堆的区别
| 对比项 | 栈 Stack | 堆 Heap |
|---|---|---|
| 管理方式 | 通常由函数调用机制自动管理 | 动态分配器管理 |
| 生命周期 | 通常随栈帧退出结束 | 按分配和释放规则决定 |
| 典型对象 | 局部变量、调用现场 | new、malloc 分配的对象 |
| 分配特点 | 通常开销较低 | 通常管理开销更高 |
| 常见问题 | 栈溢出、返回悬空指针 | 内存泄漏、重复释放 |
汇编里的栈
SUB SP, SP, #0x40STR W0, [SP,#0x10]LDR W1, [SP,#0x10]ADD SP, SP, #0x40RET| 指令 | 含义 |
|---|---|
SUB SP, SP, #0x40 | 为栈帧分配 64 字节 |
STR W0, [SP,#0x10] | 将 W0 的 4 字节写入栈偏移 16 |
LDR W1, [SP,#0x10] | 从栈偏移 16 读取 4 字节 |
ADD SP, SP, #0x40 | 回收栈空间 |
RET | 函数返回 |
所以这里也会引出一个判断问题
v1 = *(_DWORD *)(v2 + 0x10);要判断 v2 是什么:
- 如果
v2来自SP或FP,可能是栈上的变量。 - 如果
v2是函数参数传入的地址,可能是对象或缓冲区。 - 如果
v2来自new或malloc,可能是堆内存。
不要仅凭偏移就判断内存属于栈还是堆。
三、字节序
字节序决定了字节在内存里的排序。
假设有一个 32 位整数:
uint32_t value = 0x12345678;它由四个字节组成:
12 34 56 78但是在内存中,这四个字节究竟按照什么顺序排列?
这就涉及字节序。
主要有两种:
- 大端序(Big Endian)
- 小端序(Little Endian)
大端序
高位放在低地址,这也符合人类的阅读习惯
地址 数据0x1000 120x1001 340x1002 560x1003 78小端序
低位放在低地址。
地址 数据0x1000 780x1001 560x1002 340x1003 12在安卓逆向里,通常使用的是arm与thumb指令,都是小端序。
为什么说字节序重要
前面说了,编译器是按照字节序读取内存里的内容的,所以就会出现运行时读出来的内容大小端序不一致的结果。
首先先说一个前提!字节序不会改变数值大小!比如0x67452301虽然在大小端里视图不一样,但运算时依旧是按照这个数值来运算的。
唯一能体现区别的在于:字节流的读取
比如一个数值被存在内存里,那么编译器按照大小端就能读出两种结果:01 23 45 67和67 45 23 01,这两个玩意儿按照ascii转成字符后是不同的。
cpp(九)——内存序,堆栈
https://fatdog.20060113.xyz/posts/cpp-again9/ 部分信息可能已经过时









