- Implement complete cross-process injection engine via snd_inj_ctx_t - Track explicit remote_entry_point for safe thread hijacking and PE execution - Refactor standalone syscall resolvers into a unified pipeline (scan/sort) - Perfect status code system by purging generic fallbacks in favor of highly-specific, domain-aware error codes - Fix minor pointer validation and parsing bugs in the reflective loader
6.2 KiB
Loaders: API Reference
Public loader API for the Reflective PE technique under include/sindri/loaders/reflective/. Include sindri/loaders.h or sindri/loaders/reflective.h.
KnownDlls mapping is documented under mapping/object-manager — not part of this API surface.
Reflective Chain (sindri/loaders/reflective/chain.h)
Primary entry points for end-to-end reflective loading.
snd_ldr_pe_prepare_image
Runs the full local preparation pipeline:
snd_pe_parse(ctx->raw_source, FALSE, &ctx->pe)snd_ldr_pe_compatibility_checksnd_ldr_pe_allocate_and_copy_imagesnd_ldr_pe_apply_relocationssnd_ldr_pe_resolve_importssnd_ldr_pe_apply_memory_protections
Leaves context at SND_STAGE_READY_FOR_EXECUTION. Does not run TLS or invoke the entry point.
snd_status_t snd_ldr_pe_prepare_image(snd_ldr_pe_ctx_t *ctx);
Returns: SND_OK or first failing stage status
Source: src/loaders/reflective/chain.c
snd_ldr_pe_execute_image
Executes a prepared image locally. Requires SND_STAGE_READY_FOR_EXECUTION and local_base == execution_base.
- Resolves entry point
- Runs TLS callbacks (
DLL_PROCESS_ATTACH) - Invokes
DllMain(DLL) or jumps to EXE entry
snd_status_t snd_ldr_pe_execute_image(snd_ldr_pe_ctx_t *ctx);
Returns: SND_OK, SND_STATUS_DLL_INITIALIZATION_FAILED, SND_STATUS_ENTRY_POINT_NOT_FOUND, SND_STATUS_UNSUPPORTED (remote-prepared image)
snd_ldr_pe_detach_image
Tears down a locally executed image. Requires SND_STAGE_EXECUTED and matching local/execution bases. Runs DLL detach + TLS detach, then frees mapped memory.
void snd_ldr_pe_detach_image(snd_ldr_pe_ctx_t *ctx);
No-op if preconditions fail (including remote-prepared images).
Reflective Engine (sindri/loaders/reflective/engine.h)
snd_ldr_pe_ctx_t
Central context for reflective PE operations.
| Field | Type | Description |
|---|---|---|
raw_source |
const snd_buffer_t * |
Raw on-disk PE bytes |
pe |
snd_pe_parser_t |
Parsed PE context (raw then re-parsed mapped) |
target |
snd_pe_target_t |
Allocation, delta, entry point state |
stage |
snd_ldr_pe_stage_t |
Current pipeline stage |
mem_api |
const snd_memory_api_t * |
Injected memory primitives |
mod_api |
const snd_module_api_t * |
Injected module primitives |
snd_pe_target_t
| Field | Type | Description |
|---|---|---|
local_base |
LPVOID |
Local virtual mapping base |
execution_base |
LPVOID |
Base for relocation delta (remote base when baking for injection) |
delta_offset |
LONG_PTR |
execution_base - ImageBase |
entry_point |
LPVOID |
Resolved entry (after get_entry_point) |
allocated_size |
SIZE_T |
SizeOfImage allocation size |
snd_ldr_pe_stage_t
| Constant | Value |
|---|---|
SND_STAGE_UNINITIALIZED |
0 |
SND_STAGE_PARSED |
1 |
SND_STAGE_MEM_ALLOCATED |
2 |
SND_STAGE_SECTIONS_MAPPED |
3 |
SND_STAGE_RELOCATED |
4 |
SND_STAGE_IMPORTS_RESOLVED |
5 |
SND_STAGE_READY_FOR_EXECUTION |
6 |
SND_STAGE_EXECUTED |
7 |
Engine functions
| Function | Required stage | Description |
|---|---|---|
snd_ldr_pe_compatibility_check |
(after parse) | Host vs payload bitness |
snd_ldr_pe_allocate_and_copy_image |
PARSED |
Alloc + section copy + re-parse mapped |
snd_ldr_pe_apply_relocations |
SECTIONS_MAPPED |
Base relocation fixups |
snd_ldr_pe_resolve_imports |
RELOCATED |
IAT patching via mod_api |
snd_ldr_pe_apply_memory_protections |
IMPORTS_RESOLVED |
Per-page section protections |
snd_ldr_pe_get_entry_point |
>= READY_FOR_EXECUTION |
Populates target.entry_point |
snd_ldr_pe_execute_tls_callbacks |
>= READY_FOR_EXECUTION |
Invokes TLS callback array |
snd_ldr_pe_get_proc_address |
>= READY_FOR_EXECUTION |
Export resolve via snd_pe_get_export_address |
snd_ldr_pe_free_mapped_image |
(any with local_base) |
Frees mapping, resets toward PARSED |
Source: src/loaders/reflective/engine.c
snd_ldr_pe_get_proc_address
Resolves an export from the loaded image using the PE export parser. Forwarders use mod_api->get_module_base as resolver.
snd_status_t snd_ldr_pe_get_proc_address(snd_ldr_pe_ctx_t *ctx, const char *func_name, FARPROC *func_addr_out);
Export call macros
SND_CALL_EXPORT
Resolves and calls an export; discards return value.
SND_CALL_EXPORT(ctx, name, signature, status_out, ...)
SND_CALL_EXPORT_RET
Resolves and calls an export; captures return value in ret_out.
SND_CALL_EXPORT_RET(ctx, name, signature, status_out, ret_out, ...)
Both macros set status_out from snd_ldr_pe_get_proc_address and only invoke the function when resolution succeeds (SND_SUCCEEDED(status_out)).
Stage validation errors
Engine functions return SND_STATUS_INVALID_STAGE_SEQUENCE with context naming expected vs actual stage. Allocation failure during section copy rolls back to SND_STAGE_PARSED and frees local_base.
snd_ldr_pe_free_mapped_image vs snd_ldr_pe_detach_image
| Function | When to use |
|---|---|
snd_ldr_pe_free_mapped_image |
Release virtual memory after prepare-only or failed load |
snd_ldr_pe_detach_image |
Full teardown after local execute: DllMain detach, TLS detach, then free |
detach_image is a no-op when local_base != execution_base (remote-prepared images from snd_inj_classic_pe).
Usage Example (local reflective load)
snd_ldr_pe_ctx_t ctx = {0};
ctx.raw_source = &file_buf;
ctx.mem_api = &snd_mem_win;
ctx.mod_api = &snd_mod_win;
snd_status_t status = snd_ldr_pe_prepare_image(&ctx);
if (SND_FAILED(status)) goto cleanup;
status = snd_ldr_pe_execute_image(&ctx);
if (SND_FAILED(status)) goto cleanup;
snd_ldr_pe_detach_image(&ctx);
cleanup:
snd_ldr_pe_free_mapped_image(&ctx);
Related Documentation
- Injection API —
snd_inj_classic_pereuses loader engine steps - PE parser API — underlying parse/reloc/import primitives
- Memory / module primitives —
mem_api/mod_apibackends