lukarl
驱动牛犊
驱动牛犊
  • 注册日期2009-09-01
  • 最后登录2011-01-31
  • 粉丝0
  • 关注0
  • 积分24分
  • 威望121点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:2148回复:3

SSDT入门

楼主#
更多 发布于:2010-05-25 16:30
SSDT入门
  

下载 (22.7 KB)

2010-5-2 11:15
【图System Module Dependencies】揭示了系统各模块之间的依赖关系(有所简化)



从图中可以看出,所有的Win32 API调用最后都转移到了ntdll.dll,而ntdll.dll又将其转移到了ntoskrnl.exe。ntdll.dll是一个操作系统组件,它 为Native API准确地提供服务,ntdll.dll是Native API在用户模式下的前端,真正的接口在ntoskrnl.exe中实现。ntoskrnl.exe是NT操作系统内核,内核模式驱动程序对系统服务的请 求多数时候都会进入该模块。

例如,由kernel32.dll导出的Win32 API函数DeviceIoControl()最终会调用由ntdll.dll导出的NtDeviceIoControlFile()。
NtDeviceIoControlFile:
mov   eax, 38h
lea   edx, [esp+4]
int   2Eh
ret   28h
"魔术"数字0x38是一个分派ID;INT 2Eh指令将跳转到中断描述符表(Interrupt Descriptor Table,IDT)的0x2e位置上存放的中断处理例程(interrupt handler)中,分配器(dispatcher)利用它从用户模式进入内核模式。

从NtDeviceIoControlFile示例给出的反汇编代码可看出,INT 2Eh随同传入CPU寄存器EAX和EDX的两个参数一起被调用。处理INT 2Eh的代码必须确定每个调用将被分配到哪个函数。这就是提供EAX中的"魔术"数字--分派ID的原因。位于ntoskrnl.exe中的中断处理例程 将EAX中的数值作为一个索引来查询一个特定的表。这个表被称作系统服务表(System Service Table, SST)该表对应的C结构体---SYSTEM_SERVICE_TABLE:
typedef struct _SYSTEM_SERVICE_TABLE
{
    PNTPROC  ServiceTable;   // array of entry points
    PDOWRD  CounterTable;   // array of usage counters
    DWORD   ServiceLimit;    // number of table entries
    PBYTE    ArgumentTable;  // array of byte counts
}SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE,**PPSYSTEM_SERVICE_TABLE;

但是SST并不是源头,而且内核中还存在多个SST表,所以内核维护着另外一个表结构,该结构共有四个SST类型的数组,其中的前两个用于特定目的,头两 个数组保留给了ntoskrnl.exe和Win32子系统(位于win32k.sys)中的内核模式部分(来自gdi32.dll和 user32.dll的调用都通过Win32k的系统服务表(SST)进行分派)。ntolkrnl.exe导出了一个指针(符号为 KeServiceDescriptorTable)指向其主服务描述符表(Main SDT),内核还维护了一个替代的SDT,其名称为KeServiceDescriptorTableShadow,但这个SDT并没有被导出。
这个表被称作服务描述符表(The Service Descriptor Tables, SDT)该表对应的C结构体---SERVICE_DESCRIPTOR_TABLE:
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
    SYSTEM_SERVICE_TABLE ntoskrnl;  // ntoskrnl.exe ( native api )
    SYSTEM_SERVICE_TABLE win32k;    // win32k.sys (gdi/user support)
    SYSTEM_SERVICE_TABLE Table3;    // not used
    SYSTEM_SERVICE_TABLE Table4;    // not used
}SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE,**PPSYSTEM_DESCRIPTOR_TABLE;

啰啰嗦嗦一大堆,简单说,内核维护着_SERVICE_DESCRIPTOR_TABLE这样一个SDT结构数据,即 KeServiceDescriptorTable,里面保存着ntoskrnl.exe的SST结构数据(当然还包括其他SST),其中 ServiceTable指向的是由ServiceLimit个函数指针构成的数组;当执行前面提到的NtDeviceIoControlFile示例代 码时,内核找到KeServiceDescriptorTable,接着找到ntoskrnl.ServiceTable,该地址是函数指针数组的首地 址,所以NtDeviceIoControlFile函数地址为[ServiceTable+0x38*4]。

  

