自建调试体系-重写NtDebugActiveProcess

admin 2023年2月28日04:54:50评论18 views字数 3417阅读11分23秒阅读模式
自建调试体系-重写NtDebugActiveProcess

点击上方“蓝字”,关注更多精彩

自建调试体系-重写NtDebugActiveProcess

前言

最近学习游戏方面的逆向,调试器无法正常附加,所以需要自建调试体系来进行调试。

NtDebugActiveProcess重写

以下代码从wrk中抄出来的:

NTSTATUS NtDebugActiveProcess(HANDLE ProcessHandle, HANDLE DebugObjectHandle) {
    NTSTATUS Status;    KPROCESSOR_MODE PreviousMode;    PDEBUG_OBJECT DebugObject;    EPROCESS* Process;    PETHREAD LastThread;
    PAGED_CODE();
    PreviousMode = KeGetPreviousMode();
    Status = ObReferenceObjectByHandle(ProcessHandle,        PROCESS_SET_PORT,        PsProcessType,        PreviousMode,        &Process,        NULL);    if (!NT_SUCCESS(Status)) {        return Status;    }
    if (Process == PsGetCurrentProcess() || Process == PsInitialSystemProcess) {        ObDereferenceObject(Process);        return STATUS_ACCESS_DENIED;    }
   
    POBJECT_TYPE DbgkDebugObjectType = NULL;
    Status = ObReferenceObjectByHandle(DebugObjectHandle,        DEBUG_PROCESS_ASSIGN,        DbgkDebugObjectType,        PreviousMode,        &DebugObject,        NULL);
    if (NT_SUCCESS(Status)) {
        if (ExAcquireRundownProtection(&Process->RundownProtect)) {
            Status = DbgkpPostFakeProcessCreateMessages(Process,                DebugObject,                &LastThread);
            //            // Set the debug port. If this fails it will remove any faked messages.            //            Status = DbgkpSetProcessDebugObject(Process,                DebugObject,                Status,                LastThread);
            ExReleaseRundownProtection(&Process->RundownProtect);        }        else {            Status = STATUS_PROCESS_IS_TERMINATING;        }
        ObDereferenceObject(DebugObject);    }    ObDereferenceObject(Process);
    return Status;

}

这个函数并不能直接使用,需要修复一些东西。首先需要考虑的是DbgkDebugObjectType这个。这个玩意在wrk中是一个全局变量。ida中也是一个全局变量

自建调试体系-重写NtDebugActiveProcess

在ida中查看那些地方调用了它:

自建调试体系-重写NtDebugActiveProcess

发现有这样一个函数,看函数名称应该是初始化之类的:

DbgkpInitializePhase0

所以现在我们有两个思路:

  1. 直接通过特征码暴力定位该全局变量的地址

  2. 通过重写DbgkpInitializePhase0来进行初始化该变量。

两个方法各有优势和弊端,方法1优势就是省时省力,弊端是如果游戏保护将这个变量清零我们自写的东西就无法使用了。方法2优势是就算游戏保护清零也不影响,但是重写该函数又是一堆体力活。这里我选择使用方案1

定位DbgkDebugObjectType

既然要定位DbgkDebugObjectType,那么我们是需要一个信标的,回顾图2:

自建调试体系-重写NtDebugActiveProcess

发现DbgkOpenProcessDebugPort调用了DbgkDebugObjectType,而NtQueryInformationProcess会调用DbgkOpenProcessDebugPort。那么思路就很明确了:

  1. 通过MmGetSystemRoutineAddress找到NtQueryInformationProcess函数地址

  2. 暴力搜索NtQueryInformationProcess函数找到call DbgkOpenProcessDebugPort指令

  3. 计算DbgkOpenProcessDebugPort地址后在通过特征码定位到DbgkOpenProcessDebugPort

通过windbg双机调试发现DbgkOpenProcessDebugPort在NtQueryInformationProcess+0x19d7de处:

