简介:OllyDbg-加强版是一款面向逆向工程初学者与二进制调试实践者的32位汇编级分析调试工具,适合在缺少源代码时开展程序行为分析、崩溃定位与疑难兼容问题排查。其可视化界面便于观察寄存器、内存、堆栈和汇编指令流,也可用于跟踪调用关系、设置断点并验证修改思路,对学习Windows平台调试流程较有参考价值。压缩包为rar格式,大小约17.86MB;资源页显示文件总数为0,文件类型明细暂无数据,因此具体组成需以下载后实际内容为准。页面显示已有415人浏览学习,说明该工具在相关学习场景中有一定关注度。对于需要补足动态调试手段、理解32位汇编分析流程的读者,可将其作为辅助工具包使用,结合自身实验环境进行验证与练习。
1. 拿到这份 OllyDbg 加强版:它修的是哪类问题
手上这份 OllyDbg 加强版,属于平时躺在工具箱里、真出事才被翻出来的东西。它基于 OllyDbg 的老内核,外面裹了一层预置插件和配置文件,把汇编级单步、内存断点、模块与句柄查看这些基础能力拼成一套开箱就能开工的环境。圈里人说的 OD 加强版,基本就是指这一路东西——省掉装完原版还要一个个补插件的功夫。
它能修的问题很具体:手上有个没有源码的 32 位程序,想知道某个按钮点下去走了哪条分支,某个结构体字段是谁写进去的,崩溃那一刻寄存器里停着什么值。这类问题靠打日志和猜是查不动的,得上调试器。适合做二进制入门、老系统兼容性排查、样本初步分析的人;不适合指望它调 64 位程序或者带完整符号的大型工程,那属于另一个工具的活。
2. 拆包先认组件:主程序、插件目录与三份必须改的配置
压缩包解开之后别急着双击。先花十分钟把目录结构看清楚,后面能省掉一大堆“为什么我这里跟教程对不上”的疑问。加强版和原版最大的差别就在目录里:原版只有一个主程序加一个 ini,加强版把插件、脚本、配色、快捷键方案全塞进去了,有些还把 UDD 调试数据库目录挪到了程序目录下,图的是绿色免安装。
2.1 主程序、UDD、插件、配置各管一摊
一份典型的加强版目录大致分四层。根目录放OllyDbg.exe主程序,一般还有LOADDLL.EXE这个专门用来把 DLL 当主模块加载的小工具——很多场景下目标逻辑不在 exe 里,而在某个被动态加载的模块中,直接拖 DLL 进主程序是加载不起来的,必须走这个入口。根目录还会带OLLYDBG.INI,界面布局、断点默认行为、异常处理策略都记在里面。
UDD目录是调试会话数据库,OD 每调一个程序就在里面生成一份同名文件,记录你下过的断点、加过的标签、写过的注释。这个目录的价值在第二次调试同一个目标时才体现出来:上次标好的函数名和断点位置原样还在,不用从头再来一遍。反过来说,它也是排障时第一该怀疑的对象——会话文件损坏会让程序一加载目标就退出,而这个现象极容易被误判成“加强版不兼容”。
PLUGIN目录是加强版的灵魂。里面的 DLL 按文件名排序加载,加载顺序在部分插件之间是有讲究的,后面第 5 章会专门说这个坑。剩下的杂项目录一般是脚本示例、配色方案、语言包,删掉不影响主流程,但配色方案建议留着,默认配色在长时间盯汇编的时候很费眼。
| 组件 | 位置 | 作用 | 出问题时的典型现象 |
|---|---|---|---|
| 主程序 | 根目录OllyDbg.exe | 调试器本体 | 双击无反应、启动即退 |
| DLL 加载器 | 根目录LOADDLL.EXE | 把 DLL 当主模块调试 | 拖 DLL 进去报错 |
| 会话数据库 | UDD\ | 保存断点、标签、注释 | 加载目标后立刻退出 |
| 插件 | PLUGIN\*.dll | 扩展命令、脚本、对抗检测 | 菜单里插件项缺失或灰掉 |
| 配置 | OLLYDBG.INI | 界面、异常策略、路径 | 改了设置重开又变回去 |
2.2 插件列表怎么筛:留常用的,删看不懂的
加强版常见的做法是往 PLUGIN 里堆二三十个插件,实际天天用到的就那么五六个。刚上手时建议先做减法:把不认识的 DLL 挪到备份目录,只留下脚本引擎、命令行栏、标签管理、转储辅助这几类,等环境跑顺了再一个个加回来。这样做的好处是出问题时变量少——插件冲突是 OD 崩溃最常见的原因之一,一次加一个,坏了立刻知道是谁。
命令行栏类插件值得优先保留。OD 原版的表达式跟随窗口(Ctrl+G)只能跳地址,加了命令行栏之后可以直接敲dd eax、dw esp这类表达式看内存,调试时手不用在几个窗口之间来回跑。脚本引擎插件同理,把按键序列变成脚本文件,重复劳动直接归零。至于那些名字很唬人、功能描述写着“增强稳定性”的插件,先放着,等你真的遇到目标程序一加载就退出,再回来试。
这里有个血泪经验:插件不是越多越好,加载的 DLL 越多,主程序启动时初始化失败的组合就越多,而且失败往往表现为静默退出,连个报错框都不给。我一般的做法是保留一份“干净配置”压缩包,任何一次折腾之前先备份整个目录,翻车了整包还原,比重装快得多。
2.3 首次启动的基线配置:把工作目录和权限固定下来
OD 这类老程序对“当前工作目录”很敏感。快捷方式启动时工作目录可能是桌面,ini 和 UDD 就往桌面写;换一个入口启动,配置又读到别处去了,表现出来就是“我明明改了设置,重开又回去了”。解决办法是用一个固定脚本拉起,把工作目录钉死在程序目录上。
@echo off rem 固定工作目录,避免 ini 与 UDD 被写到只读或临时路径 set OD_HOME=D:\od-lab\ollydbg cd /d "%OD_HOME%" rem 先备份本次会话前的数据库目录,出问题直接回滚 if exist "%OD_HOME%\UDD" ( xcopy "%OD_HOME%\UDD" "%OD_HOME%\UDD_bak" /E /I /Y >nul ) rem 第一个空引号是窗口标题占位,后面依次是目标路径与透传参数 start "" "%OD_HOME%\OllyDbg.exe" "D:\od-lab\target\demo.exe" --selftestcd /d里的/d不能省,它允许跨盘符切换,缺了这参数从 C 盘启动时切不到 D 盘,工作目录还是错的。start ""的第一个空字符串是窗口标题占位,删掉它,后面第一个带引号的路径会被当成标题吃掉,目标程序根本传不进去。末尾的--selftest是演示用的透传参数,OD 会把主程序之后的参数原样交给目标进程,调试带命令行开关的程序时用得上。
配置本身也建议动两处。一是关掉“忽略 INT3 断点”这类容错选项,让断点被目标程序改写时能第一时间发现;二是把 UDD 路径显式指到程序目录下的相对路径,别用默认的用户目录。不同加强版的 ini 键名不完全一致,照抄之前先打开文件确认有没有这个键,没有就跳过,硬加进去不会生效但也不会报错,反而容易让你以为设置成功了。
[Options] ; 断点相关:不要静默忽略被修改的 INT3 IgnoreINT3=0 ; 会话数据库落到程序目录,绿色便携 UDD_path=.\UDD ; 恢复上次窗口布局,多显示器切换时不至于窗口跑到屏幕外 RestoreWinPos=1这三行的含义分别是:IgnoreINT3设为 0 时,目标程序改写了自己代码段里的断点字节,OD 会给出提示而不是默默继续跑;UDD_path用相对路径,跟着目录走,拷到别的机器上会话数据不丢;RestoreWinPos在多屏环境下要留意,从双屏拔掉一个屏之后再启动,窗口可能恢复到不存在的坐标上,表现为“程序在跑但看不见窗口”,这时候把这一项临时改成 0 就能救回来。
3. 第一次挂载目标:从入口点跟到判断分支的完整动作
环境配好之后,真正的活是读逻辑。这一章按一次完整的调试走一遍:先静态确认入口,再选断点类型,然后控制单步节奏,最后把过程落成文字记录。整个流程跑熟之后,多数“这个值是谁改的”类问题在十几分钟内能收敛。
3.1 载入之前先做静态核对:入口点、节区与基址
很多人习惯直接把程序拖进 OD,然后在入口处一脸茫然。更稳的顺序是先静态看一眼 PE 头,把镜像基址、入口点 RVA、各节区的虚拟地址范围算出来,心里有张地图再进动态调试。特别是程序带重定位、实际加载基址和首选基址不一致的时候,提前知道这件事能省掉大量“地址对不上”的困惑。
import sys import pefile def dump_entry(path: str) -> None: # fast_load=True 只解析头部,速度够快,调试前随手跑一下不耽误事 pe = pefile.PE(path, fast_load=True) base = pe.OPTIONAL_HEADER.ImageBase ep_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint # OD 的反汇编窗口认的是 VA,不是文件偏移,也不是 RVA print(f"ImageBase : 0x{base:08X}") print(f"EP RVA : 0x{ep_rva:08X}") print(f"EP VA : 0x{base + ep_rva:08X} <- Ctrl+G 跳这个") for sec in pe.sections: name = sec.Name.rstrip(b"\x00").decode(errors="replace") va = base + sec.VirtualAddress print( f"{name:8s} VA=0x{va:08X} " f"VSize=0x{sec.Misc_VirtualSize:X} " f"RawSize=0x{sec.SizeOfRawData:X} " f"Flags=0x{sec.Characteristics:08X}" ) if __name__ == "__main__": dump_entry(sys.argv[1])这段脚本只做一件事:把 OD 里要用的地址换算出来。ImageBase是首选加载基址,AddressOfEntryPoint是相对基址的偏移,两者相加才是反汇编窗口里能直接跳转的虚拟地址。节区循环里输出Flags是顺手加的,0xE0000020这种值表示该节可读可写可执行,看到某个数据节带可执行属性,基本可以判断那里有动态生成的代码,值得单独下内存断点盯一下。
跑法是python peinfo.py D:\od-lab\target\demo.exe,唯一参数就是目标路径。注意脚本读的是磁盘上的文件,如果目标是加过壳、运行时才解出真实代码的,这里算出的入口点只是壳的入口,真正的业务代码要在动态跑起来之后再定位。这一点必须提前想清楚,否则会对着一段解密循环单步半小时还以为是业务逻辑。
3.2 断点四件套:INT3、硬件、内存、消息各管什么
OD 里有四种断点,新手最容易全用 F2 一把梭,然后卡在“为什么断点没生效”上。这四种的机制完全不同,适用场景也不同,选错了不是不好用,是根本不会触发。
| 类型 | 下法 | 原理 | 适合场景 | 主要限制 |
|---|---|---|---|---|
| INT3 断点 | 光标处按 F2 | 把首字节改成0xCC | 代码段已知位置 | 会被目标改写或校验发现 |
| 硬件断点 | 调试选项里设 DR 寄存器 | CPU 调试寄存器 | 数据被写入时中断 | 只有四个,且易被清 |
| 内存断点 | 转储窗口选中内存后右键 | 改页属性触发异常 | 不知道谁改了某块内存 | 粒度是页,慢 |
| 消息断点 | 窗口相关菜单 | 拦截窗口消息 | 查按钮点击入口 | 只对窗口过程有效 |
INT3 断点最常用,也最脆。它改的是目标进程内存里的指令字节,任何做代码自校验或者逐段解密的程序都会发现异常,表现出来就是断点处没停、程序直接跑飞,甚至进程退出。硬件断点不改进程内存,靠 CPU 的调试寄存器触发,隐蔽性好得多,代价是只有四个槽位,用完就没了,而且部分程序会主动清调试寄存器。
内存断点的定位思路和前两种不一样:前两种是“我知道这里会执行/会读”,内存断点是“我不知道谁干的,但我知道哪块数据被动了”。查结构体字段被谁改写这类问题,先在想盯的内存上设内存断点,等它触发,再看调用栈,往往一击命中。代价是它按页生效,一设就是 4KB,附近的无关访问也会被拦下来,噪音比较大。
至于消息断点,只在目标有窗口界面的时候有意义。想找按钮点下去之后的处理函数,与其在 API 上瞎猜,不如直接在窗口过程上下消息断点,点一下按钮就停在分发处。这条路比追GetMessage/DispatchMessage短得多。
3.3 单步的节奏:F7、F8、F4 什么时候用哪个
单步控制是调试的基本功,但节奏比按键本身重要。F7是步入,遇到call会跟进去;F8是步过,把整个call当一条指令执行完;F4是运行到光标处。新手最常见的毛病是全程F7,一头扎进系统库函数里,在几千行不相关的代码中迷失方向,最后只能重启调试。
我的习惯是:在自己不熟悉的区域用F8快速掠过,看到可疑的调用再回退一个断点用F7进去。判断“可疑”的标准很朴素——参数里有你关心的缓冲区地址、有刚读进来的配置值、有循环计数器。另外Ctrl+F9是执行到返回,从函数内部快速脱身非常有用;Alt+F9是执行到用户代码,从系统库里跳出来时用它,比一层层F8快得多。
调用栈窗口要一直开着。停在某个位置时,先看栈回溯确认自己是从哪条路径过来的,再看寄存器窗口确认参数。很多人只盯反汇编窗口,结果在一段被多个地方复用的公共代码里反复打转,压根不知道这次进来是哪个业务分支调用的。栈回溯就是那根线,断了就得重新找。
3.4 把过程落成文字:日志窗口与结论导出
调试过程如果不记录,两小时之后你只会记得“好像有个地方跳转了”。OD 的日志窗口支持把命中断点、修改寄存器、脚本输出都打进去,调完一段直接全选复制到笔记里,比事后凭记忆复述靠谱得多。特别是做样本分析,调试记录本身就是交付物的一部分。
记录的重点不是把每一步都抄下来,而是记三类信息:一是关键分支的判断条件和实际取值,二是关键函数调用的参数与返回,三是每次“我以为是这样、结果不是”的认知修正——第三类最有价值,下次遇到同类结构能直接跳到正确位置。
4. 把手工动作固化成脚本:条件断点、日志断点与批量回溯
手点五百次单步能得出的结论,脚本跑三十秒就能出,而且可复现。OD 的脚本插件(不同加强版集成的可能不是同一个,命令名有细微差别,用之前先看插件自带的帮助文件)把断点、运行控制、变量运算串成文本,写一次存着,下次换目标改两个地址就能复用。
4.1 脚本的基本骨架:变量、断点、运行控制
脚本的执行模型很直白:从上往下顺序执行,遇到run就把控制权交还给调试器,等断点命中再回来继续。变量是全局的,断点条件里可以直接引用。下面这段是查某个函数被调用时参数组合的骨架。
// trace-call.txt —— 在脚本插件的编辑器里打开执行 var target var hits mov target, 0x00401234 // 目标函数入口 VA,用静态脚本算出来的地址 mov hits, 0 // 命中计数器 // 条件断点:只在第二个参数为 0 且返回值非空时停下 bpcnd target, "edx == 0" // 交给调试器运行,命中后回到下一行 run // 命中之后打一条日志,再继续跑,避免每次都要人肉点继续 add hits, 1 log "hit#{hits} eax={eax} ecx={ecx} edx={edx}" runmov target, 0x00401234里的地址来自第 3 章那个静态脚本算出的 VA,如果目标带重定位且实际加载基址变了,这个值要相应调整,稳妥的写法是脚本里先读一次模块基址再算偏移。bpcnd是条件断点,第二个参数是表达式字符串,表达式为真才中断,这一步能过滤掉绝大部分无关调用。run交回控制权,命中后脚本从下一行继续,log把一行带变量值的文本打到日志窗口。
要注意的是,脚本里的表达式语法跟调试器界面里的不完全一样,寄存器名直接写不加前缀,十六进制常量在部分版本里要写成0x前缀,写成00401234也可能被接受但不保证。跑不通的时候先拿一行log试试输出,确认脚本引擎本身工作正常,再往上叠逻辑。
4.2 日志断点:高频调用的正确打开方式
有些函数一次运行被调几千次,用条件断点一个个停根本不现实。这类场景用日志断点:命中不停,只写一行记录然后自动继续。跑完之后日志窗口里就是完整的调用序列,拿去统计比人眼看快得多。
// hot-api.txt —— 高频 API 的调用序列记录 var api mov api, 0x7C801D7B // 示例地址,实际以目标模块导出表为准 // 命中即写日志并自动继续,不打中断点 bplog api, "call {eip} arg1={ecx} arg2={edx} stack={esp}" runbplog的关键在于它不暂停进程,只写日志,所以对程序节奏影响小,能拿到比较真实的调用顺序。日志内容里加了{esp},是为了在需要的时候判断嵌套深度——同一个 API 在同一层递归里被调用多次,esp会有规律地变化,几千行日志里扫一眼就能看出递归结构。
需要提醒的是,这类日志断点在部分加强版里命令名可能是bpd或者需要在断点属性里勾“记录并继续”,写法以插件帮助为准。功能是同一个功能,名字差一个字母跑不起来,别急着怀疑资源坏了。
4.3 后处理:把几千行日志变成一张热点表
日志窗口里全选复制出来是纯文本,直接看效率极低。用几十行 Python 做个聚合,把命中次数、参数分布、出现频次最高的调用地址排出来,重点自然浮现。
import re import sys import collections # 日志行格式:hit#1 eax=00000001 ecx=00401000 edx=00000000 PATTERN = re.compile( r"hit#(?P<seq>\d+)\s+eax=(?P<eax>[0-9A-Fa-f]+)\s+" r"ecx=(?P<ecx>[0-9A-Fa-f]+)\s+edx=(?P<edx>[0-9A-Fa-f]+)" ) def aggregate(path: str) -> None: seqs = [] by_ecx = collections.Counter() # errors="replace" 兜住日志里混进来的非 UTF-8 字节 with open(path, encoding="utf-8", errors="replace") as fh: for line in fh: m = PATTERN.search(line) if not m: continue seqs.append(int(m.group("seq"))) by_ecx[m.group("ecx").upper()] += 1 if not seqs: print("没有匹配到记录,先确认日志格式与正则一致") return # 序号不连续说明中间有断点被清或脚本中断,值得回去查 gaps = [(a, b) for a, b in zip(seqs, seqs[1:]) if b != a + 1] print(f"命中总数: {len(seqs)} 断裂处: {len(gaps)}") print("ecx 取值分布 Top10:") for value, count in by_ecx.most_common(10): print(f" 0x{value} {count} 次") if __name__ == "__main__": aggregate(sys.argv[1])正则里的三组捕获对应脚本log输出的三个字段,格式改了就同步改正则,这是唯一的耦合点。序号断裂检测是刻意加的:脚本跑飞或者断点被目标清掉时,日志里会出现跳号,这个信息比任何报错都直接。ecx分布排序用来快速判断参数是不是集中在少数几个取值上——如果某个值占了八成,基本可以断定这里是按类型分发的分支点,值得回头下条件断点单独跟。
5. 避坑与排查:启动闪退、断点失效、插件打架的五条记录
OD 加强版的坑集中在“不报错”这三个字上:界面没反应、断点不下、插件菜单少了,多数情况连个提示框都不弹。下面五条是出现频率最高的,按现象、原因、解决三段写,遇到时对着排。
5.1 双击没反应,或者启动后一闪就退
现象是双击主程序,任务管理器里进程出现又消失,或者干脆什么也没发生。原因通常是两层的:一是工作目录不对,ini 读不到,某些加强版在缺配置时会直接退出;二是 UDD 目录里有上次调试留下的损坏会话文件。
解决顺序固定:先把UDD目录整体重命名(不是删除,保留现场),再启动试试;能起来说明是会话数据的问题,回滚需要的文件即可。仍然起不来,就检查启动时的工作目录,用第 2 章那个批处理把cd /d钉死,顺便看 ini 里有没有指向不存在路径的键。还有一种情况是杀软把插件 DLL 当可疑文件隔离了,表现也是静默退出,去隔离区看一眼能省不少时间。
5.2 断点下下去不命中,或者跑起来之后就没了
现象是 F2 断点明明变红了,F9一跑程序直接跑完,回头一看断点没了。原因有两类,一类是目标自己校验代码段,发现字节被改成0xCC就恢复原文并重新校验;另一类是断点下在了还没被解密的区域,那段内存当时是空的,跑起来之后才被写入真实代码,你下的断点早被覆盖了。
解决分两步走。先换成硬件断点试,硬件断点不改进程内存,能绕开自校验这一类。硬件断点管用,说明确实是代码校验,后续就统一用硬件断点或者内存断点。硬件断点也不管用,说明断点位置本身就不对,回头用静态脚本确认入口点,或者在写入该区域的代码上下断点,等解密完成再下目标断点。这个地方没有“万能设置”,得按现象分流。
5.3 插件菜单少了、灰了,或者加载到一半崩溃
现象是插件菜单里只有零星几项,原本该有的脚本引擎不见了,或者启动时直接崩在加载阶段。原因基本是插件加载顺序和版本错配:部分插件依赖另一个插件先初始化,目录里按文件名排序,名字靠后的反而先被加载,依赖就落空了;还有一种是 64 位插件混进 32 位目录,加载时直接失败但不报错。
解决思路是“最小可运行集”:把PLUGIN目录整体挪走,一次只放回一个插件,每放一个启动验证一次,加到哪个崩的立刻定位。定位之后再按依赖关系给它改个前缀名,控制加载顺序。嫌麻烦就把常用五六个插件单独打包成一个稳定配置,折腾新插件时用另外一整个目录,互不污染。
5.4 中文路径、只读目录,配置写了不生效
现象是改了 ini 里的设置,重开还是老样子;或者调试时打不开目标文件,提示找不到。原因是路径里有非 ASCII 字符,或者程序目录被放在了需要提权的位置,写入被系统静默拒绝。
解决很直接:整个 OD 目录放在纯英文、无空格的路径下,比如D:\od-lab\ollydbg,不要放桌面也不要放Program Files。目标文件同理,路径里带中文时先复制到工作目录再加载。改完 ini 后确认文件修改时间变了,没变就说明写入被拦了,去检查目录权限。
5.5 拖进去的程序根本跑不起来:位数与运行环境的边界
现象是双击加载目标,弹出“不是有效的 Win32 应用程序”或者加载后立刻退出。原因是目标实际是 64 位程序,而这个调试器只处理 32 位进程;还有一种情况是目标依赖的运行库在当前环境里缺失,加载阶段就挂了。
解决是先确认位数,用第 3 章的静态脚本看 PE 头,Machine字段能直接判断。确认是 64 位就别在这个工具上耗时间,换对应的调试环境。确认是 32 位但加载即退,把目标需要的运行库补齐再试,这类问题在调试老旧业务程序时特别常见——程序本身没问题,环境缺件而已。
6. 从一次命中断点到可复现的证据链:三条验证技巧
调试到后半段,最大的风险不是查不出来,而是查错了还以为查对了。停在某个断点上,看到eax等于某个值,很容易顺着这个值编出一个自洽的故事,然后拿去下结论。下面三条是我现在每次都会走一遍的验证习惯,能拦掉大部分“想当然”。
第一条是反向验证断点。断点命中只能证明“这条路径走过”,不能证明“只有这条路径走”。方法是在断点命中后,把这个位置的寄存器值改成一个不可能出现的哨兵值(比如0xDEADBEEF),然后继续跑,看后面哪个分支的行为变了。行为变了的地方,才是真正依赖这个值的位置;行为完全不变,说明你盯的这个值只是路过,业务逻辑根本不看它。这一步比正向单步省时间,因为你不必理解中间所有代码。
第二条是静态动态对照。静态脚本算出的入口、节区范围、导入表函数列表,跟动态跑起来之后在 OD 模块窗口里看到的内容逐项对一遍。正常情况下两者应该一致;不一致的地方就是值得挖的地方——可能是有运行时解密出来的代码,也可能是有模块被动态映射进来。我一般会把两边的差异记在笔记里,作为下一轮断点的候选位置。
第三条是用脚本复跑一遍。任何一个靠手工单步得出的结论,在写进文档之前都用脚本重跑一次:把关键位置设成日志断点,全量跑一遍,统计结果和手工结论对上,这条结论才算站住。对不上不一定是结论错,也可能是手工那次某个条件没控制好,两种都要回去查。
| 验证动作 | 目的 | 失败时的信号 |
|---|---|---|
| 反向哨兵值 | 确认值是否被真正消费 | 改了值行为不变 |
| 静态动态对照 | 找出运行时变化 | 节区或导入项对不上 |
| 脚本全量复跑 | 排除偶然性 | 统计分布与手记差太远 |
有个具体技巧值得单独说:做反向验证时,哨兵值别用0。0太常见,很多分支对0和非0的处理刚好一样,改了等于没改。用一个明显不合法的大数或者一个奇偶性相反的值,行为变化会明显得多。
我现在的习惯是,写完任何一份调试记录,都要把当时用的脚本和日志文件一起归档,命名带上日期和目标文件名。这个习惯是被一次教训换来的:某次排查一个崩溃问题,结论写得很笃定,两周后同样的问题再出现,回头想复现当时的条件,脚本早被覆盖了,只能从头再来一遍。从那以后我每次动调试器之前先建目录,脚本改一版存一版,宁可多占几百兆,也不给自己留没有后悔药的局面。希望帮到你。
本文还有配套的精品资源,点击获取