RMB系统调用4、SSDT

一、回顾
前两篇博客,我逆向分析了 KiSystemService 和 KiFastCallEntry 填充_KTRAP_FRAME 结构体的代码,二者大同小异,主要的区别是 sysenter 只改了eip,cs,ss,虽然esp也改了,但是windows不使用,而是从TSS里取esp0;另外sysenter并没有像中断门那样压栈,所以3环的 ss, esp, eflags, cs,eip都要在函数里依次保存到 _KTRAP_FRAME 。
这次课后作业是逆向 KiSystemService / KiFastCallEntry 调用内核函数部分,放在一块讲是因为这两个函数虽然入口不同,但是填充完 _KTRAP_FRAME 后,就会执行相同的代码。他们两个函数就像两头蛇一样,有两个入口,初始化的工作有区别,但是往后就共用一个函数体。
在这里插入图片描述
在逆向之前,先介绍所需的前置知识。同时,思考两个问题:

如何根据系统服务号(eax中存储)找到要执行的内核函数?
调用时参数是存储到3环的堆栈,如何传递给内核函数?

二、KeServiceDescriptorTable SSDT
注意:此SSDT不是系统服务调度表

KeServiceDescriptorTable SSDT是一个全局变量,这个全局变量是Windows内核导出,这个全局变量的结构如下;每一个成员又是一个结构体,这个结构体就是系统服务表;这个表只包含了一张表也就是SST结构,如图:

