A template-based framework for rapidly creating new AdaptixC2 beacon agents in Go, C++, or Rust.
Instead of copying & modifying existing agents, run the generator and implement only the platform-specific methods you need.
.\generator.ps1 # then select "1) Generate Agent"
.\generator.ps1 -Mode agent # or skip the menu
.\generator.ps1 -Mode agent -Language cpp -Toolchain mingw # C++ implant
.\generator.ps1 -Mode agent -Language rust # Rust implant
.\generator.ps1 -Mode agent -Language go -Toolchain go-garble # Go + garble obfuscation
.\generator.ps1 -Mode agent -OutputDir ..\AdaptixC2\AdaptixServer\extenders./generator.sh # then select "1) Generate Agent"
MODE=agent ./generator.sh # or skip the menu
MODE=agent LANGUAGE=cpp ./generator.sh # C++ implant
MODE=agent LANGUAGE=rust ./generator.sh # Rust implant
MODE=agent LANGUAGE=go TOOLCHAIN=go-garble ./generator.sh # Go + garble
MODE=agent OUTPUT_DIR=../AdaptixC2/AdaptixServer/extenders ./generator.shcd agent
chmod +x generator.sh
./generator.shcd agent
.\generator.ps1
.\generator.ps1 -OutputDir ..\..\AdaptixC2\AdaptixServer\extendersThe generator asks for:
| Prompt | Description | Example |
|---|---|---|
| Agent name | Lowercase alphanumeric identifier | phantom |
| Watermark | 8-char hex (auto-generated if empty) | a1b2c3d4 |
| Protocol | Wire-format from protocols/ |
adaptix_default |
| Language | Implant language: go, cpp, rust | go |
| Toolchain | Build toolchain (shown when multiple match) | go-standard |
When -Language / -Toolchain are omitted, the generator presents interactive menus.
Languages are discovered from agent/templates/implant/; toolchains from agent/toolchains/*.yaml.
If only one toolchain matches the language, it is auto-selected.
| Language | Default toolchain | Alternatives |
|---|---|---|
go |
go-standard |
go-garble |
cpp |
mingw |
-- |
rust |
cargo |
-- |
Protocols define shared crypto, constants, and wire types between agents and listeners. When a protocol is selected, the generator overlays:
crypto/crypto.go— fromprotocols/<name>/crypto.go.tmplprotocol/protocol.go— merged fromprotocols/<name>/types.go.tmpl+constants.go.tmplpl_utils.go— merged from the same protocol templates (server-side)
For C++ and Rust implants, protocols can also provide language-specific overlays in protocols/<name>/implant/cpp/ and protocols/<name>/implant/rust/. These override the base implant templates for protocol structs, wire format, and tasks.
Bundled: adaptix_default (RC4 + binary), adaptix_gopher (AES-GCM + msgpack), spectre (ASCON-128 + binary reflection).
Agents and listeners using the same protocol share identical encryption and wire types.
See the root README for protocol creation and crypto swap documentation.
The server-side plugin files are identical for all languages.
The implant directory (src_<name>/) varies by language.
<name>_agent/
├── config.yaml # Plugin manifest (name, watermark, listeners)
├── go.mod # Plugin Go module
├── Makefile # Plugin build (agent_<name>.so)
├── pl_utils.go # Wire types & command constants
├── pl_main.go # Server-side plugin logic
├── pl_build.go # Build logic (Go)
├── ax_config.axs # UI & command registration
└── src_<name>/
├── go.mod # Implant Go module
├── Makefile # Cross-platform implant build
├── config.go # Encrypted profile placeholder
├── main.go # Connection loop
├── tasks.go # Command dispatch
├── crypto/
│ └── crypto.go # AES-256-GCM (ready to use)
├── protocol/
│ └── protocol.go # Wire types & framing (ready to use)
└── impl/
├── interfaces.go # Interface contracts (read-only)
├── agent.go # Cross-platform: Stealth + Transport
├── agent_linux.go # Linux stubs ← IMPLEMENT
├── agent_windows.go # Windows stubs ← IMPLEMENT
└── agent_darwin.go # macOS stubs ← IMPLEMENT
<name>_agent/
├── (same plugin files)
├── pl_build.go # Build logic (C++: profile_gen.h + make)
├── ax_config.axs # C++: arch, format (Exe/DLL/Shellcode), svc_name
└── src_<name>/
├── Makefile # MinGW cross-compile targets
├── main.cpp / config.h / config.cpp
├── agent.h / agent.cpp
├── crypto.h / crypto.cpp
├── protocol.h / protocol.cpp
└── impl/
└── agent_windows.h / agent_windows.cpp ← IMPLEMENT
<name>_agent/
├── (same plugin files)
├── pl_build.go # Build logic (Rust: config.rs + cargo build)
├── ax_config.axs # OS + arch selection
└── src_<name>/
├── Cargo.toml # Release profile (size-optimised)
├── Makefile # cargo build targets
└── src/
├── main.rs # Entry point
├── config.rs # Profile data (populated at build time)
├── crypto.rs # Encrypt/decrypt stubs
├── protocol.rs # Wire protocol + watermark
└── agent.rs # Connector trait + Agent ← IMPLEMENT
All platform-specific behavior is defined via Go interfaces in impl/interfaces.go.
type Stealth interface {
IsDebugged() bool // Anti-debug check — return true to abort
Masquerade() // Process masquerading (e.g. ppid spoofing)
OnStart() // One-time setup before main loop
}type Platform interface {
GetCP() uint32 // Console code page (Windows ACP, 65001 for UTF-8)
IsElevated() bool // Running as root / admin
GetOsVersion() string // "Windows 11 23H2" / "Ubuntu 22.04" / etc.
NormalizePath(p string) string // Expand ~ or . to absolute path
}type FileSystem interface {
GetListing(dir string) (string, []protocol.DirEntry, error)
CopyFile(src, dst string) error
CopyDir(src, dst string) error
}type Execution interface {
RunShell(cmd string, risky, piped bool) (string, error)
ListProcesses() ([]protocol.ProcessEntry, error)
CaptureScreenshot() ([]byte, error)
}type Transport interface {
Dial(address string, profile *protocol.Profile) (net.Conn, error)
}A default TCP/TLS Dial is provided in agent.go. Override it for HTTP, DNS, SMB, or custom channels.
type AgentImpl interface {
Stealth
Platform
FileSystem
Execution
Transport
}- Run the generator:
.\generator.ps1 -Mode agent -Name <name> - Edit platform files in
impl/:agent_linux.go,agent_windows.go,agent_darwin.go— fill in stubsagent.go— customize Stealth methods, override Transport if needed
- Don't modify
interfaces.go,crypto/, orprotocol/ - Build:
go mod tidy cd src_<name> && go mod tidy && cd .. make full
- Run the generator:
.\generator.ps1 -Mode agent -Name <name> -Language cpp - Edit
impl/agent_windows.cpp— platform-specific logic - Build:
Formats:
go mod tidy make full
exe,service_exe,dll,shellcode(selected in Adaptix UI at build time)
- Run the generator:
.\generator.ps1 -Mode agent -Name <name> -Language rust - Edit
src/agent.rs— implement theConnectortrait - Build:
Cross-compile targets:
go mod tidy make full
rustup target add x86_64-unknown-linux-gnu x86_64-pc-windows-gnu
Copy agent_<name>.so, config.yaml, and ax_config.axs to the server's extenders directory.
| Package | Purpose |
|---|---|
crypto/ |
Encrypt/Decrypt (from protocol, e.g. AES-256-GCM) |
protocol/ |
Wire types, command constants, serialization helpers (from protocol) |
- Add a
COMMAND_*constant in bothpl_utils.goandprotocol/protocol.go. - Add
Params*/Ans*structs in both files. - Register the command in
ax_config.axsusingcreate_command(). - Handle it in
pl_main.go→CreateCommand()andProcessData(). - Implement execution in
tasks.go→TaskProcess().
- Stealth first: Implement
IsDebugged()andMasquerade()before anything else — they run at startup. - Start with Linux:
agent_linux.gois the easiest to implement. Test on Linux, then port. - RunShell: The
riskyflag indicates interactive / PTY mode.pipedmeans capture output via pipe. - Screenshots: Return raw PNG bytes from
CaptureScreenshot(). - Transport: The default TCP/TLS dial works for GopherTCP listeners. Override for other protocols.
AdaptixC2 uses string name matching to pair agents with listeners:
- Your agent declares supported listeners in
config.yaml→listeners:(list of names) - Each listener plugin declares its
listener_name:in its ownconfig.yaml - Names must match exactly — the Adaptix UI only shows matching listeners when building your agent
| Agent | listeners: |
Supported protocols |
|---|---|---|
gopher_agent |
["GopherTCP"] |
TCP only |
beacon_agent |
["BeaconHTTP", "BeaconTCP", "BeaconSMB", "BeaconDNS"] |
HTTP, TCP, SMB, DNS |
true— embed multiple listener profiles in a single build (agent tries each at runtime)false— build accepts only one listener profile
- Edit
config.yaml→ add the listener name to thelisteners:list - In
pl_main.go→GenerateProfiles(), add aswitch listenerTypebranch to parse the new listener's protocol-specific fields - In the implant's
impl/agent.go, implement or overrideDial()if the new protocol requires a different transport (e.g. HTTP polling, DNS tunneling)
The default scaffold comes with GopherTCP support. The listener selection happens in the Adaptix UI at build time, not during generation.