前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >自建调试体系-重写NtDebugActiveProcess

自建调试体系-重写NtDebugActiveProcess

作者头像
Gamma实验室
发布2023-09-13 18:50:43
2900
发布2023-09-13 18:50:43
举报
文章被收录于专栏:Gamma安全实验室Gamma安全实验室
前言

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

NtDebugActiveProcess重写

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

代码语言:javascript
复制
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中也是一个全局变量

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

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

代码语言:javascript
复制
DbgkpInitializePhase0

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

  1. 直接通过特征码暴力定位该全局变量的地址
  2. 通过重写DbgkpInitializePhase0来进行初始化该变量。

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

定位DbgkDebugObjectType

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

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

  1. 通过MmGetSystemRoutineAddress找到NtQueryInformationProcess函数地址
  2. 暴力搜索NtQueryInformationProcess函数找到call DbgkOpenProcessDebugPort指令
  3. 计算DbgkOpenProcessDebugPort地址后在通过特征码定位到DbgkOpenProcessDebugPort

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

查看NtQueryInformationProcess+0x19d7de-9:

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

代码语言:javascript
复制
(*(DWORD_PTR*)(Address + i)) == 0x244c8b48d5b60f41

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

完整定位代码为:

代码语言:javascript
复制
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函数位置

直接贴代码把:

代码语言:javascript
复制
    DWORD32 DbgkOpenOffset = *((DWORD32*)Address);
    DWORD64 DbgkOpenFuncAddres = (Address + 4) + DbgkOpenOffset;

在获取到DbgkOpenFuncAddres的函数地址后

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

特征码如下:

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

代码语言:javascript
复制
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的值了:

KeGetPreviousMode

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

代码语言:javascript
复制
KPROCESSOR_MODE KeGetPreviousMode() {

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

}

本文链接

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-02-27 23:33:21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Gamma安全实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • NtDebugActiveProcess重写
  • 定位DbgkDebugObjectType
  • 计算DbgkOpenProcessDebugPort函数位置
  • KeGetPreviousMode
  • 本文链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档