ASM Blocks
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
0when 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.