+++ 
title = "Developing Commands" 
chapter = false 
weight = 20 
pre = "3. " 
+++

# Developing New Commands for Angerona

This guide provides a step-by-step walkthrough for adding new commands to the Angerona agent. Understanding this process is essential for extending Angerona's capabilities.

## 📋 Overview

Adding a new command to Angerona requires coordination across multiple components:

1. **Python Command Definition** - Defines the command interface in Mythic
2. **Translator Mapping** - Maps command names to hex codes for agent communication
3. **C Agent Implementation** - Implements the actual command logic
4. **Command Dispatcher Registration** - Registers the handler in the command dispatcher
5. **Builder Configuration** - Enables command selection during payload creation

## 🏗️ Architecture Overview

```mermaid
graph LR
    A[Python Command] --> B[Translator]
    B --> C[C Agent Handler]
    C --> D[Command Dispatcher]
    D --> E[Builder Mapping]
    
    style A fill:#e1f5ff
    style B fill:#fff4e1
    style C fill:#e8f5e9
    style D fill:#fce4ec
    style E fill:#f3e5f5
```

**Data Flow:**
- **Mythic UI** → Python command receives task
- **Python** → Translator converts to binary format with hex code
- **Agent** → C handler processes command and returns response
- **Translator** → Converts response back to Mythic format

## 📝 Step-by-Step Guide

### Step 1: Create Python Command File

Create a new Python file in `Payload_Type/angerona/angerona/agent_functions/` named after your command (e.g., `mycommand.py`).

#### 1.1 Command Arguments Class

Define the argument parsing logic:

```python
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *

class MyCommandArguments(TaskArguments):
    def __init__(self, command_line, **kwargs):
        super().__init__(command_line, **kwargs)
        self.args = [
            CommandParameter(
                name="param1",
                type=ParameterType.String,  # or ParameterType.Number, etc.
                description="Description of parameter",
                parameter_group_info=[ParameterGroupInfo(
                    required=True,  # or False
                    ui_position=1
                )]
            ),
            # Add more parameters as needed
        ]

    async def parse_arguments(self):
        """Parse command-line arguments"""
        if len(self.command_line) == 0:
            raise ValueError("Parameter required")
        # Parse and validate arguments
        self.add_arg("param1", self.command_line)

    async def parse_dictionary(self, dictionary):
        """Parse dictionary arguments (from UI)"""
        self.load_args_from_dictionary(dictionary)
```

#### 1.2 Command Class

Define the main command class:

```python
class MyCommandCommand(CommandBase):
    cmd = "mycommand"  # Command name (lowercase, no spaces)
    needs_admin = False  # Set to True if command requires admin privileges
    help_cmd = "mycommand <param1>"
    description = "Detailed description of what the command does"
    version = 1
    author = "YourName"
    argument_class = MyCommandArguments
    attackmapping = ["T1059"]  # MITRE ATT&CK technique IDs
    
    attributes = CommandAttributes(
        supported_os=[SupportedOS.Windows],
        builtin=False,  # Set to True if command should always be included
        suggested_command=False  # Set to True to suggest in UI
    )

    async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTaskOPSECPreTaskMessageResponse:
        """Optional: OPSEC check before task creation"""
        # Add warnings about detection risks
        return PTTaskOPSECPreTaskMessageResponse(
            TaskID=taskData.Task.ID,
            Success=True,
            OpsecPreBlocked=False,
            OpsecPreBypassRole="operator",
            OpsecPreMessage="OPSEC warning message here"
        )

    async def opsec_post(self, taskData: PTTaskMessageAllData) -> PTTaskOPSECPostTaskMessageResponse:
        """Optional: OPSEC check after task creation"""
        return PTTaskOPSECPostTaskMessageResponse(
            TaskID=taskData.Task.ID,
            Success=True,
            OpsecPostBlocked=False,
            OpsecPostBypassRole="operator",
            OpsecPostMessage="Post-execution warning"
        )

    async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse:
        """Process task creation - called when task is created"""
        response = PTTaskCreateTaskingMessageResponse(
            TaskID=taskData.Task.ID,
            Success=True
        )
        # Add any pre-processing logic here
        # Access arguments: taskData.args.get_arg("param1")
        return response

    async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
        """Process agent response - called when agent sends response"""
        resp = PTTaskProcessResponseMessageResponse(
            TaskID=task.Task.ID,
            Success=True
        )
        # Process response data here if needed
        return resp
```

