一:SSDT 【1】
SSDT看来是一个兵家必争之地,无论是安全软件还是恶意代码都在上面做着手脚。
例如,由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]。
【图 SSDT Call Process】SSDT查找函数地址过程
【图 NtCreateProcess_ID】NtCreateProcess ID号
【图 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】
五:附录
对SSDT也只是有个初步了解,得益于一些参考资料:
【1】本文关于SSDT的描述,基本源于<<Undocumented Windows 2000 Secrets>>一书的第二章,所以针对的是Windows 2000系统,其中文翻译参见
【Undocumented Windows 2000 Secrets 中文翻译】http://blog.csdn.net/Kendiv/category/88780.aspx
【2】内核调试工具安装包和符号文件安装包,参见
【Debugging Tools for Windows】http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx
【3】搭建Windbg和VMware的内核调试环境,参见
【使用WinDbg和VMware调试NDIS中间层驱动程序】http://www.cppblog.com/aurain/archive/2009/01/04/71138.html
【4】关于SSDT Hook的文章比较多,<<简单说说SSDT>>是其中一篇,比较容易理解,参见
【简单说说SSDT】http://icylife.net/yunshu/show.php?id=435
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233150.html原文链接:https://javaforall.net