Reference

Encoders API

The encoder framework — AbstractEncoder, EncoderRegistry, xor, and chunk.

Encoders API

app/encoders/ is a pluggable, post-compile encoding framework. Encoders operate on the compiled shellcode bytearray and return a complete NASM program (decoder stub + encoded payload) plus metadata.

AbstractEncoder

app/encoders/base.py defines the abstract interface every encoder implements. Subclasses must provide:

  • name (property) — short unique identifier, e.g. "xor"
  • description (property) — one-line human description
  • encode(payload, os_, arch, **params) — returns (nasm_code, metadata)
  • list_params() — returns a list of {name, type, required, default, description} dicts

EncoderRegistry

app/encoders/registry.py is a singleton (app/utils/singleton.py) holding all registered encoders.

MethodDescription
register(cls)Instantiate and register an encoder class; raises TypeError if it is not an AbstractEncoder subclass and ValueError if the name is already taken.
get(name)Return a registered encoder by name; raises KeyError if unknown.
list_all()Return {name, description, params} for every encoder (MCP list_encoders / CLI discovery).
names()Return a sorted list of registered encoder names (CLI --enc choices).

The built-in encoders are auto-registered on import of the app.encoders package (app/encoders/__init__.py).

xor — XorEncoder

Single-byte XOR. payload[i] ^ key.

  • key (int, optional) — XOR key 0x01–0xFF. Omitted: auto-selects the first byte not present in the payload (null-free guarantee). 0x00 is rejected; a key that would produce null bytes raises ValueError.
  • Decoder stub: templates/decoders/{os}_{arch}/xor_decoder.asm (jmp/call/pop pattern).
from app.encoders import XorEncoder

enc = XorEncoder()
nasm, meta = enc.encode(payload, "posix", "64")        # auto key
nasm, meta = enc.encode(payload, "posix", "64", key=0x41)

chunk — ChunkEncoder

Sentinel replacement. Every 0x00 byte is replaced with a sentinel byte.

  • sentinel (int, optional) — the replacement byte. Omitted: prefers 0x90 (NOP) if unused, else the first unused byte. A value already present in the payload raises ValueError; 0x00 is rejected.
  • Decoder stub: templates/decoders/{os}_{arch}/chunk_decoder.asm.
from app.encoders import ChunkEncoder

enc = ChunkEncoder()
nasm, meta = enc.encode(payload, "win", "64")         # auto sentinel
nasm, meta = enc.encode(payload, "win", "64", sentinel=0x90)

Encoder metadata

KeyPresent inMeaning
encoderbothEncoder name (xor or chunk)
original_sizebothByte length of the input payload
encoded_sizebothByte length of the encoded payload
null_freebothTrue if the encoded payload contains no 0x00 bytes
keyxorThe XOR key used (hex string)
sentinelchunkThe sentinel byte used (hex string)
null_countchunkNumber of 0x00 bytes replaced

Writing a custom encoder

Subclass AbstractEncoder, implement the four members, then register it:

from app.encoders import EncoderRegistry
from app.encoders.base import AbstractEncoder

class Rot13Encoder(AbstractEncoder):
    name = "rot13"
    description = "Illustrative placeholder encoder."
    def encode(self, payload, os_, arch, **params):
        return "db 0x00", {"encoder": self.name}
    def list_params(self):
        return []

EncoderRegistry().register(Rot13Encoder)

Once registered, the encoder is available via --enc rot13 (CLI), the MCP encode_shellcode tool, and EncoderRegistry().get("rot13").

Copyright © 2026