windows ssdt

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

前言

我们 ring 3 跳转 ring0 另一种方式使用sysenter命令。

sysenter 相比起jmp,int xx方式相比速度更快因为sysenter指令大量的使用了MSR寄存器 存储跳转地址等。

MSR寄存器相关读/写命令

//读取msr寄存器
rdmsr xxxx
//写入msr寄存器
wrmsr xxxx

其中xxx是msr寄存器的编号比如174h等

为方便记忆我们约定了一些编号的名字
IA32_SYSENTER_CS (MSR address 174H)
IA32_SYSENTER_EIP (MSR address 176H)
IA32_SYSENTER_ESP (MSR address 175H)

上面上个寄存器就是sysenter指令会涉及的msr寄存器。

sysenter 规范

以32位操作系统举例说明这个指令相关流程.

sysenter 段描述符约定

使用这个intel 指令操作系统必须要做出如下妥协
ring0 的ds段描述符必然是ring0 的 cs+8.我们可以简述成: ds0=cs0+8
ring3 的cs和ds描述如下 cs3= CS0 + 16 ,ds3= CS0 + 24.
也就是我们可以通过任意一个段描述符下标反推出其他段描述符下标。
我们可以利用windbg验证win32系统是否按照如此规定。
在这里插入图片描述
上图编号是16进制。

0008 00000000 ffffffff Code RE Ac 0 Bg Pg P  Nl 00000c9b
0010 00000000 ffffffff Data RW Ac 0 Bg Pg P  Nl 00000c93
0018 00000000 ffffffff Code RE Ac 3 Bg Pg P  Nl 00000cfb
0020 00000000 ffffffff Data RW Ac 3 Bg Pg P  Nl 00000cf3

第一个序号0x8是cs0第二个序号是ds0 也就是 0x8+0x8=0x10.往后递推。

sysenter 调用约定

调用sysenter 前需要准备给以下寄存器准备数据
IA32_SYSENTER_CS (MSR address 174H) 存储跳转到ring0 的cs段描述符下标
IA32_SYSENTER_EIP (MSR address 176H) 存储跳转 ring0 的eip
IA32_SYSENTER_ESP (MSR address 175H) 存储跳转 ring0 的ESP

当调用SYSEXIT 时需要提前填充如下数据
edx 存储返回到ring3 EIP
ecx 存储返回到ring3 ESP

上述便是intel中的一些规范在windwow中在这个基础做了一层封装。

window 封装sysenter

