前言:
感谢:易道云学院 tiger老师指导:
调试分为:打开一个调试进程和调试一个现有的进程。
DebugByCreate为真时调用CreateProcess()函数打开一个调试进程,当DebugByCreate为假时调用DebugActiveProcess(dwPID)函数调试一个现有的程序。
一、调用CreateProcess()函数打开一个调试进程:
调用CreateProcess()函数实际底层调用:
二、调用DebugActiveProcess(dwPID)函数调试一个现有的程序
总结:
一个进程被调试,内核状态下EPROCESS结构的DebugProt 一定不为0,用户态模式下PEB结构的BeinDebuged一定不为0.
NT TP等驱动反调试原理:
使用打开模式调试:
驱动反调试可以在NTCreateProcess()处设置回调函数,如果发现创建调试的进程是自己要保护的进程时就让函数返回失败。
附件现有进程调试时
驱动反调试可以接管NtOpenProcess函数发现是自己要保护的进程时就直接返回失败,还可以把后面的每一步都破坏掉来实现跳过反调试的复杂度。
因为要建立调试关系就必须把DebugPort与DEBUG_OBJECT进行关联,那么就可以直接把DebugPort的值设置为0,也能实现反调试。
在用户层实现反调试
一、设置主线程为隐藏调试破坏调试通道
使用windows未公开的函数实现该函数位于ntdll实现代码:
typedef NTSTATUS(NTAPI* zwsetImformationThreadPtr)(DWORD, DWORD, DWORD, DWORD); //该函数原型
auto hNtDll=LoadLibrary("ntdll.dll"); //加载支持模块
if(hNtDll)
{
zwsetImformationThreadPtr zwsetImformationThread; zwsetImformationThread=(zwsetImformationThreadPtr)GetProcAddress(hNtDll,"zwsetImformationThread")
zwsetImformationThread((DWORD)GetCurrentThread(),0x11,0x0,0x0);//第一个参数时传入当前线程,第二各参数0X11就表示把当前线程设置为隐藏调试
}
实现逻辑:
调试器调试程序时,下断点实际时在断点处写入int3,然后进程走到这里会断下来然后由调试器来处理,我们使用这个函数可以使当前线程为不调试模式。当调试器下断点时依然会在断点位置写入int3,可是因为我们设置了当前线程为不调试模式,所以当程序走到这里就会崩溃,实现反调试共功能。
在用户层实现调试检测
一、通过BeinDebuged检测是否被调试 通过用户态模式下PEB结构的BeinDebuged一定不为0.我们只用检测PEB结构的BeinDebuged是否为0即可检测当前程序是否被调试。 通过汇编方法获取PEB结构: 因为TEB结构地址存放在FS段寄存器中。 typedef struct _TEB { PVOID Reserved1[12]; PPEB ProcessEnvironmentBlock; PVOID Reserved2[399]; BYTE Reserved3[1952]; PVOID TlsSlots[64]; BYTE Reserved4[8]; PVOID Reserved5[26]; PVOID ReservedForOle; PVOID Reserved6[4]; PVOID TlsExpansionSlots; } TEB, *PTEB; TEB结构体原型。
可以看到TEB结构体偏移48位刚好就是PEB结构体指针,48的十六进制是0X30 所以我们可以写如下汇编来实现获取PEB结构
PPEB _peb;
_asm { mov eax,fs:[0x30] //获取PEB指针地址 mov _peb,eax // 把地址传给变量_peb }
然后判断_peb->BeingDebugged 是否位0 即可实现调试检测。
通过API函数获取BeingDebugged的值:
CheckRemoteDebuggerPresent():
IsDebuggerPresent();
通过上面两个函数进行检测,函数具体使用不再做介绍。
二、通过内核信息检测是否被调试
typedef NTSTATUS(NTAPI* NtQueryInformationProcessPtr)(HANDLE, DWORD, PVOID,ULONG, PULONG);
使用该函数,该函数是微软未公开函数 使用前需要导入。
auto hNtdll = LoadLibrary(L"ntdll.dll"); NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hNtdll, "NtQueryInformationProcess");
然后调用该函数第一个参数需要传入当前线程句柄,第二个参数我门需要知道的三个值:
0x07 :取debugport值。 debugport !=0时说明正在被调试 0x01E:取debugobject值 debugobject !=0时说明正在被调试 0x01F:取debugflages值,debugflages=0时说明正在被调试
检测方法:
、DWORD debug_port = 0;
NtQueryInformationProcess(proce, 0x07, &debug_port, sizeof(debug_port), 0x0);
if (debug_port)return TRUE;
HANDLE debug_object = 0;
NtQueryInformationProcess(proce, 0x01E, &debug_object, sizeof(debug_object), 0x0);
if (debug_object)return TRUE;
BOOL debug_flages = 1;
NtQueryInformationProcess(proce, 0x01F, &debug_flages, sizeof(debug_flages), 0x0);
if (!debug_flages)return TRUE;
return FALSE;