内核学习-系统调用下

系统调用的学习

上文回顾

上篇文章分析得到:

1.3环进0环的两种方式,分别是中断门和快速调用,CPU支持快速调用,那么_KUSER_SHARED_DATA 结构体的 SystemCall 属性指向的函数是 KiFastSystemCall,执行 KiFastSystemCall,使用快速调用的方式进0环;如果不支持,那么SystemCall 指向的函数是KiIntSystemCall,执行 KiIntSystemCall,使用中断门的方式进0环。

2.快速调用不需要访问内存,而中断门需要读TSS和IDT表

3.int 0x2e 和 sysenter 指令进0环后,分别调用了两个函数 KiSystemService 和 KiFastCallEntry。

4.原来的寄存器存储到了_KTRAP_FRAME 结构体里,3环API参数指针通过EDX传给0环。

上篇文章分析结果,两个函数最后执行同一段代码,如图

图中标记的是407781函数,之后是执行的相同代码部分,7781函数在_KiFastCallEntry函数内部。图片太大没法截图全部。

在这里插入图片描述

KiSystemService / KiFastCallEntry 填充_KTRAP_FRAME 后续部分

这两个函数虽然入口不同,但是填充完 _KTRAP_FRAME 后,就会执行相同的代码。

预备知识:

eax中存储的系统服务号 0BAh

edx存储的三环的参数指针

系统服务表

_KTHREAD+0xE0= +0x0e0 ServiceTable : Ptr32 Void

有两张系统服务表,第一张是用来找内核函数的,第二张是找Win32k.sys驱动函数的

结构:0x10大小

typedef struct _SERVICE_DESCRIPTOR_TABLE
{
    PULONG ServiceTableBase;			// 指针,指向函数地址,每个成员占4字节
    PULONG ServiceCounterTableBase;		// 当前系统服务表被调用的次数
    ULONG  NumberOfService;				// 服务函数的总数
    PUCHAR ParamTableBase;				// 服务函数的参数总长度,以字节为单位,每个成员占一个字节
    									// 如:服务函数有两个参数,每个参数占四字节,那么对应参数总长度为8
    									// 函数地址成员 与 参数总长度成员 一一对应
} SSDTEntry, *PSSDTEntry;

  1. 系统服务表里的函数都是来自内核文件导出的函数
  2. 它并不包含内核文件导出的所有函数,而是3环最常用的内核函数
SSDT表

全称:System Services Descriptor Table(系统服务描述符表)

SSDT的每个成员叫做系统服务表

查看SSDT表

kd> dd nt!KeServiceDescriptorTable

第二张表为0,使用KeServiceDescriptorTable这个公开的导出函数,我们无法看到win32k.sys这张表结构

win32k.sys系统服务表已经可见

系统服务号:低12位就是函数参数表和函数地址表的下标,而第13位(下标12)如果是0,表示找第一张系统服务表Ntoskrl.exe,如果是1,那么找第二张表win32k.sys.后12位是函数地址表和函数参数表的索引。

分析
.text:00407781
.text:00407781 loc_407781:                             ; CODE XREF: _KiBBTUnexpectedRange+18↑j
.text:00407781                                         ; _KiSystemService+6E↑j
.text:00407781                 mov     edi, eax					;eax中是系统服务号
.text:00407783                 shr     edi, 8						;edi右移8位
.text:00407786                 and     edi, 30h					;判断系统服务号12位,0:edi == 0x00 ;1:edi == 0x10
.text:00407789                 mov     ecx, edi
.text:0040778B                 add     edi, [esi+0E0h];		[esi+0E0h]:_KTHREAD+0xE0=ServiceTable,edi指向系统服务表,这里将系统服务表所在地址直接加上edi的运算结果,巧妙地得到要查哪张表(两张表是连续的),每张表占16字节
.text:00407791                 mov     ebx, eax
.text:00407793                 and     eax, 0FFFh				;与运算后,只保留系统服务号低12位
.text:00407798                 cmp     eax, [edi+8]				;edi指向系统服务表,[edi+8]:NumberOfService,判断要找的函数是否超出范围
.text:0040779B                 jnb     _KiBBTUnexpectedRange	;若大于系统调用号的个数则跳转,即系统调用号越界
.text:004077A1                 cmp     ecx, 10h					;ecx 保存的是 edi 与0x30与运算后的结果,只能是0x00或0x10
.text:004077A4                 jnz     short loc_4077C0		;若系统调用号小于0x1000,则跳转
.text:004077A6                 mov     ecx, ds:0FFDFF018h		;只有当ecx == 0x10才会向下执行,作用是动态加载GUI等图形相关函数
.text:004077AC                 xor     ebx, ebx
.text:004077AE
.text:004077AE loc_4077AE:                             ; DATA XREF: _KiTrap0E+110↓o
.text:004077AE                 or      ebx, [ecx+0F70h]
.text:004077B4                 jz      short loc_4077C0
.text:004077B6                 push    edx
.text:004077B7                 push    eax
.text:004077B8                 call    ds:_KeGdiFlushUserBatch
.text:004077BE                 pop     eax
.text:004077BF                 pop     edx
.text:004077C0
.text:004077C0 loc_4077C0:                             ; CODE XREF: _KiFastCallEntry+B4↑j
.text:004077C0                                         ; _KiFastCallEntry+C4↑j
.text:004077C0                 inc     dword ptr ds:0FFDFF638h;
.text:004077C6                 mov     esi, edx						;edx:三环参数指针
.text:004077C8                 mov     ebx, [edi+0Ch]			; [edi+0Ch]:ParamTableBase(参数表指针)
.text:004077CB                 xor     ecx, ecx						;eax保存的是3环传入的系统调用号
.text:004077CD                 mov     cl, [eax+ebx]				;eax保存的是3环传入的系统调用号,ebx保存的是是参数表指针,这条指令的目的是得到内核函数的参数总长度,存入cl
.text:004077D0                 mov     edi, [edi]						;取出系统调用表的第一个成员(函数地址指针)
.text:004077D2                 mov     ebx, [edi+eax*4]			 ;函数地址指针 + 系统调用号*4(乘4是因为每个成员占4字节),ebx中存入函数地址
.text:004077D5                 sub     esp, ecx 						;提升堆栈,提升高度为cl,目的是要把三环堆栈中的参数存进来
.text:004077D7                 shr     ecx, 2 							;参数总长度/4=参数个数
.text:004077DA                 mov     edi, esp					;设置参数的0环堆栈地址
.text:004077DC                 cmp     esi, ds:_MmUserProbeAddress	;判断esi与用户程序能访问的最大地址范围,是否越界
.text:004077E2                 jnb     loc_407990 					;如果越界了 进行跳转,处理异常
.text:004077E8
.text:004077E8 loc_4077E8:                             ; CODE XREF: _KiFastCallEntry+2A4↓j
.text:004077E8                                         ; DATA XREF: _KiTrap0E+106↓o
.text:004077E8                 rep movsd						;复制参数(执行几次取决于参数个数<-004077D7结果 )到0环的堆栈
.text:004077EA                 call    ebx 						;调用内核函数NtReadVirtualMemory

实验:

查看函数地址:

[函数地址表 + 系统服务号*4] = 内核函数地址 805aa712

查看参数地址

[参数表 + 系统服务号] = 内核函数参数个数(单位:字节)14


查看内核函数反汇编:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值