The driver has a vulnerable IOCTL that allows us to validate specific handle by calling ExEnumHandleTable eventually in driver context.
This function called by the encryption-dedicated IOCTL handler as follows:
PAGE:FFFFF800188CD2D1 cmp ebx, 81094000h
PAGE:FFFFF800188CD2D7 jnz loc_FFFFF800188CD62B
PAGE:FFFFF800188CD2DD mov rax, [rsp+30h]
PAGE:FFFFF800188CD2E2 mov ecx, [rax]
PAGE:FFFFF800188CD2E4 call sub_FFFFF800188C35B0 // <- [SearchForHandleTableByProcessId_FUN_000135b0]
As you can see the IOCTL code is 0x81094000.
Also the function SearchForHandleTableByProcessId_FUN_000135b0 is like:
ulonglong SearchForHandleTableByProcessId_FUN_000135b0
(uint param_1_ProcessId,undefined8 param_2,undefined8 param_3,undefined8 param_4
)
{
int iVar1;
ulonglong local_res10 [3];
local_res10[0] = 0;
iVar1 = PsLookupProcessByProcessId
((ulonglong)param_1_ProcessId,local_res10,param_3,param_4,0xffffffff);
if (iVar1 < 0) {
local_res10[0] = LookupHandleTable_FUN_000134a8(param_1_ProcessId);
}
else {
ObfDereferenceObject(local_res10[0]);
}
return (ulonglong)(local_res10[0] != 0);
}
As we can see there is a some weird code there...
Especially in if (iVar1 < 0) which if the PsLookupProcessByProcessId failure, it calls LookupHandleTable_FUN_000134a8 as follows:
ulonglong LookupHandleTable_FUN_000134a8(uint param_1_ProcessId)
{
char cVar1;
ulonglong uVar2_HandleTableEntry;
ulonglong uVar2;
ulonglong uVar3_ReturnHandleTableEntry;
uVar2_HandleTableEntry = ExEnumHandleTable_FUN_0001556c(param_1_ProcessId);
cVar1 = MmIsAddressValid(uVar2_HandleTableEntry);
if ((((cVar1 == '\0') || (uVar2_HandleTableEntry == 0xffffffffffffffff)) ||
(uVar2_HandleTableEntry == 0)) ||
((cVar1 = MmIsAddressValid(uVar2_HandleTableEntry - 0x30), cVar1 == '\0' ||
(*(longlong *)(uVar2_HandleTableEntry - 0x30) == 0)))) {
uVar3_ReturnHandleTableEntry = 0;
}
else {
uVar2 = UnknownProbe_FUN_00013528(uVar2_HandleTableEntry);
uVar3_ReturnHandleTableEntry = 0;
if ((char)uVar2 != '\x01') {
uVar3_ReturnHandleTableEntry = uVar2_HandleTableEntry;
}
}
return uVar3_ReturnHandleTableEntry;
}
I've debugged the driver by hooking ExEnumHandleTable and some another major function, and confirmed it's correct.
[DBGPROT] HookedPsLookupProcessByProcessId Called with 6195 and FFFFF88665811378 -> Result: 0xC000000B
[DBGPROT] mhyprot Called MmGetSystemRoutineAddress With PsLookupProcessByProcessId
[DBGPROT] HookedMmIsAddressValid Called with FFFFF806449EE910 -> Result: 1
... (loop about 4000-5000 times)
[DBGPROT] HookedMmIsAddressValid Called with FFFFC90F5AB79080 -> Result: 1
[DBGPROT] HookedMmIsAddressValid Called with FFFFC90F5A2CA380 -> Result: 1
[DBGPROT] HookedMmIsAddressValid Called with FFFFC90F5A2CA668 -> Result: 1
[DBGPROT] HookedExEnumHandleTable Called With FFFFF80649C65730 and 0 -> 0 // Here
[DBGPROT] HookedMmIsAddressValid Called with 0000000000000000 -> Result: 0
[DBGPROT] HookedExAllocatePool Called With 0 and 0xC // encryption behavior, freeing crypt buffer
The prototype code is:
typedef struct _MHYPROT_ENUM_HANDLE_TABLE_REQUEST
{
uint64_t response;
uint32_t process_id;
} MHYPROT_ENUM_HANDLE_TABLE_REQUEST, * PMHYPROT_ENUM_HANDLE_TABLE_REQUEST;
MHYPROT_ENUM_HANDLE_TABLE_REQUEST payload;
payload.process_id = process_id + 999; // pass the wrong process id to validate it
encrypt_payload(&payload, sizeof(payload));
request_ioctl(0x81094000, &payload, sizeof(payload));
...
the result is exact same as what I expected, the if statement must be returned with failure.
I still do not know what is a purpose of this function but I guess it's a kind of a way to check out that the all of processes do not have the game's process handle.
I'll drop some related function here:
Also if there are any updates, I'll re-write this document later.
ulonglong ExEnumHandleTable_FUN_0001556c(uint param_1_ProcessId)
{
ulonglong uVar1_Result;
ulonglong *puVar2_Context;
code *pcVar1_FuncEnumeratorRoutine;
if ((DAT_0001a6d0 == 0) && (DAT_0001a6d0 = FUN_00015954(), DAT_0001a6d0 == 0)) {
return 0;
}
puVar2_Context = (ulonglong *)ExAllocatePool(1);
pcVar1_FuncEnumeratorRoutine = ProbeForThreadHandle?_FUN_00015680;
*puVar2_Context = (ulonglong)param_1_ProcessId;
if (DAT_0001a748 != 0x3d) {
pcVar1_FuncEnumeratorRoutine = EnumerateHandleRoutine_FUN_00015730;
}
ExEnumHandleTable(DAT_0001a6d0,pcVar1_FuncEnumeratorRoutine,puVar2_Context,0);
/* PHANDLE_TABLE_ENTRY */
uVar1_Result = puVar2_Context[1];
ExFreePoolWithTag(puVar2_Context,0);
return uVar1_Result;
}
ulonglong EnumerateHandleRoutine_FUN_00015730
(longlong param_1,ulonglong *param_2,uint param_3,uint *param_4)
{
ulonglong uVar1;
ulonglong uVar2;
uVar1 = ProbeForThreadHandle?_FUN_00015680(param_2,param_3,param_4);
LOCK();
uVar2 = *param_2;
*param_2 = *param_2 + 1;
uVar2 = ExfUnblockPushLock(param_1 + 0x30,0,uVar2);
return uVar2 & 0xffffffffffffff00 | uVar1 & 0xff;
}