重写部分调试体系的DebugPort隐藏

背景

逛世界上最大的交友网站的时候发现了一个开源的vt调试器,抱着学习的心态,下载下来学习学习。但是呢,实际编译出来的程序和release发布的有很大的区别,而且源码很乱,release版本的驱动至少能调试程序,但是源码编译之后的驱动还有各种bug,你还不如只放一个bin上去。

就很生气,于是呢!想着自己重写一份吧,但是我又很菜,又很懒,调试体系需要实现的函数也很多。于是在经过不断左右摇摆和尝试之后替换几个api之后完成对debugport的隐藏。==>点我前往<==

调试体系

各位都是大佬,基本都是倒背调试体系的能人,弟弟就不在这里卖弄了,但是呢!还是有必要提一下。

调试主要还是两种,附加和创建调试。

创建:处理是在创建成功之后清零debugport和PS_PROCESS_FLAGS_NO_DEBUG_INHERIT标志位,并且记录调试的目标进程到链表中

if (NT_SUCCESS(status) && ProcessHandle != NULL)
    {
        PDebugInfomation TmpDebuginfo = NULL;
        BOOLEAN isDebug = FALSE;
        KIRQL OldIrql = { 0 };
        KeAcquireSpinLock(&g_DebugLock, &OldIrql);
        for (PLIST_ENTRY pListEntry = g_Debuginfo.List.Flink; pListEntry != &g_Debuginfo.List; pListEntry = pListEntry->Flink)
        {
            PDebugInfomation pDebuginfo = CONTAINING_RECORD(pListEntry, DebugInfomation, List);
            if (pDebuginfo->SourceProcessId == PsGetCurrentProcessId())
            {
                TmpDebuginfo = pDebuginfo;
                isDebug = TRUE;
                break;
            }
        }
        KeReleaseSpinLock(&g_DebugLock, OldIrql);

        if (isDebug)
        {
            PEPROCESS temp_process = NULL;
            status = ObReferenceObjectByHandle(*ProcessHandle, 0x0400, *PsProcessType, ExGetPreviousMode(), (void**)& temp_process, NULL);
            if (!NT_SUCCESS(status))
                return status;

            HANDLE target_pid = PsGetProcessId(temp_process);
            TmpDebuginfo->TargetProcessId = target_pid;
            PVOID DebugPort__ = GetProcess_DebugPort(temp_process);
            *(ULONG64 *)(DebugPort__) = 0;
            DbgkpMarkProcessPeb(temp_process);

            PVOID Flags = GetProcess_ProcessFlags(temp_process);
            *(PULONG64)Flags &= ~PS_PROCESS_FLAGS_NO_DEBUG_INHERIT;

            return status;
        }
    }

附加:没什么特别需要处理的,记录调记录调试的目标进程到链表中

    CurrentProcess = (PEPROCESS)PsGetCurrentProcess();
    status = ObReferenceObjectByHandle(
        DebugObjectHandle,
        0x2,
        *g_DbgkDebugObjectType,
        PreviousMode,
        (PVOID*)& DebugObject,
        NULL);

    KIRQL OldIrql = { 0 };
    KeAcquireSpinLock(&g_DebugLock, &OldIrql);
    for (PLIST_ENTRY pListEntry = g_Debuginfo.List.Flink; pListEntry != &g_Debuginfo.List; pListEntry = pListEntry->Flink)
    {
        PDebugInfomation pDebuginfo = CONTAINING_RECORD(pListEntry, DebugInfomation, List);
        if (pDebuginfo->SourceProcessId == PsGetCurrentProcessId())
        {
            DebugObject = pDebuginfo->DebugObject;
            pDebuginfo->TargetProcessId = PsGetProcessId(Process);
            break;
        }
    }
    KeReleaseSpinLock(&g_DebugLock, OldIrql);

代码的结构

代码总体分未两部分,应用层负责加载dbg api的符号,内核层负责替换调试类型的值和hook函数,主要支持了win7(sp1)和win10(20h2),因为这里部分Eprocess和Ethread的结构体数据是需要自己去定位的。