#### 1.3 Key Attributes

- **`cmd`**: Command name used in Mythic (must match translator mapping)
- **`builtin`**: 
  - `True` = Always included in payload (e.g., `exit`, `sleep`)
  - `False` = Can be selected during payload creation
- **`needs_admin`**: Set to `True` if command requires administrator privileges
- **`attackmapping`**: List of MITRE ATT&CK technique IDs (e.g., `["T1059"]`)

### Step 2: Add Command to Translator

Edit `Payload_Type/angerona/translator/commands_from_c2.py` and add your command to the `commands` dictionary:

```python
commands = {
    # ... existing commands ...
    "mycommand": {"hex_code": 0x99, "input_type": "string"},
}
```

**Important Notes:**
- **`hex_code`**: Choose an unused hex code (0x00-0xFF). Check `comandos.h` for existing codes
- **`input_type`**: 
  - `"string"` - String parameter
  - `"int"` - Integer parameter
  - `None` - No parameters
  - Special types: `"bof_special"`, `"assembly_special"`, `"socks_special"` (see existing commands)

**Hex Code Allocation:**
- `0x00-0x1F`: Reserved for core commands
- `0x20-0x5F`: File system and standard commands
- `0x60-0x6F`: Network tunneling commands
- `0x70-0x7F`: Code execution commands
- `0x80+`: Control commands

### Step 3: Implement C Agent Handler

#### 3.1 Define Command Constant

Edit `Payload_Type/angerona/angerona/agent_code/angerona/comandos.h` and add:

```c
#define MYCOMMAND_CMD  0x99  // Must match hex_code from translator
```

#### 3.2 Implement Handler Function

Create or edit the appropriate C file (e.g., `SistemadeFicheros.c` for file operations, or create a new file):

```c
#include "angerona.h"
#include "comandos.h"
#include "paquete.h"

void MyCommandHandler(PAnalizador analizadorTarea) {
    _inf("===== PROCESSING MYCOMMAND_CMD (0x%02X) =====", MYCOMMAND_CMD);
    
    // Read task UUID (first 36 bytes)
    SIZE_T uuidLen = 36;
    PCHAR taskUuid = getString(analizadorTarea, &uuidLen);
    if (!taskUuid || uuidLen != 36) {
        _err("Error reading task UUID");
        if (analizadorTarea) liberarAnalizador(analizadorTarea);
        return;
    }
    
    // Read parameters based on input_type
    // For string input_type:
    SIZE_T param1Len = 0;
    PCHAR param1 = getString(analizadorTarea, &param1Len);
    
    // For int input_type:
    // UINT32 param1 = (UINT32)getInt32(analizadorTarea);
    
    // Implement your command logic here
    // ...
    
    // Create response packet
    PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);  // TRUE adds agent UUID
    addString(respuesta, taskUuid, FALSE);  // Add task UUID
    
    // Add output to response
    PackageAddFormatPrintf(respuesta, FALSE, "Command executed successfully\n");
    PackageAddFormatPrintf(respuesta, FALSE, "Parameter: %.*s\n", (int)param1Len, param1);
    
    // Optional: Add artifacts (file writes, process creates, etc.)
    // addArtifact(respuesta, "File Write", "C:\\path\\to\\file");
    
    // Optional: Add credentials if discovered
    // addCredential(respuesta, "plaintext", "DOMAIN", "password", "username");
    
    // Send response
    Analizador* resp = mandarPaquete(respuesta);
    liberarPaquete(respuesta);
    if (resp) liberarAnalizador(resp);
    
    if (analizadorTarea) liberarAnalizador(analizadorTarea);
    _inf("===== MYCOMMAND_CMD COMPLETED =====");
}
```

#### 3.3 Key C Functions

