白云点缀的蓝 发表于 2021-9-24 19:29

对反调试IsDebuggerPresent的研究

本帖最后由 白云点缀的蓝 于 2021-11-6 10:54 编辑

简介:IsDebuggerPresent是确定调用进程是否由用户模式的调试器调试。下面是简单写的一个反调试代码
#include <stdio.h>
#include <Windows.h>
int main() {
int IsDebug=      IsDebuggerPresent();
if (IsDebug) {

          MessageBoxA(NULL, "调试中", "温馨提示", NULL);
          return EXIT_FAILURE;
}
MessageBoxA(NULL, "未被调试", "温馨提示", NULL);
return EXIT_SUCCESS;

}



反调试主要逻辑:IsDebuggerPresent()

下面这个是c++的函数原型

BOOL IsDebuggerPresent();

官方文档地址:https://docs.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-isdebuggerpresent

返回值说明:


如果当前进程在调试器的上下文中运行,则返回值非零。

如果当前进程未在调试器的上下文中运行,则返回值为零。


也就是说只要返回值是非0就说明被od,或者其他调试工具调试中

我们只要判断返回值就行

这里我用的int类型的变量来接收返回值,因为bool类型就是int类型的重命名

typedef int               BOOL;

int IsDebug=      IsDebuggerPresent();

在c语言中非0就是真,也就是说用if来判断是否正在被调试中

如果被调试就弹出对话框,提示你调试中

if (IsDebug) {

MessageBoxA(NULL, "调试中", "温馨提示", NULL);
return EXIT_FAILURE;
}

如果未被调试,就执行如下代码

MessageBoxA(NULL, "未被调试", "温馨提示", NULL);
return EXIT_SUCCESS;

}

下面我们来运行调试一下

当我们直接运行不调试时,提示如下



因为调试了就无法知道IsDebuggerPresent的返回值,所以我直接打印一下返回值

打印代码:
printf("%d\n", IsDebug);

运行后可以看到打印了0



下面我们进行调试然后跟踪一下反汇编代码

下图红色部分为调用IsDebuggerPresent函数



跟踪进入isDebuggerPresent函数内部



可以看到eax的值变为了00E74000



取出eax+2地址里面的值





然后把eax的值进行返回

然后把返回的值进行赋值给IsDebug

在计算机中,每一个变量都有一个地址,就是忘IsDebug地址里写入eax存的值,也就是1

00A148A4mov         dword ptr ,eax



可以看到有一个比较,如果IsDebug为0那么就跳过提示调试中

if (IsDebug) {
00A148B8cmp         dword ptr ,0
00A148BCje          __$EncStackInitStart+66h (0A148E2h)

可以看到IsDebug为1,所以je不会跳转




然后执行下面的弹窗

MessageBoxA(NULL, "调试中", "温馨提示", NULL);
00A148BEmov         esi,esp
00A148C0push      0
00A148C2push      offset string "\xce\xc2\xdc\xb0\xcc\xe1\xca\xbe" (0A17B34h)
00A148C7push      offset string "\xb5\xf7\xca\xd4\xd6\xd0" (0A17BE0h)
00A148CCpush      0
00A148CEcall      dword ptr
00A148D4cmp         esi,esp
00A148D6call      __RTC_CheckEsp (0A11230h)

当执行完如下代码时弹出了信息框









如果未被调试执行如下代码:
MessageBoxA(NULL, "未被调试", "温馨提示", NULL);
00A148E2mov         esi,esp
00A148E4push      0
00A148E6push      offset string "\xce\xc2\xdc\xb0\xcc\xe1\xca\xbe" (0A17B34h)
00A148EBpush      offset string "\xce\xb4\xb1\xbb\xb5\xf7\xca\xd4" (0A17BE8h)
00A148F0push      0
00A148F2call      dword ptr
00A148F8cmp         esi,esp
00A148FAcall      __RTC_CheckEsp (0A11230h)
return EXIT_SUCCESS;
00A148FFxor         eax,eax

有哪些方式可以过反调试呢?

可以通过修改IsDebuggerPresent函数的返回值为0实现过反调试

这里我把eax的值改为0





修改完后点击运行,可以发现成功跳过了


另外一种方式是把je改为jmp,直接绕过反调试



在od中过反调试的方法如下

过反调试的第一种方式(适合代码被加壳,被vm,也适合无壳)
CTRL+G搜索IsDebuggerPresent






我们在头部下断



执行到retn处可以看到eax的值为1



双击eax,把1改为0



然后我们F9运行

可以看到成功绕过了反调试



未修改任何数据的情况下,可以看到检测到了



过反调试的第二钟方式(适合未加壳)

通过修改je为jmp来实现绕过反调试

我们先在IsDebuggerPresent头部下断

然后点击运行,可以看到调用这个IsDebuggerPresent函数的call

因为在调用call时,会把cal指令的下一条指令压入堆栈



选择堆栈第一条指令,然后回车快速跳转到call前的下一条指令




根据前面的分析,我们可以知道下面那一条je语句就是关键



我们把这条语句改为jmp即可实现过反调试


JMP SHORT 00A148E2

然后F9运行,可以看到成功绕过了反调试





Shark恒 发表于 2021-9-24 22:35

插件的实现原理是将PEB的BeginDebugged直接赋值为0

Very_good 发表于 2021-9-24 22:55

赞一个,有空也可以普及一下CheckRemoteDebuggerPresent
这两个API是最常见的反调试API

寒宇 发表于 2021-9-25 11:41

地板亦是同理板凳{:5_187:}

列明 发表于 2021-9-26 13:33

直接修改API的返回值麽?

白云点缀的蓝 发表于 2021-9-26 14:49

列明 发表于 2021-9-26 13:33
直接修改API的返回值麽?

retn执行前后

2098502556 发表于 2021-10-8 17:35

学习支持康康

pizazzboy 发表于 2021-10-17 13:55

学习一下反调试

保安 发表于 2021-10-21 13:06

感谢分享 这就去淦VMP内核反调试(huaji){:5_188:}

bnjzzheng 发表于 2022-1-3 12:07

多谢分享多谢分享
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 对反调试IsDebuggerPresent的研究