Skip to content
Obfuscationintermediate

Junk Code Insertion

Obfuscators interleave semantically inert instruction sequences — dead code and NOP-equivalents — among real logic to bloat binaries, confuse disassembly, and break byte-pattern signatures.

Junk code insertion sprinkles instructions that do no useful work between the instructions that do. The junk computes throwaway values, performs offsetting operations, or executes multi-byte NOP-equivalents. Because the inserted code never affects observable program state, the binary behaves identically — but its byte layout, instruction count, and control flow all change.

The technique serves two purposes. First, it defeats static signatures: any byte-pattern or hash that covered a contiguous code region is broken once junk is interleaved, and re-randomising the junk per build yields a polymorphic family. Second, it raises the cost of manual analysis by burying real logic in noise and, with carefully chosen junk, can desynchronise linear-sweep disassemblers.

How it works

The inserted sequences are inert: their results are dead, or they cancel out. A metamorphic engine selects junk templates and registers at random per build.

c
// Real work, with junk interleaved (results never read)
int compute(int a, int b) {
    int dead = a * 7;          // junk: 'dead' never used downstream
    dead ^= 0x1337;            // junk
    int r = a + b;             // real
    dead += r << 2;            // junk depends on r but is never read
    return r;                  // only r escapes
}

In assembly, common junk idioms are operations that restore state or write to scratch registers that are about to be overwritten:

asm
; --- real instruction we want to hide ---
mov   eax, [rbx+8]      ; real load

; --- junk: net-zero / dead sequences ---
push  rcx               ; junk
xor   ecx, ecx          ; junk (ecx clobbered, never read)
add   ecx, 5            ; junk
nop                     ; junk
xchg  ecx, ecx          ; junk NOP-equivalent
pop   rcx               ; restores rcx -> net effect zero

mov   [rdi], eax        ; real store

Longer multi-byte NOPs (0F 1F 40 00, 66 90) and arithmetic-then-inverse pairs (add r, k / sub r, k) are favoured because they look like plausible compiler output and resist trivial filtering.

Detection & analysis

Static analysis:

  • Run dead-code elimination mentally or via a decompiler (Ghidra/IDA Hex-Rays) — junk whose results are never read collapses away, revealing the real logic.
  • Watch for runs of multi-byte NOPs, push/pop pairs around unrelated code, and arithmetic immediately undone by its inverse.
  • Code that is unusually large relative to its behaviour, with low "useful instruction" density, is a strong indicator.

Dynamic analysis:

  • Trace execution (Intel PT, a debugger trace, or a Tiny Tracer / DynamoRIO client) and diff executed-but-irrelevant instructions; taint analysis shows which results never reach a sink.
  • Junk does not branch or syscall, so an instruction trace filtered to memory/IO-affecting operations cuts through the noise.

Detection rule hint:

Flag functions with a high proportion of instructions whose destination registers are overwritten or popped before any read (dead writes), combined with clusters of multi-byte NOPs — legitimate optimised code keeps dead-write density low, so a sustained spike strongly suggests inserted junk.

Votes

Comments(0)