Guides

ASM Blocks

Assembling and disassembling an isolated ASM block — validating instructions without emitting a payload.

ASM Blocks

Recipes generate whole payloads. Sometimes you have a fragment — a few instructions you want to check before wiring them into a template, an offset calculation you want to confirm, or raw bytes you need to read back.

ShellSNIP assembles and disassembles isolated blocks for that, independently of any recipe.

CLI

# Assemble a block and print its bytes
./generate.py asm "push rax"

# Validate a block without emitting a payload (non-zero exit on any problem)
./generate.py -o /dev/null asm "xor eax, eax
mov al, 60
syscall"

# Read the block from stdin
echo "ret" | ./generate.py asm -

# Disassemble raw bytes, flagging forbidden ones
./generate.py disasm "31c0b03c0f05"
[+] Block assembled: 1 byte(s)
[+] Formatted output (raw):
\x50
[+] 00000000  31c0  xor eax,eax
00000002  b03c  mov al,0x3c
00000004  0f05  syscall
[+] Bad characters: 1 configured, 0 occurrence(s) in 6 byte(s)

disasm uses --bad-chars to flag instructions that would be unusable: it reports the mnemonic alongside the instructions holding a forbidden byte, so you can see which instruction is the problem, not just that one exists.

Python

from app.utils.assembler import assemble
from app.utils.disassembler import disassemble, annotate, format_listing

blob = assemble("push rax", os_="posix", arch="64")     # -> bytes
listing = disassemble("31c0b03c0f05", os_="posix", arch="64")
print(format_listing(listing))

MCP

assemble_block(code="push rax\nret 4", os_="posix", arch="64")   # -> size + hex
disassemble(input_data="31c0b03c000f05", bad_chars=r"\x00")      # bad chars flagged

How a block is assembled

A block is assembled flat, as a standalone blob: no linking, no section layout, no relocations. A block that references a symbol will not resolve — that is the point of isolating it from the recipe pipeline.

NASM is invoked with -f bin and an explicit [BITS n] directive for the requested architecture, requested through the repository's run_nasm wrapper in strict mode:

NASM exits 0 when a mistyped instruction is taken for a label, emitting only a warning. Strict mode turns that into a refusal, so a typo cannot be silently accepted as a label.

Diagnostics are reported against your line numbers, not NASM's: the injected [BITS n] directive counts as a line, so the offset is subtracted before a line number is reported.

Platform parameters

assemble_block and disassemble accept os_ (win/posix/macos) and arch (32/64). The architecture selects the instruction encoding ([BITS 32] vs [BITS 64]); the OS parameter is accepted for interface consistency with the recipe tools.

Both 32- and 64-bit blocks are fully supported, including the short forms that differ between them — mov eax, eax assembles to 89 c0 in either mode, with different semantics for the upper 32 bits.

Choosing a flat blob over a container

When you need an object file rather than a flat blob, the block has to carry its own sections and entry symbol. The flat form is the one the recipe pipeline uses and the one that round-trips through disassemble, which is why it is the default here.

Copyright © 2026