几年前,刚接触逆向的时候,自己就萌生一个要单独写一个调试器的想法(当时就觉得自己动手做一个项目,是学习最快的途径)。但彼时的我没有那么多时间去折腾。今年意外地得到一份比较长的病假,遂想着把当时的初衷给完成了。经过一段时间的学习和探索,虽然还不能完全实现自己理想中的调试器,但也已经初具雏形了。
那我为啥选择这个时候发出来,而不是等到完成之后再发呢?因为随着项目的推进,自己也开始觉得个人能力和精力的有限。仅凭我个人的努力绝非一朝一夕就能完成它的,况且自己做了这段时间以来,也隐约看到了自己的瓶颈。遂有了把它开源出来的想法。
在动手做之前,我就对要实现什么样功能的调试器这点上思考了很久。最后决定要做就做逼格高点的,确定了硬件虚拟化支持+闪瞎眼的界面+动态脚本支持为发展方向。
自己在参照intel手册做硬件虚拟化驱动时,在填充VMCS区域时碰到了问题,让我一点头绪也找不到,所以这部分就暂且没做。已完成的相关源码也包含在工程 vt_driver
里面了,因为64位不支持内联汇编,所以就设定了vt_lib_32
和vt_lib_64
两个工程为其提供统一接口的的静态函数库(vs虽然为大部分指令提供了相应的函数,但当时自己为了更深入的学习以及了解各个指令的实现细节,就坚持自己写了相关指令对应的C调用函数)。
我并不喜欢OD的界面,感觉窗口切换太繁琐了。WinDBG的窗口还好点,可以浮动也可以固定,在数据对比的时候很方便。后面我在思考界面的形式的时候就萌发了新的一种交互方式:可分离式界面(反正也是学习过程,多探索总没坏处)。所谓的可分离式界面就是调试器外观的各个可视化组件都可以由用户自己去配置组合。但是怎么去配置组合,开始我也不是很确定。直至接触到了DuiLib
的时候,思路就渐渐明晰了。就像DuiLib
的界面一样,用户可以自己去通过XML
去调节配置自己需要的外观,那剩下的分离和显示,就只需要程序内部绑定好相应的元素就行了。因此我就选择了DuiLib
作为界面开发库。
python
那么火,不用它才怪,再说我也只会这种动态脚本(。。。逃)。当然,选这个还有其他理由:更快的脚本开发速度以及更多的扩展支持。当然后续自己如果想学机器学习的话,也还能在这上面折腾下。
因为自己写这个调试器的初衷就是为了学习,所以对于关键的基础设施,我都是参考资料来自己动手写。目前做的汇编引擎和反汇编引擎都是参考 intel手册
来做的。调试引擎还没怎么做,因为这部分涉及到一些系统知识,自己相关的经验不足,还没想好怎么下手。
反汇编引擎的编写主要参考了 intel手册
2517页以及后续页的 One-byte Opcode Map
、Two-byte Opcode Map
、Three-byte Opcode Map
。通过将这几张表的内容整理成一个静态的结构(详见 DbgEngine
目录下的 Disasm_one.h
Disasm_two.h
Disasm_three_3a.h
Disasm_three_38.h
),在反汇编的时候通过检索相应的字节码按图索骥就可以了。比如在处理0x01234567 的时候会先从 Disasm_one.h
中的 检索出 0x01 对应的处理函数,然后使用该处理函数处理后续的字节。多个字节的操作码也是一样比如 0x0f3812345678 ,会先从 Disasm_one.h 检索出 0x0f 的处理函数,该函数继续调用 Disasm_two.h
中的 0x38 对应处理函数,0x38处理函数继续调用 Disasm_three_38.h
中的 0x12 处理函数来处理后续的字节。
在反汇编文件的时候,目前只做了一遍扫描。从程序入口点进行解析,碰到call,jmp 等跳转指令就会将相应的地址加入待解析记录中,遇到retn 结束当前函数的解析然后循环取下一个函数地址去解析。
目前完成了32位下的一部分X86指令,还没有做64位的区分处理。
汇编引擎使用类似反汇编引擎中的表,不过汇编引擎的表是通过 python
脚本采集 intel手册 第二卷
相关的指令介绍来整合起来的(详见 DbgEngine
下的 asm_struct.h
)。
汇编引擎在初始化的时候会按照之前采集表格中的助记符在内存中来建立一个前缀树索引表。在汇编的时候,根据助记符找到对应的指令详细记录(可能会有很多条),然后匹配每一条记录的操作数个数和操作数宽度,最后会将所有符合的汇编结果(结果可能有多条)返回给调用者。
DuiLib
的版本众多,我选用的版本来源于
https://github.com/redrains/DuiLib_Redrain
这个版本的代码修复一些bug。
由于 DuiLib
的界面是基于 XML
配置的,所以通过修改 XML
就很容易修改程序的外观。而我在原来的基础上加上四条规则:
通过这条规则,你就可以方便建立自己的工具箱了,把自己的常用的工具都整合在一起,你甚至都不用切换程序就能打开了。
通过这条规则,你可以把调试时寄存器的显示和堆栈的显示内容放在一个窗口内;如果不喜欢,你也可以加入或者删除其它内容。总之,完全取决于你。
初步的构思的是把 单步、步过、暂停、开始等一些可以控制调试器的行为称为内建指令。通过把按钮绑定这些指令,你就可以在任意窗口上进行控制了。
目前是通过调用 allocconsole 函数开启控制台的,着实难看。以后再想下怎么修改。
通过以上几条规则的配合,你就可以进行混搭,打造自己的“家庭影院”了。
至于前面说的可分离式界面,举个栗子:下图的反汇编列表是分别定义在两个不同的窗口,一个定义在MainWnd.xml内,另一个定义在DisasmWnd.xml内。除此之外,没有其他的代码会对他们进行分开描述。但是他们显示的内容都是一样的,操作也一样。你也可以像这样把反汇编列表放在其他窗口上,只需要修改对应的xml就行了。通过对不同可视化元素的组合,你就可以根据个人习惯打造自己的专属交互页面了。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2019-4-24 21:08
被蓝色橘子编辑
,原因: 添加通讯方式