Documentation Index Fetch the complete documentation index at: https://mintlify.com/HavocFramework/Havoc/llms.txt
Use this file to discover all available pages before exploring further.
Havoc’s module system allows you to extend the Demon agent’s functionality without modifying the core codebase. Modules are loaded dynamically from the Havoc client and can add new commands, techniques, and capabilities.
Overview
Modules enable:
Custom Commands Add new commands to the Demon agent
No Recompilation Load modules at runtime without rebuilding
Isolation Execute in fork & run processes for safety
Integration Seamlessly integrate with existing workflows
Official Modules
Havoc provides official modules at github.com/HavocFramework/Modules :
Powerpick
Executes unmanaged PowerShell commands by loading the CLR runtime into a fork & run process.
powerpick Get-Process | Where-Object {$_.CPU -gt 100}
Features:
Loads CLR 4.0.30319 into sacrificial process
Executes PowerShell without powershell.exe
Bypasses application whitelisting
Captures output and returns to operator
InvokeAssembly
Executes .NET assemblies in a separate process by bootstrapping the CLR.
invoke-assembly /path/to/Seatbelt.exe -group=system
Features:
Custom CLR version selection (default: v4.0.30319)
Custom AppDomain name (default: DefaultAppDomain)
Argument passing to assembly
Output redirection and capture
Module Structure
A Havoc module consists of:
MyModule/
├── Module.py # Module definition and registration
├── Source/
│ ├── Main.c # Module entry point
│ └── Implementation.c
├── Include/
│ └── Headers.h
└── README.md
Creating a Module
Module Template
Start with the official template :
git clone https://github.com/HavocFramework/Modules
cd Modules/Template
cp -r Template MyModule
Module.py
Define your module in Python:
from havoc import Demon, RegisterCommand, RegisterModule
class Packer :
def __init__ ( self ):
self .buffer = b ''
def addstr ( self , string ):
self .buffer += string.encode() + b ' \x00 '
def addint ( self , integer ):
self .buffer += integer.to_bytes( 4 , 'little' )
def getbuffer ( self ):
return self .buffer
def mycommand ( demon_id , * args ):
"""
Custom command implementation.
Args:
demon_id: Agent ID
*args: Command arguments
"""
task_id = Demon.Command.task_id
if len (args) < 1 :
Demon.Console.error(task_id, "Usage: mycommand <argument>" )
return False
# Pack command data
packer = Packer()
packer.addint( 0x 1000 ) # Command ID
packer.addstr(args[ 0 ]) # Argument
# Send to agent
Demon.Console.info(task_id, f "Executing mycommand with: { args[ 0 ] } " )
Demon.Command.execute(demon_id, packer.getbuffer())
return True
# Register the module
RegisterModule(
"MyModule" ,
"1.0" ,
"Custom functionality module" ,
"@YourHandle" ,
[
# List of COFF/BOF files to load
"MyModule.x64.o" ,
"MyModule.x86.o"
]
)
# Register commands
RegisterCommand(
demon_id = None ,
command_name = "mycommand" ,
description = "Execute custom functionality" ,
help = "mycommand <argument>" ,
callback = mycommand,
mitre = "T1059" # MITRE ATT&CK technique
)
C Implementation
Implement the module logic in C:
#include <windows.h>
#include <stdio.h>
#include "Beacon.h" // Beacon API compatibility
// Command ID (must match Module.py)
#define COMMAND_MYCOMMAND 0x 1000
// Entry point
void go ( char * args , int length ) {
// Parse arguments from packed buffer
datap parser;
BeaconDataParse ( & parser, args, length);
int command_id = BeaconDataInt ( & parser);
char * argument = BeaconDataExtract ( & parser, NULL );
if (command_id == COMMAND_MYCOMMAND) {
// Execute functionality
char output [ 256 ];
sprintf (output, "[+] Executed with argument: %s " , argument);
BeaconPrintf (CALLBACK_OUTPUT, output);
// Your custom logic here
// ...
}
}
Beacon API
Havoc modules use the Beacon Object File (BOF) API for compatibility:
Output Functions
Data Parsing
Win32 API
// Print to console
BeaconPrintf ( int type , char * format , ...);
// Types:
CALLBACK_OUTPUT // Standard output
CALLBACK_OUTPUT_OEM // OEM output
CALLBACK_ERROR // Error message
Building Modules
Compiling to COFF/BOF
Modules are compiled as COFF (Common Object File Format) objects:
# x64 version
x86_64-w64-mingw32-gcc -c Source/Main.c -o MyModule.x64.o \
-I Include/ \
-masm=intel
# x86 version
i686-w64-mingw32-gcc -c Source/Main.c -o MyModule.x86.o \
-I Include/ \
-masm=intel
COFF files are position-independent code loaded via the Demon COFF loader.
Makefile Example
CC_x64 = x86_64-w64-mingw32-gcc
CC_x86 = i686-w64-mingw32-gcc
CFLAGS = -masm=intel -I Include/
all : MyModule.x64.o MyModule.x86.o
MyModule.x64.o : Source/Main.c
$( CC_x64 ) -c $< -o $@ $( CFLAGS )
MyModule.x86.o : Source/Main.c
$( CC_x86 ) -c $< -o $@ $( CFLAGS )
clean :
rm -f *.o
Loading Modules
From Havoc Client
Open Scripts Manager
Navigate to Scripts → Scripts Manager in the Havoc client
Load Module
Click Load and select your Module.py file
Verify Registration
Check the console for confirmation: [+] Loaded module: MyModule v1.0
[+] Registered command: mycommand
Use Command
Interact with a Demon session and use your new command: [demon-id] » mycommand test
[+] Executed with argument: test
Auto-load on Startup
Place modules in the scripts/ directory to load automatically:
mkdir -p ~/.havoc/scripts
cp MyModule/Module.py ~/.havoc/scripts/
Example: Powerpick Module
View Powerpick Implementation
The Powerpick module demonstrates advanced techniques: from havoc import Demon, RegisterCommand, RegisterModule
def powerpick ( demon_id , * args ):
task_id = Demon.Command.task_id
if len (args) < 1 :
Demon.Console.error(task_id, "Usage: powerpick <command>" )
return False
# Combine all args into PowerShell command
ps_command = ' ' .join(args)
# Pack command
packer = Packer()
packer.addint( 0x 2000 ) # POWERPICK_COMMAND
packer.addstr(ps_command)
Demon.Console.info(task_id, f "Executing PowerShell: { ps_command } " )
Demon.Command.execute(demon_id, packer.getbuffer())
return True
RegisterModule(
"Powerpick" ,
"1.0" ,
"Execute unmanaged PowerShell" ,
"@HavocFramework" ,
[
"Powerpick.x64.o" ,
"Powerpick.x86.o"
]
)
RegisterCommand(
demon_id = None ,
command_name = "powerpick" ,
description = "Execute unmanaged PowerShell commands" ,
help = "powerpick <command>" ,
callback = powerpick,
mitre = "T1059.001"
)
The C implementation:
Creates sacrificial process (defined in config)
Injects CLR loader shellcode
Loads mscorlib.dll and PowerShell assemblies
Executes command and captures output
Returns output to teamserver
Example: InvokeAssembly Module
View InvokeAssembly Implementation
from havoc import Demon, RegisterCommand, RegisterModule
import base64
def invoke_assembly ( demon_id , * args ):
task_id = Demon.Command.task_id
if len (args) < 1 :
Demon.Console.error(task_id, "Usage: invoke-assembly <path> [args]" )
return False
assembly_path = args[ 0 ]
assembly_args = ' ' .join(args[ 1 :]) if len (args) > 1 else ""
# Read assembly
try :
with open (assembly_path, 'rb' ) as f:
assembly_bytes = f.read()
except FileNotFoundError :
Demon.Console.error(task_id, f "Assembly not found: { assembly_path } " )
return False
# Pack command
packer = Packer()
packer.addint( 0x 2001 ) # INVOKE_ASSEMBLY_COMMAND
packer.addstr( "v4.0.30319" ) # CLR version
packer.addstr( "DefaultAppDomain" ) # AppDomain name
packer.addint( len (assembly_bytes))
packer.addbytes(assembly_bytes)
packer.addstr(assembly_args)
Demon.Console.info(task_id, f "Executing assembly: { assembly_path } " )
Demon.Command.execute(demon_id, packer.getbuffer())
return True
RegisterModule(
"InvokeAssembly" ,
"1.0" ,
"Execute .NET assemblies" ,
"@HavocFramework" ,
[
"InvokeAssembly.x64.o" ,
"InvokeAssembly.x86.o"
]
)
RegisterCommand(
demon_id = None ,
command_name = "invoke-assembly" ,
description = "Execute a .NET assembly" ,
help = "invoke-assembly <path> [arguments]" ,
callback = invoke_assembly,
mitre = "T1620"
)
Best Practices
void go ( char * args , int length ) {
__try {
// Module logic
execute_functionality ();
}
__except (EXCEPTION_EXECUTE_HANDLER) {
BeaconPrintf (CALLBACK_ERROR, "[-] Exception occurred" );
}
}
// Allocate memory
void * buffer = MSVCRT$ malloc (size);
if ( ! buffer) {
BeaconPrintf (CALLBACK_ERROR, "[-] Memory allocation failed" );
return ;
}
// Use buffer
// ...
// Free memory
MSVCRT$ free (buffer);
Use indirect syscalls where possible
Avoid suspicious API calls (e.g., CreateRemoteThread)
Clean up artifacts (files, registry keys)
Implement anti-debugging checks
Use fork & run for risky operations
# Test module before deployment
def test_mycommand ():
# Mock demon_id
result = mycommand( "test-demon" , "test-arg" )
assert result == True , "Command should succeed"
if __name__ == "__main__" :
test_mycommand()
print ( "[+] Tests passed" )
Advanced Techniques
Custom Packer Class
class AdvancedPacker ( Packer ):
def addwstr ( self , string ):
"""Add wide string (UTF-16LE)"""
self .buffer += string.encode( 'utf-16le' ) + b ' \x00\x00 '
def addptr ( self , pointer ):
"""Add pointer (8 bytes on x64)"""
self .buffer += pointer.to_bytes( 8 , 'little' )
def addbool ( self , value ):
"""Add boolean (1 byte)"""
self .buffer += b ' \x01 ' if value else b ' \x00 '
Multi-Command Modules
RegisterModule(
"AdvancedModule" ,
"1.0" ,
"Multiple related commands" ,
"@YourHandle" ,
[ "Module.x64.o" , "Module.x86.o" ]
)
# Register multiple commands
RegisterCommand(
demon_id = None ,
command_name = "cmd1" ,
description = "First command" ,
help = "cmd1 <arg>" ,
callback = command1_callback,
mitre = "T1059"
)
RegisterCommand(
demon_id = None ,
command_name = "cmd2" ,
description = "Second command" ,
help = "cmd2 <arg>" ,
callback = command2_callback,
mitre = "T1059"
)
Dynamic Library Loading
#include "Beacon.h"
// Dynamically load and use libraries
void load_custom_library () {
HMODULE hLib = KERNEL32$ LoadLibraryA ( "custom.dll" );
if ( ! hLib) {
BeaconPrintf (CALLBACK_ERROR, "[-] Failed to load library" );
return ;
}
// Get function pointer
typedef void ( * CustomFunc)( void );
CustomFunc func = (CustomFunc)KERNEL32$ GetProcAddress (hLib, "CustomFunction" );
if (func) {
func ();
}
KERNEL32$ FreeLibrary (hLib);
}
Debugging Modules
Enable Debug Output
Use BeaconPrintf for debug messages: BeaconPrintf (CALLBACK_OUTPUT, "[DEBUG] Variable value: %d " , value);
Test COFF Loader
Use standalone COFF loader for testing: # Test with COFFLoader
./COFFLoader MyModule.x64.o go "test_args"
Check Teamserver Logs
Monitor Teamserver output: sudo ./havoc server --profile ./profiles/havoc.yaotl --debug
Resources
Official Modules Powerpick, InvokeAssembly, and module template
Beacon API Reference BOF API documentation (Cobalt Strike compatible)
Python API havoc-py reference for Module.py
Custom Agents Build agents instead of modules
Troubleshooting
Check Module.py syntax
Verify COFF files exist and are referenced correctly
Check Havoc console for error messages
Ensure module name is unique
Verify command ID matches between Python and C
Check argument packing/unpacking
Enable debug output in C code
Test with simple arguments first
Ensure correct mingw-w64 toolchain installed
Check include paths for Beacon.h
Verify target architecture (x64 vs x86)
Use -masm=intel for inline assembly