windowsysenter 构建了一个复杂的数据结构(内部包含函数地址表参数数量表所有在sysenter 会首先统一跳转到相同内核处理函数在跳转前需要将参数提前压入栈中以及调用的函数下标以及服务表数组下标放入eax。内核分发函数首先会读取eax服务表下标然后读取服务表的下标然后从这表中( 系统服务描述表(system service descriptor table))根据eax传入的函数下标取出函数地址以及对应的参数数量然后拷贝ring3 栈参数到ring0 在执行跳转。

首先需要知道的知识:
window服务表数组当前有两个数组我们分别取名为KeServiceDescriptorTable,KeServiceDescriptorTableShadow数组长度上限大小为4。
KeServiceDescriptorTable内部包含非ui内核函数,当前这个数组元素数量只有1个。
KeServiceDescriptorTableShadow包含了ui内核函数和非ui函数当前元素元素数量只有2个且其中一个元素和KeServiceDescriptorTable相同。

类似结构如下

typedef struct _SYSTEM_SERVICE_TABLE
{
	PVOID ServiceTableBase; //这个指向系统服务函数地址表
	PULONG ServiceCounterTableBase;
	ULONG NumberOfService; //服务函数的个数
	ULONG ParamTableBase;
}SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;

typedef struct _SERVICE_DESCRIPTOR_TABLE
{
	SYSTEM_SERVICE_TABLE ntoskrnel; //ntoskrnl.exe的服务函数
	SYSTEM_SERVICE_TABLE win32k; //win32k.sys的服务函数,(gdi.dll/user.dll的内核支持)
	SYSTEM_SERVICE_TABLE NotUsed1;
	SYSTEM_SERVICE_TABLE NotUsed2;
}SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;

 SYSTEM_DESCRIPTOR_TABLE KeServiceDescriptorTable = { ntoskrnel=itemAddr };
 SYSTEM_DESCRIPTOR_TABLE KeServiceDescriptorTableShadow = { itemAddr,itemAddr2 };

当然需要注意的是KeServiceDescriptorTable 是一个被ntoskrnel导出的变量(KeServiceDescriptorTableShadow并没有在win32k.sys导出)
在这里插入图片描述
所以你可以在代码中直接使用类似如下的代码直接找到地址

    extern SYSTEM_DESCRIPTOR_TABLE KeServiceDescriptorTable;

关于如何获取另一个表可参阅
Windows下如何获得KeServiceDescriptorTableShadow地址

我们用windbg验证我们的想法
在这里插入图片描述
我们查看这个两个数据结构第一个地址的存储的函数地址

在这里插入图片描述
你会发现当前进程对应的ui函数地址都是空的是因为当前进程是没有ui的只有KeServiceDescriptorTable表因此第二个选项地址无法查询。
我们可以切换到一个有ui进程的程序上

在这里插入图片描述

传入eax规范
eax会传入两个参数服务表的下标以及函数下标
在这里插入图片描述
图片从转载

bits 0-11: the system service number (SSN) to be invoked.(译 被调用所在服务表的函数下标)
bits 12-13: the service descriptor table (SDT).(译 服务表下标)
bits 14-31: not used. (未使用)

查看一个记事本的案例
我们断点一个记事本ntdll.dll的createFile函数在这里插入图片描述
在这里插入图片描述

ntdll!NtCreateFile:
# 赋值对应的函数下标 和服务表
77313820 mov     eax,16Eh
77313825 call    ntdll!NtCreateFile+0xd (7731382d)
7731382a ret     2Ch

7731382d mov   edx,esp 
7731382f sysenter # 这行运行完成后跳转到7731382a 

首先我们先分析 eax的数值
在这里插入图片描述
从上面可以得知这个函数会调用最原始非UI的函数表下标为0x16。

call ntdll!NtCreateFile+0xd目的为了作为一个桩代码存储返回地址7731382a到栈中而后在内核中可以轻易读取到返回地址方便调用SYSEXIT 读取ring 3返回地址。

77313825 call  ntdll!NtCreateFile+0xd
//存储到edx寄存器中方便内核读取ring 3 读取到栈区地址
7731382d mov edx,esp

当我们运行到7731382f代码时一些寄存器堆栈数据

7731382f sysenter
msr寄存器:
msr[174] = 00000000`00000008
msr[175] = 00000000`89f7d000
msr[176] = 00000000`8234ba50

寄存器:
edx = 03d7f614
esp = 03d7f614
ebp = 03d7f6e0

栈区信息:
03d7f614  7731382a 74696b04 03d7f664 80100080
03d7f624  03d7f6a8 03d7f674 00000000 00000000
03d7f634  00000000 00000001 00000040 00000000
03d7f644  00000000 03d7f808 03d7f850 000004e8
03d7f654  00000000 00000040 00000000 00000001
03d7f664  000001bc 80100080 00000000 00000000
03d7f674  00000384 0000038c 00000000 00120010
03d7f684  03fad9f0 03fad9f0 00000000 00000000

然后我们在执行sysenter后跳转到msr[176] 地址这个是一个内核地址
在执行windbgt命令不会跳转到内核而是到7731382a ret 2Ch

这个函数会跳入内核nt!KiFastCallEntry函数。

这个函数分析不想写了。。。。网上有


参考命令


//寻找内核的notepad程序
!process 0 0

//切换到notepad程序
.process /r /p b060d780

//下断点
bp ntdll!NtCreateFile

//条件断点
bp 773e3825  ".if (@eax & 0x0`ffffffff) = 0x0`16E  { r eax} .else {gc}"

参考

操作系统 实验2 windbg双机调试+系统调用过程

System_Service_Descriptor_Table

wiki Model-specific_register

rdmsr–read-msr-

osdev SYSENTER

sysenter

System Service Descriptor Table - SSDT

静态分析nt!KiFastCallEntry

x86 CPU的MSR寄存器

hooking-system-service-dispatch-table-ssdt

HOOK SSDT

系统调用篇——SSDT

What is REX prefix in Instruction Encoding?

Model_Specific_Registers

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: windows