本帖最后由 白云点缀的蓝 于 2021-11-6 10:54 编辑
简介:IsDebuggerPresent是确定调用进程是否由用户模式的调试器调试。下面是简单写的一个反调试代码
[C] 纯文本查看 复制代码 #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++的函数原型
[C++] 纯文本查看 复制代码 BOOL IsDebuggerPresent();
官方文档地址:https://docs.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-isdebuggerpresent
返回值说明:
如果当前进程在调试器的上下文中运行,则返回值非零。
如果当前进程未在调试器的上下文中运行,则返回值为零。
也就是说只要返回值是非0就说明被od,或者其他调试工具调试中
我们只要判断返回值就行
这里我用的int类型的变量来接收返回值,因为bool类型就是int类型的重命名
[C++] 纯文本查看 复制代码 typedef int BOOL;
[C] 纯文本查看 复制代码 int IsDebug= IsDebuggerPresent();
在c语言中非0就是真,也就是说用if来判断是否正在被调试中
如果被调试就弹出对话框,提示你调试中
[C] 纯文本查看 复制代码 if (IsDebug) {
MessageBoxA(NULL, "调试中", "温馨提示", NULL);
return EXIT_FAILURE;
}
如果未被调试,就执行如下代码
[C] 纯文本查看 复制代码 MessageBoxA(NULL, "未被调试", "温馨提示", NULL);
return EXIT_SUCCESS;
}
下面我们来运行调试一下
当我们直接运行不调试时,提示如下
因为调试了就无法知道IsDebuggerPresent的返回值,所以我直接打印一下返回值
打印代码:
[C] 纯文本查看 复制代码 printf("%d\n", IsDebug);
运行后可以看到打印了0
下面我们进行调试然后跟踪一下反汇编代码
下图红色部分为调用IsDebuggerPresent函数
跟踪进入isDebuggerPresent函数内部
可以看到eax的值变为了00E74000
取出eax+2地址里面的值
然后把eax的值进行返回
然后把返回的值进行赋值给IsDebug
在计算机中,每一个变量都有一个地址,[IsDebug]就是忘IsDebug地址里写入eax存的值,也就是1
[Asm] 纯文本查看 复制代码 00A148A4 mov dword ptr [IsDebug],eax
可以看到有一个比较,如果IsDebug为0那么就跳过提示调试中
[Asm] 纯文本查看 复制代码 if (IsDebug) {
00A148B8 cmp dword ptr [IsDebug],0
00A148BC je __$EncStackInitStart+66h (0A148E2h)
可以看到IsDebug为1,所以je不会跳转
然后执行下面的弹窗
[Asm] 纯文本查看 复制代码 MessageBoxA(NULL, "调试中", "温馨提示", NULL);
00A148BE mov esi,esp
00A148C0 push 0
00A148C2 push offset string "\xce\xc2\xdc\xb0\xcc\xe1\xca\xbe" (0A17B34h)
00A148C7 push offset string "\xb5\xf7\xca\xd4\xd6\xd0" (0A17BE0h)
00A148CC push 0
00A148CE call dword ptr [__imp__MessageBoxA@16 (0A1B098h)]
00A148D4 cmp esi,esp
00A148D6 call __RTC_CheckEsp (0A11230h)
当执行完如下代码时弹出了信息框
如果未被调试执行如下代码:
[Asm] 纯文本查看 复制代码 MessageBoxA(NULL, "未被调试", "温馨提示", NULL);
00A148E2 mov esi,esp
00A148E4 push 0
00A148E6 push offset string "\xce\xc2\xdc\xb0\xcc\xe1\xca\xbe" (0A17B34h)
00A148EB push offset string "\xce\xb4\xb1\xbb\xb5\xf7\xca\xd4" (0A17BE8h)
00A148F0 push 0
00A148F2 call dword ptr [__imp__MessageBoxA@16 (0A1B098h)]
00A148F8 cmp esi,esp
00A148FA call __RTC_CheckEsp (0A11230h)
return EXIT_SUCCESS;
00A148FF xor 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即可实现过反调试
[Asm] 纯文本查看 复制代码 JMP SHORT 00A148E2
然后F9运行,可以看到成功绕过了反调试
|