首页
论坛
课程
招聘
[翻译]使用VMWare GDB和IDA调试Windows内核
2011-6-10 18:08 26959

[翻译]使用VMWare GDB和IDA调试Windows内核

obaby 活跃值
20
2011-6-10 18:08
26959
最近喜欢用IDA搞一些内核的东西,于是就到处找IDA关于内核方面的东东。这篇文章实在原文的基础上进行了一定的封装,也算是半原创的东东吧~希望大家不要拍砖撒~
VMWare的GDB调试器功能比较简单也比较基础,该调试器并不知道处理器和线程的任何信息(对于Windows系统),因而如果想要得到一些高等级的信息,我们需要自己做一些额外的工作。本文主要讲解了如何使用IDAPython脚本来让IDA处理已经加载的模块列表和加载符号库。

设置VM来进行调试

在进行这一步之前首先要保证你已经有了一个已经安装好的Windows(32位)的操作系统。在开始调试之前,首先要拷贝你想要看到符号的模块到系统目录下,如果你不确定要复制那些文件,可以将如下的文件复制到虚拟机目录下:位于System32目录下的nt*.exe和hal.dll文件、整个System32\drivers目录。在这里我将文件复制到了E:\虚拟机系统\Windows 7\Shar4ed dll\目录下。
编辑虚拟机的.vmx文件来激活GDB调试器功能:

