前置说明:
OllyDbg 默认是指官方版 OllyDbg 2.01,下载地址:
http://www.ollydbg.de/odbg201.zip
PDK 是指 OllyDbg 的插件开发工具包,下载地址:
http://www.ollydbg.de/plug201h.zip
OllyDbg 自带的即时高亮有些鸡肋,只能高亮寄存器,操作步骤繁琐,高亮时内容会变为普通文本。另外感谢 DbgDream 同学开发出了能在OllyDbg 1 下进行高亮的插件,可以提供参考,但可能是用了 GetWord2000 中的屏幕取词代码,有挺多不必要的代码,以及不必要的 AscII 与 Unicode 的字符编码转换,也有很多逻辑上可以优化的地方,效率稍微有些低,只能在 CPU 窗口和 OllyDbg 都最大化的情况下取词才准确,不然会出现偏移。lynnux 同学修改后的也只是能在 OllyDbg 2 中运行起来,基本不能实际使用。所以只能自己动手重新写一个,暂命名 Ollight 。
Ollight 的工作过程应该是,在鼠标左键按下时,对鼠标所在的屏幕坐标进行取词,取到的词便是我们需要高亮的,然后让 OllyDbg 重新显示汇编代码,判断每行汇编代码中是否包含了我们需要高亮的词,如果有,则将其设为高亮。可以看出整个插件就主要分为取词和设置高亮两个模块。
一、高亮模块的原理及实现
1.1 原理
在 OllyDbg 中子窗口框架的每个区域都用 t_table 结构体描述,我们要操作的 CPU 窗口的反汇编区域也是如此,在 t_table 中包含一个 drawfunc 成员(t_table 完整定义请参照 PDK 的 plugin.h 头文件),是一个指向该区域“窗口绘制函数”的指针,函数定义:
typedef int DRAWFUNC(wchar_t *s, uchar *mask, int *select, t_dump *pd, t_drawheader *ph, int column, void *cache);
OllyDbg 通过调用此函数来获取其对应 table 的每一列的数据内容及显示格式,然后显示在窗口上。在此函数内部,会根据 int column 参数来判断 Ollydbg 是想获取哪一列的内容和格式,然后通过第一个参数 wchar_t *s 返回要显示的数据内容,通过第二个参数 char *mask 返回内容的高亮格式,比如反汇编区域包含四列,column 参数为 1 时,应该返回地址列的内容及地址高亮格式,为 2 时返回机器码列内容,为 3 时返回汇编代码列内容及汇编代码高亮格式(最初的几次调用 column 为负数是返回所需缓冲区长度之类,为方便理解直接略过,实际调试时注意)。我们要在反汇编区域中实现指定内容的高亮,就需要在反汇编窗口的 drawfunc 函数绘制反汇编代码列时,拿到代码内容,看这行代码中是否包含需要高亮的文本,如果有,则按需修改高亮描述数据,最终达到高亮功能。反汇编代码的每个字符对应 char *mask 的一个字节,内容为“颜色代号”,我这里使用的“颜色代号”取值范围是 0 - 9 ,所代表的色彩分别对应 OllyDbg Options -> Appearance -> Colours 中的 Normal text 到 Underline & select 这十项色彩配置。
1.2 实现
(1)获取 CPU 窗口的 drawfunc 地址
OllyDbg 提供的 API Getcpudisasmtable 可以返回 CPU 反汇编窗口的 t_table 指针,以此可直接获取到 drawfunc 地址。
(2)Hook 点的选取
按照需求,我们的 Hook 点应该要满足能拿到当前要显示的汇编代码,并且能有效的修改着色数据两个要求,经过调试,发现在该 drawfunc 的尾部进行 Hook 可以满足我们的需求。
(3)演示
我们使用右侧的 OllyDbg 调试左侧的 OllyDbg ,用被调试的 OllyDbg 加载一个程序运行,然后在被调试 OllyDbg 的 drawfunc 函数的尾部下一个条件断点 [ebp+1c]==2 (表示将要开始准备反汇编窗口的数据),当被调试的 OllyDbg 的反汇编窗口滚动时,会引起反汇编窗口的重新绘制,此处便会断下。
如图 1.1 所示,我们在 EBP+0x8 处可以看到将要显示的汇编指令的内容,而 EBP+0xC 处指向的就是着色数据。如图 1.2 所示,我们尝试将“MOV EAX,”中与 “O” 和 “X” 对应的着色数据改为 09(在我的OllyDbg配置里为蓝底红字),F9恢复被调试 OllyDbg 的运行,由于我这里是向下滚动,所以接下来是将图 1.1 中左侧 OllyDbg 窗口中红色框中的代码绘制到绿色框的位置。运行后如图 1.3 所示,我们看到我们成功修改了着色效果。
图 1.1 观察反汇编代码与高亮数据
图1.2 修改高亮数据
图1.3 汇编代码更新后呈现出我们想要的高亮效果
二、OllyDbg 2 屏幕取词的实现
2.1 原理
屏幕取词已经是远古时代的技术了,这里只做简要叙述。Ollight 对 OllyDbg 的反汇编窗口进行取词的原理与通用的屏幕取词大体相同,Hook gdi32 导出的 ExtTextOutW 函数,然后让鼠标所点击的窗口重新绘制,绘制时程序会调用 ExtTextOutW 在窗口上输出文字,而 ExtTextOutW 的参数中包含了输出的基准坐标,并且可以计算出将要绘制的词的矩形范围的坐标,只要当前鼠标的坐标在这个矩形范围内,那表示当前输出的词正是鼠标所指的词,进而可以通过逐字递增计算的方式,精确确定鼠标是指向哪一个字的。
2.2 需要的更改
这样的直接 Hook 在 OllyDbg 1 中能很好的工作,而在OllyDbg 2 中改变了绘制方式,就算是同一行代码也可能是一部份一部分的绘制,并且传到 ExtTextOutW 里的 Y 坐标为一个固定值(我这里为 0x10 ),全部输出完后调用 BitBlt 一起拷贝,完成绘制,也就是说,为了精确取词还需要 Hook BitBlt 函数,但是我们只是需要一个精确的 Y 坐标而已,这样做太过复杂了,并且会影响稳定性和效率。经过调试发现,虽然精确的 Y 坐标没有传入 ExtTextOutW ,但是是出现在了栈中的,我们在自己的 ExtTextOutW 函数中通过访问上一个栈帧中的数据便可以取得精确的 Y 坐标,我的 OllyDbg 2.01 中此偏移为 EBP+0x58。
三、插件功能总结
至此,Ollight 的基本实现原理已经清楚了,要做的就是 Hook OllyDbg 反汇编区域的 drawfunc 和 ExtTextOutW ,单击反汇编代码时进行屏幕取词和分词,在 drawfunc 的 Hook 代码里判断 [ebp+1c]==2 时 [EBP+0x8] 的指令有没有包含我们要高亮的内容,有就修改 [EBP+0xC] 处对应的着色数据。
四、开源
没有做过任何兼容性测试,如果有不能工作或崩溃的情况欢迎反馈,有好的功能改进欢迎提交 Pull Request 。
项目地址:
https://github.com/sinsoul/Ollight
完整工程打包下载:
https://github.com/sinsoul/Ollight/archive/master.zip
编译好的插件下载:
https://raw.githubusercontent.com/sinsoul/Ollight/master/Ollight/OllyDbg2/plugins/Ollight.dll附效果演示:
[课程]FART 脱壳王!加量不加价!FART作者讲授!