Fix: Stop trying to execute LdrLoadDll from unmapped NTDLLs

Decouples native_load_library from the global NTDLL state. Calling LdrLoadDll on a raw disk image obviously segfaults because the loader locks aren't initialized. Always walking the PEB for this fixes it.
This commit is contained in:
sibouzitoun
2026-06-22 20:17:44 +01:00
parent 5e26626615
commit 04be820979
6 changed files with 12 additions and 26 deletions
+4
View File
@@ -25,3 +25,7 @@ SindriKit is a Windows evasion toolkit written in C. This first release provides
- **Strict Compilation:** The build system enforces `/W4 /WX` to catch implicit truncations and pointer mismatches at compile time.
- **SILENT Tier:** Compiling with `SND_ENABLE_DEBUG=OFF` removes all state-machine prints and error contexts, ensuring no framework strings end up in the `.rdata` section.
- **CRT Independence:** The `SND_CRTLESS=ON` flag builds the engine without the C Standard Library, relying on compiler-intrinsic fallbacks.
## [1.0.1] - 2026-06-22
### Fixed
- **Loaders:** Fixed an access violation in `native_load_library` caused by resolving `LdrLoadDll` from an unmapped/disk NTDLL image. The loader now strictly resolves via the active PEB to maintain loader lock integrity.
+1 -1
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(SindriKit VERSION 1.0.0 LANGUAGES C ASM_MASM)
project(SindriKit VERSION 1.0.1 LANGUAGES C ASM_MASM)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded")
option(SND_ENABLE_DEBUG "Enable debug prints and tracking" OFF)
+1 -1
View File
@@ -57,7 +57,7 @@ When operating in native mode, SindriKit needs a clean `ntdll.dll` to extract SS
At boot, the Session Manager (`smss.exe`) creates pre-mapped, read-only section objects for core system DLLs under the `\KnownDlls` Object Manager directory. These sections are snapshots taken before any userland security software is initialized. Opening `\KnownDlls\ntdll.dll` via `NtOpenSection` and mapping it into the current process with `NtMapViewOfSection` yields a pristine image whose text section has never been touched by EDR hooks.
The base address of this clean mapping is then fed directly to `snd_set_ntdll()` (from `sindri/primitives/ntdll.h`), ensuring that all subsequent SSN resolution and native module lookups operate on unmodified stubs.
The base address of this clean mapping is then fed directly to `snd_set_ntdll()` (from `sindri/primitives/ntdll.h`), ensuring that all subsequent SSN resolution operate on unmodified stubs.
```c
PVOID clean_ntdll = NULL;
@@ -11,7 +11,7 @@ Two pre-built instances of the `snd_module_api_t` interface are exported globall
| Symbol | Backend Paradigm |
|---|---|
| `snd_mod_win` | `LoadLibraryA` / `GetProcAddress` / `GetModuleHandleW` |
| `snd_mod_native` | Global cache (`snd_get_ntdll`) + hash-based export resolution (`snd_pe_get_export_address_by_hash`) + PEB walking (`snd_peb_get_module_base_by_hash`) |
| `snd_mod_native` | PEB walking (`snd_peb_get_module_base_by_hash`) + hash-based export resolution (`snd_pe_get_export_address_by_hash`) |
---
@@ -62,21 +62,3 @@ Hash-based variant. Eliminates the plaintext module name string from the binary
## Global NTDLL State (`sindri/primitives/ntdll.h`)
SindriKit maintains global state for the `ntdll.dll` base address. This address is used ubiquitously by native components to extract clean stubs or parse export tables without relying on repetitive PEB lookups or the potentially hooked PEB-resident image.
### `snd_set_ntdll`
Sets the globally cached base address of `ntdll.dll`.
| Parameter | Type | Description |
|---|---|---|
| `ntdll_base` | `PVOID` | Base address to cache. Typically obtained via KnownDlls mapping or PEB walking. |
**Returns:** `void`
---
### `snd_get_ntdll`
Retrieves the globally cached base address.
**Returns:** `PVOID` — The base address, or `NULL` if not set.
@@ -65,7 +65,7 @@ Appends a fallback strategy to the resolution pipeline. The pipeline is evaluate
### NTDLL Base Address
> [!NOTE]
> The `ntdll.dll` base address is no longer set locally per-subsystem. It is now managed globally via `snd_set_ntdll` and `snd_get_ntdll` from `sindri/primitives/ntdll.h`. You MUST set the global base before attempting to resolve syscalls.
> The `ntdll.dll` base address is managed globally via `snd_set_ntdll` and `snd_get_ntdll` from `sindri/primitives/ntdll.h`. You MUST set the global base before attempting to resolve syscalls.
---
+4 -4
View File
@@ -4,7 +4,6 @@
#include <sindri/parsers/pe/pe_exports.h>
#include <sindri/parsers/pe/pe_parser.h>
#include <sindri/primitives/modules.h>
#include <sindri/primitives/ntdll.h>
#include <sindri/primitives/peb.h>
#include <sindri_hashes.h>
#include <stddef.h>
@@ -36,9 +35,10 @@ static snd_status_t WINAPI native_load_library(const char *module_name, HMODULE
return SND_OK;
}
PVOID ntdll = snd_get_ntdll();
if (!ntdll)
return SND_ERR_CTX(SND_STATUS_NOT_INITIALIZED, "NTDLL base is NULL");
PVOID ntdll;
snd_status_t resolve_status = snd_peb_get_module_base_by_hash(SND_HASH_NTDLL_DLL, &ntdll);
if (resolve_status.code != SND_SUCCESS)
return resolve_status;
FARPROC ldr_addr = NULL;
snd_pe_get_export_address_by_hash(ntdll, SND_SYS_DLL_SIZE_DEFAULT, SND_HASH_LDRLOADDLL, &ldr_addr,