011、内存—内存管理与端序
内存管理的三个动作
对用户态程序来说,使用一块内存通常会经历三个阶段:
- 保留(Reserve)一段虚拟地址范围;
- 提交(Commit)页面,使其具备实际存储支持;
- 使用结束后释放(Release)。
调试器显示的是线性的虚拟地址,但操作系统会通过页表把虚拟页映射到物理页或其他后备存储。逆向入门阶段,先掌握“进程地址空间、页面和权限”即可,不需要一开始就陷入完整分页结构。
常见内存区域
| 区域 | 主要内容 | 典型特征 |
|---|---|---|
| 代码区 | 编译后的机器指令 | 通常可读、可执行 |
| 全局数据区 | 全局变量、静态变量 | 生命周期接近整个进程 |
| 栈 | 参数、返回地址、局部变量 | 每个线程独立,通常向低地址增长 |
| 堆 | 动态申请的数据 | 生命周期由程序控制 |
| 模块映像 | EXE、DLL 的映射内容 | 可按 PE 节区划分 |
同一种 C/C++ 变量并不必然位于固定区域。存储位置还会受到作用域、存储期、编译优化和链接方式影响。
端序
端序(Endianness)描述多字节数据在内存中的字节排列顺序。
Windows 常见的 x86/x64 环境使用小端序(Little Endian):低有效字节放在低地址,高有效字节放在高地址。
1 | DWORD value = 0x12345678 |
寄存器中把这个数写作 12345678,内存窗口逐字节看到的却是 78 56 34 12。
数值没有“倒过来”,改变的只是多字节数值在内存中的排列方式。
大端与小端对比
数值 0x12345678 |
低地址到高地址 |
|---|---|
| 小端序 | 78 56 34 12 |
| 大端序 | 12 34 56 78 |
端序只影响多字节数据。单个 BYTE 只有一个字节,不存在字节顺序问题。
从内存还原数值
假设从低地址到高地址读到:
1 | 34 12 00 00 |
按小端 DWORD 解释为:
1 | 0x00001234 |
可以按下面的方式理解:
1 | 0x34 × 0x100^0 |
字符串与端序
ASCII 字符串通常按字符顺序逐字节存放:
1 | "ABCD" -> 41 42 43 44 00 |
如果调试器把这 4 Byte 合并成一个小端 DWORD,可能显示为 44434241。这并不表示字符串反了,而是同一组字节被按整数格式重新解释。
调试观察
1 | unsigned int value = 0x12345678; |
在调试器中同时打开:
- 寄存器窗口:观察寄存器中的整体数值;
- 内存窗口:转到
&value,观察四个独立字节; - 监视窗口:切换十六进制和十进制显示。
网络协议和文件格式可能自行规定字节序。分析二进制文件或网络数据时,不要因为 CPU 是小端就直接假设所有字段都按小端编码。
学习检查清单
| 检查项 | 状态 |
|---|---|
| 能说明保留、提交和释放的区别 | 待复盘 |
| 能列出代码区、栈、堆和全局数据区的主要特征 | 待复盘 |
能把 0x12345678 写成小端内存字节 |
待复盘 |
能由 78 56 34 12 还原 DWORD 数值 |
待复盘 |
| 能解释字符串字节与整数显示结果为何不同 | 待复盘 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ruiqy~!











