-
Notifications
You must be signed in to change notification settings - Fork 328
Description
Hello,
I encountered a VMProtect protected executable that caused a BSOD inside HyperHide driver.
After looking at the minidump in WinDbg I found out that RtlEqualUnicodeString called from HookedNtQueryObject tried to dereference a null pointer which led to a crash.
Here is an excerpt output from WinDbg:
CONTEXT: fffff58743898fe0 -- (.cxr 0xfffff58743898fe0)
rax=0000000000000000 rbx=0000000000000016 rcx=0075006200650044
rdx=fffff58743899a60 rsi=0000000000000000 rdi=00007ff6d3675710
rip=fffff8047c279103 rsp=fffff58743899a28 rbp=fffff58743899b80
r8=0000000000000000 r9=0000000000000016 r10=fffff804cdbc9dd0
r11=fffff58743899a00 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl nz ac pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010212
nt!RtlEqualUnicodeString+0x43:
fffff804`7c279103 483908 cmp qword ptr [rax],rcx ds:002b:00000000`00000000=????????????????
Resetting default scope
BLACKBOXBSD: 1 (!blackboxbsd)
BLACKBOXNTFS: 1 (!blackboxntfs)
BLACKBOXPNP: 1 (!blackboxpnp)
BLACKBOXWINLOGON: 1
CUSTOMER_CRASH_COUNT: 1
PROCESS_NAME: NtQueryObjectTest.exe
STACK_TEXT:
fffff587`43899a28 fffff804`cdbc4d1d : 00000000`00000002 00000000`00000000 00007ff6`d3675710 ffffd289`00001000 : nt!RtlEqualUnicodeString+0x43
fffff587`43899a30 fffff804`7c012908 : 00000000`00000000 ffffd289`ac6e4080 00000000`0014fe58 fffff587`43899aa8 : HyperHideDrv!HookedNtQueryObject+0x9d [HookedFunctions.cpp @ 572]
fffff587`43899a90 00007ff8`7c98d6f4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x28
00000000`0014fe38 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x00007ff8`7c98d6f4
FAULTING_SOURCE_LINE: HookedFunctions.cpp
FAULTING_SOURCE_FILE: HookedFunctions.cpp
FAULTING_SOURCE_LINE_NUMBER: 572
FAULTING_SOURCE_CODE:
No source found for 'HookedFunctions.cpp'
SYMBOL_NAME: HyperHideDrv!HookedNtQueryObject+9d
MODULE_NAME: HyperHideDrv
IMAGE_NAME: HyperHideDrv.sys
STACK_COMMAND: .cxr 0xfffff58743898fe0 ; kb
BUCKET_ID_FUNC_OFFSET: 9d
FAILURE_BUCKET_ID: AV_HyperHideDrv!HookedNtQueryObject
OS_VERSION: 10.0.19041.1
BUILDLAB_STR: vb_release
OSPLATFORM_TYPE: x64
OSNAME: Windows 10
The line where string comparison happens:
HyperHide/HyperHideDrv/HookedFunctions.cpp
Line 572 in cbbf364
if (RtlEqualUnicodeString(&Type->TypeName, &DebugObject, FALSE) == TRUE) |
Seems like VMProtect zeros the TypeName.Buffer
pointer inside the OBJECT_TYPE_INFORMATION
before HyperHide has a chance to compare the strings.
I was able to replicate the issue with following PoC:
BYTE memory[0x1000];
void zeroStrPtr()
{
POBJECT_TYPE_INFORMATION info = (POBJECT_TYPE_INFORMATION)memory;
while (true)
{
info->TypeName.Buffer = 0;
}
}
int main()
{
HANDLE debugObject;
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, 0, 0, 0, 0);
if (NtCreateDebugObject(&debugObject, DEBUG_ALL_ACCESS, &oa, 0) >= 0)
{
std::thread(zeroStrPtr).detach();
NTSTATUS status = 0;
do
{
status = NtQueryObject(debugObject, ObjectTypeInformation, memory, sizeof(memory), 0);
}
while (status >= 0);
NtClose(debugObject);
}
std::cin.get();
return 0;
}
PoC uses two threads that share a pointer to a single OBJECT_TYPE_INFORMATION
structure. Main thread constantly calls NtQueryObject to fill the structure with data while the other one constantly clears the TypeName.Buffer
pointer in it. When debugging such PoC with HyperHide enabled it will sonner or later cause a BSOD. Otherwise it will loop indefinitely.
TitanHide seems to "fix" it by wrapping string comparison in __try / __except block and returning an error status to the caller but such approach can be used to detect that an anti-anti-debug tool is running.
I believe that the only proper solution is to allocate memory for a temporary OBJECT_TYPE_INFORMATION
struct, call the original NtQueryObject with pointer to that memory, modify it accordingly and then memcpy its contents back into memory block provided by the caller. Similar thing should be done with ReturnLength
param. This way a different user-mode thread won't be able to read and write data HyperHide driver hasn't finished working with.