自建调试体系-重写NtDebugActiveProcess

查看NtQueryInformationProcess+0x19d7de-9:

自建调试体系-重写NtDebugActiveProcess

所以第一个特征码产生了:

(*(DWORD_PTR*)(Address + i)) == 0x244c8b48d5b60f41

但是显然仅仅靠这一个特征码是没办法定位出来的,我们在取call指令下面的8字节:

自建调试体系-重写NtDebugActiveProcess

完整定位代码为:

UNICODE_STRING funcName;RtlInitUnicodeString(&funcName, L"NtQueryInformationProcess");
DWORD_PTR Address = (DWORD_PTR)MmGetSystemRoutineAddress(&funcName);
for (size_t i = 0; i < 0x200000; i++){
    if ((*(DWORD_PTR*)(Address + i)) == 0x244c8b48d5b60f41 && (*(DWORD_PTR*)(Address + i +0xE)) == 0x0098248c8b48d88b)    {        Address = Address + i + 10;        break;    }}

计算DbgkOpenProcessDebugPort函数位置

直接贴代码把:

    DWORD32 DbgkOpenOffset = *((DWORD32*)Address);
    DWORD64 DbgkOpenFuncAddres = (Address + 4) + DbgkOpenOffset;

在获取到DbgkOpenFuncAddres的函数地址后

自建调试体系-重写NtDebugActiveProcess

通过查看汇编代码来找出特征码进行定位:

自建调试体系-重写NtDebugActiveProcess

特征码如下:

自建调试体系-重写NtDebugActiveProcess

最终获取到DbgkOpenProcessDebugPort的代码如下:

DWORD64 ObjectTypeOffset = 0;DWORD64 ObjectTypeAddress = 0;for (size_t i = 0; i < 0x200000; i++){

    if ((*(DWORD_PTR*)(DbgkOpenFuncAddres + i)) == 0x4828246c8840d8f6)    {        ObjectTypeOffset = (* (DWORD32*)(DbgkOpenFuncAddres + i + 10)) | ~(DWORD64)0xFFFFFFFF;        ObjectTypeAddress = (DbgkOpenFuncAddres + i + 0xE); //        break;    }}
ObjectTypeAddress = ObjectTypeAddress + ObjectTypeOffset;

ObjectTypeAddress 就是DbgkDebugObjectType的值了:

自建调试体系-重写NtDebugActiveProcess

KeGetPreviousMode

这个函数就是获取了Kthread结构体中的PreviousMode,所以直接上代码应该很好理解:

KPROCESSOR_MODE KeGetPreviousMode() {
    PKTHREAD pKthread = KeGetCurrentThread();    return (KPROCESSOR_MODE)((DWORD64)pKthread + 0x232); //Windows10 1909
}

本文链接

http://www.pentester.top/index.php/archives/109/

自建调试体系-重写NtDebugActiveProcess

END

自建调试体系-重写NtDebugActiveProcess
自建调试体系-重写NtDebugActiveProcess

看完记得点赞,关注哟,爱您!

请严格遵守网络安全法相关条例!此分享主要用于学习,切勿走上违法犯罪的不归路,一切后果自付!
在看你就赞赞我!
自建调试体系-重写NtDebugActiveProcess
自建调试体系-重写NtDebugActiveProcess
自建调试体系-重写NtDebugActiveProcess
自建调试体系-重写NtDebugActiveProcess
扫码关注我们
自建调试体系-重写NtDebugActiveProcess

扫码领hacker资料,常用工具,以及各种福利

自建调试体系-重写NtDebugActiveProcess

转载是一种动力 分享是一种美德

原文始发于微信公众号(Gamma实验室):自建调试体系-重写NtDebugActiveProcess

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年2月28日04:54:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   自建调试体系-重写NtDebugActiveProcesshttp://cn-sec.com/archives/1578975.html

发表评论

匿名网友 填写信息