IsDebuggerPresent 反调试填坑记录
2019-07-31 14:59:21 Author: bbs.pediy.com(查看原文) 阅读量:133 收藏

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

17小时前 409

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

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



[公告]看雪 2019 安全开发者峰会,圆满落幕!现场精彩回顾!


文章来源: https://bbs.pediy.com/thread-253467.htm
如有侵权请联系:admin#unsafe.sh