概述
调用门是Intel提供的一个机制,用于控制不同权限级(ring0-ring3)的程序函数调用。简单点就是提供了一个ring3 调用ring0 函数的机制。
在intel
手册volume3-Chapter 5.83
描述如下
Call gates facilitate controlled transfers of program control between different privilege levels.
They are typically used only in operating systems or executives that use the privilege-level protection mechanism
详细可参阅intel Volume3-Chapter5
实现调用门需要构造一个调用门描述符Call-Gate Descriptor
放入GDT
或者LDT
中。
Segment Selector
指向代码段的段选择子,P表示门是否有效,如果栈转化那么Param Count
指示要从调用方栈拷贝到目标栈的word(16位)
数。type
固定为1100
.
typedef struct _GateDescriptor {
unsigned int offset_low16 : 16;
unsigned int selector : 16;
unsigned int Param_count : 5;
unsigned int res : 3;
unsigned int type : 4;
unsigned int s : 1;
unsigned int dp1 : 2;
unsigned int p : 1;
unsigned int offset_hei16 : 16;
}GateDescriptor;
栈转化
如果调用的代码段是ring3权限(CPL),而目标调用门是ring0(RPL)权限,栈区是不共享的因此需要将栈区的参数拷贝到目标栈中。
因此调用调用门intel会自动按照如下图进行栈拷贝。
但是你需要注意FS
寄存器intel并没有保存,但是window在ring3程序fs存储的TIB
,在ring0存储KPCR
,也就是说在window
下你需要手动处理。
调用调用门
call 调用门选择子
jmp 调用门选择子
比如下面的汇编调用0x4bh的选择子
call 0x004B:00000000
但是VC编译器无法编写上面的指令,你只能利用下面的汇编指令
__asm {
_emit 0x9a;
_emit 0x00;
_emit 0x00;
_emit 0x00;
_emit 0x00;
_emit 0x4b;
_emit 0x00;
//call 0x004B:00000000;
}
调用门函数的编写
首先函数一般使用裸函数编写,结尾使用retf返回,如下图所示
void Syscall() {
DbgPrint("[My learning] %s \r\n", __FUNCTION__);
}
//对外提供的调用门函数
__declspec(naked) void SyscallProxy() {
__asm {
push ebp;
mov ebp, esp;
//windwow ring 0 fs应该指向30h
//注意!! windbg调试内核的话会自动修改fs为30h
mov ax, 30h;
mov fs, ax;
call Syscall;
mov esp, ebp;
pop ebp;
//这里要返回到ring 3所以应该还原fs
//ring 3程序固定指向3bh
mov ax, 3bh;
mov fs,ax;
retf 0;
}
}
为什么要使用裸函数?假设我们代码如下:
void SyscallProxy(){
}
对应的汇编指令
可以发现返回的时候使用ret而不是retf,两个指令最大的差别在于是否会修正cs等。如果直接使用原始的函数那么调用门将不会正确的返回。(cs没有被正确的修正)
实现调用门
我们通过一个驱动程序来编写一个调用门函数。
首先我们需要查看系统哪个GDT表项是空的,让我们插入自己实现的调用门描述符。
//查看gdt表 0到100的表项
dg 0 100
我们注意到0x48是空白的,所以我们可以利用这个进行插入我们自己的调用门描述符。0x48
对应的ring3
的段选择子是0x4bh,
计算过程如下:
首先段选择子格式
index: 1001(第9个gdt项)
TI :0
RPL:11
RPL 表示当前权限因为是ring3 所以是11
组合上面的数据后就是 1001011
也就是4bh
#pragma push
#pragma pack(1)
typedef struct _GDTR {
short limit;
int base;
}GDTR;
#pragma pop
typedef struct _GateDescriptor {
unsigned int offset_low16 : 16;
unsigned int selector : 16;
unsigned int Param_count : 5;
unsigned int res : 3;
unsigned int type : 4;
unsigned int s : 1;
unsigned int dp1 : 2;
unsigned int p : 1;
unsigned int offset_hei16 : 16;
}GateDescriptor;
void Syscall() {
//KdBreakPoint();
DbgPrint("[My learning] %s \r\n", __FUNCTION__);
}
//对外暴露的调用门函数
__declspec(naked) void SyscallProxy() {
__asm {
//int 3;
push ebp;
mov ebp, esp;
//push fs;
mov ax, 30h;
mov fs, ax;
call Syscall;
mov esp, ebp;
pop ebp;
mov ax, 3bh;
mov fs,ax;
retf 0;
}
}
//安装调用门到GDT中
void InstallGate() {
//KdBreakPoint();
DbgPrint("[My learning] %s \r\n", __FUNCTION__);
GateDescriptor gate = { 0 };
//指向代码段的选择子,因为ring0代码段是gdt第1个项目且DPL是0
gate.selector = 0x8;
//函数
gate.offset_low16 = (ULONG)SyscallProxy & 0xffff;
gate.offset_hei16 = ((ULONG)SyscallProxy >> 16) & 0xffff;
//参数是0
gate.Param_count = 0;
//固定数值
gate.type = 0xc;
gate.s = 0;
gate.p = 1;
//权限因为是给ring3准备的所以是3
gate.dp1 = 3;
KAFFINITY mask = KeQueryActiveProcessors();
KAFFINITY shift = 1;
while (mask)
{
KeSetSystemAffinityThread(shift);
GDTR gdt = { 0 };
__asm sgdt gdt;
DbgPrint("[My learning] %s base:%p limit %p \r\n", __FUNCTION__, gdt.base, gdt.limit);
GateDescriptor*pGate = (GateDescriptor*)gdt.base;
if (MmIsAddressValid(pGate))
{
pGate[9] = gate;
}
shift <<= 1;
mask >>= 1;
}
}
//卸载函数
void UnInstallGate() {
//KdBreakPoint();
DbgPrint("[My learning] %s \r\n", __FUNCTION__);
KAFFINITY mask = KeQueryActiveProcessors();
KAFFINITY shift = 1;
while (mask)
{
KeSetSystemAffinityThread(shift);
GDTR gdt = { 0 };
__asm sgdt gdt;
DbgPrint("[My learning] %s base:%p limit %p \r\n", __FUNCTION__, gdt.base, gdt.limit);
GateDescriptor*pGate = (GateDescriptor*)gdt.base;
if (MmIsAddressValid(pGate))
{
pGate[9].p = 0;
}
shift <<= 1;
mask >>= 1;
}
}
结合驱动代码
#include<ntifs.h>
#include <Ntddk.h>
#include<intrin.h>
#pragma push
#pragma pack(1)
typedef struct _GDTR {
short limit;
int base;
}GDTR;
#pragma pop
typedef struct _GateDescriptor {
unsigned int offset_low16 : 16;
unsigned int selector : 16;
unsigned int Param_count : 5;
unsigned int res : 3;
unsigned int type : 4;
unsigned int s : 1;
unsigned int dp1 : 2;
unsigned int p : 1;
unsigned int offset_hei16 : 16;
}GateDescriptor;
void Syscall() {
//KdBreakPoint();
DbgPrint("[My learning] %s \r\n", __FUNCTION__);
}
__declspec(naked) void SyscallProxy() {
__asm {
//int 3;
push ebp;
mov ebp, esp;
//push fs;
mov ax, 30h;
mov fs, ax;
call Syscall;
mov esp, ebp;
pop ebp;
mov ax, 3bh;
mov fs,ax;
retf 0;
}
}
void InstallGate() {
//KdBreakPoint();
DbgPrint("[My learning] %s \r\n", __FUNCTION__);
GateDescriptor gate = { 0 };
gate.selector = 0x8;
gate.offset_low16 = (ULONG)SyscallProxy & 0xffff;
gate.offset_hei16 = ((ULONG)SyscallProxy >> 16) & 0xffff;
gate.Param_count = 0;
gate.type = 0xc;
gate.s = 0;
gate.dp1 = 3;
gate.p = 1;
KAFFINITY mask = KeQueryActiveProcessors();
KAFFINITY shift = 1;
while (mask)
{
KeSetSystemAffinityThread(shift);
GDTR gdt = { 0 };
__asm sgdt gdt;
DbgPrint("[My learning] %s base:%p limit %p \r\n", __FUNCTION__, gdt.base, gdt.limit);
GateDescriptor*pGate = (GateDescriptor*)gdt.base;
if (MmIsAddressValid(pGate))
{
pGate[9] = gate;
}
shift <<= 1;
mask >>= 1;
}
}
void UnInstallGate() {
//KdBreakPoint();
DbgPrint("[My learning] %s \r\n", __FUNCTION__);
KAFFINITY mask = KeQueryActiveProcessors();
KAFFINITY shift = 1;
while (mask)
{
KeSetSystemAffinityThread(shift);
GDTR gdt = { 0 };
__asm sgdt gdt;
DbgPrint("[My learning] %s base:%p limit %p \r\n", __FUNCTION__, gdt.base, gdt.limit);
GateDescriptor*pGate = (GateDescriptor*)gdt.base;
if (MmIsAddressValid(pGate))
{
pGate[9].p = 0;
}
shift <<= 1;
mask >>= 1;
}
}
//这个函数被注册用于驱动卸载调用
VOID myUnload(
struct _DRIVER_OBJECT* DriverObject
) {
UNREFERENCED_PARAMETER(DriverObject);
DbgPrint("hello drive unloaded");
PDEVICE_OBJECT DeviceObject = DriverObject->DeviceObject;
UnInstallGate();
if (DriverObject->DeviceObject != NULL)
{
DbgPrint("驱动文件不为空执行删除");
IoDeleteDevice(DeviceObject);
UNICODE_STRING symbolDevName;
RtlInitUnicodeString(&symbolDevName, L"\\DosDevices\\MytestDriver");
IoDeleteSymbolicLink(&symbolDevName);
}
}
//驱动被加载的时候会调用此函数
NTSTATUS
DriverEntry(
_In_ struct _DRIVER_OBJECT* DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
//如果你没有用到参数需要告诉系统。
UNREFERENCED_PARAMETER(RegistryPath);
InstallGate();
//打印信息
DbgPrint("[My learning] drive loaded");
DriverObject->DriverUnload = myUnload;
UNICODE_STRING ustrDevName;
RtlInitUnicodeString(&ustrDevName, L"\\Device\\MytestDriver");
PDEVICE_OBJECT pDevObj = NULL;
auto ret = IoCreateDevice(DriverObject, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDevObj);
if (NT_SUCCESS(ret))
{
//指定IO模式
pDevObj->Flags |= DO_DIRECT_IO;
DbgPrint("IoCreateDevice 成功 \r\n");
}
else {
DbgPrint("IoCreateDevice 失败 %d\r\n", ret);
return STATUS_FAIL_CHECK;
}
UNICODE_STRING symbolDevName;
RtlInitUnicodeString(&symbolDevName, L"\\DosDevices\\MytestDriver");
ret = IoCreateSymbolicLink(&symbolDevName, &ustrDevName);
if (NT_SUCCESS(ret))
{
DbgPrint("IoCreateSymbolicLink 成功 \r\n");
}
else {
DbgPrint("IoCreateSymbolicLink 失败%d\r\n", ret);
IoDeleteDevice(pDevObj);
return STATUS_FAIL_CHECK;
}
return STATUS_SUCCESS;
}
最后ring3 层的调用代码
// ring3Demo.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
int main()
{
__asm {
_emit 0x9a;
_emit 0x00;
_emit 0x00;
_emit 0x00;
_emit 0x00;
_emit 0x4b;
_emit 0x00;
}
printf("调用完毕syscall");
system("pause");
return 0;
}