基于STARTUPINFO的反调试与反反调试

一、反调试

     为了保护自己的程序,现在很多程序都加入了反调试,那么,有什么简单的方法来进行调试检测么?

在程序启动后,会有一个STARTUPINFO的结构体变量,来保存程序启动的信息,我们通过其中参数的改变来检测程序是正常运行还是在调试器中运行的。

在一般情况下,程序是由explorer.exe调用的(如果对此有疑问,你可以安装一个hips来了解下,例如:2012/4/17 星期二 15:23:42 c:\windows\explorer.exe 创建新进程 d:\program files\屏幕录像专家\屏录专家.exe 允许 [应用程序]* 命令行: "D:\Program Files\屏幕录像专家\屏录专家.exe" ),具体就是explorer.exe使用shell32中的ShellExecute来运行程序,而ShellExecute会在执行时清除不使用的值。而如果程序是由OD调用的话,会对STARTUPINFO结构体中的dwFlags 进行修改。

好,下面我们先来具体看看STARTUPINFO结构体的情况:

 

 1     typedef struct _STARTUPINFOW {
 2         DWORD   cb;
 3         LPWSTR  lpReserved;
 4         LPWSTR  lpDesktop;
 5         LPWSTR  lpTitle;
 6         DWORD   dwX;
 7         DWORD   dwY;
 8         DWORD   dwXSize;
 9         DWORD   dwYSize;
10         DWORD   dwXCountChars;
11         DWORD   dwYCountChars;
12         DWORD   dwFillAttribute;
13         DWORD   dwFlags;
14         WORD    wShowWindow;
15         WORD    cbReserved2;
16         LPBYTE  lpReserved2;
17         HANDLE  hStdInput;
18         HANDLE  hStdOutput;
19         HANDLE  hStdError;
20     } STARTUPINFOW, *LPSTARTUPINFOW;

其中的参数很多,不过我们只关心DWORD类型的变量。

要想弄懂问题,我们就先来看看代码吧。

 1 #include <windows.h> 
 2 #include <stdio.h> 
 3 
 4 #pragma comment(linker, "/subsystem:windows /entry:main") 
 5 
 6 int main() 
 7 { 
 8     STARTUPINFO si; 
 9     si.cb=sizeof(si);
10     GetStartupInfo(&si); 
11     DWORD   dwX;
12     DWORD   dwY;
13     DWORD   dwXSize;
14     DWORD   dwYSize;
15     DWORD   dwXCountChars;
16     DWORD   dwYCountChars;
17     DWORD   dwFillAttribute;
18     DWORD   dwFlags;
19     
20     if ( 
21         (si.dwX != 0) || 
22         (si.dwY != 0) || 
23         (si.dwXCountChars != 0) || 
24         (si.dwYCountChars != 0) || 
25         (si.dwFillAttribute != 0) || 
26         (si.dwXSize != 0) || 
27         (si.dwYSize != 0) || 
28         (si.dwFlags & STARTF_FORCEOFFFEEDBACK) 
29         ) 
30     { 
31         MessageBox(NULL, "found debugger!", NULL, 0);
32         ExitProcess(0);
33     } 
34     else 
35     { 
36         MessageBox(NULL, "not found debugger!", NULL, 0); 
37     } 
38     
39     return 0; 
40 } 

程序生成好了之后,如果程序正常运行,就会提示“not found debugger!”,而如果在OD中运行,则会提示“found debugger!”。

 

二、反反调试

 

我们先用OD载入debug版的程序。对汇编代码进行观察发现,我们在程序中加入的if判断语句变成了几个cmp比较和jnz跳转。

 

我们来看看代码

 1 00401033   .  50            push    eax                              ; /pStartupinfo
 2 00401034   .  FF15 90A14200 call    dword ptr [<&KERNEL32.GetStartup>; \GetStartupInfoA
 3 0040103A   .  3BF4          cmp     esi, esp
 4 0040103C   .  E8 DF000000   call    _chkesp
 5 00401041   .  837D CC 00    cmp     dword ptr [ebp-34], 0
 6 00401045   .  75 31         jnz     short 00401078
 7 00401047   .  837D D0 00    cmp     dword ptr [ebp-30], 0
 8 0040104B   .  75 2B         jnz     short 00401078
 9 0040104D   .  837D DC 00    cmp     dword ptr [ebp-24], 0
10 00401051   .  75 25         jnz     short 00401078
11 00401053   .  837D E0 00    cmp     dword ptr [ebp-20], 0
12 00401057   .  75 1F         jnz     short 00401078
13 00401059   .  837D E4 00    cmp     dword ptr [ebp-1C], 0
14 0040105D   .  75 19         jnz     short 00401078
15 0040105F   .  837D D4 00    cmp     dword ptr [ebp-2C], 0
16 00401063   .  75 13         jnz     short 00401078
17 00401065   .  837D D8 00    cmp     dword ptr [ebp-28], 0
18 00401069   .  75 0D         jnz     short 00401078
19 0040106B   .  8B4D E8       mov     ecx, dword ptr [ebp-18]
20 0040106E   .  81E1 80000000 and     ecx, 80
21 00401074   .  85C9          test    ecx, ecx
22 00401076   .  74 2D         je      short 004010A5
23 00401078   >  8BF4          mov     esi, esp
24 0040107A   .  6A 00         push    0                                ; /Style = MB_OK|MB_APPLMODAL
25 0040107C   .  6A 00         push    0                                ; |Title = NULL
26 0040107E   .  68 34204200   push    00422034                         ; |Text = "found debugger!"
27 00401083   .  6A 00         push    0                                ; |hOwner = NULL
28 00401085   .  FF15 ACA24200 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA

我们看到,从00401041开始,就是一连串的比较,其实我们只要找到正常执行代码的起始地址,然后直接一个jmp即可,或者修改所有的jnzje

 

1 004010A5   > \8BF4          mov     esi, esp
2 004010A7   .  6A 00         push    0                                ; /Style = MB_OK|MB_APPLMODAL
3 004010A9   .  6A 00         push    0                                ; |Title = NULL
4 004010AB   .  68 1C204200   push    0042201C                         ; |Text = "not found debugger!"
5 004010B0   .  6A 00         push    0                                ; |hOwner = NULL
6 004010B2   .  FF15 ACA24200 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA

 

我们看到004010A5为当前正常运行的地址,我们在第一个比较的地方,即地址为00401041的地方进行反汇编,写入

JMP 004010A5后保存即可。

 

 

下面我们再来看看Release版的程序,其实都是一个意思,只不过release版的程序代码有些不同罢了,比较由cmp变成了test,但也是同样的效果,我们同样在比较的开始修改为jmp +

 

“正常运行的地址”。然后保存就可以了。

 

 

三、扩展

 

除了对OD的检测,现在对虚拟机检测的程序也越来越多,方式也越来越多。

 

比如,通过特权指令来检测、利用IDT基址检测、利用LDTGDT的检测方法、基于STR(Store Task Register)的检测方法、通过注册表检测虚拟机(是否安装了tools工具等)、基于时间差的检测方式(通过RDTSC)、利用虚拟硬件指纹检测(MAC地址检查等)等。

 

 

 

你可能感兴趣的:(基于STARTUPINFO的反调试与反反调试)