//KeServiceDescriptorTable	是 ntoskrnl.exe 所导出的全局变量 声明一下就可以直接使用了,里面已经有值了
//extern 是告诉编译器,不是我定义的,是别人定义的
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable
kd> dd KeServiceDescriptorTable					SSDT结构体
843769c0  8428ad9c 00000000 00000191 8428b3e4	KSYSTEM_SERVICE_TABLE	ntoskrnl;			//ntoskrnl.exe 的函数
843769d0  00000000 00000000 00000000 00000000	KSYSTEM_SERVICE_TABLE	win32k;			//win32k.sys 的函数
843769e0  842e96af 00000000 02ca1dac 000000bb	
843769f0  00000011 00000100 5385d2ba d717548f	
84376a00  8428ad9c 00000000 00000191 8428b3e4	
84376a10  99c96000 00000000 00000339 99c9702c
84376a20  00000000 00000000 84376a24 00000340
84376a30  00000340 867f3f78 00000007 00000000
SST结构体:
8428ad9c 是参数1 指向函数地址表 SSDT
00000000 是参数2
00000191 是参数3
8428b3e4 是参数4 指向函数参数表 说明函数中参数所占的字节数,一个参数占4个字节; 这个参数本身占1个字节
SSDT:系统服务调
#pragma pack(1)     
typedef struct _KSYSTEM_SERVICE_TABLE{
     unsigned int *ServiceTableBase;	//函数地址表(SSDT)基址
     unsigned int *ServiceCounterTableBase; //Used only in checked build;SSDT函数被调用的次数
     unsigned int NumberOfServices;		//函数的个数
     unsigned char *ParamTableBase;		//函数参数表基址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
#pragma pack()

//KeServiceDescriptorTable 结构体
typedef struct  _KSERVICE_TABLE_DESCRIPTOR
{
	KSYSTEM_SERVICE_TABLE	ntoskrnl;			//ntoskrnl.exe 的函数
	KSYSTEM_SERVICE_TABLE	win32k;			//win32k.sys 的函数
	KSYSTEM_SERVICE_TABLE 	notUsed1;
	KSYSTEM_SERVICE_TABLE 	notUsed2;
}	KSERVICE_TABLE_DESCRIPTOR,	*PKSERVICE_TABLE_DESCRIPTOR;

SST:SystemServiceTable 系统服务表
SSDT:系统服务调度表 SSDT(system service dispatch table) 系统服务分派表
SSPT:系统服务参数表 SSPT(system service parameter table) 系统服务参数表
关系:
在这里插入图片描述
3.
看示意图:
在这里插入图片描述
通过此图,我们可以得知以下信息:

通过 _KTHREAD 可以找到系统服务表
系统服务表又指向了函数地址表和函数参数表

有两张系统服务表,第一张是用来找内核函数的,第二张是找Win32k.sys驱动函数的。
通过逆向,我们还可以判定,其实两张系统服务表是线性地址连续的,每张16字节。

KeServiceDescriptorTableShadow SSDT表是指向Win32k.sys的表;黄色的表,也就是win32k.sys这张表只有在GDI进程才会挂,否则不会挂,可以dd 99c96000 验证,发现都是问号,运行一个需要GDI的的程序就可会挂上

kd> dd KeServiceDescriptorTableShadow
84376a00  8428ad9c 00000000 00000191 8428b3e4	KSYSTEM_SERVICE_TABLE	ntoskrnl;			//ntoskrnl.exe 的函数
84376a10  99c96000 00000000 00000339 99c9702c	KSYSTEM_SERVICE_TABLE	win32k;			//win32k.sys 的函数;	99c96000 就是指向Win32k.sys的指针
84376a20  00000000 00000000 84376a24 00000340
84376a30  00000340 867f3f78 00000007 00000000
84376a40  867f3eb0 867f3c58 867f3de8 867f3d20
84376a50  00000000 867f3b90 00000000 00000000
84376a60  84284809 84291eed 842a03a5 00000003
84376a70  86745000 86746000 00000120 ffffffff

说明一下表的4个属性:
ServiceTable 指向函数地址表,Count没有用,ArgmentTable 指向函数参数表,ServiceLimit 是这两张表的长度。

要注意函数参数表每项存储的是对应函数参数占的字节数,每项只有1字节。 这个在逆向中也可以验证。

最后补充一点,我们之前逆向API三环部分时,它进0环之前,无论是中断门还是快速调用,都会在 eax 里存一个值,我们称之为系统调用号或者服务号,这个东西的低12位就是函数参数表和函数地址表的下标,而第13位(下标12)如果是0,表示找第一张系统服务表(绿色的表),如果是1,那么找第二张表(黄色的表)。这点可以先记住,待会逆向的时候可以印证这个结论。
在这里插入图片描述
三、KeServiceDescriptorTableShadow和KeServiceDescriptorTable 拓展

早先“盗用”过公开的Re SSDT的源代码,对SSDT多少还是了解些,也Hook 过SSDT,但一直对另外一个SSDT——Shadow SSDT不甚了解,只知道它跟GUI调用有莫大的关系,具体怎么联系起来的,说不清楚。
 
最近研究起了Hook ShadowSSDT这个东西,经过查找资料、阅读Win2k的源码,以及WinDbg闹腾了半天,才终于明白了KeServiceDescriptorTable 和 KeServiceDescriptorTableShadow到底是什么样的关系。
 
众所周知,KeServiceDescriptorTable在ntoskrnl.exe中被导出了,在我们的代码中只需要extern一下就可以方便的对其进行引用。网上流行的代码,把KeServiceDescriptorTable的类型定义为指向类似下面结构的指针(这是我从Win2k源码中摘出来的):

typedef struct _KSERVICE_TABLE_DESCRIPTOR {
    PULONG_PTR Base;
    PULONG Count;
    ULONG Limit;
    PUCHAR Number;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;

遗憾的是,这些代码让我有一个误判断,认为SSDT就是一个KSERVICE_TABLE_DESCRIPTOR结构,而KeServiceDescriptorTable就是指向这个KSERVICE_TABLE_DESCRIPTOR的指针。这种理解让我对ShadowSSDT相当的迷糊。真正今天,才算是明白了这个ShadowSSDT到底是个什么东西。
实际上,KeServiceDescriptorTable描述成下面的结构更便于描述和理解:

typedef struct _KSERVICE_TABLE_DESCRIPTOR_TABLE {
   KSERVICE_TABLE_DESCRIPTOR NativeApiTable;
   KSERVICE_TABLE_DESCRIPTOR Win32kApiTable;
   KSERVICE_TABLE_DESCRIPTOR NotusedTable;
   KSERVICE_TABLE_DESCRIPTOR NotusedTable;
} KSERVICE_TABLE_DESCRIPTOR_TABLE, * KSERVICE_TABLE_DESCRIPTOR_TABLE;

其中所谓的ShadowSSDT,就是由KSERVICE_TABLE_DESCRIPTOR_TABLE结构的第二个表:Win32kApiTable(注意这个名字是我起的,仅便于理解)来描述的。但是KeServiceDescriptorTable的这个成员是没有被使用的,全值为0。这就到KeServiceDescriptorTableShadow上场了。

KeServiceDescriptorTableShadow其实就是KeServiceDescriptorTable的复制品,唯一的区别就是它的Win32kApiTable是有效的。当Win32k.sys初始化之前,KeServiceDescriptorTableShadow与KeServiceDescriptorTable完全一致,Win32kApiTable同样为0,没有任何的意义。Win32k.sys初始化时,会调用KeAddSystemService将GUI相关的系统调用添加进KeServiceDescriptorTableShadow中的Win32kApiTable中,直到这时,KeServiceDescriptorTableShadow才和KeServiceDescriptorTable有了区别。

也就是说,Win32k的GUI系统调用表,保存在KeServiceDescriptorTableShadow的第二个成员:Win32kApiTable中,也就是KeServiceDescriptorTableShadow的0x10偏移处。这么简单点东西,让我花了一整个下午的时间来闹腾,真是笨到家了。

最后附上Windows 2000源代码中KeServiceDescriptorTableShadow 和 KeServiceDescriptorTable的声明:
#define NUMBER_SERVICE_TABLES 4
extern KSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable[NUMBER_SERVICE_TABLES];
extern KSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTableShadow[NUMBER_SERVICE_TABLES];

四、函数调用过程分析

(1)用户调用kenel32.dll中的ReadFile,kenel32.dll中都是包装函数,kenel32.dll会用这些包装函数完成参数的有效性检查,将所有东西转换为unicode,接着锁定NTDLL.dll中的NtReadFile函数。

(2)NTDLL.dll中的都是服务的包装函数,当调用其中的NtReadFile时,这些服务包装函数将所需的Servcie ID送入EAX寄存器,将参数堆栈帧的指针送入EDX寄存器,然后发出INT 2e中断。这条指令会将处理器切换到内核模式。INT 2e对应的处理程序是由windows NT 的 executive(估计是内核)建立的,它将参数从用户模式的堆栈拷贝到内核模式的堆栈。堆栈帧的基址为EDX寄存器的值。而这个中断程序被称为KiSystemService()

(3)接着进入内核态,NTOSKRNL.exe开始工作,由它进行系统服务的最终调用,它的系统服务的用户接口是以包装函数(wrapper functions)的形式提供的。这些函数都在一个叫做NTDLL.DLL的dll里。NTOSKNL.EXE先初始化,初始化的过程中,先为NTOSKRNL提供的不同服务创建一个函数表即SSDT,表中的每一项都指定了Service ID所需函数的地址,每个函数代码都位于内核之中。类似的,SSPT也开始创建。

在这里插入图片描述

转载于:https://my.oschina.net/magicdmer/blog/408081
原文链接:https://blog.csdn.net/Kwansy/article/details/109476899

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值