首页
论坛
专栏
课程

[调试逆向] [原创]IsDebuggerPresent 反调试填坑记录

2019-7-30 20:02 2843

[调试逆向] [原创]IsDebuggerPresent 反调试填坑记录

2019-7-30 20:02
2843

之前分析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,排除相关因素影响。



但最后发现不论是直接运行或者是被调试器调试,这个字节一直是0x64。


所以如果使用这个逻辑判断是否处于调试环境中,可能会导致误报,实际上这里判断函数首字节是否为0x64(原始)只是为了判断当前此API函数是否被HOOK了。这也证明了以前的代码对这个函数首字节是否为0x64作判断是不准的,因为在win7以上系统,需要对应kernelBase中的IsDebuggerPresent,而不是kernel32中的IsDebuggerPresent。



文中如有错误,欢迎指正。


参考: 

https://blog.csdn.net/hgy413/article/details/7995244

https://blog.csdn.net/huadetianxia/article/details/17054877





[公告]安全测试和项目外包请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最新回复 (7)
大道在我 2019-7-31 00:41
2
0
(BOOL(*)())GetProcAddress(hModule, "IsDebuggerPresent")    楼主问下(BOOL(*)())这个是什么作用
BkHumor 2019-7-31 05:11
3
0
大道在我 (BOOL(*)())GetProcAddress(hModule, "IsDebuggerPresent") 楼主问下(BOOL(*)())这个是什么作用
老铁不好好睡觉
jishuzhain 2 2019-7-31 10:43
4
0
大道在我 (BOOL(*)())GetProcAddress(hModule, "IsDebuggerPresent") 楼主问下(BOOL(*)())这个是什么作用
typedef BOOL (_stdcall *LPAPI_IDP)();
LPAPI_IDP IsDebuggerPresent = GetProcAddress(hModule, "IsDebuggerPresent");  // 获取下地址
这段代码,在vc6.0下报错,error C2275: 'LPAPI_IDP' : illegal use of this type as an expression
只能转换为如下:
BOOL (*IsDebuggerPresent)(); //新增的部分
IsDebuggerPresent = (BOOL(*)())GetProcAddress(hModule, "IsDebuggerPresent");  // 获取下地址
不用typedef,直接转换为函数指针。
kabeor 2019-7-31 10:45
5
0
大道在我 (BOOL(*)())GetProcAddress(hModule, "IsDebuggerPresent") 楼主问下(BOOL(*)())这个是什么作用
判断GetProcAddress是否成功吧,下面不是还有个if。。。
大道在我 2019-7-31 16:19
6
0
jishuzhain typedef BOOL (_stdcall *LPAPI_IDP)(); LPAPI_IDP IsDebuggerPresent = GetProcAddress(hModule, "I ...
这个强制转换是将他转换为一个BOOL类型的地址还是什么?
最后于 2019-7-31 16:31 被大道在我编辑 ,原因:
joker陈 2019-7-31 20:12
7
0
一本正经的胡说八道。BOOL IsDebuggerPresent(); 跟0x64有毛关系。
jishuzhain 2 2019-8-2 16:38
8
0
大道在我 jishuzhain typedef BOOL (_stdcall *LPAPI_IDP)(); LPAPI_IDP IsDebuggerPresent = ...
是转为返回值为BOOL类型的函数指针,与该函数原本声明的要求一致。
游客
登录 | 注册 方可回帖
返回