010、内存—内存特性
内存是什么
内存(Memory)是 CPU 执行程序时临时保存代码和数据的空间。站在程序视角看,它更像一排连续编号的存储单元:编号是地址,单元中保存的是数据。
在 Windows 用户态调试器中看到的地址,通常是进程的虚拟地址,不是可以直接拿来表示物理内存条位置的物理地址。
| 概念 | 说明 | 示例 |
|---|---|---|
| 地址 | 存储单元的编号 | 00401000 |
| 内容 | 该地址当前保存的数据 | 55 8B EC |
| 宽度 | 一次解释或访问多少位 | BYTE、WORD、DWORD、QWORD |
| 权限 | 页面允许的操作 | 读、写、执行 |
地址本身也是一个整数。在 32 位进程中,指针通常占 4 Byte;在 64 位进程中,指针通常占 8 Byte。
字节编址
x86/x64 使用字节编址:每个地址对应 1 Byte,相邻地址相差 1。
1 | 地址 内容 |
如果从 00401000 开始按 DWORD 读取,会一次取得 4 Byte;如果按 BYTE 读取,则只取得第一个字节。
易错点:地址递增 1 表示向后移动 1 Byte,不是移动 1 Bit,也不是自动移动一个
int。
虚拟地址空间
Windows 会为每个进程提供相对独立的虚拟地址空间。相同数值的虚拟地址出现在两个进程中,不代表它们一定指向同一块物理内存。
一个进程常见的内存区域包括:
- 可执行模块的代码和全局数据;
- 线程栈;
- 进程堆;
- 动态链接库;
- 内存映射文件和系统分配区域。
逆向时先判断地址属于哪个模块或区域,再解释其中的数据。只看一个裸地址,很容易把代码、指针、整数和字符串混在一起。
页面属性
操作系统以页(Page)为基本单位管理虚拟内存。页面会带有访问属性,常见组合如下:
| 属性 | 含义 | 常见区域 |
|---|---|---|
| R | 可读 | 常量、代码 |
| W | 可写 | 全局变量、堆、栈 |
| X | 可执行 | 代码段 |
| Guard | 保护页,首次访问触发异常 | 栈边界等 |
| No Access | 不允许访问 | 未提交或保护区域 |
不能仅凭“地址存在”就认为它可读写。访问未映射页面、向只读页写入、从不可执行页取指,都可能触发访问异常。
调试器中的观察顺序
看到一个陌生地址时,可以按下面的顺序判断:
- 看地址位于主模块、DLL、栈还是堆。
- 看该区域的页面属性和边界。
- 分别按 BYTE、WORD、DWORD 尝试解释数据。
- 检查内容是否像指针,继续跟随一次引用。
- 对照反汇编,确认是读取、写入还是执行。
1 | mov eax, dword ptr [00403000h] |
这条指令表示:把地址 00403000h 开始的 4 Byte 读入 EAX。
1 | mov dword ptr [00403000h], eax |
这一条则相反:把 EAX 的 4 Byte 写入该地址。
小实验
1 | int value = 0x12345678; |
调试时分别记录:
value的数值;&value的地址;pointer自己保存的内容;&pointer的地址。
变量地址、指针保存的地址、指针变量自己的地址是三个不同概念。
学习检查清单
| 检查项 | 状态 |
|---|---|
| 能区分地址、内容和访问宽度 | 待复盘 |
| 能说明调试器中看到的通常是虚拟地址 | 待复盘 |
| 能判断 R、W、X 页面属性的含义 | 待复盘 |
能解释 mov eax, [address] 的读取过程 |
待复盘 |
| 能区分变量地址与指针变量自身地址 | 待复盘 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ruiqy~!











