10、动态分析工具(下):内存查看与修改、堆栈跟踪、Run Trace
好,咱们接着聊动态分析。上一章我们把断点和单步执行讲透了,这一章要聊的,是真正让你「看见」程序灵魂的东西——内存、堆栈,还有执行轨迹。
我个人觉得,这三样东西才是逆向工程的硬功夫。断点只是敲门砖,真正的高手,是能通过内存和堆栈的变化,把程序的逻辑「读」出来的。
10.1 内存查看与修改:直接动程序的「内脏」
你想想看,程序跑起来之后,所有的变量、对象、标志位,最终都落在内存里。能直接看内存,就等于能透视程序的内部状态。
10.1.1 内存窗口怎么用
大部分调试器(比如 x64dbg、OllyDbg、IDA Pro)都有内存窗口。用法其实很简单:
- 地址输入:直接输入十六进制地址,或者输入表达式(比如
ebp-0x4) - 数据解读:默认显示十六进制字节,右侧是 ASCII 字符
- 修改数据:双击字节,直接改,回车生效
核心要点:内存修改是动态分析的「核武器」。改一个字节,可能就跳过了验证、解锁了功能。
10.1.2 实战:改内存跳过密码验证
我在项目中遇到过这样一个场景:一个老旧的 Windows 程序,登录框死活找不到正确的密码。我懒得去逆向算法,直接动态调试,找到密码比对后的标志位。
// 伪代码示意
if (strcmp(input, password) == 0) {
flag = 1; // 这个 flag 在内存地址 0x00401234
} else {
flag = 0;
}
我在内存窗口找到 0x00401234,看到 flag 是 0。输入错误密码,断点命中后,我直接把 0 改成 1。程序就认为验证通过了。
小技巧:改内存比改汇编代码更隐蔽。有些程序会做代码完整性校验,但很少校验数据段。
10.1.3 内存搜索技巧
有时候你不知道变量在哪个地址,怎么办?搜!
- 精确搜索:搜具体的数值,比如搜
0x12345678 - 模糊搜索:搜字符串,比如搜
"password" - 变化搜索:先搜一次,让程序运行一下,再搜变化后的值
嗯,这里要注意:内存搜索在大型程序里会很慢。我建议你先缩小范围,比如只搜某个 DLL 的内存空间。
10.2 堆栈跟踪:看穿函数的「前世今生」
堆栈跟踪(Stack Trace),说白了就是告诉你「当前这行代码是谁调过来的」。这玩意儿在分析崩溃、理解调用流程时,简直是神器。
10.2.1 堆栈的结构
堆栈是后进先出的。每次函数调用,都会压入返回地址、参数、局部变量。堆栈跟踪就是把这些信息一条条列出来。
// 堆栈跟踪示例(从顶部到底部)
0x00401234 - main() 调用了 funcA()
0x00405678 - funcA() 调用了 funcB()
0x004089AB - funcB() 调用了 funcC() <-- 当前在这里
你看,从下往上读,就是完整的调用链。
10.2.2 怎么用好堆栈跟踪
我个人习惯是:当程序崩溃时,第一件事就是看堆栈。它能告诉你崩溃发生在哪个函数,以及是谁调了它。
避坑指南:我曾经遇到一个程序,堆栈被破坏了(栈溢出攻击),显示的调用链完全乱掉。这时候别信堆栈,得靠内存分析来还原。
10.2.3 手动分析堆栈
有时候调试器给的堆栈跟踪不准(比如优化过的代码),你就得手动看:
- 找到当前
ESP(栈顶指针) - 往上读,找到返回地址(通常是
call指令的下一条) - 根据返回地址,反推是哪个函数调用的
这活儿有点累,但很锻炼人。我刚开始学的时候,经常手动画堆栈图,画着画着就懂了。
10.3 Run Trace:程序的「行车记录仪」
Run Trace,也叫执行轨迹。它会把程序执行过的每一条指令都记录下来。你想想看,这得有多大信息量?
10.3.1 什么时候用 Run Trace
- 分析复杂算法:比如加密算法,你单步跟会累死,用 Run Trace 跑一遍,然后分析日志
- 找隐藏功能:有些代码只在特定条件下执行,Run Trace 能帮你找到那些「暗门」
- 对比分析:跑两次,一次正常,一次异常,对比轨迹差异
10.3.2 怎么用 x64dbg 做 Run Trace
x64dbg 的 Run Trace 功能很强大。我简单说一下步骤:
- 设置好断点,让程序跑到你感兴趣的位置
- 打开 Run Trace 窗口,点击「开始记录」
- 让程序继续跑(或者用 Trace Into)
- 停止记录,查看日志
注意:Run Trace 会产生海量数据。跑几秒钟可能就几百万条指令。我建议你设置过滤条件,比如只记录某个模块的指令。
10.3.3 分析 Run Trace 日志
日志里每一行都是一条指令。你会看到:
地址 指令 备注
0x00401000 mov eax, dword ptr [ebp-4]
0x00401003 add eax, 0x1
0x00401006 mov dword ptr [ebp-4], eax
你可以搜索特定的指令模式,比如搜索 call 指令,看看程序调了哪些函数。
嗯,这里有个坑:Run Trace 日志太大,普通文本编辑器打不开。我建议用专门的日志分析工具,或者写脚本过滤。
10.4 知识体系总览
为了让你更直观地理解这三样工具的关系,我画了一张图:
10.5 三者配合的实战思路
光会单个工具不够,你得把它们串起来用。我举个例子:
假设你在分析一个恶意软件,它解密了一段数据。你怎么搞?
- 用内存搜索:找到解密后的数据在内存中的位置
- 下内存断点:看谁在读写这块内存
- 看堆栈跟踪:找到读写内存的函数调用链
- 用 Run Trace:记录这个函数的执行轨迹,分析解密算法
你看,一套组合拳下来,再复杂的逻辑也能给你拆得明明白白。
我的经验:别指望一次就能搞定。动态分析是个反复迭代的过程——改内存、看效果、跟踪堆栈、再改内存。多试几次,规律就出来了。
好了,这一章的内容就到这儿。内存查看、堆栈跟踪、Run Trace,这三样东西你练熟了,逆向工程就算入了门。剩下的,就是多练、多踩坑、多总结。
公众号:蓝海资料掘金营,微信 deep3321