BeingDebugged 是Windows
系统PEB
结构体中的一个成员,它是一个标志位,用于标识当前进程是否正在被调试。BeingDebugged的值为0表示当前进程未被调试,值为1表示当前进程正在被调试。由于BeingDebugged
是在PEB
结构体中存储的,因此可以通过访问PEB
结构体来获取BeingDebugged
的值。恶意软件可以使用BeingDebugged
来判断自己是否正在被调试,以此来防止被反病毒工程师或调试程序进行分析。反病毒工程师们也可以通过检查BeingDebugged
的值来判断程序是否正被调试从而进行恶意软件的检测和分析。
进程在运行时,位置FS:[30h]
指向PEB的基地址,为了实现反调试,恶意代码通过这个位置来检查BeingDebugged
标志位是否为1,如果为1则说明进程被调试。
首先我们可以使用dt _teb
命令解析一下TEB
的结构,如下TEB
结构的起始偏移为0x0,而0x30
的位置指向的是ProcessEnvironmentBlock
也就是指向了进程环境块PEB。
0:000> dt _teb
ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB // PEB 进程环境块
只需要在进程环境块的基础上+0x2
就能定位到线程环境块TEB
中BeingDebugged
的标志,此处的标志位如果为1则说明程序正在被调试,为0则说明没有被调试。
0:000> dt _peb
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 BitField : UChar
+0x003 ImageUsesLargePages : Pos 0, 1 Bit
+0x003 IsProtectedProcess : Pos 1, 1 Bit
我们手动来验证一下,首先线程环境块地址是007f1000
,在此基础上加0x30
即可得到进程环境快的基地址,位置FS:[0x30]
指向PEB的基地址,007ee000
继续加0x2即可得到BeingDebugged
的状态ffff0401
此处我们只需要看byte位是否为1即可。
0:000> r $teb
$teb=007f1000
0:000> dd 007f1000 + 0x30
007f1030 007ee000 00000000 00000000 00000000
007f1040 00000000 00000000 00000000 00000000
0:000> r $peb
$peb=007ee000
0:000> dd 007ee000 + 0x2
007ee002 ffff0401 0000ffff 0c400112 19f0775f
007ee012 0000001b 00000000 09e0001b 0000775f
有了上述知识点的理解,写出一段反调试代码来将变得很容易,如下代码片段所示,如果独立运行则会提示正常程序,一旦进程被调试则会提示异常,此处分别使用三段实现方式,读者可通过向IsDebug()
传入不同的参数启用。
#include <stdio.h>
#include <Windows.h>
// 判断程序是否被调试
int IsDebug(DWORD x)
{
BYTE Debug = 0;
if (x == 1)
{
__asm
{
mov eax, dword ptr fs : [0x30]
mov bl, byte ptr[eax + 0x2]
mov Debug, bl
}
}
if (x == 2)
{
__asm
{
push dword ptr fs : [0x30]
pop edx
mov al, [edx + 2]
mov Debug, al
}
}
if (x == 3)
{
__asm
{
mov eax, fs:[0x18] // TEB Self指针
mov eax, [eax + 0x30] // PEB
movzx eax, [eax + 2] // PEB->BeingDebugged
mov Debug, al
}
}
return Debug;
}
int main(int argc, char* argv[])
{
if (IsDebug(1) && IsDebug(2) && IsDebug(3))
{
printf("[-] 进程正在被调试器调试 \n");
}
else
{
printf("[*] 正常运行 \n");
}
system("pause");
return 0;
}
上述程序被运行,一旦处于调试器模式则会触发被调试的告警,如果恶意代码中使用该种技术阻碍我们正常调试,只需要在x64dbg
的命令行中执行dump fs:[30]+2
来定位到BeingDebugged()
的位置,并将其数值改为0然后运行程序,会发现反调试已经被绕过了。
这里补充一个知识点,通过运用IsDebuggerPresent()
调试函数同样可实现此类功能,IsDebuggerPresent 返回一个布尔值,用于指示调用进程是否正在被调试器调试。该函数不接受参数,并且如果进程正在被调试,则返回 TRUE,否则返回 FALSE。该函数的实现原理同样应用了BeingDebugged
标志位的检测方法。
#include <stdio.h>
#include <Windows.h>
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
while (TRUE)
{
// 检测用ActiveDebugProcess()来创建调试关系
if (IsDebuggerPresent() == TRUE)
{
printf("当前进程正在被调试 \r\n");
// 产生int3异常
DebugBreak();
break;
}
Sleep(1000);
}
return 0;
}
int main(int argc, char * argv[])
{
HANDLE hThread = CreateThread(0, 0, ThreadProc, 0, 0, 0);
if (hThread == NULL)
{
return -1;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
system("pause");
return 0;
}
上述代码中我们通过使用CreateThread()
函数创建了一个子线程用于每隔1000
毫秒就检测一次是否被调试了,如果被调试则直接产生一个DebugBreak()
也就是int3断点,其反调试效果如下图所示;
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容