下载 (46.49 KB)

2010-5-2 11:15
【图 SSDT Call Process】SSDT查找函数地址过程


二:内核调试
对SSDT的观察与相关调试,需要使用到内核调试工具,当了解了内核调试,你也许就不会把下面这样的字符串看作是天外飞仙了:
kd> dd KeServiceDescriptorTable

“Debugging Tools for Windows”是Microsoft调试器套件,它对Windows系统的符号支持要好一些,我一般是使用图形界面的Windbg,下载安装即可。【2】

内核调试最方便的应该是用虚拟机做调试,可以很方便的重启系统,搭建一个WinDbg和VMware的内核调试环境网上有很多资料可供参考。【3】
需要指出的是在配置虚拟操作系统boot.ini文件时需要特别注意,否则启动系统时会报“Virtual machine kernel stack fault”此样的错误且无法启动系统。

启动Windbg和虚拟操作系统,Windows XP SP2系统为例:

等待连接被调试内核
>>Opened \\.\pipe\com_1
>>Waiting to reconnect...

连接被调试内核
>>Connected to Windows XP 2600 x86 compatible target at (Sun Jun  7 21:48:35.875 2009 (GMT+8)), ptr64 FALSE
>>Kernel Debugger connection established.  (Initial Breakpoint requested)

成功连接被调试内核后,中断下来等待命令
>>nt!RtlpBreakWithStatusInstruction:
>>8052b5d8 cc              int     3

三:SSDT观察分析与操作
当内核中断下来时,系统内核已经启动,这时可以观察SSDT数据:
nt!RtlpBreakWithStatusInstruction:
8052b5d8 cc              int     3
kd> dd keServiceDescriptorTable
8055c6e0  80504734 00000000 0000011c 80504ba8
8055c6f0  00000000 00000000 00000000 00000000
8055c700  00000000 00000000 00000000 00000000
8055c710  00000000 00000000 00000000 00000000
8055c720  00000002 00002710 00000000 00000000
8055c730  8055c730 8055c730 806f5040 806f5040
8055c740  00000000 00000000 00000000 00000000
8055c750  00000000 00000000 00000000 00000000

根据SDT的结构定义,ntoskrnl.ServiceTable=0x80504734;



  

下载 (18.86 KB)

2010-5-2 11:15
【图 NtCreateProcess_ID】NtCreateProcess ID号


以NtCreateProcess为例,反汇编ntdll.dll,可以看出其编号是0x2F,从图【图 NtCreateProcess_ID】可以看出这一点,0x80504734+0x2F*4=0x805047F0, 所以
kd> dd 80504734 805047f0
80504734  805a4054 805f02d8 805f3b0e 805f030a
80504744  805f3b48 805f0340 805f3b8c 805f3bd0
80504754  80614adc 8061581e 805eb67a 805eb2d2
80504764  805d430c 805d42bc 80615102 805b593a
80504774  8061471e 805a84de 805aff5e 805d5dd0
80504784  80501c00 80615810 80576900 80538bbc
80504794  8060dd26 805bbeb4 805f4048 80622c18
805047a4  805f853a 805a4742 80622e6c 805a3ff4
805047b4  80544e5c 80640e5a 805bdd68 8060dd76
805047c4  80616094 80578e5e 805776f0 805d4d94
805047d4  805d4acc 80623048 80578f6c 8061648c
805047e4  80578e98 805ab414 805a4b10 805d0a1c

在反汇编窗口输入805d0a1c可得:
nt!NtCreateProcess:
805d0a1c 8bff            mov     edi,edi
805d0a1e 55              push    ebp
805d0a1f 8bec            mov     ebp,esp
805d0a21 33c0            xor     eax,eax
805d0a23 f6451c01        test    byte ptr [ebp+1Ch],1
805d0a27 7401            je      nt!NtCreateProcess+0xe (805d0a2a)
805d0a29 40              inc     eax
805d0a2a f6452001        test    byte ptr [ebp+20h],1
805d0a2e 7403            je      nt!NtCreateProcess+0x17 (805d0a33)
805d0a30 83c802          or      eax,2
805d0a33 807d1800        cmp     byte ptr [ebp+18h],0
805d0a37 7403            je      nt!NtCreateProcess+0x20 (805d0a3c)
805d0a39 83c804          or      eax,4