图01
在文档末尾加入如下两行(VM更多的功能可以查看VM的相关文档http://wiki.osdev.org/VMWare):
debugStub.listen.guest32 = "TRUE"
debugStub.hideBreakpoints= "TRUE"
修改之后保存文件并且启动虚拟机,等待虚拟机启动。

图02
虚拟机启动之后启动IDA,如果出现如图03所示的提示窗口则直接点GO进入程序界面即可。

图03
进入住界面之后执行菜单的Debugger->Attach->Remote GDB Debugger如图04所示。

图04
打开如图05所示的调试器附加窗口。

图05
在Hostname中输入localhost,端口输入8832。点击确定之后将会打开如图06所示的进程选择窗口。

图06
选择ID为0的进程进行附加,如果附加成功将会弹出如图07所示的提示信息。

图07
点击OK之后将会中断在如图08所示的代码处。

图08
此时将会中断在内核中地址大于0×80000000的地方,现在就可以进行单步调试了,但是没有任何的名称调试起来是非常不爽的,那么我们就来收集更多的信息让IDA的显示看起来更加的直观。

获取内核模块列表

内核模块列表保存在一个有PsLoadedModuleList符号指向的内核列表中。为了获取这个列表的地址就要用到KPCR的方法,KPCR的全称是Kernel Processor Control Region。内核用这个区域来存储每个处理器所包含的各种信息。它被放置在fs寄存器指向的区段中(类似于应用层中的TEB)。它有一个区域叫做KdVersionBlock,这个区域指向了内核调试使用的一个结构体。而这个结构体则包含了各种内存结构的指针,其中就包括PsLoadedModuleList。

KPRC的定义
这个结构体可以在很多地方找到,其中IDA的ntddk.til文件中也有这个结构的定义。现在我们只需要知道KdVersionBlock位于KPRC结构体的的0x34处,并且它指向了DBGKD_GET_VERSION64。在DBGKD_GET_VERSION64的偏移量0x18处则可以找到PsLoadedModuleList。
现在我们就可以写一个小的Python函数来找到这个指针的的值。为了得到fs指向的区段的基址我们可以使用VMWare的“r”调试命令。GDB 调试器插件注册了一个IDC函数,叫做SendGDBMonitor()来发送命令到监视器,所以我们可以使用IDAPython的Eval()函数来调用它。
fs_str = Eval(‘SendGDBMonitor("r fs")’)
返回的数据格式如下所示:
fs 0×30 base 0x82744a00 limit 0×00002008 type 0×3 s 1 dpl 0 p 1 db 1
我们需要的是在base标记之后的数值。
kpcr = int(fs_str[13:23], 16) #extract and convert as base 16 (hexadecimal) number
然后来获取KdVersionBlock的数值:
kdversionblock = Dword(kpcr+0×34)
最后我们来获取PsLoadedModuleList的地址:
PsLoadedModuleList = Dword(kdversionblock+0×18)

遍历内核模块

现在就可以根据上面的地址来遍历内核模块了,PsLoadedModuleList被声明为PLIST_ENTRY。PLIST_ENTRY的定义如下所示(双向链表):
typedef struct _LIST_ENTRY
{
     PLIST_ENTRY Flink;
     PLIST_ENTRY Blink;
} LIST_ENTRY, *PLIST_ENTRY;
所以要遍历所有的模块只需要跟随Flink指针,直到我们回到开始的地方就可以了。每一个模块的结构定义如下所示:
struct LDR_MODULE
{
  LIST_ENTRY InLoadOrderModuleList;
  LIST_ENTRY InMemoryOrderModuleList;
  LIST_ENTRY InInitializationOrderModuleList;
  PVOID BaseAddress;
  PVOID EntryPoint;
  ULONG SizeOfImage;
  UNICODE_STRING FullDllName;
  UNICODE_STRING BaseDllName;
  ULONG Flags;
  SHORT LoadCount;
  SHORT TlsIndex;
  LIST_ENTRY HashTableEntry;
  ULONG TimeDateStamp;
};
现在我们可以来编写一个小函数遍历这个链表并且为每个模块创建一个区段。
#get the first module
cur_mod = Dword(PsLoadedModuleList)
while cur_mod != PsLoadedModuleList and cur_mod != BADADDR:
  BaseAddress  = Dword(cur_mod+0×18)
  SizeOfImage  = Dword(cur_mod+0×20)
  FullDllName  = get_unistr(cur_mod+0×24)
  BaseDllName  = get_unistr(cur_mod+0x2C)
  #create a segment for the module
  SegCreate(BaseAddress, BaseAddress+SizeOfImage, 0, 1, saRelByte, scPriv)
  #set its name
  SegRename(BaseAddress, BaseDllName)
  #get next entry
  cur_mod = Dword(cur_mod)

加载符号库

已经能够获取内核模块列表固然不错,但是如果不能加载符号库那么上面的工作也就没有多少用处。我们可以通过IDA的File->LoadFile->PDB file手动为每个模块加载符号库,但是这样做太蛋疼了。为什么不让它自动加载呢?
为了达到我们的目的这里就要用到PDB插件,通过查阅源代码(SDK)我们可以发现它支持下面的三个调用代码:
//call_code==0: user invoked ‘load pdb’ command, load pdb for the input file
//call_code==1: ida decided to call the plugin itself
//call_code==2: load pdb for an additional exe/dll
//              load_addr: netnode("$ pdb").altval(0)
//              dll_name:  netnode("$ pdb").supstr(0)
第二个调用代码看起来正是我们需要的功能。但是,当前版本的IDAPython只包含一个非常基础的节点类,所以没有办法是用Python来实现这个功能。但是我们如果查看其它的调用代码可以看到这个插件重定义了模块的基址(通过$ PE header)和模块的路径(通过get_input_file_path()),并且我们可以通过set_root_filename()函数来设置输入文件的路径。同样如果我们使用第三个调用代码那么我们要避免出现“你是否想要加载符号库?”(”Do you want to load the symbols?”)的提示。
#new netnode instance
penode = idaapi.netnode()
#create netnode the in database if necessary
penode.create("$PE header")
#set the imagebase (-2 == 0xFFFFFFFE)
penode.altset(0xFFFFFFFE, BaseAddress)
#set the module filename
idaapi.set_root_filename(filename)
#run the plugin
RunPlugin("pdb",3)
但是我们需要用使用前面的文件路径(想要看到符号库的文件)来取代内核模块路径:
#path to the local copy of System32 directory
local_sys32 = r" E:\虚拟机系统\Windows 7\Shar4ed dll"
if FullDllName.lower().startswith(r"\systemroot\system32"):
#translate into local filename
filename = local_sys32 + FullDllName[20:]
也可以直接运行vmmodules.py脚本,在执行的过程中将会弹出如图09所示的提示窗口。

图09
如果Input file的路径不对则需要手工进行修改,点击确定之后将会弹出如图10所示的符号库加载确认窗口。

图10
点击yes之后就开始加载符号库了,但是比较繁琐的是每次加载一个新的模块的符号库时都会弹出图09种的确认窗口,直到所有的模块的符号加载完毕。这也是个痛苦的过程~
在执行完脚本之后再来看下程序的内存镜像将会是如图11所示。

图11
再来看下名称窗口可以发现只要加载符号库的模块中的名称都已经显示出来了,如图12所示。

图12
现在看起来就好多啦,调试愉快!

PDF和Python脚本见附件!
Advanced Windows Kernel Debugging with VMWare and IDA’s GDB debugger.rar

第五届安全开发者峰会(SDC 2021)议题征集正式开启!

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (30)
雪    币: 220
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
广海混沌 活跃值 2011-6-10 20:22
2
0
嗯 不错 支持一下
雪    币: 91
活跃值: 活跃值 (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
swlilike 活跃值 2011-6-10 22:46
3
0
IDA工具的使用,标记下
雪    币: 1834
活跃值: 活跃值 (15)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
yingyue 活跃值 2011-6-11 08:54
4
0
顶,有空多给我们翻译些 IDA 的文章
雪    币: 421
活跃值: 活跃值 (65)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
tornodo 活跃值 1 2011-6-11 09:07
5
0
学习了
雪    币: 2259
活跃值: 活跃值 (362)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
非虫 活跃值 7 2011-6-11 10:09
6
0
老板,你的标题不应该是GDB??
雪    币: 107
活跃值: 活跃值 (106)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yangya 活跃值 2011-6-11 10:11
7
0
mark~~~~~
雪    币: 4981
活跃值: 活跃值 (34)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
奘和 活跃值 2011-6-11 10:22
8
0
翻译是辛苦活儿
谢谢了哈
雪    币: 122
活跃值: 活跃值 (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
butian 活跃值 2011-6-11 10:36
9
0
顶一个,呵呵,强大的ida
雪    币: 120
活跃值: 活跃值 (156)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
雪yaojun 活跃值 2011-6-11 11:09
10
0
楼主IDA上的前辈为什么这么靓????
雪    币: 540
活跃值: 活跃值 (240)
能力值: ( LV12,RANK:320 )
在线值:
发帖
回帖
粉丝
evilkis 活跃值 7 2011-6-11 11:13
11
0
习惯Windbg,其他的浮云
雪    币: 14254
活跃值: 活跃值 (1917)
能力值: ( LV15,RANK:880 )
在线值:
发帖
回帖
粉丝
obaby 活跃值 20 2011-6-11 13:25
12
0
我也注意到写错了,已经改过来了~谢谢提示~
雪    币: 284
活跃值: 活跃值 (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jerrynpc 活跃值 2011-6-11 13:27
13
0
求ida 6.1
雪    币: 14254
活跃值: 活跃值 (1917)
能力值: ( LV15,RANK:880 )
在线值:
发帖
回帖
粉丝
obaby 活跃值 20 2011-6-11 13:29
14
0
恩恩。有源代码的话用windbg是最好的,如果没有的话我感觉ida的调试器看起来更直观一些~呵呵,个人喜好问题~
雪    币: 1834
活跃值: 活跃值 (15)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
yingyue 活跃值 2011-6-11 14:15
15
0
那是 6.0 后的 idaq.exe
雪    币: 232
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
dlmu 活跃值 1 2011-6-11 23:06
16
0
只用过windbg,来长见识
雪    币: 4792
活跃值: 活跃值 (239)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
willapple 活跃值 2011-6-12 11:49
17
0
还是支持windbg!
雪    币: 1292
活跃值: 活跃值 (1187)
能力值: ( LV12,RANK:490 )
在线值:
发帖
回帖
粉丝
熊猫正正 活跃值 9 2011-6-12 15:15
18
0
有时间试试~~嘿嘿
雪    币: 71
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mumaren 活跃值 2011-6-12 17:24
19
0
不知道能不能调试DNF的驱动啊
雪    币: 1356
活跃值: 活跃值 (344)
能力值: (RANK:860 )
在线值:
发帖
回帖
粉丝
仙果 活跃值 19 2011-6-13 11:23
20
0
楼主辛苦了。。翻译是个累活,
雪    币: 284
活跃值: 活跃值 (34)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
darkplayer 活跃值 2011-6-13 15:27
21
0
运行vmmodules.py出错:
The initial autoanalysis has been finished.
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "D:\tools\IDA\python\init.py", line 62, in runscript
    execfile(script, globals())
  File "C:/vmmodules.py", line 100, in <module>
    list = get_PsLoadedModuleList()
  File "C:/vmmodules.py", line 9, in get_PsLoadedModuleList
    if not fs_str.startswith('fs '):
AttributeError: 'int' object has no attribute 'startswith'
雪    币: 14254
活跃值: 活跃值 (1917)
能力值: ( LV15,RANK:880 )
在线值:
发帖
回帖
粉丝
obaby 活跃值 20 2011-6-13 15:44
22
0
你的python是哪个版本?
雪    币: 284
活跃值: 活跃值 (34)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
darkplayer 活跃值 2011-6-13 15:48
23
0
python2.5
雪    币: 284
活跃值: 活跃值 (34)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
darkplayer 活跃值 2011-6-13 15:50
24
0
python2.5
雪    币: 14254
活跃值: 活跃值 (1917)
能力值: ( LV15,RANK:880 )
在线值:
发帖
回帖
粉丝
obaby 活跃值 20 2011-6-13 17:22
25
0
那应该不至于会出错的啊~
游客
登录 | 注册 方可回帖
返回