- **`getString(analizador, &len)`**: Read string from buffer
- **`getInt32(analizador)`**: Read 32-bit integer
- **`getByte(analizador)`**: Read single byte
- **`getBytes(analizador, &len)`**: Read byte array
- **`nuevoPaquete(type, add_uuid)`**: Create response packet
- **`addString(paquete, str, copy)`**: Add string to packet
- **`PackageAddFormatPrintf(paquete, copy, format, ...)`**: Add formatted output
- **`addArtifact(paquete, type, data)`**: Add artifact (file write, process create, etc.)
- **`addCredential(paquete, type, realm, credential, account)`**: Add credential
- **`mandarPaquete(paquete)`**: Send packet to Mythic
- **`liberarPaquete(paquete)`**: Free packet memory
- **`liberarAnalizador(analizador)`**: Free analyzer memory

### Step 4: Register Handler in Command Dispatcher

Edit `Payload_Type/angerona/angerona/agent_code/angerona/comandos.c` and add your command to the dispatch chain in `handleGetTasking()`:

```c
#ifdef ENABLE_MYCOMMAND_CMD
        else if (tarea == MYCOMMAND_CMD) {
            _inf("===== PROCESSING MYCOMMAND_CMD (0x%02X) =====", tarea);
            MyCommandHandler(analizadorTarea);
            _inf("===== MYCOMMAND_CMD COMPLETED =====");
        }
#endif
```

**Placement:**
- Add after `SLEEP_CMD` (which always closes with `}`)
- Before the final `else` block for unknown commands
- Use `else if` (not `} else if`) to maintain the chain

**Important:** The command handler must be wrapped in `#ifdef ENABLE_MYCOMMAND_CMD` to support conditional compilation.

### Step 5: Add to Builder Mapping

Edit `Payload_Type/angerona/angerona/agent_functions/builder.py` and add your command to the `command_to_define` dictionary (around line 209):

```python
command_to_define = {
    # ... existing commands ...
    "mycommand": "ENABLE_MYCOMMAND_CMD",
}
```

This maps the Python command name to the C preprocessor define name.

### Step 6: Update Includes (if needed)

If your command requires a new header file:

**In `comandos.h`:**
```c
#ifdef ENABLE_MYCOMMAND_CMD
#include "mycommand.h"
#endif
```

**In `comandos.c`:**
```c
#ifdef ENABLE_MYCOMMAND_CMD
#include "mycommand.h"
#endif
```

### Step 7: Add Forward Declaration (if needed)

If your handler function is defined in a separate file, add a forward declaration in `comandos.c`:

```c
#ifdef ENABLE_MYCOMMAND_CMD
void MyCommandHandler(PAnalizador analizadorTarea);
#endif
```

## 🔍 Complete Example: Adding a "ping" Command

Let's walk through a complete example of adding a simple `ping` command that sends a message back to Mythic.

### Example 1: Python Command File (`ping.py`)

```python
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *

class PingArguments(TaskArguments):
    def __init__(self, command_line, **kwargs):
        super().__init__(command_line, **kwargs)
        self.args = [
            CommandParameter(
                name="message",
                type=ParameterType.String,
                description="Message to echo back",
                parameter_group_info=[ParameterGroupInfo(required=False)]
            ),
        ]

    async def parse_arguments(self):
        if len(self.command_line) > 0:
            self.add_arg("message", self.command_line)
        else:
            self.add_arg("message", "pong")

    async def parse_dictionary(self, dictionary):
        self.load_args_from_dictionary(dictionary)

class PingCommand(CommandBase):
    cmd = "ping"
    needs_admin = False
    help_cmd = "ping [message]"
    description = "Echo a message back to verify agent communication"
    version = 1
    author = "YourName"
    argument_class = PingArguments
    attackmapping = []
    
    attributes = CommandAttributes(
        supported_os=[SupportedOS.Windows],
        builtin=False
    )

    async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse:
        response = PTTaskCreateTaskingMessageResponse(
            TaskID=taskData.Task.ID,
            Success=True
        )
        return response

    async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
        resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
        return resp
```

