- What: Discussion on the poor tooling in offensive C development
- Impact: Primarily affects security researchers and developers
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 ofdecade-oldGitHub 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 isinherently 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 onGitHub at SindriKit. 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 techniqueandthe mechanics of its execution. Your reflective loader doesn’t just load a PE image. It loads a PE imageusing 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 thattechnique and execution profile should be decoupled at the design level. 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, usingsnd_loader_ctx_tto orchestrate a payload: 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: 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. This is where the design pays off. In a traditional loader, execution mechanics are fused directly to the logic. Your code looks like this: When that gets flagged by an EDR, you have to rewrite the capability. SindriKit removes the hardcoded APIs. Before starting the loading chain, you populate your context with an execution profile: