Show HN: SindriKit – A C framework applying dependency injection to exploit dev

sibouzitoun1 pts0 comments

SindriKit: Offensive Development Deserves Better Architecture<br>SindriKit: Offensive Development Deserves Better Architecture

Offensive C development has a structural flaw: the tooling is embarrassing.

Not the techniques. The techniques are sophisticated, well-researched, constantly evolving. The people building them are sharp. But the infrastructure they build on? It’s a pile of decade-old GitHub PoCs, abandoned loaders with no documentation, and the kind of copy-paste archaeology that would make a software engineer physically recoil.

You want to implement a reflective loader? You hit GitHub, rip a chunk of code from a five-year-old PoC that hasn’t seen a commit since the original author got a real job, and try to make it fit your implant. Cue the next six hours of your life evaporating in a debugger, chasing arbitrary access violations, wondering why you chose this career path.

We’ve collectively accepted a standard of tooling that the rest of the industry moved past years ago. And we’ve accepted it because the assumption has always been that low-level offensive code is inherently unmaintainable . That you can have clean architecture or operational effectiveness, but not both. That the “opsec” build will always be an unreadable nightmare of inline assembly and opaque macros, and that’s just the cost of doing business.

That assumption is wrong. While offensive tooling has long embraced reusable techniques, it has rarely treated execution mechanics as a first-class engineering boundary. SindriKit applies Dependency Injection to that boundary, allowing capabilities to remain stable while execution profiles evolve independently.

You can explore the source code, view the architecture, and contribute to the project over on GitHub at SindriKit.

The Problem Is Structural

Let’s be specific about what’s broken, because “the tooling is bad” isn’t an argument, it’s a complaint.

The real problem is that offensive tools couple two things that should never be coupled: the logic of a technique and the mechanics of its execution . Your reflective loader doesn’t just load a PE image. It loads a PE image using VirtualAlloc, using LoadLibrary, using whatever Win32 calls you hardcoded three months ago. The technique and its execution profile are fused together at the source code level.

This creates the nightmare every offensive developer knows intimately . You build something. It works. Then you need to evade an EDR and those Win32 calls are glowing red flares. So you rewrite. Now you have two codebases: the “clean” one for testing, and the “opsec” one that nobody wants to touch. The EDR changes. You rewrite again. The target is CrowdStrike instead of SentinelOne. You rewrite again.

You are not fighting the problem. You are fighting your own architecture.

The solution isn’t a new technique. It’s a new structure. One the rest of software engineering figured out decades ago.

And this is the point worth being precise about: SindriKit is not a loader. The loader is the first module that proves the architecture works. What SindriKit actually is is a framework for composing offensive capabilities around interchangeable execution mechanics. That distinction matters, because there are already many loaders. There are not many frameworks built around the insight that technique and execution profile should be decoupled at the design level .

The Architecture

SindriKit is built on a single premise: isolate the intent of an offensive technique from its underlying execution mechanics.

Everything flows from this. Rather than hardcoding APIs, execution is orchestrated through stateful context objects. The loader module provides the first proof of this architecture in action, using snd_loader_ctx_t to orchestrate a payload:

typedef struct {<br>const snd_buffer_t *raw_source;<br>snd_pe_parser_t pe;<br>snd_pe_target_t target;<br>snd_loader_stage_t stage;<br>} snd_loader_ctx_t;<br>As the payload moves through the pipeline, ctx.stage tracks its lifecycle to ensure teardown and cleanup are deterministic. By centering everything on this context object and its state, the framework offers a strictly layered API:

+-----------------------------------------------------------+<br>| Chains (High-Level) |<br>| snd_prepare_reflective_image(&ctx) |<br>+-----------------------------------------------------------+<br>+-----------------------------------------------------------+<br>| Engine (Low-Level) |<br>| snd_apply_relocations / snd_resolve_imports |<br>+-----------------------------------------------------------+<br>+-----------------------------------------------------------+<br>| Universal Modules |<br>| snd_pe_parse (parsers/pe) |<br>+-----------------------------------------------------------+<br>Operators get fire-and-forget chains at the top. Developers who need granular control drop down to the engine level.

The framework is structured around clear layers, but the real innovation isn’t the context struct itself. It’s what the context object allows you to do next.

Dependency...

offensive sindrikit architecture execution level loader

Related Articles