之前分析rdpscan远控后门的时候接触到Windows API下的反调试,这里就记录下。
首先是 IsDebuggerPresent 函数
IsDebuggerPresent函数可以用来检测本进程是否处于被调试状态,当然,这种方法的实用性不大。
作用:
如果当前进程在调试器的上下文中运行,则返回值为非零值。
如果当前进程未在调试器的上下文中运行,则返回值为零。
实际原理:
这个函数会看PEB中的BeingDebugged是否为0,不为0就表示无调试器,否则非0表示有调试器。
关于IsDebuggerPresent在调试时没有返回非0值退出进程,是ollydbg存在反反调试插件造成的IsDebuggerPresent失效。
原文出处: https://bbs.pediy.com/thread-144819.htm
里面有用到这段代码
*(BYTE *)IsDebuggerPresent != 0x64
来判断是否被调试,这里经过实际测试后发现与结果不同,与当前系统环境有关系。
在使用文中介绍的环境vc++6.0 进行编译,发现失败,之后修改代码后才成功编译(修改后的部分见截图)。
此函数在winbase.h中声明如下:WINBASEAPI BOOL WINAPI IsDebuggerPresent(void);
直接调用此函数的源程序在用vc++6.0编译时会报连接错误,原因是kernel32.lib中找不到_IsDebuggerPresent这个符号。
为了使用此函数,不得不使用LoadLibrary动态加载kernel32.dll,或者使用GetModuleHandle获取kernel32.dll的映像地址,然后使用GetProcAddress取得IsDebuggerPresent的地址。
代码如下:
BOOL (*IsDebuggerPresent)(); HMODULE hModule= GetModuleHandle("kernel32.dll"); IsDebuggerPresent = (BOOL(*)())GetProcAddress(hModule, "IsDebuggerPresent");
内部汇编代码:
7C813093 64:A1 18000000 mov eax, dword ptr fs:[18] |kernel32.IsDebuggerPresent //指向FS:[0] FS段寄存器在内存中的镜像地址 7C813099 8B40 30 mov eax, dword ptr [eax+30] //PEB结构地址(进程结构) 7C81309C 0FB640 02 movzx eax, byte ptr [eax+2] //BeingDebugged 7C8130A0 C3 ret
通过搜索发现,在XP环境下没有KernelBase.dll,XP之后的系统才会存在这个dll。
环境1:vc6.0、WinXp
demo:
#include <windows.h> //typedef BOOL (_stdcall *LPAPI_IDP)(VOID); int main(int argc, char* argv[]) { BOOL (*IsDebuggerPresent)(); //新增的部分 //HMODULE hModule = LoadLibrary("Kernel32"); // 加载模块 Kernel32 HMODULE hModule = GetModuleHandle("Kernel32.dll"); // 加载模块 Kernel32 if (hModule == NULL) { printf("GetModuleHandle faild!\n"); system("pause"); ExitProcess(0); // 如果发现程序被调试 直接退出进程 } //LPAPI_IDP IsDebuggerPresent = GetProcAddress(hModule, "IsDebuggerPresent"); // 获取下地址 IsDebuggerPresent = (BOOL(*)())GetProcAddress(hModule, "IsDebuggerPresent"); // 获取下地址 if (IsDebuggerPresent == NULL) { printf("GetProcAddress faild!\n"); system("pause"); ExitProcess(0); // 如果发现程序被调试 直接退出进程 } if (*(BYTE *)IsDebuggerPresent == 0xcc || *(BYTE *)IsDebuggerPresent != 0x64 || IsDebuggerPresent()) // 调用 { printf("程序被调试 *(BYTE *)IsDebuggerPresent is %X\n",*(BYTE *)IsDebuggerPresent); system("pause"); ExitProcess(0); // 如果发现程序被调试 直接退出进程 } // 如果程序能执行到这里 说明程序没有被调试状态 MessageBox(NULL, "没有被调试", NULL, MB_OK); printf("最终的 *(BYTE *)IsDebuggerPresent is %X\n",*(BYTE *)IsDebuggerPresent); system("pause"); return 0; }
在第一种环境下,取出的字节都为0x64。
环境2:vs2017、Win10
demo:
#include <windows.h> #include<stdio.h> //typedef BOOL (_stdcall *LPAPI_IDP)(VOID); int main(int argc, char* argv[]) { BOOL(*IsDebuggerPresent)(); //新增的部分 //HMODULE hModule = LoadLibrary("Kernel32"); // 加载模块 Kernel32 HMODULE hModule = GetModuleHandle("KernelBase.dll"); // 加载模块 Kernel32 if (hModule == NULL) { printf("GetModuleHandle faild!\n"); system("pause"); ExitProcess(0); // 如果发现程序被调试 直接退出进程 } //LPAPI_IDP IsDebuggerPresent = GetProcAddress(hModule, "IsDebuggerPresent"); // 获取下地址 IsDebuggerPresent = (BOOL(*)())GetProcAddress(hModule, "IsDebuggerPresent"); // 获取下地址 if (IsDebuggerPresent == NULL) { printf("GetProcAddress faild!\n"); system("pause"); ExitProcess(0); // 如果发现程序被调试 直接退出进程 } if (*(BYTE *)IsDebuggerPresent == 0xcc || *(BYTE *)IsDebuggerPresent != 0x64 || IsDebuggerPresent()) // 调用 { printf("程序被调试 *(BYTE *)IsDebuggerPresent is %X\n", *(BYTE *)IsDebuggerPresent); system("pause"); ExitProcess(0); // 如果发现程序被调试 直接退出进程 } // 如果程序能执行到这里 说明程序没有被调试状态 MessageBox(NULL, "没有被调试", NULL, MB_OK); printf("The return value of IsDebuggerPresent is %d\n", IsDebuggerPresent()); printf("*(BYTE *)IsDebuggerPresent is %X\n", *(BYTE *)IsDebuggerPresent); system("pause"); return 0; }
在第二种环境下,取出的字节也都为0x64。
环境3:Win7 vs2015
demo:
#include <windows.h> //typedef BOOL (_stdcall *LPAPI_IDP)(VOID); int main(int argc, char* argv[]) { BOOL (*IsDebuggerPresent)(); //新增的部分 //HMODULE hModule = LoadLibrary("Kernel32"); // 加载模块 Kernel32 HMODULE hModule = GetModuleHandle("Kernel32.dll"); // 加载模块 Kernel32 if (hModule == NULL) { printf("GetModuleHandle faild!\n"); system("pause"); ExitProcess(0); // 如果发现程序被调试 直接退出进程 } //LPAPI_IDP IsDebuggerPresent = GetProcAddress(hModule, "IsDebuggerPresent"); // 获取下地址 IsDebuggerPresent = (BOOL(*)())GetProcAddress(hModule, "IsDebuggerPresent"); // 获取下地址 if (IsDebuggerPresent == NULL) { printf("GetProcAddress faild!\n"); system("pause"); ExitProcess(0); // 如果发现程序被调试 直接退出进程 } if (*(BYTE *)IsDebuggerPresent == 0xcc || *(BYTE *)IsDebuggerPresent != 0x64 || IsDebuggerPresent()) // 调用 { printf("程序被调试 *(BYTE *)IsDebuggerPresent is %X\n",*(BYTE *)IsDebuggerPresent); system("pause"); ExitProcess(0); // 如果发现程序被调试 直接退出进程 } // 如果程序能执行到这里 说明程序没有被调试状态 MessageBox(NULL, "没有被调试", NULL, MB_OK); printf("最终的 *(BYTE *)IsDebuggerPresent is %X\n",*(BYTE *)IsDebuggerPresent); system("pause"); return 0; }
使用KernelBase.dll 发现可以正确显示首字节为0x64
因为前面使用了含有反调试插件的ollydbg版本,导致IsDebuggerPresent一直没有效果。
这里就使用原版ollydbg2.01测试如下,IsDebuggerPresent有了效果,执行完后返回值为非0,排除相关因素影响。