Refactor: Encapsulate NTDLL state within syscall subsystem

This commit is contained in:
sibouzitoun
2026-06-22 20:51:58 +01:00
parent 04be820979
commit fc65395be6
12 changed files with 44 additions and 64 deletions
-1
View File
@@ -91,7 +91,6 @@ add_library(sindri_engine STATIC
src/primitives/modules/win.c
src/primitives/modules/native.c
src/primitives/modules/peb.c
src/primitives/modules/ntdll.c
# Syscall resolvers
src/primitives/syscalls/syscalls.c
src/primitives/syscalls/hellsgate.c
+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 operate on unmodified stubs.
The base address of this clean mapping is then fed directly to `snd_set_ntdll()`, ensuring that all subsequent SSN resolution operate on unmodified stubs.
```c
PVOID clean_ntdll = NULL;
-14
View File
@@ -14,17 +14,3 @@ The primitives domain forms the absolute foundation of SindriKit. Everything bui
Techniques for interacting with loaded DLLs and extracting function addresses via PEB traversal.
- [syscalls/](syscalls/)
The framework's advanced system for bypassing userland EDR hooks via a cascading fallback mechanism.
---
## Global Primitives (`sindri/primitives/ntdll.h`)
SindriKit maintains global state for the `ntdll.dll` base address. This address is used ubiquitously by native components (like the syscall resolver and the native module API) 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`.
- **`ntdll_base`**: Base address to cache. Typically obtained via KnownDlls mapping or PEB walking.
### `snd_get_ntdll`
Retrieves the globally cached base address.
- **Returns:** `PVOID` The base address, or `NULL` if not set.
+1 -1
View File
@@ -27,4 +27,4 @@ The `snd_mem_native` implementation bypasses `kernel32.dll` and `ntdll.dll` enti
By executing the `syscall` instruction directly, the implant cleanly bypasses userland EDR hooks placed inside the `ntdll.dll` stubs. The kernel receives the memory request without userland telemetry sensors ever recording the event.
> [!WARNING]
> `snd_mem_native` requires the operator to manually configure the syscall pipeline (via `snd_set_syscall_strategy` and `sindri/primitives/ntdll.h`'s `snd_set_ntdll`) prior to invocation. If the pipeline is not bootstrapped, the native memory calls will immediately fail.
> `snd_mem_native` requires the operator to manually configure the syscall pipeline (via `snd_set_syscall_strategy` and `snd_set_ntdll`) prior to invocation. If the pipeline is not bootstrapped, the native memory calls will immediately fail.
@@ -56,9 +56,3 @@ Hash-based variant. Eliminates the plaintext module name string from the binary
| `out_base` | `PVOID*` | Receives the base address of the located module |
**Returns:** `snd_status_t``SND_OK` on success.
---
## 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.
+1 -1
View File
@@ -3,7 +3,7 @@
This directory details SindriKit's advanced system for extracting System Service Numbers (SSNs) and bypassing inline userland EDR hooks.
> [!WARNING]
> **Initialization Constraint:** The syscall pipeline requires setting up the NTDLL base address. `snd_set_ntdll()` (from `sindri/primitives/ntdll.h`) must be called prior to invoking any resolution function.
> **Initialization Constraint:** The syscall pipeline requires setting up the NTDLL base address. `snd_set_ntdll()` must be called prior to invoking any resolution function.
## Table of Contents
- [pipeline.md](pipeline.md)
@@ -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 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`. You MUST set the global base before attempting to resolve syscalls.
---
@@ -91,3 +91,19 @@ ASM stub that performs the actual `syscall` instruction. Architecture-specific i
| `args` | `snd_syscall_args_t*` | Pointer to a populated arguments structure |
**Returns:** `NTSTATUS` — the raw kernel return value.
### `snd_set_ntdll`
Sets the global `ntdll.dll` base address.
| Parameter | Type | Description |
|---|---|---|
| `base` | `PVOID` | The base address of the `ntdll.dll` image |
**Returns:** `void`
---
### `snd_get_ntdll`
Returns the global `ntdll.dll` base address.
**Returns:** `PVOID` — the base address of the `ntdll.dll` image, or `NULL` if not set.
-1
View File
@@ -5,7 +5,6 @@
#include <sindri/primitives/heavens_gate.h>
#include <sindri/primitives/memory.h>
#include <sindri/primitives/modules.h>
#include <sindri/primitives/ntdll.h>
#include <sindri/primitives/os_api.h>
#include <sindri/primitives/peb.h>
#include <sindri/primitives/syscalls.h>
-23
View File
@@ -1,23 +0,0 @@
#ifndef SND_PRIMITIVES_NTDLL_H
#define SND_PRIMITIVES_NTDLL_H
#include <sindri/common/helpers.h>
#include <windows.h>
SND_BEGIN_EXTERN_C
/**
* @brief Sets the global base address of the ntdll.dll module.
* @param ntdll_base The base address of the ntdll module.
*/
void snd_set_ntdll(PVOID ntdll_base);
/**
* @brief Retrieves the global base address of the ntdll.dll module.
* @return The base address of the ntdll module, or NULL if not set.
*/
PVOID snd_get_ntdll(void);
SND_END_EXTERN_C
#endif // SND_PRIMITIVES_NTDLL_H
+12
View File
@@ -59,6 +59,18 @@ void snd_set_syscall_strategy(snd_syscall_resolver_t resolver);
*/
snd_status_t snd_add_syscall_strategy(snd_syscall_resolver_t resolver);
/**
* @brief Sets the global base address of the ntdll.dll module.
* @param ntdll_base The base address of the ntdll module.
*/
void snd_set_ntdll(PVOID ntdll_base);
/**
* @brief Retrieves the global base address of the ntdll.dll module.
* @return The base address of the ntdll module, or NULL if not set.
*/
PVOID snd_get_ntdll(void);
/**
* @brief Core resolution entrypoint (evaluates the internal fallback chain
* automatically).
-14
View File
@@ -1,14 +0,0 @@
#include <sindri/primitives/ntdll.h>
static PVOID g_PristineNtdll = NULL;
void snd_set_ntdll(PVOID ntdll_base) {
if (ntdll_base == NULL) {
return;
}
g_PristineNtdll = ntdll_base;
}
PVOID snd_get_ntdll(void) {
return g_PristineNtdll;
}
+12 -1
View File
@@ -1,5 +1,4 @@
#include <sindri/common/status.h>
#include <sindri/primitives/ntdll.h>
#include <sindri/primitives/peb.h>
#include <sindri/primitives/syscalls.h>
#include <windows.h>
@@ -9,6 +8,18 @@
// State management for the resolution pipeline
static snd_syscall_resolver_t g_strategy_chain[SND_MAX_INTERNAL_STRATEGIES] = {0};
static int g_strategy_count = 0;
static PVOID g_syscall_ntdll_target = NULL;
void snd_set_ntdll(PVOID ntdll_base) {
if (ntdll_base == NULL) {
return;
}
g_syscall_ntdll_target = ntdll_base;
}
PVOID snd_get_ntdll(void) {
return g_syscall_ntdll_target;
}
void snd_set_syscall_strategy(snd_syscall_resolver_t resolver) {
if (!resolver)