点击上方“蓝字”,关注更多精彩
前言
最近学习游戏方面的逆向,调试器无法正常附加,所以需要自建调试体系来进行调试。
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
所以现在我们有两个思路:
-
直接通过特征码暴力定位该全局变量的地址
-
通过重写DbgkpInitializePhase0来进行初始化该变量。
两个方法各有优势和弊端,方法1优势就是省时省力,弊端是如果游戏保护将这个变量清零我们自写的东西就无法使用了。方法2优势是就算游戏保护清零也不影响,但是重写该函数又是一堆体力活。这里我选择使用方案1
定位DbgkDebugObjectType
既然要定位DbgkDebugObjectType,那么我们是需要一个信标的,回顾图2:
发现DbgkOpenProcessDebugPort调用了DbgkDebugObjectType,而NtQueryInformationProcess会调用DbgkOpenProcessDebugPort。那么思路就很明确了:
-
通过MmGetSystemRoutineAddress找到NtQueryInformationProcess函数地址
-
暴力搜索NtQueryInformationProcess函数找到call DbgkOpenProcessDebugPort指令
-
计算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)) ==
0x244c8b48d5b60f
41 && (*(DWORD_PTR*)(Address + i +
0xE
)) ==
0x0098248c8b48d
88b)
{
Address = Address + i +
10
;
break
;
}
}
计算DbgkOpenProcessDebugPort函数位置
直接贴代码把:
DWORD32 DbgkOpenOffset = *((DWORD32*)Address); DWORD64 DbgkOpenFuncAddres = (Address + 4) + DbgkOpenOffset;
在获取到DbgkOpenFuncAddres的函数地址后
通过查看汇编代码来找出特征码进行定位:
特征码如下:
最终获取到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的值了:
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/
END
看完记得点赞,关注哟,爱您!
扫码领hacker资料,常用工具,以及各种福利
原文始发于微信公众号(Gamma实验室):自建调试体系-重写NtDebugActiveProcess
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论