An open API service indexing awesome lists of open source software.

https://github.com/x0reaxeax/rtschecker

EFI RTS Hook Detector (Windows Kernel Driver)
https://github.com/x0reaxeax/rtschecker

detection efi hooks kernel-driver malware-research poc undocumented windows windows-kernel windows-x64

Last synced: 3 months ago
JSON representation

EFI RTS Hook Detector (Windows Kernel Driver)

Awesome Lists containing this project

README

          

# 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](#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:
```c
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:
```c
/**
* 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](https://standa-note.blogspot.com/2020/12/experiment-in-extracting-runtime.html)
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`.

```asm
; 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](#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](https://github.com/x0reaxeax/RTSChecker/tree/master/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)

```yara
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:
```c
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.
```asm
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:
```asm
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):
```asm
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:
```asm
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` -
```c
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:
```c
//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:
```c
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

```c
//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):

```c
PopFreeHiberContext
PopUnlockAfterSleepWorker
PopTransitionSystemPowerStateEx
NtSetSystemPowerState
```

## Sources, credits, references, special thanks
- [Zydis](https://zydis.re/), [Zydis - GitHub](https://github.com/zyantific/zydis) - Disassembler backing
- [EDK2](https://github.com/tianocore/edk2) - UEFI Spec
- [Satoshi's note](https://standa-note.blogspot.com/2020/12/experiment-in-extracting-runtime.html) - "Experiment in extracting runtime drivers on Windows"
- [Vergilius Project](https://www.vergiliusproject.com/) - Undocumented Windows kernel structures
- [gmh5225/ntoskrnl_file_collection](https://github.com/gmh5225/ntoskrnl_file_collection) - NTOSKRNL.EXE archive collection for pattern scanning