今天开始逐一解决反调试,今天来学习最简单的IsDebuggerPresent(),IsDebuggerPresent()函数是一个Windows API函数,用于检测当前进程是否附加了调试器。看一下它的微软官方文档,
自己写了一段代码,然后编译成可执行文件,再在IDA中分析一下。
#include<stdio.h>
#include<windows.h>
int main()
{
BOOL isdebug = IsDebuggerPresent();
if (isdebug) {
printf("正在被调试\n");
}
else {
printf("没有被调试\n");
}
system("pause");
return 0;
}
再在IDA中动态调试,确实会被检测到,额,这里写的中文字符不知道为啥变成了乱码,别人出题的中文字符应该有处理?先不管这个了
像我这里写的,将IsDebuggerPresent()函数的返回值单独作为if判断的条件的话,我们可以直接在IDA中右键Set IP来绕过反调试,如图
但是我们来看看IsDebuggerPresent()函数的原理,我们F7跟进该函数看一下
F7跟进该函数加载了好一会,然后就是三行汇编指令,这个gs寄存器不知道是啥,应该是属于段寄存器的一种。网上搜索了一下,都说gs:60h,指向的是PEB,如图
那么啥是PEB呢?关于PEB,里面是有大学问的,在这我们暂且了解一点点就好。
PEB(Process Environment Block)是Windows NT操作系统中用于存储进程信息的一个数据结构。它包含了诸如进程ID、父进程ID、命令行参数等重要信息,还包含指向其他重要系统结构的指针,例如加载的模块列表(Ldr)、进程的堆(Heap)和线程局部存储(TLS)等。
我们利用 PEB 可以完成很多事情,比如说动态获取 api,进程伪装,反调试等等
微软的官方文档,它的结构体,如下
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;
这里我们关注第二个数据,BeingDebugged,我们看一下汇编指令
mov rax, gs:60h
movzx eax, byte ptr [rax+2]
retn
寄存器rax此时存的应该是PEB的地址,第一个数组Reserved1[2],那么[rax+2]应该就是得到BeingDebugged,看看网上其他Windows版本的PEB也可以看出就是BeingDebugged这个数据。
再将这个值赋值给寄存器eax,我们就跟踪一下[rax+2],发现它的值就是1,根据前面微软官方对该函数的描述,我们修改这个值为0也可以达到绕过该函数反调试的目的。
当然,有时候你会看到该函数的汇编指令是这样的,而且大多数时候都是这样的。
如上图,fs:[30]指向的也是PEB,所以和用gs寄存器寻址是一样的,汇编指令不同的原因可能是操作系统的不同,fs寄存器通常出现在x86,gs寄存器出现在x64。
嗯,我才知道x86是32位的操作系统,x64是64位的操作系统。
所以绕过方法和上面一样。。