或是执行命令ln 805d0a1c可得
kd> ln 805d0a1c
(805d0a1c)   nt!NtCreateProcess   |  (805d0a62)   nt!NtQueueApcThread

所以可以证实,[0x805047f0]=0x805d0a1c,该值就是NtCreateProcess的入口地址。

该虚拟操作系统中安装有安全防护软件,具有进程监控的功能,应该会Hook NtCreateProcess操作,可以看看会有何变化:

让虚拟系统退出中断,继续运行
kd> g

当系统正常完成启动后再次进行中断执行命令
nt!RtlpBreakWithStatusInstruction:
8052b5d8 cc              int     3
kd> dd keServiceDescriptorTable
8055c6e0  80504734 00000000 0000011c 80504ba8
8055c6f0  00000000 00000000 00000000 00000000
8055c700  00000000 00000000 00000000 00000000
8055c710  00000000 00000000 00000000 00000000
8055c720  00000002 00002710 bf80da45 00000000
8055c730  f8b70a80 824bd220 823fca90 806f5040
8055c740  00000000 00000000 0b50f518 00000000
8055c750  c7c06c58 01c9e5b6 00000000 00000000


kd> dd 80504734 805047f0
80504734  805a4054 805f02d8 805f3b0e 805f030a
80504744  805f3b48 805f0340 805f3b8c 805f3bd0
80504754  80614adc 8061581e 805eb67a 805eb2d2
80504764  805d430c 805d42bc 80615102 805b593a
80504774  8061471e 805a84de 805aff5e 805d5dd0
80504784  80501c00 80615810 80576900 80538bbc
80504794  8060dd26 805bbeb4 805f4048 80622c18
805047a4  805f853a 805a4742 80622e6c 805a3ff4
805047b4  80544e5c 80640e5a 805bdd68 8060dd76
805047c4  80616094 80578e5e 805776f0 805d4d94
805047d4  805d4acc 820ffbe0 80578f6c 8061648c
805047e4  80578e98 805ab414 805a4b10 820ff0e0

可以看到,[0x805047f0]已经被更改为0x820ff0e0,即NtCreateProcess操作被Hook了。

这就是SSDT Hook,而且也可以从IceSword中得到肯定的答案。
  

下载 (24.18 KB)

2010-5-2 11:15
【图 IceSword】IceSword检测到NtCreateProcess被Hook



四:SSDT访问
也许你现在已经迫不及待的想要操控一下SSDT,好消息是,ntolkrnl.exe导出了一个指针(符号为 KeServiceDescriptorTable)指向其主服务描述符表(Main SDT),从处于内核模式的模块中访问主服务描述符表是非常容易,你只需要两个C指令,首先是由extern关键字修饰的变量说明,这告诉链接器该变量并 不包含在此模块中,而且不需要在链接时解析相应的符号名称,当该模块被加载到进程的地址空间后,针对该符号的引用才会动态连接到相应的模块中:
// Import SDT pointer
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;


其次是创建一个对KeServiceDescriptorTable的引用:
// Create SDT reference
PSERVICE_DESCRIPTOR_TABLE psdt = KeServiceDescriptorTable;

现在,你的内核模块就可以访问SSDT数据了。

到此为止,不再深入,因为文章和文章的作者都是入门级的,至于后续SSDT Hook之类的话题,你就自己玩吧。【4】



 
yanggao178
驱动牛犊
驱动牛犊
  • 注册日期2010-05-27
  • 最后登录2011-03-20
  • 粉丝0
  • 关注0
  • 积分5分
  • 威望51点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2010-05-28 10:25
很不错啊
ycht_cn
驱动牛犊
驱动牛犊
  • 注册日期2007-01-22
  • 最后登录2010-07-13
  • 粉丝0
  • 关注0
  • 积分4分
  • 威望24点
  • 贡献值0点
  • 好评度1点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2010-06-03 21:00
很详细啊,谢谢
li375268037
驱动牛犊
驱动牛犊
  • 注册日期2010-06-03
  • 最后登录2011-09-04
  • 粉丝0
  • 关注0
  • 积分24分
  • 威望231点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地板#
发布于:2010-06-04 12:04
帮顶!!!!!!!!!!!!!!!
游客

返回顶部