Security News

Cybersecurity news aggregator

🔓
MEDIUM Vulnerabilities Reddit r/netsec

Post-Compilation Obfuscation Is Outdated: Moving Polymorphism Directly into CMake

  • What: New polymorphism technique in CMake for offensive tools
  • Impact: Makes static analysis more difficult for security tools
Read Full Article →

A recurring theme in offensive tool development is the battle against static signatures. You can dynamically resolve your APIs, string-hash your module names… but the static shape of your compiled code remains deterministic. When an EDR fingerprints the specific sequence of your custom syscall stub or the memory layout of your execution context, the entire toolchain is burned. SindriKit 1.5.0changes this dynamic with the introduction of theSND_MORPHMutation Engine. Instead of relying solely on post-compilation obfuscators, SindriKit now injects structural and instruction-level polymorphism directly into the source tree before the compiler even touches it. The result is a unique binary footprint on every compilation, without altering a single byte of semantic behavior. Source, architecture docs, and PoCs:SindriKit on GitHub. The engine is integrated into the CMake build system. By simply passing-DSND_MORPH=ONat configure time, the orchestrator is on. It creates a tempmorphed/source tree, copies the framework source code into it, and executes a series of Python-based mutation passes. The compiler is then redirected to build from this mutated directory. This means your codebase remains untouched, but the generated binary is different every time you build. The first mutation pass targets the C codebase. EDRs often build control flow graphs (CFGs) to signature malicious behavior. To defeat this,junk_c.pyinjectsVolatile Opaque Predicatesdirectly into the execution flow. These are dynamically generated C logic blocks (simple **if,whileloops, dummyswitchstatements) that usevolatilevariables and randomized magic constants. Because they are volatile, the compiler’s optimizer cannot optimize them away. They evaluate to a predictable outcome at runtime (essentially a highly convoluted NOP), but they obscure the static control flow graph. To take this a step further, SindriKit 1.5.0 introducesCall Graph Splitting. The mutator now generates completely random, non-existentstaticC functions at the global scope, filled with random math loops. The opaque predicates randomly call these “dead functions” from within their unreachable branches. This forces static analysis tools (and human reverse engineers) to map out a function call graph that leads completely to dead ends, increasing the time required to analyze the binary. The C parser actively tracks brace depth and C scopes to intelligently buffer statements. It will never inject code immediately after areturn,break, orgoto, ensuring that all injected junk is mathematically reachable and preventingC4702(unreachable code) compiler errors. SindriKit utilizes hand-written MASM assembly for its FFI bridges, Syscall Invoker… These stubs are prime targets for signature generation. Themasm_mutate.pypass adds instruction-level polymorphism to all.asmfiles: Injecting random assembly is incredibly dangerous. SindriKit’s mutator is strictly architecture and ABI aware. It differentiates between x86 and x64 files to prevent undefined register errors. Furthermore, it strictly uses instructions likeleafor math injections because they are guaranteed tonot modify the CPU’s EFLAGS register. This guarantees that the mutator will never accidentally destroy the conditional jump flags (like the Zero Flag) of the framework’s native execution flow. Shuffling diagram for clarity The final mutation pass (struct_shuffle.py) targets the memory layout of the framework’s internal data structures. By wrapping sensitive structures inSND_SHUFFLE_STARTandSND_SHUFFLE_ENDmacros, the parser will randomly shuffle the order of the structure’s fields on every build. If a security product attempts to signature the memory layout of thesnd_engine_ctx_tcontext block, the signature will be broken upon the next compilation. The parser tracks brace depth to safely shuffle nested structures and anonymous unions intact, and it automatically detects and preserves#ifdefpreprocessor blocks to prevent cross-compilation errors.

Share this article