Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Access violation in HookedNtQueryObject triggered by race condition #60

Open
jagger-meister opened this issue Jan 18, 2025 · 0 comments

Comments

@jagger-meister
Copy link

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:

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant