Skip to content
Obfuscationbeginner

Stack Strings

Malware builds sensitive strings character-by-character on the stack at runtime so they never appear as static literals in the binary, defeating simple string-search analysis.

A naive way to hide a string from strings(1) or IDA's string window is to build it one character at a time on the stack. Because the bytes are never contiguous in the binary's .data or .rdata section, automated string extraction tools see nothing; the string only materialises in memory during execution.

Compilers can produce stack strings accidentally (for small constant strings under optimisation), but malware authors generate them intentionally — often through a code-generation script or LLVM obfuscation pass — for API names, registry keys, URLs, and mutex names.

How it works

c
// Instead of: const char *url = "http://evil.example/c2";
// Malware writes:
void BuildC2Url(char *buf)
{
    buf[0]  = 'h';
    buf[1]  = 't';
    buf[2]  = 't';
    buf[3]  = 'p';
    buf[4]  = ':';
    buf[5]  = '/';
    buf[6]  = '/';
    buf[7]  = 'e';
    buf[8]  = 'v';
    buf[9]  = 'i';
    buf[10] = 'l';
    // ... etc.
    buf[22] = '\0';
}

In optimised assembly this compiles to a sequence of immediate-byte MOV instructions with no contiguous string in the code section:

asm
; x86-64 — building "http" on the stack
mov  byte [rsp+00h], 68h   ; 'h'
mov  byte [rsp+01h], 74h   ; 't'
mov  byte [rsp+02h], 74h   ; 't'
mov  byte [rsp+03h], 70h   ; 'p'

A faster variant stores the string as a series of 4- or 8-byte integer constants moved into stack slots:

asm
mov  dword [rsp+00h], 70747468h   ; "http" (little-endian)
mov  dword [rsp+04h], 2F2F3A68h   ; "h://"

This form is harder to spot visually but equally hidden from static string extraction.

Detection & analysis

Static analysis:

  • FLOSS (FLARE Obfuscated String Solver) from Mandiant emulates short function sequences to extract stack strings automatically; it is the standard tool for this technique.
  • IDA/Ghidra: look for long sequences of MOV byte [rbp/rsp+N], imm8 targeting adjacent stack offsets — a characteristic pattern.
  • Entropy analysis is not useful here (stack strings don't increase section entropy).

Dynamic analysis:

  • Set a breakpoint immediately after the build function returns and inspect the stack or heap buffer — the string is fully assembled there.
  • Frida: hook the first downstream API call (e.g., URLDownloadToFile) that consumes the string; log the argument.

Detection rule hint:

Flag functions containing 8 or more single-byte immediate stack writes targeting sequential offsets within a 64-byte window — this pattern is almost never produced by legitimate, unobfuscated code.

Votes

Comments(0)