Skip to content

server

build_firmware(firmware_name) async

Runs 'make build FIRMWARE=name'. Compiles the instrumented binary.

Source code in wintermute/integrations/surgeon/server.py
243
244
245
246
@mcp.tool()
async def build_firmware(firmware_name: str) -> str:
    """Runs 'make build FIRMWARE=name'. Compiles the instrumented binary."""
    return _run(["make", "build", f"FIRMWARE={firmware_name}"])

create_hook_skeleton(firmware_name, peripheral_name, address_base, peripheral_type='GENERIC', malicious_snippet='// No malicious payload injected') async

Generates a C-based emulation hook for a specific peripheral type.

Parameters:

Name Type Description Default
firmware_name str

Target firmware folder.

required
peripheral_name str

Name of the peripheral (e.g., 'wifi_chip').

required
address_base str

Base address hex string (e.g., '0x40001000').

required
peripheral_type str

One of [UART, WIFI, BLUETOOTH, ETHERNET, USB, PCIE, JTAG, TPM].

'GENERIC'
malicious_snippet str

C code to inject for fault injection or fuzzing. Example: "*val = 0xFFFFFFFF; // Integer overflow attack"

'// No malicious payload injected'
Source code in wintermute/integrations/surgeon/server.py
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
@mcp.tool()
async def create_hook_skeleton(
    firmware_name: str,
    peripheral_name: str,
    address_base: str,
    peripheral_type: str = "GENERIC",
    malicious_snippet: str = "// No malicious payload injected",
) -> str:
    """
    Generates a C-based emulation hook for a specific peripheral type.

    Args:
        firmware_name: Target firmware folder.
        peripheral_name: Name of the peripheral (e.g., 'wifi_chip').
        address_base: Base address hex string (e.g., '0x40001000').
        peripheral_type: One of [UART, WIFI, BLUETOOTH, ETHERNET, USB, PCIE, JTAG, TPM].
        malicious_snippet: C code to inject for fault injection or fuzzing.
                           Example: "*val = 0xFFFFFFFF; // Integer overflow attack"
    """
    handler_dir = Path(SURGEON_ROOT) / "src" / "runtime" / "handlers" / firmware_name
    handler_dir.mkdir(parents=True, exist_ok=True)
    file_path = handler_dir / f"{peripheral_name}.c"

    ptype = peripheral_type.upper()

    # --- 1. Define The Logic Templates (C Code) ---

    if ptype in ["WIFI", "BLUETOOTH", "ZIGBEE"]:
        # SDR MODE: Treat writes as "Transmits" and reads as "Receives"
        body = f"""
    // --- {ptype} SDR Emulation (Radio Abstraction) ---
    uint32_t offset = addr - {address_base};

    // Offset 0x00: Status Register
    // Offset 0x04: TX Buffer (Firmware writes here to send)
    // Offset 0x08: RX Buffer (Firmware reads here to receive)

    if (offset == 0x04) {{
        printf("[{peripheral_name}] [TX-RADIO] Packet Broadcast: 0x%08x\\n", val);
        // MALICIOUS HOOK: Intercept outbound traffic?
    }}
    else if (offset == 0x08) {{
        // MALICIOUS HOOK: Inject Inbound Radio Attack
        {malicious_snippet}

        if (*val == 0) {{
             *val = 0xDEADBEEF; // Default "noise" on the air
        }}
        printf("[{peripheral_name}] [RX-RADIO] Firmware read radio data: 0x%08x\\n", *val);
    }}
    else {{
        *val = 1; // Always Ready status
    }}
        """

    elif ptype == "ETHERNET":
        body = f"""
    // --- Ethernet MAC Emulation ---
    uint32_t offset = addr - {address_base};

    if (offset == 0x00) {{ // MAC Control Register
        printf("[{peripheral_name}] MAC Config Write: 0x%08x\\n", val);
    }}
    else if (offset >= 0x100 && offset < 0x200) {{ // RX Descriptor Ring
        // MALICIOUS HOOK: Buffer Overflow via huge packet size?
        {malicious_snippet}
        printf("[{peripheral_name}] RX Descriptor Access\\n");
    }}
        """

    elif ptype == "USB":
        body = f"""
    // --- USB Endpoint Emulation ---
    uint32_t offset = addr - {address_base};

    // Emulate Endpoint 0 (Control) setup packets
    if (offset == 0x00) {{
        printf("[{peripheral_name}] USB EP0 Setup\\n");
        // MALICIOUS HOOK: Fuzzing USB Descriptor responses
        {malicious_snippet}
    }}
        """

    elif ptype == "JTAG":
        body = f"""
    // --- JTAG TAP Controller Spy ---
    // We don't emulate the chain, we just log the activity to find debug backdoors.

    if (val & 0x1) printf("[{peripheral_name}] TMS High\\n");
    if (val & 0x2) printf("[{peripheral_name}] TCK Clock\\n");

    // MALICIOUS HOOK: Unlock Debug Interface?
    {malicious_snippet}
        """

    elif ptype == "PCIE":
        body = f"""
    // --- PCIe Config Space ---
    uint32_t offset = addr - {address_base};

    // Vendor ID / Device ID at 0x00
    if (offset == 0x00) {{
        *val = 0x80868086; // Fake Intel ID
        printf("[{peripheral_name}] PCIe ID Read\\n");
    }}
    // MALICIOUS HOOK: DMA Attack emulation
    {malicious_snippet}
        """

    else:
        # UART / Generic
        body = f"""
    // --- Generic / UART Emulation ---
    uint32_t offset = addr - {address_base};
    if (offset == 0x04) {{
        printf("%c", (char)(val & 0xFF)); // Print UART output to console
    }}
    // MALICIOUS HOOK: Fault Injection
    {malicious_snippet}
        """

    # --- 2. Construct Final C File ---

    c_content = f"""
/* AUTOMATICALLY GENERATED BY SURGEON AGENT */
#include "surgeon/emu_handler.h"
#include <stdio.h>

// Hook for {peripheral_name} ({ptype}) at {address_base}

bool {peripheral_name}_read_hook(struct emu_state *state, uint32_t pc, uint32_t addr, uint32_t *val, uint32_t size) {{
    {body}
    return true;
}}

bool {peripheral_name}_write_hook(struct emu_state *state, uint32_t pc, uint32_t addr, uint32_t val, uint32_t size) {{
    {body}
    return true;
}}
    """

    with open(file_path, "w") as f:
        f.write(c_content)

    return f"Created {ptype} hook at {file_path} with malicious payload size: {len(malicious_snippet)} bytes."