### Example 2: Translator Mapping

In `translator/commands_from_c2.py`:

```python
commands = {
    # ... existing commands ...
    "ping": {"hex_code": 0x90, "input_type": "string"},
}
```

### Example 3: C Header Definition

In `comandos.h`:

```c
#define PING_CMD  0x90
```

### Example 4: C Handler Implementation

Create `ping.c`:

```c
#include "angerona.h"
#include "comandos.h"
#include "paquete.h"

void PingHandler(PAnalizador analizadorTarea) {
    _inf("===== PROCESSING PING_CMD (0x%02X) =====", PING_CMD);
    
    // Read task UUID
    SIZE_T uuidLen = 36;
    PCHAR taskUuid = getString(analizadorTarea, &uuidLen);
    
    // Read message parameter
    SIZE_T messageLen = 0;
    PCHAR message = getString(analizadorTarea, &messageLen);
    
    // Create response
    PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);
    addString(respuesta, taskUuid, FALSE);
    
    // Echo the message back
    PackageAddFormatPrintf(respuesta, FALSE, "Ping received: %.*s\n", (int)messageLen, message);
    PackageAddFormatPrintf(respuesta, FALSE, "Agent is alive and responding!\n");
    
    // Send response
    Analizador* resp = mandarPaquete(respuesta);
    liberarPaquete(respuesta);
    if (resp) liberarAnalizador(resp);
    
    if (analizadorTarea) liberarAnalizador(analizadorTarea);
    _inf("===== PING_CMD COMPLETED =====");
}
```

### Example 5: Register in Dispatcher

In `comandos.c`, add to the dispatch chain:

```c
#ifdef ENABLE_PING_CMD
        else if (tarea == PING_CMD) {
            _inf("===== PROCESSING PING_CMD (0x%02X) =====", tarea);
            PingHandler(analizadorTarea);
            _inf("===== PING_CMD COMPLETED =====");
        }
#endif
```

### Example 6: Builder Mapping

In `builder.py`:

```python
command_to_define = {
    # ... existing commands ...
    "ping": "ENABLE_PING_CMD",
}
```

## 🎯 Advanced Features

### Adding Artifacts

Artifacts track system modifications for OPSEC and reporting:

```c
// File write artifact
addArtifact(respuesta, "File Write", "C:\\path\\to\\file");

// Process create artifact
addArtifact(respuesta, "Process Create", "cmd.exe /c whoami");

// Network connection artifact
addArtifact(respuesta, "Network Connection", "Connection to 192.168.1.1:80");

// Registry write artifact
addArtifact(respuesta, "Registry Write", "HKEY_LOCAL_MACHINE\\...");
```

### Adding Credentials

Automatically extract and report credentials:

```c
// Plaintext password
addCredential(respuesta, "plaintext", "DOMAIN", "password123", "username");

// NTLM hash
addCredential(respuesta, "hash", "", "aad3b435b51404ee:hashvalue", "Administrator");

// Certificate
addCredential(respuesta, "certificate", "", "cert_data_base64", "account_name");
```

### OPSEC Checking

Implement OPSEC warnings in Python:

```python
async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTaskOPSECPreTaskMessageResponse:
    message = "⚠️ OPSEC WARNING\n\n"
    message += "This command may be detected by:\n"
    message += "  • EDR/XDR solutions\n"
    message += "  • Network monitoring\n"
    
    return PTTaskOPSECPreTaskMessageResponse(
        TaskID=taskData.Task.ID,
        Success=True,
        OpsecPreBlocked=False,  # Set to True to block execution
        OpsecPreBypassRole="operator",
        OpsecPreMessage=message
    )
```

### Process Browser Integration

To integrate with Mythic's Process Browser:

```python
supported_ui_features = ["process_browser:list"]  # or "process_browser:kill"
```

Then in the translator, format process data as JSON matching Mythic's Process Browser format.

### File Browser Integration

To integrate with Mythic's File Browser:

```python
supported_ui_features = ["file_browser:list"]
```

The translator will automatically convert file listings to File Browser format.

## 🧪 Testing Your Command

