mrexodia / TitanHide

Hiding kernel-driver for x86/x64.
MIT License
2.12k stars 421 forks source link

replace some Nt* APIs with Zw* version #77

Closed HyperSine closed 1 year ago

HyperSine commented 1 year ago

Hi, I noticed there is a failure log line in TitanHide.log when I started debugging a non-administrator process:

[TITANHIDE] Failed to undo HideThreadHideFromDebugger in running threads! Status = 0xC0000005
[TITANHIDE] HiderProcessData OK!

After some investigation, I found the bad NTSTATUS code was came from line 213, TitanHide/threadhidefromdbg.cpp: https://github.com/mrexodia/TitanHide/blob/6a5a68a2447ad9454adfcbd9390ec05b9dcef2d6/TitanHide/threadhidefromdbg.cpp#L212-L215

The argument &Size is a pointer to the variable on kernel stack. However, NtQuerySystemInformation sees PreviousMode is UserMode, because PreviousMode is set and remains unchanged since IRP_MJ_WRITE handling begins. An STATUS_ACCESS_VIOLATION is returned, bacause NtQuerySystemInformation sees a user-mode program is requesting to write something to kernel address space.

To fix it, Undocumented::NtQuerySystemInformation should be replaced with Undocumented::ZwQuerySystemInformation. Moreover, according to microsoft's guide, all Nt API calls in threadhidefromdbg.cpp shoud be replaced with Zw version.

Mattiwatti commented 1 year ago

Thanks for the fix. Merged.

Moreover, according to microsoft's guide, all Nt API calls in threadhidefromdbg.cpp shoud be replaced with Zw version.

Though replacing Nt calls with Zw is usually correct, I don't fully agree with this interpretation:

  1. Apart from the difference with regards to buffer probing, using Zw instead of Nt also means argument validation will be skipped. In the case where PreviousMode is UserMode, this can be a security risk, or in the case of TitanHide hooks, it can make TitanHide detectable. (To be clear, this is not an issue in your PR, only a theoretical one.)
  2. Calling Zw functions is technically speaking redundant in some contexts; most importantly DriverEntry. The reason for this is that DriverEntry is guaranteed to run in the context of the system process, i.e. PreviousMode will be KernelMode. Calling Zw instead of Nt in these contexts (e.g. in FindCrossThreadFlagsOffset which is called from DriverEntry) is essentially harmless but it adds a tiny overhead to each call.

Above are just my notes on this interpretation of the docs, they're not an issue with the code which is fine. Thanks again.