#写在最前
这篇文章的原理自于加密解密(第三版),读者有兴趣可以自行实现这个检测和隐藏debugger的小功能。
原理很简单:使用debugger的时候,程序会带上debugger的标记,我们需要做的就是找到标记点然后清除这个标记。
标记:
debug的连锁反应:
BeingDebugged->NtGlobalFlag->flag->heap magic
不过笔者认为纸上得来终觉浅,实际操作解决各种不知道那里冒出来的问题才会提升自己的能力(所以多写多练方才是正道)。在整个学习过程中,笔者花了大部分的时间在于使用win的API编程和规范代码。
#检测
#BeingDebugged
位置:peb+2
大小:byte
影响范围:NtGlobalFlag
被调试 | 正常 | |
---|---|---|
值 | 1 | 0 |
#NtGlobalFlag
位置:peb+68h
大小:byte
影响范围:heap magic
被调试 | 正常 | |
---|---|---|
值 | 70h | 0 |
值得注意的是:如果BeingDebugged
为True,NtGlobalFlag
的70h并非直接赋值,而是通过一系列标志设置(如FLG_HEAP_ENABLE_CHECK
)。而由此所引发的连锁反应可就不那么简单了。他将留下许多的痕迹(笑:)
#heap magic,Flags
Heap位置:peb+18h
Heap->Flags位置:heap+0ch
Heap->ForceFlags位置:heap+10h
影响范围:堆相关
依据NtGlobalFlag的值来设置了堆的Flags,堆的Flags会影响进程堆的Flags和ForceFlags,同时函数RtlCreateHeap
需要Flags的值来创建堆栈。故此堆栈会带上Flags的印记。
NTSYSAPI PVOID RtlCreateHeap(
ULONG Flags,
PVOID HeapBase,
SIZE_T ReserveSize,
SIZE_T CommitSize,
PVOID Lock,
PRTL_HEAP_PARAMETERS Parameters
);
其中调试堆会被标记填充一些奇怪的东西,一般为以下三个
BAADF00D
FEEEFEEE
ABABABAB
可以通过对调试堆中检测以上三个来判定是否被调试。这是对创建堆结果的检测来判断是否被调试。
不过根据《加密解密》其中更简单的方法检测Heap 的Flags和ForceFlags的值,这是对创建堆源头检测来判断是否被调试。
正常 | 被调试(通常) | |
---|---|---|
Flags | 2 | 50000062h |
ForceFlags | 0 | 40000060h |
可惜这种方法在编写程序和查看的时候并没有得到印证,数据跟书中所说并不符合。这里留个疑问,以后解决
代码
bool MyIsDebugger(ULONG PebBase, HANDLE hProcess) {
//读取目标进程PEB
_myPEB* Peb_buffer = (_myPEB*)malloc(sizeof(_myPEB));
memset((void *)Peb_buffer, 0, sizeof(_myPEB));
int ret = ReadProcessMemory(hProcess, (LPCVOID)PebBase, Peb_buffer, sizeof(_myPEB), NULL);
//读取成功
if (ret == 1) {
//判断BeingDebugged,NtGlobalFlag标志
byte bedebugged = Peb_buffer->bBeingDebugged;
DWORD NtGlobalFlag = Peb_buffer->dwNtGlobalFlag;
cout << "-----------------------show status----------------\n" << endl;
if ((int)bedebugged == 1 || (int)NtGlobalFlag == 0x70)
{
//判断堆中Magic信息
cout << "your are debugging me\n" << endl;
}
else {
cout << "your are not debugging me\n" << endl;
}
cout << "-------------------------------------------------\n" << endl;
}
//读取失败
else {
cout << "读取目标进程的内存失败" << endl;
//perror("perror say");
char buffer[256];
strerror_s(buffer, 100, GetLastError());
cout << "error:" << GetLastError() << ":" << buffer << endl;
free(Peb_buffer);
return FALSE;
}
}
修改
//清除PEB中BeingDebugged,NtGlobalFlag信息
bool clearBeingDebugged(ULONG PebBase, HANDLE hProcess) {
//清空BeingDebugged为0
byte flag = 0;
int ret = WriteProcessMemory(hProcess, (LPVOID)(PebBase + 2), &flag, sizeof(byte), NULL);
int ret2 = WriteProcessMemory(hProcess, (LPVOID)(PebBase + 0x68), &flag, sizeof(byte), NULL);
//写入成功
if (ret != 0 && ret2 != 0) {
cout << "----------------------\n清空BeingDebugged,NtGlobalFlag为0写入成功,隐藏debug-----------\n " << endl;
return TRUE;
}
//写入失败
else {
cout << "----------------------\n读取目标进程的内存失败------------------\n" << endl;
//perror("perror say");
char buffer[256];
strerror_s(buffer, 100, GetLastError());
cout << "error:" << GetLastError() << ":" << buffer << endl;
return FALSE;
}
}
#效果
input pid
12068
pid:12068 PebBase: 0xe64000
-----------------------show status----------------
your are debugging me
-------------------------------------------------
PEB: e64000
BeingDebugged: 1
NtGlobalFlag:0x70
Heap->Flags:0x2
Heap->ForceFlags:0x12f00a4
----------------------
清空BeingDebugged,NtGlobalFlag为0写入成功,隐藏debug-----------
PEB: e64000
BeingDebugged: 0
NtGlobalFlag:0x0
Heap->Flags:0x2
Heap->ForceFlags:0x12f00a4
-----------------------show status----------------
your are not debugging me
-------------------------------------------------
可从上面运行结果显示如果清除BeingDebugged
,NtGlobalFlag
可以得到很好的效果,但是其他影响却不能得到很好的印证,例如heap的Flags和ForceFlags的值确实跟书中提及到的不同。猜测OS版本的更新导致了细微的变化。
#检测进阶
使用CheckRemoteDebuggerPresent
来检测是否使用调试器,这个函数检测的是用户调试接口ProcessDebugPort
(代号7)。
由于这种检测方式是直接检测接口,所以方式很直观,没有什么好说的。
代码
bool CheckDebugger(HANDLE hProcess) {
HINSTANCE hModule;
BOOL bDebuggerPresent = FALSE;
check_remote_debugger_present CheckRemoteDebuggerPresent;
hModule = GetModuleHandleA("kernel32");
CheckRemoteDebuggerPresent = (check_remote_debugger_present)GetProcAddress(hModule, "CheckRemoteDebuggerPresent");
bool retn = CheckRemoteDebuggerPresent(hProcess, &bDebuggerPresent)?bDebuggerPresent:FALSE;
if (retn == TRUE)
cout<< "your are debugging me\n" <<endl;
else {
cout<< "your are not debugging me\n" <<endl;
}
return retn;
}