get_fuzzer_stats(firmware_name) async

Reads the fuzzer_stats file from the AFL output directory.

Source code in wintermute/integrations/surgeon/server.py
257
258
259
260
261
262
263
264
@mcp.tool()
async def get_fuzzer_stats(firmware_name: str) -> str:
    """Reads the fuzzer_stats file from the AFL output directory."""
    stats_file = FIRMWARE_DIR / firmware_name / "fuzz_out" / "default" / "fuzzer_stats"
    if not stats_file.exists():
        return "Fuzzer stats not found. Is the fuzzer running?"
    with open(stats_file, "r") as f:
        return f.read()

list_firmware_symbols(firmware_subpath) async

Lists function symbols in an ELF file. Essential for finding hook addresses.

Source code in wintermute/integrations/surgeon/server.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
@mcp.tool()
async def list_firmware_symbols(firmware_subpath: str) -> str:
    """Lists function symbols in an ELF file. Essential for finding hook addresses."""
    elf_path = FIRMWARE_DIR / firmware_subpath
    if not elf_path.exists():
        return "Error: ELF file not found."

    # Try generic nm, then cross-compile nm
    nm_bin = "nm"
    if shutil.which("arm-none-eabi-nm"):
        nm_bin = "arm-none-eabi-nm"

    try:
        # -n: sort numeric, -C: demangle, --defined-only
        output = subprocess.check_output(
            [nm_bin, "-n", "-C", "--defined-only", str(elf_path)], text=True
        )
        symbols = []
        for line in output.splitlines():
            parts = line.split()
            if len(parts) >= 3 and parts[1].upper() in ("T", "t"):
                symbols.append({"addr": f"0x{parts[0]}", "name": " ".join(parts[2:])})
        return (
            json.dumps(symbols[:200], indent=2) + f"\n... ({len(symbols) - 200} more)"
        )
    except Exception as e:
        return f"Error running nm: {e}"

start_fuzzing(firmware_name) async

Runs 'make run-fuzz FIRMWARE=name'. Starts the AFL++ docker container.

Source code in wintermute/integrations/surgeon/server.py
249
250
251
252
253
254
@mcp.tool()
async def start_fuzzing(firmware_name: str) -> str:
    """Runs 'make run-fuzz FIRMWARE=name'. Starts the AFL++ docker container."""
    # We define a timeout or run detached in production.
    # For now, we return the startup output.
    return _run(["make", "run-fuzz", f"FIRMWARE={firmware_name}"])

write_config_file(rel_path, content) async

Writes a configuration file (like YAML) to the SURGEON directory.

Source code in wintermute/integrations/surgeon/server.py
227
228
229
230
231
232
233
234
235
236
237
@mcp.tool()
async def write_config_file(rel_path: str, content: str) -> str:
    """Writes a configuration file (like YAML) to the SURGEON directory."""
    full_path = SURGEON_ROOT / rel_path
    try:
        full_path.parent.mkdir(parents=True, exist_ok=True)
        with open(full_path, "w") as f:
            f.write(content)
        return f"Wrote {len(content)} bytes to {rel_path}"
    except Exception as e:
        return f"Error writing file: {e}"