Skip to content
Security
Skill

/analyzing-shellcode

Analyze raw shellcode and position-independent code — extracting the bytes, guessing architecture, disassembling at the right base, decoding self-decoder stubs, resolving hashed Windows APIs, emulating to the decoded stage, and pulling C2 and stage IOCs. Use when handed a raw

BOOST
From plugin
secskills
17094 skills
Install
$ npx -y skills add trilwu/secskills --skill analyzing-shellcode --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/analyzing-shellcode

Context preview

The summary Claude sees to decide when to auto-load this skill.

Analyze raw shellcode and position-independent code — extracting the bytes, guessing architecture, disassembling at the right base, decoding self-decoder stubs, resolving hashed Windows APIs, emulating to the decoded stage, and pulling C2 and stage IOCs. Use when handed a raw

SKILL.md

analyzing-shellcode.SKILL.md
name: analyzing-shellcode
description: Analyze raw shellcode and position-independent code — extracting the bytes, guessing architecture, disassembling at the right base, decoding self-decoder stubs, resolving hashed Windows APIs, emulating to the decoded stage, and pulling C2 and stage IOCs. Use when handed a raw blob of position-independent code, an extracted payload with no PE/ELF header, shellcode lifted from an exploit or a macro/loader, a `\x`-escaped or base64 buffer, a suspected Cobalt Strike or Metasploit stager, or an egg-hunter.
verified: 2026-07-26

Analyzing Shellcode

Shellcode is code with no container — no PE/ELF header, no imports table, no fixed load address — so it must find everything it needs at runtime. That self-reliance is exactly what gives it away: the PEB walk, the API hashing, the position-independent GetPC trick are structural fingerprints you can read even before you know what the payload does.

When to Use

  • A raw blob of position-independent code with no PE/ELF/Mach-O header
  • A payload carved from an exploit, a macro, a loader, or a JS/HTA dropper
  • A `\x`-escaped, `0x`-prefixed, base64, or XOR-obfuscated byte buffer
  • A suspected Metasploit stager (reverse_tcp/https) or Cobalt Strike beacon stage
  • An egg-hunter or GetPC/decoder stub found while triaging a larger binary
  • Bytes recovered from network capture, RWX memory, or a document stream

When NOT to Use

  • **The sample is a normal PE/ELF/Mach-O with headers** — use

`analyzing-binaries` for the loader-backed disassembly workflow

  • **You need broader sample triage or family ID** — use `analyzing-malware`;

come here only for the raw payload stage inside it

  • **The blob is a packed executable, not raw shellcode** — use

`unpacking-protected-binaries` to unwrap it first

  • **You are carving the shellcode out of a memory dump first** — use

`analyzing-memory-images` to locate and extract the region, then return here

  • **Deep static reversing once decoded** — `reverse-engineering` is not a

skill; route sustained disassembly work back to `analyzing-binaries`

Get the Bytes First

Shellcode arrives wrapped. Normalize it to a flat binary before anything else.

# \x-escaped or 0x C-array pasted from an exploit → raw bytes
python3 -c "import sys,re; d=open('sc.txt').read(); \
  b=bytes(int(x,16) for x in re.findall(r'(?:0x|\\\\x)([0-9a-fA-F]{2})',d)); \
  open('sc.bin','wb').write(b); sys.stderr.write(f'{len(b)} bytes\n')"

# base64 buffer
base64 -d payload.b64 > sc.bin

# single-byte XOR brute (find the key that yields printable / low entropy)
python3 -c "import sys; d=open('sc.bin','rb').read(); \
  [print(k, bytes(c^k for c in d[:16]).hex()) for k in range(256)]"

Then sniff what you have before disassembling:

file sc.bin && wc -c < sc.bin           # size: stagers ~200-800B, stages tens of KB
ent sc.bin 2>/dev/null || binwalk -E sc.bin   # high entropy → encoded/self-decoder

For document- and capture-borne shellcode, extract first: `olevba --deobf` for Office macros (`analyzing-malware` covers this), and carve from PCAP with `tshark -r c.pcap --export-objects http,out/` or a `tcp.stream` follow, then treat the bytes as above.

First-Look Triage

xxd sc.bin | head -40

Read the head for structure, not meaning:

  • **Architecture guess.** x86 shows `55 8B EC` prologues, `64 A1 30 00` /

`64 8B` (`fs:[0x30]`) PEB access, and `E8 00000000 5x` call/pop. x64 shows `65 48 8B` (`gs:[0x60]`), REX-prefixed `48 ...`, and RSP alignment `48 83 EC`. ARM/AArch64 is uniform 4-byte words with no x86 prologue signature.

  • **GetPC trick.** `E8 00 00 00 00 58/59/5A/5B/5E` (call $+5 then pop) or the

`fnstenv` / `D9 EE D9 74 24 F4 5B` FPU trick recovers EIP for position-independence. Its presence means the code relocates itself and a decoder often follows.

  • **Egg-hunter tags.** Repeated 8-byte marker like `w00tw00t` (`77 30 30 74`

×2) is an egg-hunter searching memory for the real payload.

  • **Null-free / alnum encoding.** No `00` bytes, or all-printable bytes, means

an encoder stub (Metasploit `alpha_mixed`, `shikata_ga_nai`) wraps the real code — you are looking at the decoder, not the payload.

Static Disassembly at the Right Base

The single most common mistake is disassembling at the wrong architecture or mid-instruction. Try both widths and read which one produces a sane prologue.

# objdump on a raw blob — set the arch explicitly, Intel syntax
objdump -D -b binary -m i386        -M intel sc.bin     # 32-bit
objdump -D -b binary -m i386:x86-64 -M intel sc.bin     # 64-bit

# ndisasm — quick, and -e skips a leading offset if the entry is not at 0
ndisasm -b 32 sc.bin
ndisasm -b 64 sc.bin

# rizin / radare2: assemble/disassemble a blob directly
rasm2 -a x86 -b 32 -d "$(xxd -p sc.bin | tr -d '\n')"
r2 -a x86 -b 32 -m 0x400000 sc.bin      # load raw at a base
#   e asm.arch=x86 ; e asm.bits=32 ; s 0 ; pd 60      disassemble entry
#   pdf @ 0                                            treat entry as a function

To load raw shellcode in Ghidra/IDA/Cutter: import as a **raw binary**, set the processor (x86:LE:32 or x86:LE:64), pick a base address, and **define an entry point / disassemble at the true first instruction** — not offset 0 if a length prefix or junk sled precedes it. Without a defined entry the auto-analyzer will disassemble data as code and the listing is noise.

Decode Self-Decoders

Encoded shellcode is a small decoder stub followed by an encrypted body. Do not try to read the body statically — disassemble only the stub, then let it run.

  • **Recognize the stub.** A short loop with `xor`/`add`/`sub`/`rol` against a

running pointer, a length counter, and a key or key schedule. The loop's tail jumps back until the counter hits zero, then falls through into the decoded body.

  • **shikata_ga_nai.** Metasploit's polymorphic feedback XOR decoder: a GetPC

block, then an XOR loop where each key word is mutated by t

Read more
Ships withsecskills

**Security skills for Claude Code that encode the judgment professionals bring, not another wrapper around a scanner.** Offense, defense, and the reverse-engineering and code-audit core both sides share. Every skill is fact-checked against primary sources.

Get the whole plugin

Other skills on secskills.