4e286cd901813a5f80411e417fb5defe25ff9af00706e68509392f6e75cc3908goloader: 4e286cd9 — Go 1.25.1 reflective loader with AES-CBC encrypted payload in PE resource
Executive Summary
PE64 AMD64 binary compiled with Go 1.25.1. It is a reflective-code loader built on the legitimate github.com/pkujhd/goloader package, embedding an AES-CBC + zlib-encrypted second-stage Go object in a 912-byte PE resource. Eighteen randomized main.* function names suggest the payload, once decrypted, is dynamically linked in-memory via goloader rather than statically compiled into the outer binary. No network IOCs, registry persistence, or Authenticode signature were recovered in static strings.
What It Is
| Trait | Value |
|---|---|
| SHA-256 | 4e286cd901813a5f80411e417fb5defe25ff9af00706e68509392f6e75cc3908 |
| File type | PE32+ executable (GUI) x86-64, 9 sections^[file.txt] |
| Compiler | Go 1.25.1^[strings.txt:9] |
| Build mode | CGO_ENABLED=0, static, -trimpath implied (no absolute paths)^[strings.txt:5300-5305] |
| Module path | goloader^[strings.txt:2045] |
| Build ID | TJ7FBPtJdZI82i_gZMjm/59TZNuuZCej16dO3e6xW/qX4SXpL3Z1nCXw7RdXvx/ekT21_npJso311hGWjXF^[strings.txt:9] |
| Authenticode | Unsigned^[pefile.txt] |
| Manifest | vertexexpertleaf.service_decoder, version 4.0.32.156, description Discount Persona Guide^[pefile.txt:459-488] |
| Embedded payload | PE resource id 16, RVA 0x1FB0A0, size 0x390 (912 bytes)^[pefile.txt:454-455] |
| Capa | Failed — default signature path missing^[capa.txt] |
| YARA | Generic PE hit only^[yara.txt] |
| Dynamic | Skipped — no CAPE Windows guest available^[dynamic-analysis.md] |
Family ascription: The triage pipeline tagged this as legionloader. No in-corpus precedent exists for legionloader, and no external intelligence was available to verify the link. The binary’s build artifacts point squarely to the open-source pkujhd/goloader project, a Go runtime linker that loads .o / .a object files into a running process. The family page is therefore created under goloader, not legionloader, with a note that the attribution remains contested.
How It Works
-
Outer loader — A standard Go Windows AMD64 binary compiled with
CGO_ENABLED=0and likely-trimpath=true. On entry, the Go runtime initializes normally. -
Payload retrieval — At runtime the binary reads PE resource id 16 (offset
0x1FB0A0, 912 bytes)^[pefile.txt:454-455]. The xxd dump shows the resource body is high-entropy alphanumeric ASCII, consistent with an encrypted or encoded payload^[terminal:xxd resource 16]. -
Decryption — The binary imports
crypto/internal/fips140/aesincludingNewCBCDecrypter^[strings.txt:4864-4878],crypto/internal/fips140/sha256^[strings.txt:4924-4934],crypto/internal/fips140/sha3(SHAKE, Digest)^[strings.txt:4942-4962], andcrypto/internal/fips140/hmac(HMAC-SHA2-256)^[strings.txt:4975-4984]. AES-CBC is the most probable decryption layer. The AES S-Box and inverse S-Box are present in.rdata^[binwalk.txt:8-9]. -
Decompression — After decryption, the payload is decompressed via
compress/zlibandhash/adler32^[strings.txt:4841-4848, 4830-4840]. -
Runtime linking — The decompressed bytes are passed to
goloader.Read(from thepkujhd/goloaderpackage) which parses a Go object / archive file.goloader.Loadthen resolves the object’s symbols against the host process’s symbol table and links them. The randomizedmain.*symbols —main.htg21w9j,main.j7kxray,main.h1eq17,main.ekfgcw,main.fsdya3m0r,main.rwnd58880,main.jtksgnv9cp,main.nwntqwqupe,main.iyz67zv,main.e4i55n1,main.cwnjg3,main.j16287wq,main.w8axetvs1y,main.o0oazr,main.adjfhfxv,main.l6pwhoovki^[strings.txt:4879-4900] — are the second-stage functions thatgoloadermakes callable. -
Execution — The loader ultimately calls the linked module’s
main.mainentry (or equivalent) frommain.main^[strings.txt:4880], effectively transferring control to the second stage without ever writing it to disk.
Decompiled Behavior
Static-only analysis; no Ghidra decompilation was attempted because the malicious logic lives inside an encrypted object file rather than the main .text section. The outer binary’s entry point is standard Go AMD64 PE startup: CPUID checks, GS segment register reads, and runtime.main dispatch. The noteworthy control flow is:
main.maincalls intomain.htg21w9jor similar randomized wrappers.- Those wrappers call
goloader/loader.goroutines (object parsing) andgoloader/vm.goroutines (virtual-machine-style symbol relocation). - The IAT is thin:
kernel32.dllonly —VirtualAlloc,CreateThread,SuspendThread,ResumeThread,SetThreadContext,GetThreadContext,WriteFile,LoadLibraryW,GetProcAddress^[strings.txt:5431-5463] andsyscall.loadlibrary/syscall.getprocaddress^[strings.txt:3988-3989]. These APIs are available to the second stage for process injection if desired.
C2 Infrastructure
No hardcoded C2 URLs, IPs, DGA domains, or mutex names were found in 5,507 lines of static strings^[strings.txt]. net/http and net package references appear only as part of the Go runtime’s syscall table, not as non-runtime package imports. The actual C2 logic is presumed to exist inside the encrypted second-stage payload and is therefore invisible without dynamic execution or successful payload decryption.
Interesting Tidbits
- Manifest masquerade —
vertexexpertleaf.service_decoderwith descriptionDiscount Persona Guideis a random-word construction, not a known legitimate product^[pefile.txt]. - No Authenticode — Unlike the ACR/Lumma/OrderRe Go infostealer cluster, this sample carries no signature, only a fabricated manifest^[pefile.txt].
- No icon resource — The PE has no
.rsrcicon section (onlyRT_MANIFESTand the encrypted blob in resource id 16)^[pefile.txt], another divergence from the typical Go-stealer build pattern documented at golang-stealer-build-pattern. - Go 1.25.1 — This is a bleeding-edge Go release (current stable at time of writing). Using the latest compiler may be an anti-sandbox measure (sandboxes running older Go runtime detection heuristics) or simply the builder’s default.
- Binwalk false positive — Binwalk flagged
mcrypt 2.2 encrypted data, algorithm: blowfish-448at offset0x18E6E3^[binwalk.txt:5]. This is almost certainly an entropy artifact; the actual crypto is AES-CBC as evidenced by the AES S-Box imports andNewCBCDecrypterstring. *main.z56lo5j— One type symbol survives obfuscation with a recognizable pattern. It may be an internal struct used by the loader or the second stage.
How To Mess With It (Homelab Replication)
Toolchain:
GOVERSION=go1.25.1
GOOS=windows
GOARCH=amd64
CGO_ENABLED=0
Build recipe:
- Create a Go module that imports
github.com/pkujhd/goloader. - Write your payload as a normal Go package with an exported
func Main(). - Build the payload as an object file:
go build -buildmode=c-archiveor produce.oviago tool compile. - Encrypt the object with AES-CBC (key derived via SHA-256 or HMAC), then zlib-compress it.
- Embed the blob as a PE resource using
github.com/akavel/rsrcorwindres. - Write the loader: read resource → decrypt → decompress →
goloader.Read(objBytes)→goloader.Load(symPtr, module). - Compile the loader:
go build -trimpath -ldflags="-s -w".
Verification: Detonate in a sandbox. The outer binary should allocate a private memory region (VirtualAlloc), create no child processes, and make no network connections — all behavior is contained inside the in-memory linked module.
What you’ll learn: How to build a position-independent Go reflective loader that evades static string extraction of the payload.
Deployable Signatures
YARA Rule
rule GoLoader_Reflective_AES_Resource
{
meta:
description = "Go reflective loader using pkujhd/goloader with AES-CBC encrypted PE resource payload"
author = "pp-hermes"
date = "2026-06-08"
family = "goloader"
sha256 = "4e286cd901813a5f80411e417fb5defe25ff9af00706e68509392f6e75cc3908"
strings:
$goloader_loader = "goloader/loader.go"
$goloader_vm = "goloader/vm.go"
$goloader_exe = "goloader/exe_main.go"
$aes_cbc = "crypto/cipher.NewCBCDecrypter"
$aes_fips = "crypto/internal/fips140/aes"
$zlib = "compress/zlib.NewReader"
$adler = "hash/adler32"
// AES S-Box presence in .rdata
$aes_sbox_lo = { 63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76 }
// Go build ID prefix
$go_build = "Go build ID:"
condition:
uint16(0) == 0x5A4D and
filesize < 5MB and
3 of ($goloader_*) and
$aes_cbc and
$zlib and
$go_build and
// High-entropy PE resource (id 16 or 24) — heuristic
// Prefer behavioural sigma for this
1 of ($aes_sbox_lo)
}
Behavioral Hunt Query (Sigma)
title: Go Reflective Loader with VirtualAlloc + CreateThread Sequence
logsource:
category: process_creation
product: windows
detection:
selection:
CommandLine|contains:
- '.exe'
# Memory-allocation followed by thread creation in same process, short window
condition: selection
# EDR/Sysmon telemetry required: sequence of VirtualAlloc(RWX) then CreateThread in same PID within 5s
# Sigma does not express sequences natively; deploy as EQL or Splunk SPL:
# index=sysmon EventCode=7 OR EventCode=8 | stats dc(EventCode) as events by Image, ProcessId _time span=5s | where events>=2
IOC List
| Type | Value | Note |
|---|---|---|
| SHA-256 | 4e286cd9... |
Outer loader |
| SHA-256 | unknown | Second-stage payload (encrypted, static-only) |
| Resource | PE resource id 16, RVA 0x1FB0A0, size 0x390 |
Encrypted payload container |
| Manifest | vertexexpertleaf.service_decoder |
Masquerade identity |
| C2 | none observed | Expected inside encrypted payload |
Behavioral Fingerprint
A Go static PE64 compiled with CGO_ENABLED=0 and -trimpath=true that imports github.com/pkujhd/goloader package routines. The binary contains an AES S-Box in .rdata, imports crypto/cipher.NewCBCDecrypter, compress/zlib.NewReader, and hash/adler32. It has a high-entropy PE resource (id 16) and no .rsrc icon. On execution it calls VirtualAlloc followed by CreateThread with no preceding child-process creation or network connection — all malicious behavior is staged inside the in-memory linked module.
Detection Signatures
| ATT&CK ID | Technique | Evidence |
|---|---|---|
| T1203 | Execution | goloader loads and executes Go code at runtime^[strings.txt:5301-5305] |
| T1620 | Reflective Code Loading | Encrypted payload decrypted in-memory, linked without file write^[pefile.txt:454-455] |
| T1027 | Obfuscated Files or Information | AES-CBC + zlib compression of embedded payload^[strings.txt:4864-4878,4841-4848] |
| T1083 | File and Directory Discovery | GetTempPathW, FindFirstFileW imports^[strings.txt:1971-1990] |
References
- pkujhd/goloader: https://github.com/pkujhd/goloader
- Go 1.25.1 release notes: https://go.dev/doc/go1.25
- Analysis report:
/intel/analyses/4e286cd901813a5f80411e417fb5defe25ff9af00706e68509392f6e75cc3908.html
Provenance
Static analysis performed on 2026-06-08 using file, strings, rabin2, xxd, and manual review of pefile.txt, binwalk.txt, strings.txt, floss.txt, capa.txt, and yara.txt. CAPE detonation was not available (no Windows guest). No Ghidra decompilation was attempted because the malicious payload is encrypted and linked at runtime. All claims cite the specific artifact file and line number.