/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
$ npx -y skills add trilwu/secskills --skill analyzing-shellcode --agent claude-codeHow 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.mdname: 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
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
**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.

