Guides

Template Syntax

How the ShellSnip template engine resolves snippets, helpers, and parameters.

Template Syntax

TemplateManager (app/managers/templateManager.py) implements a small template language used by both recipes and custom code passed to generate_from_template / compile_shellcode.

1. Snippet inclusion — [[SNIPPET_NAME]]

Include a reusable ASM block by name (case-insensitive, matched against .asm files in templates/snippets/{os}_{arch}/):

[[SETUP_HASH_RESOLVER]]

Pass parameters as inline JSON after a comma:

[[RESOLVE_API_HASH, {"base": "rbp", "name": "WinExec", "rr": "r14"}]]

Snippets can include other snippets. Resolution is recursive with a hard limit of 10 levels (TemplateManager.MAX_RESOLVE_DEPTH); exceeding it raises RecursionError.

2. Helper functions — #function()#

Call a whitelisted Python function from app/utils/helpers.py to generate ASM dynamically:

#push_string(_params.cmd)#
#push_command(_params.cmd, _params.visible)#
#api_hash("WinExec")#

Only names listed in TemplateManager.ALLOWED_HELPERS may be called; any other name raises ValueError: Helper function '<name>' is not allowed. The call is evaluated with Python's eval() against a restricted _params object, so referenced helpers must be whitelisted — this is not a general-purpose sandbox.

Available helpers

HelperDescription
push_string(cmd, invert=True, reg='rax')Push a string on the stack, 8 bytes at a time, LittleEndian-reversed; optionally NOT-inverted.
push_command(cmd, visible=True, reg='rax')push_string() plus an RDX visibility flag (SW_HIDE / SW_SHOWNORMAL) for WinExec-style calls.
push_download_exec(url, tmp, reg='rax')Builds and pushes a wget <url> -O <tmp> && chmod +x <tmp> && <tmp> command string (Linux dlexec).
push_sockaddr_in(ip, port, reg='rax')Pushes a sockaddr_in structure using a NOT-trick to avoid null bytes.
api_hash(name)Computes the ROR13 hash of a Windows API name, e.g. api_hash('WinExec').
gnu_hash(name)Computes the djb2 hash of an ELF symbol name, e.g. gnu_hash('system').
format_shellcode(shellcode_hex)Converts a hex string into db 0xNN, ... NASM directives, 16 bytes per line.
shellcode_len(shellcode_hex)Returns the byte count of a hex-encoded shellcode string.
set_counter(value)Zeroes RCX and adds a counter (string length, or integer value).

3. Parameter access — #_params.key#

Direct substitution of a value from the params dict passed to load() / render():

mov #_params.rr#, rax    ; Store result in user-specified register
mov ecx, #_params.pid#   ; Target process ID

Only a single attribute level is supported (_params.key, not _params.key.sub). If key is absent from the params dict, the placeholder is left unresolved (__replace_param returns the snippet unchanged for that occurrence).

Recipe parameters

Recipewin_64posix_64
execcmd (str), visible (bool)cmd (str), visible (bool — ignored on Linux)
dlexecurl (str), tmp (str), visible (bool)url (str), tmp (str)
connectip (str), port (int)ip (str), port (int)
injectpid (int), shellcode (str, hex-encoded)pid (int), shellcode (str, hex-encoded)

Custom templates

TemplateManager.render(template_code, params) applies the same pipeline (parameter/helper evaluation, then recursive snippet resolution) to arbitrary ASM source instead of a named recipe file — this is what powers the generate_from_template and compile_shellcode MCP tools.

; Custom template using existing snippets
[[SETUP_HASH_RESOLVER]]
[[RESOLVE_API_HASH, {"base": "rbp", "name": "WinExec", "rr": "r14"}]]

#push_command(_params.cmd, _params.visible)#

mov rcx, rsp
call r14
Copyright © 2026