IDA Pro 分析技巧
前言
IDA Pro 是逆向工程界的瑞士军刀,但真正强大的地方不仅仅是反汇编和 F5 反编译,更在于是一个可交互的数据库。分析一个复杂的程序(如 .so 文件、.exe 等)是一个漫长且需要迭代的过程。
核心理念: 我们的目标不是一次性看懂所有代码,而是把每次的理解都记录回 IDA 的数据库 (IDB) 中。添加的注释、重命名的函数、定义的结构体越多,这个程序就越“透明”。
基础导航与视图
掌握最基本的“移动”方式是最高效的。
Space(空格键):在汇编图表视图(Graph View)和汇编文本视图(Text View)之间切换。图表视图更直观,文本视图适合阅读线性逻辑。F5:最核心的功能。查看当前函数的 C 语言伪代码。 90% 的分析时间都应该在这里。- **
\(反斜杠)**:在 F5 伪代码窗口中,切换回对应的汇编代码视图。再次按下可切换回来。 - **
G(Go)**:跳转到指定地址或函数名。 X(Xrefs):查看交叉引用。即“谁调用了这个函数?”或“谁访问了这个数据?”。在函数名、变量名、字符串上按X是最常用的定位手段。- **
Esc(Escape)**:在代码中后退。 - **
Ctrl + Enter**:在代码中前进。(与Esc对应) - **
Tab**:在 F5 伪代码窗口中,按下Tab可以在伪代码和对应的汇编代码之间快速同步跳转。
关键技巧:注释与重命名
这是最需要养成习惯的部分。一个全是 sub_XXXX 和 v5、a1 的伪代码是无法阅读的。
重命名 (Rename) - 快捷键 N
重命名是给一个“地址”赋予一个有意义的“符号名”。不支持中文,必须遵循 C 语言的命名规范(英文、数字、下划线)。
为什么不支持中文? 因为
N修改的是**标识符 (Identifier)**,会直接影响 F5 反编译出的伪代码。v5 = Calculate_Damage(a1);是合法的 C 代码,但v5 = 计算伤害(a1);不是。
- 重命名函数:在
sub_12345上按N-> 输入ParseNetworkPacket。 - 重命名变量:在 F5 窗口的
a1、v10上按N-> 输入this_ptr、decrypted_buffer。 - 重命名全局数据:在
dword_ABCDE上按N-> 输入g_PlayerConfig。
添加注释 (Commenting)
IDA 提供三种注释,用途完全不同:
;(分号) - 可重复注释 (Repeatable Comment)- 用途:给一个地址添加注释。所有引用这个地址的地方都会显示该注释。
- 示例:
- 在一个关键函数
sub_12345(已重命名为DoLoginRequest)的开头按:,输入:“登录包加密函数”。 - 在程序中任何调用
DoLoginRequest的地方(例如BL DoLoginRequest),旁边都会自动显示“登录包加密函数”的注释。
- 在一个关键函数
- 一般用来:标记函数、全局变量、重要代码块的“一句话总结”。
:(冒号) - 普通注释 (Regular Comment)用途:给当前行添加注释。只在当前视图(汇编或 F5)的这一行显示。
示例:在 F5 伪代码中:
1
2if ( v10 > 100 ) // ; 检查玩家等级是否超过100
return -1; // ; 返回-1表示等级错误我用来:解释单行代码的复杂逻辑或意图。
;(分号) - 函数注释 (Function Comment)- 用途:给整个函数添加一大段注释,会显示在函数的最开头。
- 示例:在函数
ParseNetworkPacket上按Shift + ;,输入:1
2
3
4此函数是网络包的核心处理函数。
- 作用:解密、解压、分发。
- 算法:AES-128-CBC
- 注意:参数 a2 是一个 PlayerState 结构体指针。 - 一般用来:写函数的详细分析文档。
逆向的灵魂:数据结构
分析 C++/C 代码的核心就是还原结构体和枚举。
结构体 (Structures) - 快捷键 Shift + F9
当看到大量的 [r0, #4]、[r0, #8] 或者 F5 里的 *(a1 + 4)、*(a1 + 8) 时,a1 几乎 100% 是一个结构体指针。
工作流:
- 打开结构体窗口:按
Shift + F9。 - 创建新结构体:按
Ins(Insert) 键,输入结构体名字,例如PlayerState。 - 定义成员:
- 光标停在
PlayerState上,按D键来定义数据成员(比如db字节,dw字,dd双字,dq四字)。 - 技巧:如果不知道多大,就一直按
D,IDA 会帮创建field_0,field_4,field_8… - 可以按
N重命名这些field_成员,例如field_0改为health,field_4改为mana。 - 还可以按
Y更改成员类型,例如把field_8的类型dd(4字节) 改为char*(一个指针)。
- 光标停在
- 应用结构体:
- 回到 F5 伪代码窗口。
- 找到那个被当作指针的变量(比如
a1)。 - 按
Y(Set Type)。 - 输入类型:
PlayerState*(注意,是指针*)。
- 见证奇迹:
- 所有
*(a1 + 4)会自动变成a1->mana。 - 所有
*(a1 + 8)会自动变成a1->name。 - 代码可读性瞬间提升 100 倍。
- 所有
枚举 (Enums) - 快捷键 Shift + F10
当看到 if (v5 == 1)、if (v5 == 2) 这种“魔法数字”(Magic Number) 时,们很可能是枚举值。
工作流:
- 打开枚举窗口:按
Shift + F10。 - 创建新枚举:按
Ins键,输入枚举名字,例如LoginStatus。 - 添加成员:在
LoginStatus上按N,添加成员,例如STATUS_SUCCESS = 0、STATUS_FAILED = 1、STATUS_BANNED = 2。 - 应用枚举:
- 回到 F5 伪代码或汇编。
- 光标停在那个数字
1上。 - 按
M(Enum)。 - 选择
STATUS_FAILED。
- 见证奇迹:
- 代码从
if (v5 == 1)变为if (v5 == STATUS_FAILED)。
- 代码从
如何保存与快速定位
如何保存?
不需要“保存笔记”!
我们所做的一切——重命名、注释、结构体——都会自动保存在 .idb (32位) 或 .i64 (64位) 数据库文件中。
- 手动保存:按
Ctrl + W可以随时保存当前数据库状态。 - 备份:这个
.i64文件就是所有分析心血的结晶。一定要定期备份! - 快照:
File -> Take database snapshot...可以在做“危险”操作(如运行未知脚本)前拍个快照,以便回滚。
下次打开如何快速定位?
当分析了几天后,如何快速找到上次的进度?
Shift + F4- 名称窗口 (Names Window)- 最快、最常用的定位方式。
- 这里列出了所有重命名过的函数、变量、地址。
- 打开后按
J可以快速搜索命名的函数(例如ParseNetworkPacket)。
Shift + F12- 字符串窗口 (Strings Window)- 如果只记得某个函数里有一条 “Login Failed” 错误信息。
- 打开字符串窗口,找到 “Login Failed”,按
X查看交叉引用,就能瞬间定位到使用的函数。
Shift + F3- 函数窗口 (Functions Window)- 列出所有函数。可以按名称排序,重命名过的函数会和
sub_...分开,便于查找。
- 列出所有函数。可以按名称排序,重命名过的函数会和
Alt + M/Ctrl + M- 书签 (Bookmarks)Alt + M:在当前位置添加一个书签(可以写描述)。Ctrl + M:打开书签列表,快速跳转。- 用途:标记“这个地方我还没看懂,明天再来”或“这里是核心,要常看”。
View -> Open subviews -> Comments- 注释窗口- 列出写过的所有中文注释。
- 可以直接搜索的注释内容,然后双击跳转。
总结:一套高效的工作流
- **先看
Shift + F12(字符串)**,找一些可疑的错误信息、URL、关键字。 - 对可疑字符串按
X(交叉引用),找到使用的函数。 - 在函数中按
F5进入伪代码。 - 开始分析:
- 看到
a1、v5不顺眼 -> 按N重命名为player_ptr、loop_index。 - 看到
[ptr + 8]->Shift + F9创建结构体,回来按Y应用。 - 看到
if (v10 == 3)->Shift + F10创建枚举,回来按M应用。 - 看懂了单行逻辑 -> 按
:写中文行注释。 - 看懂了函数功能 -> 按
N重命名函数 (英文),再按;写详细中文功能注释。 - 发现是关键函数 -> 按
;添加一个简短的中文“可重复注释”。
- 看到
- 分析累了 -> 按
Ctrl + W保存 IDB。 - 第二天打开 -> 按
Shift + F4(名称窗口) 或Ctrl + M(书签) 快速回到昨天的进度。
逆向是一个迭代的过程。坚持做笔记,几周后,这个程序对来说就会像自己的源码一样清晰。