首页
论坛
课程
招聘
内核学习-系统调用下
2021-7-21 11:33 1265

内核学习-系统调用下

2021-7-21 11:33
1265

系统调用的学习

上文回顾

上篇文章分析得到:

 

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大小

1
2
3
4
5
6
7
8
9
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表

1
kd> dd nt!KeServiceDescriptorTable

 

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

 

 

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

 

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

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.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与运算后的结果,只能是0x000x10
.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

 


查看内核函数反汇编:

 


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

最后于 2021-7-21 11:33 被pyikaaaa编辑 ,原因: 改个标题
收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回