DebugObject 对象

一、检测 DebugObject 对象

当调试器附加或打开一个程序建立调试的时候,会先创建一个调试对象 DebugObject,通过这个调试对象与被调试进程进行交互,因此我们可以对该对象进行检测来判断是否有调试器正在调试。

1 通过 NtQueryObject 函数来获取调试对象

我们可以通过 NtQueryObject 函数指定枚举常量 ObjectTypesInformation(3) 来对所有的内核对象进行查询,看是否存在调试对象 DebugOBject

#include <stdio.h>
#include <Windows.h>

// 查询的类型枚举常量
typedef enum _OBJECT_INFORMATION_CLASS {
    ObjectBasicInformation,
    ObjectNameInformation,
    ObjectTypeInformation,
    ObjectTypesInformation,
    ObjectHandleFlagInformation,
    ObjectSessionInformation,
    MaxObjectInfoClass  // MaxObjectInfoClass should always be the last enum
} OBJECT_INFORMATION_CLASS;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
#ifdef MIDL_PASS
    [size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT* Buffer;
#else // MIDL_PASS
    PWSTR  Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;

// 单个内核对象结构体
typedef struct _OBJECT_TYPE_INFORMATION {
    UNICODE_STRING TypeName;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccessMask;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    ULONG PoolType;
    ULONG DefaultPagedPoolCharge;
    ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;

// 接收所有查询到的内核对象的结构体
typedef struct _OBJECT_TYPES_INFORMATION
{
    ULONG numberOfTypesInfo;
    OBJECT_TYPE_INFORMATION typeInfo[1];
}OBJECT_TYPES_INFORMATION, * POBJECT_TYPES_INFORMATION;

typedef NTSTATUS(NTAPI* PFN_NtQueryObject)(
    __in HANDLE Handle,
    __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
    __out_bcount_opt(ObjectInformationLength) PVOID ObjectInformation,
    __in ULONG ObjectInformationLength,
    __out_opt PULONG ReturnLength
    );

PFN_NtQueryObject NtQueryObject;

int main()
{
    // 获取 NtQueryObject 函数
    NtQueryObject = (PFN_NtQueryObject)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryObject");

    // 查询内核对象
    POBJECT_TYPES_INFORMATION typesInfo = (POBJECT_TYPES_INFORMATION)malloc(0x4000);
    DWORD realSize = 0;
    NTSTATUS ret = NtQueryObject(NULL, ObjectTypesInformation, typesInfo, 0x4000, &realSize);
    if (ret != 0)
    {
        printf("NtQueryObject error\r\n");
        return -1;
    }

    // 遍历所有内核对象
    BOOL isDebugged = FALSE;
    POBJECT_TYPE_INFORMATION typeInfo = typesInfo->typeInfo;
    printf("遍历所有内核对象:\r\n");
    for (ULONG i = 0; i < typesInfo->numberOfTypesInfo; i++)
    {
        
        wprintf(L"%ls\r\n", typeInfo->TypeName.Buffer);
        if (wcscmp(L"DebugObject", typeInfo->TypeName.Buffer) == 0)
        {
            if (typeInfo->TotalNumberOfObjects > 0)
            {
                isDebugged = TRUE;
                printf("检测到了调试对象 DebugObject,数量为:%d 个\r\n", typeInfo->TotalNumberOfObjects);
            }
            //break;
        }

        // 指向下一个内核对象结构体
        // 先计算结构体对齐后的长度
        ULONG_PTR structLen =sizeof(OBJECT_TYPE_INFORMATION) + typeInfo->TypeName.MaximumLength;

#ifdef _WIN64
        // 64 位系统下上面的结构体按照 8 字节对齐
        if (structLen % 8 != 0)
        {
            structLen = int(structLen / 8) * 8 + 8;
        }
        typeInfo = (POBJECT_TYPE_INFORMATION)((ULONG_PTR)typeInfo + structLen);
#else
        // 32 位系统下上面的结构体按照 4 字节对齐
        if (structLen % 4 != 0)
        {
            structLen = int(structLen / 4) * 4 + 4;
        }
        typeInfo = (POBJECT_TYPE_INFORMATION)((ULONG_PTR)typeInfo + structLen);
#endif
       
        
    }

    if (!isDebugged)
    {
        printf("未检测到调试对象\r\n");
    }

    system("pause");
}

需要注意的是,我们查询返回的结构体数组里面的结构体是经过结构体对齐的,我们需要根据不同系统下结构体的对齐长度来对其进行正确解析。

当我们用 VS 编译器进行调试的时候,查询的结果如下:

posted @ 2025-02-22 15:54  lostin9772  阅读(5)  评论(0)    收藏  举报