### 1. Restart Mythic Agent Container

After adding your command, restart the Angerona container:

```bash
cd ~/Mythic
./mythic-cli restart angerona
```

### 2. Verify Command Appears

1. Open Mythic UI
2. Navigate to Payloads → Create Payload
3. Select Angerona
4. Check that your command appears in the command selection list

### 3. Build Test Payload

1. Select your command (and any others you need)
2. Configure C2 profile
3. Build the payload
4. Check build logs for any compilation errors

### 4. Test Command Execution

1. Deploy payload to test system
2. Execute your command from Mythic UI
3. Verify response is received correctly
4. Check for any errors in agent logs

## 🐛 Common Issues and Solutions

### Issue: Command Not Appearing in UI

**Solution:**
- Verify Python file is in `agent_functions/` directory
- Check that class name ends with `Command` (e.g., `PingCommand`)
- Restart Mythic agent container
- Check Mythic logs for import errors

### Issue: Compilation Errors

**Solution:**
- Verify hex code is unique (check `comandos.h`)
- Ensure `#ifdef` blocks are properly closed
- Check that handler function is declared before use
- Verify all includes are correct

### Issue: Command Not Executing

**Solution:**
- Verify command was selected during payload build
- Check that hex code matches in translator and `comandos.h`
- Verify handler is registered in `comandos.c`
- Check agent logs for error messages

### Issue: Response Not Received

**Solution:**
- Verify response packet is created correctly
- Check that task UUID is added to response
- Ensure `mandarPaquete()` is called
- Verify packet is properly formatted

## 📚 Reference: Command Hex Codes

Current hex code allocations in Angerona:

- `0x00`: GET_TASKING
- `0x01`: POST_RESPONSE
- `0x15`: PROCESS_CMD (ps)
- `0x20-0x28`: File system commands (cd, ls, pwd, cp, mkdir, rm, cat, download, upload)
- `0x29`: SCREENSHOT_CMD
- `0x30-0x31`: Keylogging (start, stop)
- `0x32-0x37`: Token operations (list_tokens, steal_token, make_token, rev2self, whoami, kill)
- `0x38`: SLEEP_CMD
- `0x40-0x42`: Browser and SAM (browser_info, browser_dump, samdump)
- `0x54`: SHELL_CMD
- `0x60-0x63`: Network tunneling (socks, rpfwd)
- `0x70-0x71`: Code execution (inline_execute, inline_execute_assembly)
- `0x80`: EXIT_CMD
- `0xF1`: CHECKIN

**Available ranges for new commands:**
- `0x43-0x53`: Available
- `0x55-0x5F`: Available
- `0x64-0x6F`: Available
- `0x72-0x7F`: Available
- `0x81-0xF0`: Available (avoid 0xF1-F5, 0xF7-F8 which are used for markers)

## ✅ Checklist

Use this checklist when adding a new command:

- [ ] Python command file created in `agent_functions/`
- [ ] Command class inherits from `CommandBase`
- [ ] `cmd` attribute matches command name
- [ ] `builtin` attribute set appropriately
- [ ] Command added to translator `commands` dictionary
- [ ] Hex code defined in `comandos.h`
- [ ] C handler function implemented
- [ ] Handler registered in `comandos.c` dispatch chain
- [ ] Command added to `builder.py` `command_to_define` mapping
- [ ] Handler wrapped in `#ifdef ENABLE_XXX_CMD`
- [ ] Includes added if needed
- [ ] Command tested in payload build
- [ ] Command tested in execution
- [ ] Documentation updated (optional)

## 🔗 Related Documentation

- [Commands Reference](commands.md) - Complete list of all commands
- [Getting Started](getting-started.md) - Installation and setup
- [Features Overview](features.md) - Detailed feature explanations
- [OPSEC Guide](opsec.md) - Operational security considerations

---

**Next Steps:** After implementing your command, consider:
- Adding OPSEC warnings for risky operations
- Implementing artifact tracking for system modifications
- Adding credential extraction if applicable
- Writing unit tests for your command logic
- Documenting your command in the commands reference
