8.2 BeingDebugged

打印 上一主题 下一主题

主题 935|帖子 935|积分 2805

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。
  1. 0:000> dt _teb
  2. ntdll!_TEB
  3.    +0x000 NtTib            : _NT_TIB
  4.    +0x01c EnvironmentPointer : Ptr32 Void
  5.    +0x020 ClientId         : _CLIENT_ID
  6.    +0x028 ActiveRpcHandle  : Ptr32 Void
  7.    +0x02c ThreadLocalStoragePointer : Ptr32 Void
  8.    +0x030 ProcessEnvironmentBlock : Ptr32 _PEB       // PEB 进程环境块
复制代码
只需要在进程环境块的基础上+0x2就能定位到线程环境块TEB中BeingDebugged的标志,此处的标志位如果为1则说明程序正在被调试,为0则说明没有被调试。
  1. 0:000> dt _peb
  2. ntdll!_PEB
  3.    +0x000 InheritedAddressSpace : UChar
  4.    +0x001 ReadImageFileExecOptions : UChar
  5.    +0x002 BeingDebugged    : UChar
  6.    +0x003 BitField         : UChar
  7.    +0x003 ImageUsesLargePages : Pos 0, 1 Bit
  8.    +0x003 IsProtectedProcess : Pos 1, 1 Bit
复制代码
我们手动来验证一下,首先线程环境块地址是007f1000,在此基础上加0x30即可得到进程环境快的基地址,位置FS:[0x30]指向PEB的基地址,007ee000继续加0x2即可得到BeingDebugged的状态ffff0401此处我们只需要看byte位是否为1即可。
  1. 0:000> r $teb
  2. $teb=007f1000
  3. 0:000> dd 007f1000 + 0x30
  4. 007f1030  007ee000 00000000 00000000 00000000
  5. 007f1040  00000000 00000000 00000000 00000000
  6. 0:000> r $peb
  7. $peb=007ee000
  8. 0:000> dd 007ee000 + 0x2
  9. 007ee002  ffff0401 0000ffff 0c400112 19f0775f
  10. 007ee012  0000001b 00000000 09e0001b 0000775f
复制代码
有了上述知识点的理解,写出一段反调试代码来将变得很容易,如下代码片段所示,如果独立运行则会提示正常程序,一旦进程被调试则会提示异常,此处分别使用三段实现方式,读者可通过向IsDebug()传入不同的参数启用。
  1. #include <stdio.h>
  2. #include <Windows.h>
  3. // 判断程序是否被调试
  4. int IsDebug(DWORD x)
  5. {
  6.     BYTE Debug = 0;
  7.     if (x == 1)
  8.     {
  9.         __asm
  10.         {
  11.             mov eax, dword ptr fs : [0x30]
  12.             mov bl, byte ptr[eax + 0x2]
  13.             mov Debug, bl
  14.         }
  15.     }
  16.     if (x == 2)
  17.     {
  18.         __asm
  19.         {
  20.             push dword ptr fs : [0x30]
  21.             pop edx
  22.             mov al, [edx + 2]
  23.             mov Debug, al
  24.         }
  25.     }
  26.     if (x == 3)
  27.     {
  28.         __asm
  29.         {
  30.             mov eax, fs:[0x18]         // TEB Self指针
  31.             mov eax, [eax + 0x30]      // PEB
  32.             movzx eax, [eax + 2]       // PEB->BeingDebugged
  33.             mov Debug, al
  34.         }
  35.     }
  36.     return Debug;
  37. }
  38. int main(int argc, char* argv[])
  39. {
  40.     if (IsDebug(1) && IsDebug(2) && IsDebug(3))
  41.     {
  42.         printf("[-] 进程正在被调试器调试 \n");
  43.     }
  44.     else
  45.     {
  46.         printf("[*] 正常运行 \n");
  47.     }
  48.     system("pause");
  49.     return 0;
  50. }
复制代码
上述程序被运行,一旦处于调试器模式则会触发被调试的告警,如果恶意代码中使用该种技术阻碍我们正常调试,只需要在x64dbg的命令行中执行dump fs:[30]+2来定位到BeingDebugged()的位置,并将其数值改为0然后运行程序,会发现反调试已经被绕过了。

这里补充一个知识点,通过运用IsDebuggerPresent()调试函数同样可实现此类功能,IsDebuggerPresent 返回一个布尔值,用于指示调用进程是否正在被调试器调试。该函数不接受参数,并且如果进程正在被调试,则返回 TRUE,否则返回 FALSE。该函数的实现原理同样应用了BeingDebugged标志位的检测方法。
  1. #include <stdio.h>
  2. #include <Windows.h>
  3. DWORD WINAPI ThreadProc(LPVOID lpParam)
  4. {
  5.     while (TRUE)
  6.     {
  7.         // 检测用ActiveDebugProcess()来创建调试关系
  8.         if (IsDebuggerPresent() == TRUE)
  9.         {
  10.             printf("当前进程正在被调试 \r\n");
  11.             // 产生int3异常
  12.             DebugBreak();
  13.             break;
  14.         }
  15.         Sleep(1000);
  16.     }
  17.     return 0;
  18. }
  19. int main(int argc, char * argv[])
  20. {
  21.     HANDLE hThread = CreateThread(0, 0, ThreadProc, 0, 0, 0);
  22.     if (hThread == NULL)
  23.     {
  24.         return -1;
  25.     }
  26.     WaitForSingleObject(hThread, INFINITE);
  27.     CloseHandle(hThread);
  28.     system("pause");
  29.     return 0;
  30. }
复制代码
上述代码中我们通过使用CreateThread()函数创建了一个子线程用于每隔1000毫秒就检测一次是否被调试了,如果被调试则直接产生一个DebugBreak()也就是int3断点,其反调试效果如下图所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/800bf906.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

傲渊山岳

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表