反调试——1——开始反调试

前面的文章可以算是反调试的一些铺垫了,现在算是正式开始。

调试器的工作流程

调试器调试分两种情况,一种是附加进程,一种是通过可执行程序来创建进程调试。

创建进程:

通过CreateProcess并设置为DEBUG_PROCESS模式来启动。

BOOL CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);


 

附加进程:

通过调用DebugActiveProcess函数来附加进程。

在进程被调试时,进程执行的一些操作会以事件的方式通知给调试器。。

当有事件需要通知调试器时,操作系统会挂起进程所有线程,然后把事件发送给调试器。

调试器通过WaitForDebugEvent来等待事件。

BOOL WaitForDebugEvent(
LPDEBUG_EVENT lpDebugEvent, //操作系统传递给调试器的事件
DWORD dwMilliseconds //等待时间
);


 

程序被调试的特征

调试程序时,调试器会对被调试程序的程序设置标志。

 

 

标志

描述

位置

函数

BeginDebugged

被创建/附加进程都会被设置为1

PEB+02偏移处

IsDebuggerPresent()

NtGlobalFlag

创建进程会被设置为0x70,但是以附加进程设置时该值不会发生变化

PEB+0x68处

没有API

ProcessDebugPort

程序处于调试状态下可以获取调试端口,通过该端口也可以判断是否被调试。

checkRemoteDebugPresent

代码实现简单的反调试:

#include<Windows.h>
#include<iostream>
using namespace std;

void testBeginDebugged()
{
if (IsDebuggerPresent())
{
MessageBoxA(0, "BeginDebugged验证失败,程序被调试", 0, 0);
}
else
{
MessageBoxA(0, "BeginDebugged验证正常", 0, 0);
}
}
void testNtGlobalFlag()
{
DWORD IsDebug = 1;
__asm
{
push eax
mov eax,fs:[0x30]
mov eax,[eax+0x68]
mov IsDebug,eax
pop eax
}
if (IsDebug == 0x70)
{
MessageBoxA(0, "NtGlobalFlag验证失败,程序被调试", 0, 0);
}
else
{
MessageBoxA(0, "NtGlobalFlag验证正常", 0, 0);
}
}
void testProcessDebugPort()
{
BOOL IsDebug = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &IsDebug);
if(IsDebug == TRUE)
{
MessageBoxA(0, "ProcessDebugPort验证失败,程序被调试", 0, 0);
}
else
{
MessageBoxA(0, "ProcessDebugPort验证正常,程序未被调试", 0, 0);

}
}


int main()
{
cout << "Welcome" << endl;
testBeginDebugged();
testNtGlobalFlag();
testProcessDebugPort();

return 0;
}


通过xdbg反汇编来分析下:

testBeginDebugged()

反调试——1——开始反调试_创建进程

 

 

这个是我们自己写的void testBeginDebugged()函数,然后通过该函数查看IsDebuggerPresent函数:

反调试——1——开始反调试_#include_02

 

 

就很明显的可以看到了这个就是一个简答的赋值语句,将fs:[30]也就是PEB偏移0x2的内容作为返回值赋值给eax。

 

 

testNtGlobalFlag()

反调试——1——开始反调试_反调试_03

 

 

这里就很明显了,因为本来就是我们自己写的汇编代码,哈哈。

 

testProcessDebugPort()

反调试——1——开始反调试_创建进程_04

 

 

这个函数跟我们源代码比较一下可以看到有一个比较重要的函数CheckRemoteDebuggerPresent:

反调试——1——开始反调试_调试器_05

 

 

反调试——1——开始反调试_反调试_06

 

 

通过这样一层一层解析下来,其实也还是很明确了。它最后是调用了一个比较关键的NtQueryInformationProcess查询进行信息的一个API,这个是NtDll里面的:

反调试——1——开始反调试_创建进程_07