8.2 BeingDebugged

beingdebugged · 浏览次数 : 19

小编点评

**反调试代码** ```c++ #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 } } else if (x == 2) { __asm { push dword ptr fs : [0x30]; pop edx; mov al, [edx + 2]; mov Debug, al } } else if (x == 3) { __asm { mov eax, fs:[0x18]; mov eax, [eax + 0x30]; movzx eax, [eax + 2]; mov Debug, al } } return Debug; } int main() { if (IsDebug(1) && IsDebug(2) && IsDebug(3)) { printf("\[-] 进程正在被调试器调试 \\"); } else { printf("[*] 正常运行 \\"); } system("pause"); return 0; } ``` **代码功能:** 1. 使用 `CreateThread()` 创建一个子线程,每隔 1000 毫秒检查是否被调试。 2. 在子线程中使用 `IsDebuggerPresent()` 函数检测进程是否被调试器调试。 3. 如果被调试,会产生 `DebugBreak()` 异常,使反调试程序停止运行。 **注意:** * 此代码仅用于测试目的,不应用于实际调试。 * 恶意代码可以通过设置 `DebugBreak()` 的跳跃指令避免被调试。

正文

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就能定位到线程环境块TEBBeingDebugged的标志,此处的标志位如果为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断点,其反调试效果如下图所示;

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

与8.2 BeingDebugged相似的内容:

8.2 BeingDebugged

BeingDebugged 是`Windows`系统`PEB`结构体中的一个成员,它是一个标志位,用于标识当前进程是否正在被调试。BeingDebugged的值为0表示当前进程未被调试,值为1表示当前进程正在被调试。由于`BeingDebugged`是在`PEB`结构体中存储的,因此可以通过访问`PEB`结构体来获取`BeingDebugged`的值。恶意软件可以使用`BeingDebugged`

CAP 8.2 版本发布通告

前言 今天我们很高兴宣布 CAP 发布 8.2 版本正式版,我们在这个版本中主要致力于对订阅着并行执行的特性提供支持,同时添加了对在订阅者中对消息头的控制行为。 下面,具体看一下我们新版本的功能吧。 总览 可能有些人还不知道 CAP 是什么,老规矩来一个简介。 CAP 是一个用来解决微服务或者分布式

INFINI Labs 产品更新 | Easysearch 1.8.2 发布优化 CCR 性能

INFINI Labs 产品又更新啦~,包括 Easysearch v1.8.0、Gateway、Console、Agent、Loadgen v1.25.0。本次各产品更新了很多亮点功能,如 Easysearch 新增数据写入限流功能,可实现节点、分片级限流;Gateway 修复数据迁移过程中因消费

达梦8.2专用版部署过程

背景 之前总结过.本次再进行一下总结和归集. 达梦的驱动也是经常变动,需要进行一些处理 获取安装介质 https://eco.dameng.com/download/ 但是专用机版本需要单独在获取 有其他的路径. 安装与初始化 rpm -ivh xxxx.rpm # 然后进行初始化 cd /opt/

算法学习笔记(8.2): 上下界网络流

# 上下界网络流 [TOC] > 前置知识以及更多芝士参考下述链接 > 网络流合集链接:[网络流](https://www.cnblogs.com/jeefy/p/17050215.html) 上下界网络流是普通网络流的一种变体,对于网络流,我们不仅关注其流量的上界,下届同样有所体现。 题型大致有五

.NET周刊【8月第2期 2023-08-14】

本周由于Myuki大佬感染新冠,国际板块暂停更新一周,将在下周补齐,所以本周只有国内板块。 ## 国内文章 ### 解决 Blazor 中因标签换行导致的行内元素空隙问题 https://www.cnblogs.com/ElderJames/p/resolves-width-issues-in-bl

上古神兵,先天至宝,Win11平台安装和配置NeoVim0.8.2编辑器搭建Python3开发环境(2023最新攻略)

毫无疑问,我们生活在编辑器的最好年代,Vim是仅在Vi之下的神级编辑器,而脱胎于Vim的NeoVim则是这个时代最好的编辑器,没有之一。异步支持、更好的内存管理、更快的渲染速度、更多的编辑命令,是大神Thiago de Arruda对开发者们最好的技术馈赠。 之前一篇:Win10系统下安装编辑器之神

K3S 系列文章-RHEL7.8 离线有代理条件下安装 K3S

一 基础信息 1.1 前提 本次安装的为 k3s 1.21.7+k3s1 VM 版本为 RHEL 7.8, 7.9 或 8.2, 8.3, 8.4(K3s 官网要求) VM YUM 仓库:已配置对应版本的 RHEL 和 EPEL YUM 仓库 VM 提供 root 权限 已配置 ntp(防止因为时间

Rancher 系列文章-RHEL7.8 离线有代理条件下安装单节点 Rancher

一 基础信息 1.1 前提 本次安装的为 20220129 最新版:Rancher v2.6.3 VM 版本为 RHEL 7.8, 7.9 或 8.2, 8.3, 8.4(Rancher 官网要求) VM YUM 仓库:已配置对应版本的 RHEL 和 EPEL YUM 仓库 VM 提供 root 权

Redhat/CentOS Linux 系统进入单用户模式

Redhat/CentOS Linux 系统进入单用户模式 以 CentOS 7.9 和 Redhat 8.2 为例进行操作,因为CentOS是Redhat的发行版,所以同版本号界面和操作是一样的。 CentOS 7.9 开机在 grub 引导界面时,按下 e 键进入编辑模式: 找到 linux16