在 SoftICE 不再更新以后,WinDbg 几乎成了 windows 内核专有调试器,另有国人开发的一款 Syser 单机内核调试器,不过不争气的是总是喜欢蓝屏,真不知道是自己写的程序先崩溃还是Syser先崩溃,不过毕竟是国货,期望它能够越来越完美。
WinDbg 不仅能调试用户态进程,还能调试内核,最近逆向一个驱动程序,有些地方不进行调试还真不好猜测其运行方式。之前使用WinDbg都是有pdb和源码的驱动,没有试过仅有一个sys的驱动文件,相比有源码的调试,看到的仅仅是汇编代码而已,调试方法应该相同,不过有一点,如何下断点?
有源码可以直接使用 模块+函数名断,没有源码只能断绝对地址,对于用户态进程,每个程序都有2G的虚拟地址空间,但对于内核,由于共享一块虚拟地址空间,所以不能根据PE文件偏移来断,只能想办法得到其绝对地址,配置好调试环境并且加载驱动以后,使用WinDbg命令 lm 可以列举出操作系统加载所有模块的信息,其中有加载的实际起始地址和终止地址(事前可能需要使用.reload重新加载一遍所有模块),如果模块过多可以使用m选项进行过滤。最后得到信息如下:
还可用过命令 !dh 命令查看 PE 格式信息,其中可以找出基址地址:
其中 image base 展示 PE 文件期望加载地址为 0x00010000,再通过IDA可以查看任意代码偏移,这个偏移实际上是相对0x00010000,通过简单的计算就可得出实际虚拟地址的位置。
比如一个函数位于 .text:000228D3 处,计算实际地址 = 偏移(0x000228D3)- 期望基址(0x00010000)+ 实际基址(0xb21df000),接下来可通过 bp address 下断。
当然,这需要驱动被加载之后方可。对于 DriverEntry 中的代码便无法调试,因为在驱动加载之前无法得知加载地址。
不过如果对于有代码驱动而言,可以通过 DriverName!FuncName 来下断,那DriverName是否能表示驱动加载的基址呢?通过试验发现可以使用 DriverName + 偏移来实现,其中偏移为减去基址 0×00010000 以后的地址。
比如刚才的断点可以使用 bp DriverName+0x000128D3 来下断,如此一来,便可通过IDA查看驱动DriverEntry的偏移,使用bu DriverName+offset来再驱动加载之前下段,可以方便的调试DriverEntry代码。而且下断无需计算,比之刚才的方法是要方便多了。
另外,IDA 支持 WinDbg 插件,可以在IDA中直接使用Windbg调试,把二者结合在一起,使用IDA直接调试内核,配置方式也很简单,我在IDA官网寻得一份详细的配置说明——IDA 使用 WinDbg Plugin,另外,再附上WinDbg使用手册——WinDbg 手册,顺便体会一下WinDbg的强大之处(不过命令行还是感觉 GDB 比较完美,只是不能调试Windows内核)。