专栏名称: Gamma实验室
Gamma实验室是专注于网络安全攻防研究的实验室,不定时向外输出技术文章以及自主研发安全工具,技术输出不限于:渗透,内网,红队,免杀,病毒分析,逆向,ctfwp等,实验室只用于技术研究,一切违法犯罪与实验室无关!
51好读  ›  专栏  ›  Gamma实验室

自建调试体系-重写NtDebugActiveProcess

Gamma实验室  · 公众号  ·  · 2023-02-27 23:33

正文

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


前言

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

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中也是一个全局变量

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

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

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:

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

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

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

完整定位代码为:

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的函数地址后

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







请到「今天看啥」查看全文