Skip to content

Nanomites

Branch instructions are replaced with INT3 traps that a debugger-parent resolves from a hidden table at runtime, erasing control flow from the binary.

Nanomites are a debugger-assisted control-flow obfuscation popularised by the Armadillo (SoftwarePassport) protector. Selected branch instructions in the protected code are stripped out and overwritten with single-byte INT3 (0xCC) software breakpoints. The branches no longer exist in the binary at all — neither the opcode, the condition, nor the destination — so a static disassembler sees a breakpoint trap where a jmp/jcc used to be and cannot reconstruct the edge.

The trick is that the protected process is launched under a parent debugger (the protector's own loader, running as a debug-attached supervisor). When the child executes a planted 0xCC, the CPU raises a breakpoint exception that the parent receives via the debug event loop. The parent looks up the faulting address in an encrypted nanomite table that records, for each INT3, the original branch type, condition, and target. It evaluates the condition against the child's EFLAGS, then rewrites the child's EIP/RIP to the correct destination and resumes. Detach the debugger-parent and the program simply crashes on the first unhandled breakpoint.

How it works

A normal conditional branch is replaced by a one-byte trap; the surrounding bytes are left intact so the function still looks plausible to linear sweep:

asm
; ---- Original (pre-protection) ----
00401050: 3B C3              cmp  eax, ebx
00401052: 0F 84 A6 00 00 00  je   0x004010FE     ; 6-byte near-jump, real edge
00401058: 8B 4D F8           mov  ecx, [ebp-8]

; ---- After nanomite insertion (what the disassembler sees) ----
00401050: 3B C3              cmp  eax, ebx
00401052: CC                 int3                ; <- branch erased, trap planted
00401053: A6                 cmpsb               ; \
00401054: 00 00              add  [eax], al      ;  > leftover je operand bytes
00401056: 00 00              add  [eax], al      ; /  now decode as garbage
00401058: 8B 4D F8           mov  ecx, [ebp-8]

The five operand bytes of the original je (A6 00 00 00 plus alignment) are now orphaned and the sweep decodes them as a meaningless cmpsb / add sequence, fragmenting the function. The disassembler has no way to know that 0x00401052 was ever a branch, let alone that its target was 0x004010FE. At runtime the parent intercepts the INT3 at 0x00401052, reads the nanomite record, checks ZF in the child's flags, and sets the child EIP to either 0x004010FE (taken) or 0x00401053 (fall-through) before continuing.

Detection & analysis

Static analysis:

  • A function body peppered with stray 0xCC bytes inside live code (not as padding between functions) is the signature — especially when each INT3 sits immediately after a cmp/test/sub that would normally feed a jcc.
  • The branch targets are absent from the binary, so the recovered CFG has dead-ending basic blocks with no outgoing edge. Cross-references collapse and the decompiler produces truncated functions.
  • Locate the nanomite table by tracing the loader's debug-event handler (WaitForDebugEventEXCEPTION_BREAKPOINT dispatch) and the decryption routine that indexes it by faulting address.

Dynamic analysis:

  • Run the sample intact under its own protector loader, attach a second observer non-intrusively (a hypervisor-level tracer such as a Intel PT trace or an emulator), and log every EIP rewrite the parent performs — each one reveals a resolved branch and its target.
  • Dump the reconstructed branches and patch them back into the binary, or script the debugger to replay the parent's table lookups, rebuilding the CFG offline.

Detection rule hint:

Flag executables that spawn a child via CreateProcess with DEBUG_PROCESS/DEBUG_ONLY_THIS_PROCESS and whose code sections contain dense, non-aligned 0xCC bytes interleaved with arithmetic/compare instructions. The combination of a self-debugging parent and breakpoint bytes embedded mid-function is near-exclusive to nanomite protectors and effectively never produced by legitimate compilers.

Votes

Comments(0)