The driver resolves a pointer to an undocumented table HalEfiRuntimeServicesTable, which holds vaddr64 pointers to EFI Runtime Services.
This table is consistently (across multiple versions) loaded from nt!HalEfiSetEnvironmentVariable function in latest Windows versions (tested up to W11 24H2).
Previously, these functions were not present in NTOSKRNL.EXE, but instead mapped from HAL thunks (HAL.DLL) (I haven't looked much into this.)
For such old NTOSKRNL versions, this driver won't work (see Tested NTOSKRNL versions for more info).
More on this below.
Note: All disassembly + references are specific to NTOSKRNL.EXE v10.0.19041.5007 (Win10 22H2).
The HalEfiRuntimeServicesTable table differs from the UEFI-spec layout, which Windows internally defines like this:
00000000 struct _VIRTUAL_EFI_RUNTIME_SERVICES // sizeof=0x70
00000000 { // XREF: _KSR_FIRMWARE_INFORMATION/r
00000000 unsigned __int64 GetTime;
00000008 unsigned __int64 SetTime;
00000010 unsigned __int64 GetWakeupTime;
00000018 unsigned __int64 SetWakeupTime;
00000020 unsigned __int64 SetVirtualAddressMap;
00000028 unsigned __int64 ConvertPointer;
00000030 unsigned __int64 GetVariable;
00000038 unsigned __int64 GetNextVariableName;
00000040 unsigned __int64 SetVariable;
00000048 unsigned __int64 GetNextHighMonotonicCount;
00000050 unsigned __int64 ResetSystem;
00000058 unsigned __int64 UpdateCapsule;
00000060 unsigned __int64 QueryCapsuleCapabilities;
00000068 unsigned __int64 QueryVariableInfo;
00000070 };This UEFI-spec compatible table comes from struct _EFI_FIRMWARE_INFORMATION.
The HalEfiRuntimeServicesTable is basically another undocumented and stripped-down version of this table, and instead looks like this:
/**
* Windows uses internal implementations in place of some EFI RTS functions.
* Example:
* Get/SetWakeupTime are using internal implementation `HalpAcpiPmRegisterWrite` -
* The firmware implementations remain in physical memory, close to `(PHYSMEM) SetTime()`.
*/
typedef struct _HAL_EFI_RUNTIME_SERVICES_TABLE {
LPVOID GetTime; // 0x00
LPVOID SetTime; // 0x08
LPVOID ResetSystem; // 0x10
LPVOID GetVariable; // 0x18
LPVOID GetNextVariableName; // 0x20
LPVOID SetVariable; // 0x28
LPVOID UpdateCapsule; // 0x30
LPVOID QueryCapsuleCapabilities; // 0x38
LPVOID QueryVariableInfo; // 0x40
} HAL_EFI_RUNTIME_SERVICES_TABLE, *PHAL_EFI_RUNTIME_SERVICES_TABLE;I couldn't find any type references to this table in IDA or in general, therefore this structure is built upon a direct phys64 pointer comparison with dumped EFI_RUNTIME_SERVICES addresses from a UEFI application.
EDIT:
Of course, I found this article after doing manual comparison, super cool stuff, check it out - Experiment in extracting runtime drivers on Windows
This article also talks about resolving this pointer via a call to HalQuerySystemInformation, however, as stated in the article - "HalEfiRuntimeServicesBlock can be found with HalQuerySystemInformation() up until only 19H2."
A pointer to this table can be resolved in various ways, however in this driver, this project opts for grabbing the offset from a function called HalEfiSetEnvironmentVariable(), which seems to be a reliable way for grabbing this reference.
The only problem is that the function HalEfiSetEnvironmentVariable() is not exported, and therefore can't be retrieved via MmGetSystemRoutineAddress() function.
However, the wrapper function HalSetEnvironmentVariableEx() - which internally calls HalEfiSetEnvironmentVariable(), seems to be consistently exported across new NTOSKRNL versions.
In order to locate this CALL, a unique (one match per whole image) binary pattern / signature is used to search in a modest range, starting from HalSetEnvironmentVariableEx+0x0.
; Disassembly of NTOSKRNL.EXE - 10.0.19041.6328
;
HalSetEnvironmentVariableEx+11D 4D 8B CF mov r9, r15
HalSetEnvironmentVariableEx+120 4C 89 6C 24 20 mov [rsp+0A0h+var_80], r13
HalSetEnvironmentVariableEx+125 44 8B C7 mov r8d, edi
HalSetEnvironmentVariableEx+128 49 8B D4 mov rdx, r12
HalSetEnvironmentVariableEx+12B 49 8B CE mov rcx, r14
HalSetEnvironmentVariableEx+12E E8 55 00 00 00 call HalEfiSetEnvironmentVariable ; ─────────────────────────┐
; │
HalEfiSetEnvironmentVariable HalEfiSetEnvironmentVariable proc near ; CODE XREF: HalSetEnvironmentVariableEx+111↑p <──┘
HalEfiSetEnvironmentVariable ; HalSetEnvironmentVariableEx+12E↑p
HalEfiSetEnvironmentVariable 40 53 push rbx
HalEfiSetEnvironmentVariable+2 48 83 EC 30 sub rsp, 30h
HalEfiSetEnvironmentVariable+6 48 8B 05 BB D8 DB 00 mov rax, cs:HalEfiRuntimeServicesTable ; <- PHAL_EFI_RUNTIME_SERVICES_TABLEFrom the resolved pointer to HalEfiRuntimeServicesTable, the code starts analyzing physaddr32 pointers for hooks.
Note: See Another juicy reference for an additional interesting reference to HalEfiRuntimeServicesTable.
The hook analysis relies on a dumb approach of having a known trusted baseline physical address, around which the EFI RTS are supposed to be located in memory.
If the option RTS_MSB_FROM_TRUSTED_BASELINE in RiskScore.c is set to 0 (default), a selected EFI RuntimeService physaddr32 pointer will be used for this baseline address.
If the option is set to 1, this baseline address is instead retrieved from RTS_TRUSTED_BASELINE_ADDR, which can be specified by the user.
Currently, I have no sane click-to-load-and-ready way of resolving the address of gRT, so the code is limited to this demented approach, until I find something sane.
This baseline address is used for offset/distance checks across the EFI RTS pointers.
Zydis-backed detection for presence of simple inline hooks at the very beginning of the RTS functions' prologues.
Based on a combined analysis, a "risk score" is calculated.
Swapping gRT pointer = bypass
Inline hook after function prologue = bypass
All EFI RTS pointers hooked + no RTS_TRUSTED_BASELINE_ADDR set = bypass
A lot more, LOL
| Windows Version | NTOSKRNL Version |
|---|---|
| Win11 24H2 | 10.0.26100.2605 |
| Win10 22H2 | 10.0.19045.5854 10.0.19041.6328 10.0.19041.2006 |
Should work with any NTOSKRNL version starting from (>=) 10.0.18936.1000 (based on binary pattern search).
The latest NTOSKRNL version I could get my hands on was from Win11 25H2 - 10.0.26100.6584, with confirmed match from binary pattern search.
Signature for HallEfiSetEnvironentVariableSignature found/verified in following NTOSKRNL.EXE versions:
Matching Version List (click to expand)
| NTOSKRNL Version |
|---|
| 10.0.26100.6584 (Win11 25H2) |
| 10.0.26100.2605 |
| 10.0.22621.2861 |
| 10.0.22621.2506 |
| 10.0.22621.2428 |
| 10.0.22621.2283 |
| 10.0.22621.1848 |
| 10.0.22000.978 |
| 10.0.19041.6328 |
| 10.0.19045.5854 |
| 10.0.19041.3030 |
| 10.0.19041.2006 |
| 10.0.19631.1 |
| 10.0.18980.1 |
| 10.0.18965.1005 |
| 10.0.18950.1000 |
| 10.0.18936.1000 |
YARA binary pattern rule (click to expand)
rule CALL_HalEfiSetEnvironmentVariable_Universal {
strings:
$sig1 = {
4D 8B CF
4C 89 6C 24 20
44 8B C7
49 8B D4
49 8B CE
E8
}
condition:
($sig1)
}[RTSChecker] DriverEntry called
[RTSChecker] HalSetEnvironmentVariableEx address: 0xFFFFF8045B2BC8B0
[RTSChecker] Signature for call to HalSetEnvironmentVariableEx found at 0xFFFFF8045B2BC9E5
[RTSChecker] Call to HalSetEnvironmentVariableEx located at 0xFFFFF8045B2BC9F6
[RTSChecker] Relative address offset: 0x00007D3D
[RTSChecker] HalEfiSetEnvironmentVariable address: 0xFFFFF8045B2C4739
[RTSChecker] HAL_EFI_RUNTIME_SERVICES_TABLE located at 0xFFFFF8045BC01870
[RTSChecker] RTS Addresses:
* GetTime................... @ 0xFFFFF8045FA06191 [PHYS: 0x0FB74191]
* SetTime................... @ 0xFFFFF8045FA061A7 [PHYS: 0x0FB741A7]
* ResetSystem............... @ 0xFFFFF8045FA14163 [PHYS: 0x0FB82163]
* GetVariable............... @ 0xFFFFF8045FA0D175 [PHYS: 0x0FB7B175]
* GetNextVariableName....... @ 0xFFFFF8045FA0D1AF [PHYS: 0x0FB7B1AF]
* SetVariable............... @ 0xFFFFF8045FA0D28A [PHYS: 0x0FB7B28A]
* UpdateCapsule............. @ 0xFFFFF8045FA111EB [PHYS: 0x0FB7F1EB]
* QueryCapsuleCapabilities.. @ 0xFFFFF8045FA11297 [PHYS: 0x0FB7F297]
* QueryVariableInfo......... @ 0xFFFFF8045FA0D21D [PHYS: 0x0FB7B21D]
[RTSChecker] Analyzing RTS addresses for risk score...
[RTSChecker] MSB Trusted Baseline: 0x0f000000 -> MSB=0x0F SecondHighNibble=0x0
[RTSChecker] Majority nibble baseline (addresses): 0xB
[RTSChecker] RTS address analysis (count = 9)
* [00] Addr=0x0fb74191 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B74191 -> risk=0
* [00] Checking for inline hooks at [V] 0xFFFFF8045FA06191...
* [00] No inline hooks detected at [V] 0xFFFFF8045FA06191 [OK]
* [**] * * *
* [01] Addr=0x0fb741a7 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B741A7 -> risk=0
* [01] Checking for inline hooks at [V] 0xFFFFF8045FA061A7...
* [01] No inline hooks detected at [V] 0xFFFFF8045FA061A7 [OK]
* [**] * * *
* [02] Addr=0x0fb82163 Top12=0xFB8 MSB=0x0F Second=0xB8 HighNibble=0xB GapFromTrusted=0x00B82163 -> risk=0
* [02] Checking for inline hooks at [V] 0xFFFFF8045FA14163...
* [02] No inline hooks detected at [V] 0xFFFFF8045FA14163 [OK]
* [**] * * *
* [03] Addr=0x0fb7b175 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B175 -> risk=0
* [03] Checking for inline hooks at [V] 0xFFFFF8045FA0D175...
* [03] No inline hooks detected at [V] 0xFFFFF8045FA0D175 [OK]
* [**] * * *
* [04] Addr=0x0fb7b1af Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B1AF -> risk=0
* [04] Checking for inline hooks at [V] 0xFFFFF8045FA0D1AF...
* [04] No inline hooks detected at [V] 0xFFFFF8045FA0D1AF [OK]
* [**] * * *
* [05] Addr=0x0fb7b28a Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B28A -> risk=0
* [05] Checking for inline hooks at [V] 0xFFFFF8045FA0D28A...
* [05] No inline hooks detected at [V] 0xFFFFF8045FA0D28A [OK]
* [**] * * *
* [06] Addr=0x0fb7f1eb Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7F1EB -> risk=0
* [06] Checking for inline hooks at [V] 0xFFFFF8045FA111EB...
* [06] No inline hooks detected at [V] 0xFFFFF8045FA111EB [OK]
* [**] * * *
* [07] Addr=0x0fb7f297 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7F297 -> risk=0
* [07] Checking for inline hooks at [V] 0xFFFFF8045FA11297...
* [07] No inline hooks detected at [V] 0xFFFFF8045FA11297 [OK]
* [**] * * *
* [08] Addr=0x0fb7b21d Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B21D -> risk=0
* [08] Checking for inline hooks at [V] 0xFFFFF8045FA0D21D...
* [08] No inline hooks detected at [V] 0xFFFFF8045FA0D21D [OK]
* [**] * * *
[RTSChecker] Majority: MSB=0x0F MajorityNibble=0xB
[RTSChecker] Mismatches aggregated.....[OK]
[RTSChecker] Inline hooks analyzed.....[OK]
[RTSChecker] -> total_risk= 0 [OK]
[RTSChecker] RTS address analysis completed, no issues found.
[RTSChecker] DriverEntry called
[RTSChecker] HalSetEnvironmentVariableEx address: 0xFFFFF8045B2BC8B0
[RTSChecker] Signature for call to HalSetEnvironmentVariableEx found at 0xFFFFF8045B2BC9E5
[RTSChecker] Call to HalSetEnvironmentVariableEx located at 0xFFFFF8045B2BC9F6
[RTSChecker] Relative address offset: 0x00007D3D
[RTSChecker] HalEfiSetEnvironmentVariable address: 0xFFFFF8045B2C4739
[RTSChecker] HAL_EFI_RUNTIME_SERVICES_TABLE located at 0xFFFFF8045BC01870
[RTSChecker] RTS Addresses:
* GetTime................... @ 0xFFFFF8045FA06191 [PHYS: 0x0FB74191]
* SetTime................... @ 0xFFFFF8045FA061A7 [PHYS: 0x0FB741A7]
* ResetSystem............... @ 0xFFFFF8045FA14163 [PHYS: 0x0FB82163]
* GetVariable............... @ 0xFFFFF8045FA0D175 [PHYS: 0x0FB7B175]
* GetNextVariableName....... @ 0xFFFFF8045FA0D1AF [PHYS: 0x0FB7B1AF]
* SetVariable............... @ 0xFFFFF8045FA0D28A [PHYS: 0x0FB7B28A]
* UpdateCapsule............. @ 0xFFFFF8045B212D4A [PHYS: 0x02C12D4A]
* QueryCapsuleCapabilities.. @ 0xFFFFF8045FA11297 [PHYS: 0x0FB7F297]
* QueryVariableInfo......... @ 0xFFFFF8045FA0D21D [PHYS: 0x0FB7B21D]
[RTSChecker] Analyzing RTS addresses for risk score...
[RTSChecker] MSB Trusted Baseline: 0x0f000000 -> MSB=0x0F SecondHighNibble=0x0
[RTSChecker] Majority nibble baseline (addresses): 0xB
[RTSChecker] RTS address analysis (count = 9)
* [00] Addr=0x0fb74191 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B74191 -> risk=0
* [00] Checking for inline hooks at [V] 0xFFFFF8045FA06191...
* [00] No inline hooks detected at [V] 0xFFFFF8045FA06191 [OK]
* [**] * * *
* [01] Addr=0x0fb741a7 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B741A7 -> risk=0
* [01] Checking for inline hooks at [V] 0xFFFFF8045FA061A7...
* [01] No inline hooks detected at [V] 0xFFFFF8045FA061A7 [OK]
* [**] * * *
* [02] Addr=0x0fb82163 Top12=0xFB8 MSB=0x0F Second=0xB8 HighNibble=0xB GapFromTrusted=0x00B82163 -> risk=0
* [02] Checking for inline hooks at [V] 0xFFFFF8045FA14163...
* [02] No inline hooks detected at [V] 0xFFFFF8045FA14163 [OK]
* [**] * * *
* [03] Addr=0x0fb7b175 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B175 -> risk=0
* [03] Checking for inline hooks at [V] 0xFFFFF8045FA0D175...
* [03] No inline hooks detected at [V] 0xFFFFF8045FA0D175 [OK]
* [**] * * *
* [04] Addr=0x0fb7b1af Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B1AF -> risk=0
* [04] Checking for inline hooks at [V] 0xFFFFF8045FA0D1AF...
* [04] No inline hooks detected at [V] 0xFFFFF8045FA0D1AF [OK]
* [**] * * *
* [05] Addr=0x0fb7b28a Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B28A -> risk=0
* [05] Checking for inline hooks at [V] 0xFFFFF8045FA0D28A...
* [05] No inline hooks detected at [V] 0xFFFFF8045FA0D28A [OK]
* [**] * * *
* [06] Addr=0x02c12d4a Top12=0x2C1 MSB=0x02 Second=0xC1 HighNibble=0xC GapFromTrusted=0x0C3ED2B6 -> risk=65 [HOOK DETECTED]
* [06] Checking for inline hooks at [V] 0xFFFFF8045B212D4A...
* [06] No inline hooks detected at [V] 0xFFFFF8045B212D4A [OK]
* [**] * * *
* [07] Addr=0x0fb7f297 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7F297 -> risk=0
* [07] Checking for inline hooks at [V] 0xFFFFF8045FA11297...
* [07] No inline hooks detected at [V] 0xFFFFF8045FA11297 [OK]
* [**] * * *
* [08] Addr=0x0fb7b21d Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B21D -> risk=0
* [08] Checking for inline hooks at [V] 0xFFFFF8045FA0D21D...
* [08] No inline hooks detected at [V] 0xFFFFF8045FA0D21D [OK]
* [**] * * *
[RTSChecker] Majority: MSB=0x0F MajorityNibble=0xB
[RTSChecker] Mismatches aggregated.....[OK]
[RTSChecker] Inline hooks analyzed.....[OK]
[RTSChecker] -> total_risk= 65 [WARNING: HOOK DETECTED]
[RTSChecker] RTS address analysis completed, RISK SCORE: 65
[RTSChecker] DriverEntry called
[RTSChecker] HalSetEnvironmentVariableEx address: 0xFFFFF8045B2BC8B0
[RTSChecker] Signature for call to HalSetEnvironmentVariableEx found at 0xFFFFF8045B2BC9E5
[RTSChecker] Call to HalSetEnvironmentVariableEx located at 0xFFFFF8045B2BC9F6
[RTSChecker] Relative address offset: 0x00007D3D
[RTSChecker] HalEfiSetEnvironmentVariable address: 0xFFFFF8045B2C4739
[RTSChecker] HAL_EFI_RUNTIME_SERVICES_TABLE located at 0xFFFFF8045BC01870
[RTSChecker] RTS Addresses:
* GetTime................... @ 0xFFFFF8045FA06191 [PHYS: 0x0FB74191]
* SetTime................... @ 0xFFFFF8045FA061A7 [PHYS: 0x0FB741A7]
* ResetSystem............... @ 0xFFFFF8045FA14163 [PHYS: 0x0FB82163]
* GetVariable............... @ 0xFFFFF8045FA0D175 [PHYS: 0x0FB7B175]
* GetNextVariableName....... @ 0xFFFFF8045FA0D1AF [PHYS: 0x0FB7B1AF]
* SetVariable............... @ 0xFFFFF8045FA0D28A [PHYS: 0x0FB7B28A]
* UpdateCapsule............. @ 0xFFFFF8045FA111EB [PHYS: 0x0FB7F1EB]
* QueryCapsuleCapabilities.. @ 0xFFFFF8045FA11297 [PHYS: 0x0FB7F297]
* QueryVariableInfo......... @ 0xFFFFF8045FA0D21D [PHYS: 0x0FB7B21D]
[RTSChecker] Analyzing RTS addresses for risk score...
[RTSChecker] MSB Trusted Baseline: 0x0f000000 -> MSB=0x0F SecondHighNibble=0x0
[RTSChecker] Majority nibble baseline (addresses): 0xB
[RTSChecker] RTS address analysis (count = 9)
* [00] Addr=0x0fb74191 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B74191 -> risk=0
* [00] Checking for inline hooks at [V] 0xFFFFF8045FA06191...
* [00] No inline hooks detected at [V] 0xFFFFF8045FA06191 [OK]
* [**] * * *
* [01] Addr=0x0fb741a7 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B741A7 -> risk=0
* [01] Checking for inline hooks at [V] 0xFFFFF8045FA061A7...
* [01] No inline hooks detected at [V] 0xFFFFF8045FA061A7 [OK]
* [**] * * *
* [02] Addr=0x0fb82163 Top12=0xFB8 MSB=0x0F Second=0xB8 HighNibble=0xB GapFromTrusted=0x00B82163 -> risk=0
* [02] Checking for inline hooks at [V] 0xFFFFF8045FA14163...
* [02] MOV/LEA + JMP reg trampoline found at 0xFFFFF8045FA14163 ('lea rax, [0xFFFFF8045FA1417E]' + 'jmp rax')
* [02] Inline hook detected at [V] 0xFFFFF8045FA14163 -> risk +55 [CRITICAL]
* [**] * * *
* [03] Addr=0x0fb7b175 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B175 -> risk=0
* [03] Checking for inline hooks at [V] 0xFFFFF8045FA0D175...
* [03] No inline hooks detected at [V] 0xFFFFF8045FA0D175 [OK]
* [**] * * *
* [04] Addr=0x0fb7b1af Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B1AF -> risk=0
* [04] Checking for inline hooks at [V] 0xFFFFF8045FA0D1AF...
* [04] No inline hooks detected at [V] 0xFFFFF8045FA0D1AF [OK]
* [**] * * *
* [05] Addr=0x0fb7b28a Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B28A -> risk=0
* [05] Checking for inline hooks at [V] 0xFFFFF8045FA0D28A...
* [05] No inline hooks detected at [V] 0xFFFFF8045FA0D28A [OK]
* [**] * * *
* [06] Addr=0x0fb7f1eb Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7F1EB -> risk=0
* [06] Checking for inline hooks at [V] 0xFFFFF8045FA111EB...
* [06] No inline hooks detected at [V] 0xFFFFF8045FA111EB [OK]
* [**] * * *
* [07] Addr=0x0fb7f297 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7F297 -> risk=0
* [07] Checking for inline hooks at [V] 0xFFFFF8045FA11297...
* [07] No inline hooks detected at [V] 0xFFFFF8045FA11297 [OK]
* [**] * * *
* [08] Addr=0x0fb7b21d Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B21D -> risk=0
* [08] Checking for inline hooks at [V] 0xFFFFF8045FA0D21D...
* [08] No inline hooks detected at [V] 0xFFFFF8045FA0D21D [OK]
* [**] * * *
[RTSChecker] Majority: MSB=0x0F MajorityNibble=0xB
[RTSChecker] Mismatches aggregated.....[OK]
[RTSChecker] Inline hooks analyzed.....[OK]
[RTSChecker] -> total_risk= 55 [WARNING: HOOK DETECTED]
[RTSChecker] RTS address analysis completed, RISK SCORE: 55
[RTSChecker] DriverEntry called
[RTSChecker] HalSetEnvironmentVariableEx address: 0xFFFFF8045B2BC8B0
[RTSChecker] Signature for call to HalSetEnvironmentVariableEx found at 0xFFFFF8045B2BC9E5
[RTSChecker] Call to HalSetEnvironmentVariableEx located at 0xFFFFF8045B2BC9F6
[RTSChecker] Relative address offset: 0x00007D3D
[RTSChecker] HalEfiSetEnvironmentVariable address: 0xFFFFF8045B2C4739
[RTSChecker] HAL_EFI_RUNTIME_SERVICES_TABLE located at 0xFFFFF8045BC01870
[RTSChecker] RTS Addresses:
* GetTime................... @ 0xFFFFF8045FA06191 [PHYS: 0x0FB74191]
* SetTime................... @ 0xFFFFF8045FA061A7 [PHYS: 0x0FB741A7]
* ResetSystem............... @ 0xFFFFF8045FA14163 [PHYS: 0x0FB82163]
* GetVariable............... @ 0xFFFFF8045FA0D175 [PHYS: 0x0FB7B175]
* GetNextVariableName....... @ 0xFFFFF8045FA0D1AF [PHYS: 0x0FB7B1AF]
* SetVariable............... @ 0xFFFFF8045FA0D28A [PHYS: 0x0FB7B28A]
* UpdateCapsule............. @ 0xFFFFF8045B212D4A [PHYS: 0x02C12D4A]
* QueryCapsuleCapabilities.. @ 0xFFFFF8045FA11297 [PHYS: 0x0FB7F297]
* QueryVariableInfo......... @ 0xFFFFF8045FA0D21D [PHYS: 0x0FB7B21D]
[RTSChecker] Analyzing RTS addresses for risk score...
[RTSChecker] MSB Trusted Baseline: 0x0f000000 -> MSB=0x0F SecondHighNibble=0x0
[RTSChecker] Majority nibble baseline (addresses): 0xB
[RTSChecker] RTS address analysis (count = 9)
* [00] Addr=0x0fb74191 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B74191 -> risk=0
* [00] Checking for inline hooks at [V] 0xFFFFF8045FA06191...
* [00] No inline hooks detected at [V] 0xFFFFF8045FA06191 [OK]
* [**] * * *
* [01] Addr=0x0fb741a7 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B741A7 -> risk=0
* [01] Checking for inline hooks at [V] 0xFFFFF8045FA061A7...
* [01] No inline hooks detected at [V] 0xFFFFF8045FA061A7 [OK]
* [**] * * *
* [02] Addr=0x0fb82163 Top12=0xFB8 MSB=0x0F Second=0xB8 HighNibble=0xB GapFromTrusted=0x00B82163 -> risk=0
* [02] Checking for inline hooks at [V] 0xFFFFF8045FA14163...
* [02] CALL found at 0xFFFFF8045FA14163 ('call 0xFFFFF8043E4F0052' | len=5)
* [02] Inline hook detected at [V] 0xFFFFF8045FA14163 -> risk +55 [CRITICAL]
* [**] * * *
* [03] Addr=0x0fb7b175 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B175 -> risk=0
* [03] Checking for inline hooks at [V] 0xFFFFF8045FA0D175...
* [03] No inline hooks detected at [V] 0xFFFFF8045FA0D175 [OK]
* [**] * * *
* [04] Addr=0x0fb7b1af Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B1AF -> risk=0
* [04] Checking for inline hooks at [V] 0xFFFFF8045FA0D1AF...
* [04] No inline hooks detected at [V] 0xFFFFF8045FA0D1AF [OK]
* [**] * * *
* [05] Addr=0x0fb7b28a Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B28A -> risk=0
* [05] Checking for inline hooks at [V] 0xFFFFF8045FA0D28A...
* [05] No inline hooks detected at [V] 0xFFFFF8045FA0D28A [OK]
* [**] * * *
* [06] Addr=0x02c12d4a Top12=0x2C1 MSB=0x02 Second=0xC1 HighNibble=0xC GapFromTrusted=0x0C3ED2B6 -> risk=65 [HOOK DETECTED]
* [06] Checking for inline hooks at [V] 0xFFFFF8045B212D4A...
* [06] No inline hooks detected at [V] 0xFFFFF8045B212D4A [OK]
* [**] * * *
* [07] Addr=0x0fb7f297 Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7F297 -> risk=0
* [07] Checking for inline hooks at [V] 0xFFFFF8045FA11297...
* [07] No inline hooks detected at [V] 0xFFFFF8045FA11297 [OK]
* [**] * * *
* [08] Addr=0x0fb7b21d Top12=0xFB7 MSB=0x0F Second=0xB7 HighNibble=0xB GapFromTrusted=0x00B7B21D -> risk=0
* [08] Checking for inline hooks at [V] 0xFFFFF8045FA0D21D...
* [08] No inline hooks detected at [V] 0xFFFFF8045FA0D21D [OK]
* [**] * * *
[RTSChecker] Majority: MSB=0x0F MajorityNibble=0xB
[RTSChecker] Mismatches aggregated.....[OK]
[RTSChecker] Inline hooks analyzed.....[OK]
[RTSChecker] -> total_risk= 120 [CRITICAL: MULTIPLE HOOKS DETECTED]
[RTSChecker] RTS address analysis completed, RISK SCORE: 120
Looking at xrefs for HalEfiRuntimeServicesBlock, the following function pops up:
NTSTATUS HalInitializeOnResume(
PMDL FirmwareRuntimeInformationMdl,
PVOID FirmwareRuntimeInformationVa // (mapped VA)
);The reason for initially choosing to look into this function is the fact that it can be relatively easily resolved via MmGetSystemRoutineAddress, although not directly.
The actual export is a function called HalInitializeOnResume(), which due to being just a small wrapper, can be easily walked to resolve HalInitializeOnResume() address.
HalInitializeOnResume ; __int64 __fastcall HalInitializeOnResume(PMDL FirmwareRuntimeInformationMdl, PVOID FirmwareRuntimeInformationVa)
HalInitializeOnResume public HalInitializeOnResume
HalInitializeOnResume HalInitializeOnResume proc near ; CODE XREF: PopHiberCheckResume+109↓p
HalInitializeOnResume ; DATA XREF: .pdata:00000000000DCFA8↑o
HalInitializeOnResume 48 83 EC 28 sub rsp, 28h ; Integer Subtraction
HalInitializeOnResume+4 33 C0 xor eax, eax ; Logical Exclusive OR
HalInitializeOnResume+6 38 05 4C D1 8B 00 cmp byte ptr cs:HalpIommuDeviceCreatedList.ArcWindowsSysPartName, al ; Compare Two Operands
HalInitializeOnResume+C 75 06 jnz short loc_38CD44 ; Jump if Not Zero (ZF=0)
HalInitializeOnResume+E
HalInitializeOnResume+E loc_38CD3E: ; CODE XREF: HalInitializeOnResume+19↓j
HalInitializeOnResume+E 48 83 C4 28 add rsp, 28h ; Add
HalInitializeOnResume+12 C3 retn ; Return Near from Procedure
HalInitializeOnResume+12 ; ---------------------------------------------------------------------------
HalInitializeOnResume+13 CC align 4
HalInitializeOnResume+14
HalInitializeOnResume+14 loc_38CD44: ; CODE XREF: HalInitializeOnResume+C↑j
HalInitializeOnResume+14 E8 8B 83 13 00 call HalpEfiInitializeOnResume ; Call Procedure
HalInitializeOnResume+19 EB F3 jmp short loc_38CD3E ; Jump
HalInitializeOnResume+19 HalInitializeOnResume endpThe function prototype had to be manually fixed, since IDA doesn't see any parameter passing inside this wrapper.
By looking at HalpEfiInitializeOnResume(), it is immediately clear that there are in fact 2 parameters passed and checked for:
HalpEfiInitializeOnResume HalpEfiInitializeOnResume proc near ; CODE XREF: HalInitializeOnResume:loc_38CD44↑p
HalpEfiInitializeOnResume ; DATA XREF: .pdata:00000000000F01CC↑o
HalpEfiInitializeOnResume
HalpEfiInitializeOnResume var_18 = qword ptr -18h
HalpEfiInitializeOnResume
HalpEfiInitializeOnResume 40 53 push rbx
HalpEfiInitializeOnResume+2 48 83 EC 30 sub rsp, 30h ; Integer Subtraction
HalpEfiInitializeOnResume+6 48 8B DA mov rbx, rdx
HalpEfiInitializeOnResume+9 48 85 C9 test rcx, rcx ; Logical Compare
HalpEfiInitializeOnResume+C 0F 84 2E 01 00 00 jz loc_4C5214 ; Jump if Zero (ZF=1)
HalpEfiInitializeOnResume+12 48 85 D2 test rdx, rdx ; Logical Compare
HalpEfiInitializeOnResume+15 0F 84 25 01 00 00 jz loc_4C5214 ; Jump if Zero (ZF=1)
HalpEfiInitializeOnResume+1B 81 79 28 90 00 00 00 cmp dword ptr [rcx+28h], 90h ; Compare Two OperandsThese come all the way from a function called PopHiberCheckResume(), and an undocumented structure struct _POP_HIBER_CONTEXT (this global structure unfortunately doesn't stay populated after use):
PopHiberCheckResume+40 48 8B 2D 09 7B 28 00 mov rbp, cs:HiberContext
PopHiberCheckResume+47 33 DB xor ebx, ebx ; Logical Exclusive OR
PopHiberCheckResume+49 48 8B B5 C8 00 00 00 mov rsi, [rbp+0C8h]
PopHiberCheckResume+50 39 1E cmp [rsi], ebx ; Compare Two Operands
PopHiberCheckResume+52 0F 84 93 01 00 00 jz loc_99B91B ; Jump if Zero (ZF=1)And later passed to HalInitializeOnResume(), if a single condition (if ( *(DWORD32*)([HiberContext+0xC8]) != 0 )) is passed:
PopHiberCheckResume+FB 48 8B 95 E8 00 00 00 mov rdx, [rbp+0E8h] ; lpRtsBlock
PopHiberCheckResume+102 48 8B 8D E0 00 00 00 mov rcx, [rbp+0E0h] ; a1
PopHiberCheckResume+109 E8 F2 14 9F FF call HalInitializeOnResume ; Call ProcedureIf we check the definition of HiberContext structure and the offset 0xC8 -
struct PO_MEMORY_IMAGE* MemoryImage; //0xc8We can see that this this reads sizeof(DWORD32) bytes from struct PO_MEMORY_IMAGE* MemoryImage;, i.e. the first DWORD32 member, which is:
//0x3e0 bytes (sizeof)
struct PO_MEMORY_IMAGE
{
ULONG Signature; //0x0
//...
}If the signature is 0, HAL args are never setup, and the execution jumps to the function's epilogue.
It might be worth noting that a few checks are evaluated. These appear to be non-blocking if KdDebugger isn't present, otherwise, they can land on INT3 breakpoints:
- Check for Hypervisor presence
(HvlHypervisorConnected)-> do HVL restore/config -> rejoin and continue.` KdDebuggerEnabled / KdEventLoggingEnabled / KdPitchDebuggerchecks may clear/reinit KD -> if KD still present +BRKPmagic present -> oneINT3assert, then rejoin.(PopSimulate & 0x40000000)->INT3-> rejoin.
The HiberContext structure contains useful stuff, such as:
struct PO_MEMORY_IMAGE* MemoryImage; //0xc8
struct _MDL* FirmwareRuntimeInformationMdl; //0xe0
VOID* FirmwareRuntimeInformationVa; //0xe8 (PHAL_EFI_RUNTIME_SERVICES_TABLE)The PO_MEMORY_IMAGE structure contains yet even more juicy stuff:
struct PO_MEMORY_IMAGE
//0x3e0 bytes (sizeof)
struct PO_MEMORY_IMAGE
{
ULONG Signature; //0x0
ULONG ImageType; //0x4
ULONG CheckSum; //0x8
ULONG LengthSelf; //0xc
ULONGLONG PageSelf; //0x10
ULONG PageSize; //0x18
union _LARGE_INTEGER SystemTime; //0x20
ULONGLONG InterruptTime; //0x28
ULONGLONG FeatureFlags; //0x30
UCHAR HiberFlags; //0x38
UCHAR HiberSimulateFlags; //0x39
UCHAR spare[2]; //0x3a
ULONG NoHiberPtes; //0x3c
ULONGLONG HiberVa; //0x40
ULONG NoFreePages; //0x48
ULONG FreeMapCheck; //0x4c
ULONG WakeCheck; //0x50
ULONGLONG NumPagesForLoader; //0x58
ULONGLONG FirstSecureRestorePage; //0x60
ULONGLONG FirstBootRestorePage; //0x68
ULONGLONG FirstKernelRestorePage; //0x70
ULONGLONG FirstChecksumRestorePage; //0x78
ULONGLONG NoChecksumEntries; //0x80
struct _PO_HIBER_PERF PerfInfo; //0x88
ULONG FirmwareRuntimeInformationPages; //0x280
ULONGLONG FirmwareRuntimeInformation[1]; //0x288
ULONG SpareUlong; //0x290
ULONG NoBootLoaderLogPages; //0x294
ULONGLONG BootLoaderLogPages[24]; //0x298
ULONG NotUsed; //0x358
ULONG ResumeContextCheck; //0x35c
ULONG ResumeContextPages; //0x360
UCHAR Hiberboot; //0x364
UCHAR SecureLaunched; //0x365
UCHAR SecureBoot; //0x366
ULONGLONG HvPageTableRoot; //0x368
ULONGLONG HvEntryPoint; //0x370
ULONGLONG HvReservedTransitionAddress; //0x378
ULONGLONG HvReservedTransitionAddressSize; //0x380
ULONGLONG BootFlags; //0x388
ULONGLONG RestoreProcessorStateRoutine; //0x390
ULONGLONG HighestPhysicalPage; //0x398
ULONGLONG BitlockerKeyPfns[4]; //0x3a0
ULONG HardwareSignature; //0x3c0
union _LARGE_INTEGER SMBiosTablePhysicalAddress; //0x3c8
ULONG SMBiosTableLength; //0x3d0
UCHAR SMBiosMajorVersion; //0x3d4
UCHAR SMBiosMinorVersion; //0x3d5
UCHAR HiberResumeXhciHandoffSkip; //0x3d6
UCHAR InitializeUSBCore; //0x3d7
UCHAR ValidUSBCoreId; //0x3d8
UCHAR USBCoreId; //0x3d9
UCHAR SkipMemoryMapValidation; //0x3da
};Lot of good shit, but unfortunately, as said earlier, the HiberContext structure is freed/zeroed out after use via PopFreeHiberContext():
(call stack):
PopFreeHiberContext
PopUnlockAfterSleepWorker
PopTransitionSystemPowerStateEx
NtSetSystemPowerState- Zydis, Zydis - GitHub - Disassembler backing
- EDK2 - UEFI Spec
- Satoshi's note - "Experiment in extracting runtime drivers on Windows"
- Vergilius Project - Undocumented Windows kernel structures
- gmh5225/ntoskrnl_file_collection - NTOSKRNL.EXE archive collection for pattern scanning