3.7 KiB
State Machines
SindriKit is fundamentally designed around the concept of discrete State Machines. This is a global toolkit architecture pattern used to track the progression of any offensive capability—whether that is reflective loading, remote process injection, call stack spoofing, or token manipulation.
Instead of executing complex offensive operations as a single, monolithic function, tasks are broken down into logical, sequential stages managed by a persistent context structure.
The Global Context Pattern
Every major domain within SindriKit revolves around a domain-specific context structure.
While the exact fields vary depending on the operation, every context struct adheres to a common pattern:
- Execution Primitives: Pointers to the injected OS API tables (e.g.,
mem_api,mod_api) that govern how the operation interacts with the host. - Target Data: Pointers, handles, and metadata regarding the objective of the operation.
- The State Tracker: An enumeration (
stage) tracking precisely how far the operation has progressed.
Advantages of State Machines
By decentralizing execution into trackable stages, the framework gains several critical capabilities:
1. Granular Error Handling
If an operation fails, the context retains the exact stage where the failure occurred. This allows operators to pinpoint whether a payload failed during memory allocation, import resolution, or thread execution, enabling rapid triage without needing verbose debug logs that could compromise OpSec.
2. Execution Pausing and Sleep Obfuscation
Because the state of the operation is entirely self-contained within the context structure, execution can be intentionally suspended. An operator can allocate memory, put the implant to sleep for a significant duration (to evade heuristic timing checks and memory scanners), and then safely resume mapping sections at a later time. The state machine ensures the engine picks up exactly where it left off.
3. Enforced Ordering
The framework's internal API functions validate the current stage before executing. This prevents undefined behavior or out-of-order execution during manual, staged, or highly fragmented deployments.
Case Study: The Reflective Loader
To demonstrate how this global pattern is implemented, the Reflective Loader domain—the first concrete system built into SindriKit—serves as the primary reference.
The loader utilizes the snd_loader_ctx_t structure (defined in include/sindri/loaders/reflective/engine.h), which tracks its progression using the snd_loader_stage_t enumeration:
SND_STAGE_UNINITIALIZED: The context has been created but no PE parsing has occurred.SND_STAGE_PARSED: The raw payload has been validated and parsed.SND_STAGE_MEM_ALLOCATED: Virtual memory has been allocated via the injectedmem_api.SND_STAGE_SECTIONS_MAPPED: PE sections have been copied to the virtual base.SND_STAGE_RELOCATED: Base relocations have been applied.SND_STAGE_IMPORTS_RESOLVED: The IAT has been populated via the injectedmod_api.SND_STAGE_READY_FOR_EXECUTION: Memory protections have been applied; the payload is ready.SND_STAGE_EXECUTED: The entry point has been invoked.
For example, if an operator attempts to call snd_resolve_imports(ctx), the engine will explicitly verify that the context has at least reached SND_STAGE_SECTIONS_MAPPED. If it has not, the engine aborts and returns an error (SND_STATUS_INVALID_STAGE_SEQUENCE), preserving memory integrity and preventing catastrophic crashes.
As future domains (such as injectors and spoofers) are added to the toolkit, they will inherently inherit and implement this identical State Machine paradigm.