Skip to content

x0reaxeax/RTSChecker

Repository files navigation

RTSChecker - PoC Windows Kernel Driver for detecting simple hooks on EFI Runtime Services

How it works

Resolving pointers

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).

HalEfiRuntimeServicesTable layout

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_TABLE

From 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.

Hook analysis

1. Offset/distance checks

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.

2. Inline hook detection

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.

Limitations and bypasses

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

Tested NTOSKRNL versions

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)
}

Tests

Test with no hooks

[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.

Test with single pointerswap hook

[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

Test with inline hook

[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

Test with multiple hooks (combined)

[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

Extra stuff for read

Another juicy reference

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 endp

The 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 Operands

These 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 Procedure

If we check the definition of HiberContext structure and the offset 0xC8 -

struct PO_MEMORY_IMAGE* MemoryImage;                                    //0xc8

We 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:

  1. Check for Hypervisor presence (HvlHypervisorConnected) -> do HVL restore/config -> rejoin and continue.`
  2. KdDebuggerEnabled / KdEventLoggingEnabled / KdPitchDebugger checks may clear/reinit KD -> if KD still present + BRKP magic present -> one INT3 assert, then rejoin.
  3. (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

Sources, credits, references, special thanks

Releases

No releases published

Packages

No packages published

Languages