这一部分把高级语言数据结构翻译成内存布局。数组、指针、结构体和对象成员,在反汇编里最后都会变成地址计算、偏移访问和间接调用。

1、内容范围

小节 内容 复盘点
14.0 - 14.4 数组逆向 数组布局、寻址公式、多维数组、越界覆盖
14.5 - 14.8 移位与数据转换 SHL/SHR/SAL/SAR、二进制转换、数据完整性
14.9 - 15.2 数组地址与多维数组 数组地址传递、返回、指针数组、二维数组
15.3 - 16.1 指针逆向 指针赋值、解引用、运算、多级指针、安全性
16.2 - 16.4 函数指针与段机制 CALL 编码、函数指针、段选择子
16.5 - 16.9 结构体与 this 结构体布局、对象模型、成员偏移、thiscall

2、数组内存布局

一维数组是连续内存。

1
int nums[4] = {1, 2, 3, 4};

寻址公式:

1
元素地址 = 数组基址 + 下标 * 元素大小

反汇编中常看到:

1
mov eax, [base + index * 4]

这往往对应 int 数组访问。

3、多维数组

二维数组本质上仍然是连续内存。以 int a[3][4] 为例:

1
a[row][col] 地址 = base + (row * 4 + col) * sizeof(int)

复盘时先确认每行列数,再还原索引计算。

4、越界覆盖

越界问题只按防护复盘:数组访问超过边界后,可能覆盖邻近变量、保存寄存器、返回地址或堆元数据。

复盘项 说明
缓冲区大小 实际分配多少字节
写入长度 外部输入或循环次数是否超限
覆盖目标 邻近内存保存了什么
修复 长度检查、边界判断、安全函数

5、移位指令

指令 含义
shl/sal 左移,常见于乘 2 的幂
shr 逻辑右移,适合无符号
sar 算术右移,保留符号位

数组索引乘元素大小时,编译器可能用移位优化。

6、指针赋值与解引用

指针在汇编里就是地址。区别在于是否加了中括号:

1
2
mov eax, ecx     ; 复制地址/值
mov eax, [ecx] ; 把 ecx 当地址,读取内存

7、多级指针

多级指针表现为多次间接访问:

1
int **pp;

可能对应:

1
2
mov eax, [pp]
mov eax, [eax]

复盘时把每一级画出来,不要跳步。

8、函数指针与 CALL

直接调用和间接调用的区别:

1
2
3
call 401000h       ; 直接调用
call eax ; 间接调用,eax 保存函数地址
call [addr] ; 从内存取函数地址再调用

函数指针、虚函数、多态、回调都会出现间接调用。

9、结构体布局

结构体成员访问通常是“结构体基址 + 字段偏移”。

1
2
3
4
typedef struct User {
int id;
int age;
} User;

可能看到:

1
mov eax, [ecx+4]   ; 读取 age

要结合字段大小、对齐和访问顺序还原结构体。

10、this 指针

C++ 成员函数会隐式接收 this 指针。x86 thiscall 常用 ECX 传递 this,x64 下则按统一调用约定处理。

观察点 说明
this 当前对象地址
成员偏移 [this + offset]
成员函数 普通函数加隐式对象参数
虚函数 通过虚表间接调用

11、学习检查清单

检查项 状态
能根据寻址公式还原数组访问 待复盘
能画出二维数组线性内存布局 待复盘
能解释越界写会覆盖邻近内存 待复盘
能区分直接调用和函数指针间接调用 待复盘
能根据成员偏移推测结构体字段 待复盘