《逆向工程核心原理》学习笔记(未待完续)
第一部分
第1章 关于逆向工程
主要是些背景介绍,略。
第2章 逆向分析Hello World程序
- 认识调试器
文中提到Ollydbg已经停止维护,而且只能调试32位程序,所以切换到x64dbg作为替代。x64dbg是开源软件,在可以官网免费下载使用。

- 编译生成Helloworld程序
我是通过VS 2026的Helloworld项目模板来直接编译的。然后用x64dbg打开。接下来就是小试牛刀,怎么找到函数入口。
- 代码执行法
顾名思义,一步一步的执行代码(Step Over),同时观察什么时候出现特征现象,比如打印出Helloworld。
这种方法在代码量不大的时候才可行。
- 字符串检索法
因为我们知道关键代码部分会打印出Helloworld,所以在软件CPU界面右键->搜索->当前模块->字符串 ,就会出现Helloworld以及对应的代码地址,双击即可跳转到关键代码附近。

- API检索法
因为Helloworld是通过Windows的对话弹框出现的,所以对Windows API有所了解的话,就会知道一定是通过调用某个API来实现的。
- 按
Ctrl+G,然后输入messageboxw就会带出来,说明x64dbg已经帮我们自动识别这些Windows函数。

- 在另一个地方
符号里,会列出所有模块和调用函数,在user32.kernel模块里,搜索messageboxw,同样可以找到。

第3章 小端序标记法
简单介绍了大端序和小端序。Windows平台主要采用的是小端序标记法。比如0x12345678,内存地位是从数的低位开始的,即78 56 34 12这样表示的。
第4章 IA-32寄存器
之前在学习汇编的时候所有连接。而且现在大多数程序都是64位的。仅做了解。
第5-7章 栈,栈帧
介绍了什么是栈,一些调用约定。栈帧就是从函数调用开始到函数返回的范围。
第8-9章 用一个crackme来练习说明,略。
第10章 函数调用约定
函数调用后如何处理ESP,这就是函数调用约定要解决的问题。cdecl(由调用者清理栈), stdcall(由被调用者清理栈,常用于win32api)和fastcall(和stdcall类似,不过是用寄存器来传递参数).
第11-12章,略。
第二部分
第13章 PE文件格式
在Windows上,exe、dll、sys等这些都输入PE格式的种类。PE文件本质上一个以二进制格式存储的文件,可以用Hex Editor类似软件打开和编辑。网上有很多开源的PEviewer工具。

- 基本结构
从DOS头(DOS header)到节区头(Section header)是PE头部分。文件中使用偏移(offset),内存中使用VA(Virtual Address,虚拟地址)来表示位置。文件加载到内存时,情况就会发生变化(节区的大小、位置等)。文件的内容一般可分为代码(.text)、数据(.data )、资源(.rsrc)节,分别保存。
- VA和RVA
VA指的是进程虚拟内存的绝对地址,RVA(Relative Virtual Address,相对虚拟地址)指从某个基准位置(ImageBase)开始的相对地址。VA与RVA满足下面的换算关系。
VA = ImageBase + RVA
PE头内部信息大多以RVA形式存在。
- RAW和RVA
RAW是磁盘里的文件内偏移,RVA是加载到内存后的地址偏移。

- IAT(Import Address Table)
IAT里有包含导入了哪些DLL。PE头的IAT是代码逆向分析的核心内容。
- INT(Import Name Table)
INT与IAT的各元素同时指向相同地址,但也有很多情况下它们是不一致的。
可以把 PE 文件的导入表(Import Table)解析与 IAT 填充过程概括成一条主线:
找到 DLL → 加载 DLL → 找到要导入的函数 → 获取函数真实地址 → 写入 IAT。
- 读取 IID(IMAGE_IMPORT_DESCRIPTOR)
Name→ 得到 DLL 名称,例如kernel32.dllOriginalFirstThunk→ 得到 INT(Import Name Table)FirstThunk→ 得到 IAT(Import Address Table)
- 加载 DLL
LoadLibrary("kernel32.dll")DLL 被映射进当前进程。
- 遍历 INT
OriginalFirstThunk指向一个数组,里面描述程序需要从这个 DLL 导入哪些函数。
INT
│
├─ GetCurrentThreadId
├─ CreateFileW
├─ ReadFile
└─ NULL ← 结束- 确定函数
每个 INT 项可能通过:
Name:函数名
Ordinal:序号
找到需要导入的函数。例如:
IMAGE_IMPORT_BY_NAME
├─ Hint
└─ Name = "GetCurrentThreadId"- 获取函数的真实内存地址
概念上相当于:
GetProcAddress(hKernel32, "GetCurrentThreadId")
↓
0x7FFxxxxxxxxx- 把地址写入 IAT
FirstThunk指向 IAT。加载器把上一步得到的函数实际地址写入对应位置:
INT IAT
GetCurrentThreadId ───→ 0x7FFxxxxxxx
CreateFileW ───→ 0x7FFxxxxxxx
ReadFile ───→ 0x7FFxxxxxxx
NULL NULL- 处理完当前 DLL,再处理下一个 IID
直到遇到全零的IMAGE_IMPORT_DESCRIPTOR,整个导入表解析结束。
上述内容可以很直观的在PE-Bear等PEviewer工具了查看到。书中作者也提到可以自己去开发类似的PEviewer工具,加深理解。

当然这些也能通过x64dbg来查看。
其中IAT/EAT相关内容是运行时压缩器(Run-time Packer)、反调试、DLL注人、API钩取等多种中高级逆向主题的基础知识。
第14章 运行时压缩
- 压缩器
运行时压缩器是针对可执行(PE,Portable Executable)文件而言的,可执行文件内部含有解压缩代码,文件在运行瞬间于内存中解压缩后执行。
使用压缩器的另一个原因在于,它可以隐藏PE文件内的代码及资源(字符串、API名称字符串)等。压缩后的数据以难以辨识的二进制文件保存,从文件本身来看,这能有效隐藏内部代码与资源(当然解压缩后可以通过内存的Dump窗口查看)。
- 保护器
PE保护器是一类保护PE文件免受代码逆向分析的实用程序。它们不像普通的压缩器一样仅对PE文件进行运行时压缩,而应用了多种防止代码逆向分析的技术(反调试、反模拟、代码混乱、多态代码、垃圾代码、调试器监视等)。
以UPX为例,UPX是一款开源的压缩器。我用它来压缩Notepad++,然后用PE-bear打开,能看出结构明显变化。

解压缩代码与压缩的源代码都在第二个节区(UPX1)。文件运行时首先执行解压缩代码,把处于压缩状态的源代码解压到第一个节区(UPX0)。解压过程结束后即运行源文件的EP代码。
第15章