#ifdef WIN7
#define  Thread_CrossThreadFlags 0x448
#define  Thread_RundownProtect 0x430
#define  Process_DebugPort 0x1f0
#define  Process_RundownProtect 0x178
#define  ProcessFlagS 0x440
#define  ProcessSectionObject 0x268
#define  ProcessSectionBaseAddress 0x270
#define  ThreadStartAddress 0x388
#else
#define  Thread_CrossThreadFlags 0x510
#define  Thread_RundownProtect 0x4f8
#define  Process_DebugPort 0x578
#define  Process_RundownProtect 0x458
#define  ProcessFlagS 0x464
#define  ProcessSectionObject 0x518
#define  ProcessSectionBaseAddress 0x520
#define  ThreadStartAddress 0x450
#endif

应用层

利用sym api从http://msdl.microsoft.com/download/symbols下载符号到本地,然后将对应的api函数地址传输到驱动中

BOOLEAN isSuccess = FALSE;
    do
    {
        HMODULE hNtdll = GetModuleHandle("ntdll.dll");
        if (!hNtdll)
            break;
        g_ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll, "ZwQuerySystemInformation");
        if (!g_ZwQuerySystemInformation)
            break;
        Module_INFO Module = { 0 };
        GetKernelModuleInfo(&Module);
        if (!InitSymHandler())
            break;

        HMODULE hDll = LoadLibraryEx("ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
        char SymFile1[MAX_PATH] = { "" };
        char szFile[MAX_PATH], SymFile[MAX_PATH] = { "" };
        GetModuleFileNameA(hDll, szFile, sizeof(szFile) / sizeof(szFile[0]));
        if (!SymGetSymbolFile((HANDLE)-1, NULL, szFile, sfPdb, SymFile, MAX_PATH, SymFile1, MAX_PATH))
            break;

        char FileName[MAX_PATH];
        GetSystemDirectoryA(FileName, sizeof(FileName));
        strcat_s(FileName, "\\");
        strcat_s(FileName, Module.KernelName);
        HANDLE hFile = CreateFileA(FileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
            break;

        DWORD dwfilesize = GetFileSize(hFile, NULL);
        DWORD64 BaseOfDll = SymLoadModule64((HANDLE)-1, hFile, FileName, NULL, (DWORD64)Module.KernelBass, dwfilesize);
        CloseHandle(hFile);
        if (!BaseOfDll)
            break;

        if (!SymEnumSymbols((HANDLE)-1, BaseOfDll, 0, (PSYM_ENUMERATESYMBOLS_CALLBACK)& EnumAllSymbolsCallBack, CallBack))
            break;
        isSuccess = TRUE;
    } while (FALSE);
    return isSuccess;

会用到的api

typedef struct _SYMBOLS_DATA{
    PVOID NtCreateDebugObject;
    PVOID PsGetNextProcessThread;
    PVOID DbgkpPostFakeThreadMessages;
    PVOID DbgkpWakeTarget;
    PVOID DbgkpSetProcessDebugObject;
    PVOID DbgkCreateThread;
    PVOID DbgkpQueueMessage;
    PVOID PsCaptureExceptionPort;
    PVOID DbgkpSendApiMessage;
    PVOID DbgkpSendApiMessageLpc;
    PVOID DbgkpSendErrorMessage;
    PVOID DbgkForwardException;
    PVOID DbgkpSuppressDbgMsg;
    PVOID DbgkpSectionToFileHandle;
    PVOID DbgkUnMapViewOfSection;
    PVOID DbgkpPostFakeProcessCreateMessages;
    PVOID NtDebugActiveProcess;
    PVOID DbgkpMarkProcessPeb;
    PVOID KiDispatchException;
    PVOID NtCreateUserProcess;
    PVOID DbgkDebugObjectType;
    PVOID ObTypeIndexTable;
    PVOID NtTerminateProcess;
    PVOID DbgkMapViewOfSection;
    PVOID DbgkSendSystemDllMessages;
}SYMBOLS_DATA, * PSYMBOLS_DATA;

内核

初始化记录debugport对象的链表,替换部分使用到debugport的函数和DbgkDebugObjectType,其中这里的hook使用了airhv版本和ShotHv版本的vt,ShotHv版本的是放到源代码中的,并且ShotHv也比较适合学习,只handle必要的vmexit和ept

    //初始化调试对象的链表和锁
    InitializeListHead(&g_Debuginfo.List);
    KeInitializeSpinLock(&g_DebugLock);

    //这里开始hook函数
#ifdef WINVM
    PHHook(g_SymbolsData.DbgkCreateThread, DbgkCreateThread, (PVOID*)&OriginalDbgkCreateThread);
    PHHook(g_SymbolsData.DbgkpQueueMessage, DbgkpQueueMessage, (PVOID*)&OriginalDbgkpQueueMessage);
    PHHook(g_SymbolsData.NtTerminateProcess, NtTerminateProcess, (PVOID*)&OrignalNtTerminateProcess);
    PHHook(g_SymbolsData.NtCreateUserProcess, NtCreateUserProcess, (PVOID*)&OrignalNtCreateUserProcess);
    PHHook(g_SymbolsData.KiDispatchException, KiDispatchException, (PVOID*)&OrignalKiDispatchException);
    PHHook(g_SymbolsData.NtCreateDebugObject, NtCreateDebugObject, (PVOID*)&OriginalNtCreateDebugObject);
    PHHook(g_SymbolsData.NtDebugActiveProcess, NtDebugActiveProcess, (PVOID*)&OriginalNtDebugActiveProcess);
    PHHook(g_SymbolsData.DbgkForwardException, DbgkForwardException, (PVOID*)&OriginalDbgkForwardException);
    PHHook(g_SymbolsData.DbgkMapViewOfSection, DbgkMapViewOfSection, (PVOID*)&OriginalDbgkMapViewOfSection);
    PHHook(g_SymbolsData.DbgkUnMapViewOfSection, DbgkUnMapViewOfSection, (PVOID*)&OriginalDbgkUnMapViewOfSection);
    PHHook(g_SymbolsData.DbgkpSetProcessDebugObject, DbgkpSetProcessDebugObject, (PVOID*)&OriginalDbgkpSetProcessDebugObject);
    PHActivateHooks();
#else
    hook_function(g_SymbolsData.DbgkCreateThread, DbgkCreateThread, (PVOID*)&OriginalDbgkCreateThread);
    hook_function(g_SymbolsData.DbgkpQueueMessage, DbgkpQueueMessage, (PVOID*)&OriginalDbgkpQueueMessage);
    hook_function(g_SymbolsData.NtTerminateProcess, NtTerminateProcess, (PVOID*)&OrignalNtTerminateProcess);
    hook_function(g_SymbolsData.NtCreateUserProcess, NtCreateUserProcess, (PVOID*)&OrignalNtCreateUserProcess);
    hook_function(g_SymbolsData.KiDispatchException, KiDispatchException, (PVOID*)&OrignalKiDispatchException);
    hook_function(g_SymbolsData.NtCreateDebugObject, NtCreateDebugObject, (PVOID*)&OriginalNtCreateDebugObject);
    hook_function(g_SymbolsData.NtDebugActiveProcess, NtDebugActiveProcess, (PVOID*)&OriginalNtDebugActiveProcess);
    hook_function(g_SymbolsData.DbgkForwardException, DbgkForwardException, (PVOID*)&OriginalDbgkForwardException);
    hook_function(g_SymbolsData.DbgkMapViewOfSection, DbgkMapViewOfSection, (PVOID*)&OriginalDbgkMapViewOfSection);
    hook_function(g_SymbolsData.DbgkUnMapViewOfSection, DbgkUnMapViewOfSection, (PVOID*)&OriginalDbgkUnMapViewOfSection);
    hook_function(g_SymbolsData.DbgkpSetProcessDebugObject, DbgkpSetProcessDebugObject, (PVOID*)&OriginalDbgkpSetProcessDebugObject);
#endif

    if (!HookDbgkDebugObjectType())
        return FALSE;

hook函数的说明

需要处理的函数大概就是下面所罗列的,但是由于有懒人所以并没有都处理。

/*
    win7-win10下
    会用到debugport的函数
    PsGetProcessDebugPort       //获取debugport的值,不处理
    DbgkpSetProcessDebugObject  //这里不将debugport的值写到eprocess的debugport字段,也不调用DbgkpMarkProcessPeb
    DbgkpMarkProcessPeb         //DbgkClearProcessDebugObject、DbgkpCloseObject(objectType的CloseProcedure,这里直接不实现)和DbgkpSetProcessDebugObject中会调用
    DbgkCreateThread            //简单实现内部有点长,不实现线程回调
    PspExitThread                 //不实现,不要线程退出消息
    DbgkExitThread              //PspExitThread会调用DbgkExitThread,上面都不实现
    DbgkpQueueMessage            //实现比较简单
    KiDispatchException           //可以不实现,但内核调试器会先捕获到异常需要gn,不方便内核调试,而且不处理过不了int 2d
    DbgkForwardException        //调用了三个原函数
    NtQueryInformationProcess     //不处理
    DbgkClearProcessDebugObject //不实现 
    DbgkpCloseObject            //不实现  
    DbgkMapViewOfSection        //调用了两个原函数
    DbgkUnMapViewOfSection        //调用了两个原函数
    DbgkExitProcess                //不实现
*/

KiDispatchException

可以不处理,但是不处理就过不了int 2d

    if (PreviousMode != KernelMode)
    {
        BOOLEAN isDebug = FALSE;
        KIRQL OldIrql = { 0 };
        KeAcquireSpinLock(&g_DebugLock, &OldIrql);
        for (PLIST_ENTRY pListEntry = g_Debuginfo.List.Flink; pListEntry != &g_Debuginfo.List; pListEntry = pListEntry->Flink)
        {
            PDebugInfomation pDebuginfo = CONTAINING_RECORD(pListEntry, DebugInfomation, List);
            if (pDebuginfo->TargetProcessId == PsGetCurrentProcessId())
            {
                isDebug = TRUE;
                break;
            }
        }
        KeReleaseSpinLock(&g_DebugLock, OldIrql);

        if (isDebug)
        {
            if ((TrapFrame->SegCs & 0xfff8) == KGDT64_R3_CMCODE)
            {
                switch (ExceptionRecord->ExceptionCode)
                {
                case STATUS_BREAKPOINT:
                    ExceptionRecord->ExceptionCode = STATUS_WX86_BREAKPOINT;
                    break;
                case STATUS_SINGLE_STEP:
                    ExceptionRecord->ExceptionCode = STATUS_WX86_SINGLE_STEP;
                    break;
                }
            }

            if (DbgkForwardException(ExceptionRecord, TRUE, FALSE))
            {
                //int 2d 不返回,直接下发异常到异常处理
                if (*(PUSHORT)((ULONG64)(TrapFrame->Rip) - 3) != 0x2DCD)//int 2d
                    return;
            }

            if ((TrapFrame->SegCs & 0xfff8) == KGDT64_R3_CMCODE)
            {
                switch (ExceptionRecord->ExceptionCode)
                {
                case STATUS_WX86_BREAKPOINT:
                    ExceptionRecord->ExceptionCode = STATUS_BREAKPOINT;
                    break;
                case STATUS_WX86_SINGLE_STEP:
                    ExceptionRecord->ExceptionCode = STATUS_SINGLE_STEP;
                    break;
                }
            }
        }
    }

    OrignalKiDispatchException(ExceptionRecord, ExceptionFrame, TrapFrame, PreviousMode, FirstChance);
    return;

NtTerminateProcess

主要是进程结束之后删除记录,避免出现调试器不重启不能调试的情况

    NTSTATUS st;
    PEPROCESS Process = NULL;
    if (ProcessHandle)
    {
        st = ObReferenceObjectByHandle(ProcessHandle,
            PROCESS_TERMINATE,
            *PsProcessType,
            ExGetPreviousMode(),
            (PVOID*)& Process,
            NULL);
    }
    else
    {
        Process = PsGetCurrentProcess();
    }

    if (Process)
    {
        KIRQL OldIrql = { 0 };
        KeAcquireSpinLock(&g_DebugLock, &OldIrql);
        for (PLIST_ENTRY pListEntry = g_Debuginfo.List.Flink; pListEntry != &g_Debuginfo.List; pListEntry = pListEntry->Flink)
        {
            PDebugInfomation pDebuginfo = CONTAINING_RECORD(pListEntry, DebugInfomation, List);
            if (pDebuginfo->TargetProcessId == PsGetProcessId(Process))
            {
                RemoveEntryList(&pDebuginfo->List);
                ExFreePool(pDebuginfo);
                break;
            }
        }
        KeReleaseSpinLock(&g_DebugLock, OldIrql);


        if (ProcessHandle)
            ObDereferenceObject(Process);
    }

    return OrignalNtTerminateProcess(ProcessHandle,ExitStatus);

最后的结果

能正常调试vmp和se,并且没有debugport的痕迹(如果你想调试某游戏,我的评价是我不知道,因为我不玩)。

al-khaser_x86下除了父进程和watch memory,debug的痕迹都没有了(当然检测的方式还有窗口名和进程名的方式,这里并没有处理,你可以参考hyperhiden把这些也加上),NtYieldExecution在当前开了vt的虚拟机下就是bad(不管)。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值