136b57507a3cfe1fc39cf6973869dca7e871e91d63f283c77380fc33298d6d51unattributed: 136b5750 — MSVC 14.12 PE32 with PEB-walking, XOR-NOT string crypto, and CPUID anti-VM
Executive Summary
A 150 KB PE32 GUI binary compiled with MSVC 14.12 (VS 2017 15.5+) in September 2022. Labelled by OpenCTI as dropped-by-phorpiex, but static traits differ sharply from known Phorpiex downloaders: no masquerade-name list, no ip-api.com gating, and a heavier anti-analysis / reflective-loader stack. All threat APIs are resolved at runtime via PEB-walking; strings are encrypted with a custom XOR-NOT scheme using a hard-coded alphabet array. The binary carries anti-VM (CPUID hypervisor-bit checks + RDTSC timing), an LCG PRNG, and network C2 logic that builds HTTP POST payloads with encrypted body data. Static-only analysis (CAPE skipped).
What It Is
| Field | Value |
|---|---|
| SHA-256 | 136b57507a3cfe1fc39cf6973869dca7e871e91d63f283c77380fc33298d6d51 |
| Size | 149,504 bytes (150 KB) |
| Type | PE32 executable (GUI) Intel 80386, 6 sections ^[file.txt] |
| Compiler | MSVC 14.12 (LinkerVersion 14.12) — Visual Studio 2017 15.5 or newer ^[pefile.txt:18] ^[exiftool.json:18] |
| Timestamp | 0x631A9665 — Fri Sep 9 01:27:01 2022 UTC ^[pefile.txt:34] |
| Debug | POGO (IMAGE_DEBUG_TYPE_POGO, size 0xF4) ^[pefile.txt:313] |
| ASLR / DEP | Enabled (DYNAMIC_BASE, NX_COMPAT) ^[pefile.txt:68] |
| Canary | Enabled (canary: true) ^[rabin2-info.txt:6] |
| Signed | Unsigned ^[rabin2-info.txt:27] |
| Overlay | None ^[rabin2-info.txt:23] |
| Static imports | Minimal surface: GDI32 (6), USER32 (11), KERNEL32 (8) — all GUI housekeeping ^[pefile.txt:249] |
| YARA | Generic PE only; no family-specific hits ^[yara.txt] |
Static import table is a facade. The binary resolves ~30+ threat APIs at runtime via PEB-walking through ntdll → kernel32 → wininet, caching results in a pseudo-import table at 0x425xxx in the .data section.
How It Works
1. Entry Point & Runtime Loader (0x417034)
The entry-point stub at 0x1946F (RVA) delegates to fcn.00417034, which acts as the runtime orchestrator:
- Loads module "C" (i.e., kernel32) via PEB-walking by pushing
'C'(0x43) to slot0x4256c4and invoking the walker. ^[r2:fcn.00417034] - Creates two worker threads:
- Thread A at
0x407468— file-system enumeration (*wildcard, recursive). ^[r2:fcn.00417034] - Thread B at
0x40782c— network / C2 communication (WinInet-style handle allocation, HTTP POST construction). ^[r2:fcn.00417034]
- Thread A at
- Checks a series of boolean gates in
.data(0x42512f,0x425128,0x425127,0x425125,0x425131,0x425132,0x425126) to decide whether to spawn additional threads (0x408f68,0x407e58,0x409628,0x40c064) or skip them. ^[r2:fcn.00417034] - Calls a reflective loader / memory mapper at
0x406668which performsVirtualAlloc-style allocation (via indirect slot0x4254e4), maps sections, and writes through slots0x42552c/0x425544. ^[r2:fcn.00417034]
2. PEB-Walking API Resolution
No imports for VirtualAlloc, CreateThread, InternetOpen, CryptAcquireContext, etc. The binary walks the InMemoryOrderModuleList from the PEB (fs:[0x30]), iterates export tables, and resolves APIs by hash or name. Resolved pointers are cached in .data slots:
| Slot VA | Likely API Class | Evidence |
|---|---|---|
0x4256c4 |
PEB-walk / module resolver | Called with 'C' (0x43) to load kernel32 ^[r2:fcn.00417034] |
0x42551c |
CreateThread |
Used to spawn threads with entry-point addresses ^[r2:fcn.00417034] |
0x425480 |
VirtualAlloc / memory allocation |
Called with size arguments before loader execution ^[r2:fcn.00417034] |
0x4254d0 |
CloseHandle / WaitForSingleObject |
Used on thread handles after creation ^[r2:fcn.00417034] |
0x425548 |
WaitForSingleObject |
Called with 0xffffffffffffffff (INFINITE) timeout ^[r2:fcn.00417034] |
0x4255c0 |
LoadLibraryA / GetModuleHandleA |
Loads modules by encrypted name ^[r2:fcn.00406ae8] |
0x4255c4 |
GetProcAddress |
Resolves APIs after module load ^[r2:fcn.00406ae8] |
0x425470 |
InternetOpen / InternetConnect / HttpOpenRequest |
Network init; called with XOR-decrypted host/port values ^[r2:fcn.00406ae8] |
0x425464 |
CryptStringToBinary / string helper |
Used during payload encoding ^[r2:fcn.0040cfcc] |
0x4254e4 |
VirtualAlloc |
Allocates RWX or RW memory for reflective mapping ^[r2:fcn.00417034] |
0x42552c |
WriteProcessMemory / memcpy |
Writes decrypted payload into allocated memory ^[r2:fcn.00417034] |
0x425544 |
VirtualProtect |
Changes memory protection after write ^[r2:fcn.00417034] |
0x4254cc |
ResumeThread / CreateRemoteThread |
Resumes suspended thread after injection ^[r2:fcn.00417034] |
0x425590 |
GetTickCount / Sleep |
Timing / anti-emulation ^[r2:fcn.00417034] |
0x425744 |
CryptAcquireContextW / BCryptOpenAlgorithmProvider |
Crypto init ^[r2:fcn.0040782c] |
0x425758 |
CryptImportKey / BCryptImportKey |
Key import for payload decryption ^[r2:fcn.0040782c] |
0x4257a0 |
CryptSetKeyParam |
Sets key parameters (mode, IV) ^[r2:fcn.0040782c] |
0x4257a4 |
CryptEncrypt / CryptDecrypt |
Payload encryption / decryption ^[r2:fcn.0040782c] |
0x425760 |
CryptGenRandom / RtlGenRandom |
Generates random data for C2 payload ^[r2:fcn.0040782c] |
0x425814 |
HttpOpenRequestW / HttpSendRequestW |
HTTP POST to C2 ^[r2:fcn.0040cfcc] |
0x42565c |
OpenProcess |
Used for process injection targeting ^[r2:fcn.0040782c] |
0x42543c |
lstrlenA |
String length helper ^[r2:fcn.0040792c] |
0x425444 |
lstrcpyA |
String copy helper ^[r2:fcn.0040792c] |
0x425440 |
lstrcmpA |
String comparison helper ^[r2:fcn.0040792c] |
Slot values are stored XOR-encrypted with key 0x10035fff and decrypted at first use. ^[r2:fcn.00401240]
3. String Encryption — XOR-NOT Alphabet Cipher
The binary decrypts strings with a two-step routine at 0x401240:
void decrypt_dwords(uint32_t *buf, int count) {
for (int i = 0; i < count; i++) {
buf[i] ^= 0x10035fff;
buf[i] = ~buf[i];
}
}
^[r2:fcn.00401240]
At 0x40d4b0, the malware builds a 62-character alphabet table by decrypting a 16-DWORD array:
Encrypted: 0xabbfe241 0xa7bbe645 0xa3b7ea49 ... 0xeffc9938
Decrypted: "ABCD" "EFGH" "IJKL" ... "0123" "4567" "89"
This yields the ordered alphabet ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789. ^[r2:fcn.0040d4b0]
The malware then uses this alphabet as a lookup table to construct host names, URLs, and User-Agent strings character-by-character via the LCG PRNG (see below). This is a custom base-62 string encoder.
At 0x40cfcc, a second encrypted array decodes to "POST" (wide-character fragments 0x4f0050, 0x540053, 0x0000), confirming HTTP POST as the C2 verb. ^[r2:fcn.0040cfcc]
4. Anti-Analysis & Anti-VM (0x4010bc)
A dedicated gate function performs three checks:
- CPUID leaf 1, ECX bit 31 (
0x80000000) — hypervisor present bit. If set, the binary alters code paths. ^[r2:fcn.004010bc] - CPUID leaf 7, EBX bit 18 (
0x40000) — additional hypervisor feature flag. ^[r2:fcn.004010bc] - RDTSC differential timing — reads
rdtsc, appliesror/rol 0xd(13-bit rotate), and compares deltas. Triggers if execution is slower than expected (emulation / instrumentation). ^[r2:fcn.004010bc]
No debugger-specific API calls (IsDebuggerPresent, CheckRemoteDebuggerPresent) are imported statically; detection is entirely CPU-feature based.
5. PRNG (0x40110c)
Linear congruential generator used for:
- Sleep jitter (anti-emulation)
- Character selection from the alphabet table (string building)
- Randomized C2 URL / filename generation
uint32_t lcg() {
seed = (seed * 0x19660d + 0x3c6ef35f) & 0x7ffffff;
return seed;
}
^[r2:fcn.0040110c]
6. Network / C2 Behavior (0x40cfcc, 0x40782c)
0x40cfccallocates a buffer, decrypts the POST verb, and builds an HTTP request body with encrypted payload data. It calls0x425814(HttpSendRequest) to transmit. ^[r2:fcn.0040cfcc]0x40782cperforms network initialization (likelyInternetOpen/InternetConnect), creates request handles, and reads responses. The function allocates multiple handles and closes them on error paths. ^[r2:fcn.0040782c]- The request User-Agent and target URL are constructed at runtime from the alphabet table + PRNG; no hard-coded domains survive in the binary.
7. File-System Enumeration (0x407468, 0x40766c)
0x407468enumerates drives / directories with"*"wildcard, using0x425564(likelyFindFirstFile/FindNextFile) and checks result codes (2 = ERROR_FILE_NOT_FOUND, 3 = ERROR_PATH_NOT_FOUND). ^[r2:fcn.00407468]0x40766cwalks recursively, caching file paths for exfiltration or payload staging. Uses indirect API calls at0x42543c(lstrlen),0x425444(lstrcpy),0x425440(lstrcmp). ^[r2:fcn.0040766c]
Decompiled Behavior
| Address | Role | Key Observations |
|---|---|---|
0x4010bc |
Anti-debug/VM gate | CPUID hypervisor bits + RDTSC rotate-13 timing ^[r2:fcn.004010bc] |
0x4011c4 |
String helper | ASCII case-conversion ('A'–'Z' bounds, OR 0x20) ^[r2:fcn.004011c4] |
0x40110c |
LCG PRNG | Multiplier 0x19660d, increment 0x3c6ef35f, mask 0x7ffffff ^[r2:fcn.0040110c] |
0x401240 |
Decrypt stub | XOR 0x10035fff then NOT; called 147 times ^[r2:fcn.00401240] |
0x406668 |
Reflective mapper | VirtualAlloc, section mapping, 0x42552c/0x425544 writes ^[r2:fcn.00406668] |
0x406ae8 |
Network init | Decrypts host/port values, calls 0x425470 (WinInet) ^[r2:fcn.00406ae8] |
0x407468 |
File enumerator | FindFirstFile style enumeration with wildcard "*" ^[r2:fcn.00407468] |
0x40766c |
Directory traversal | Recursive walk with indirect string APIs ^[r2:fcn.0040766c] |
0x40782c |
C2 comms | InternetOpen, handle allocation, HTTP POST body assembly ^[r2:fcn.0040782c] |
0x409bb0 |
Init stub | Sets flag gates (0x425129, 0x42512e), calls 0x425580 (likely TlsSetValue or init) ^[r2:fcn.00409bb0] |
0x40b734 |
Alloc wrapper | Wrapper around 0x4068c0 (heap alloc with size rounding) ^[r2:fcn.0040b734] |
0x40cfcc |
HTTP POST builder | Decrypts "POST", assembles encrypted body, calls 0x425814 ^[r2:fcn.0040cfcc] |
0x40d4b0 |
Alphabet builder | Decrypts 16-DWORD array into A-Z a-z 0-9 lookup table ^[r2:fcn.0040d4b0] |
0x417034 |
Main orchestrator | PEB-walk, thread creation, flag-gated execution flow ^[r2:fcn.00417034] |
C2 Infrastructure
No hard-coded C2 endpoints survive in the binary. The C2 domain, path, and User-Agent are generated at runtime via:
- LCG PRNG (
0x40110c) producing indices into the alphabet table. - Character-by-character assembly into a wide-character buffer.
- HTTP POST verb decrypted from
0x40cfcc("POST" wide). - Payload body encrypted (likely via
CryptEncryptat slot0x4257a4) before transmission.
Static inference only — no CAPE detonation available. The presence of HttpOpenRequest/HttpSendRequest patterns, CryptEncrypt, and the POST verb assembly confirms outbound C2 capability, but exact domains are runtime-resolved.
Interesting Tidbits
- POGO optimization: The binary carries
IMAGE_DEBUG_TYPE_POGO(Profile Guided Optimization) metadata, typical of MSVC release builds tuned for hot-path performance. This is unusual for commodity malware and suggests a builder that compiles with full optimization. ^[pefile.txt:313] - Canary + ASLR + DEP: All modern mitigations are enabled, yet the binary manually allocates RWX memory (
VirtualAlloc→WriteProcessMemory→VirtualProtect). The reflective loader bypasses DEP by design. ^[rabin2-info.txt] - Alphabet cipher novelty: The base-62 alphabet table construction via encrypted DWORD arrays is not a standard obfuscation pattern. It evades string-scanning tools because no C2 strings exist in the binary — only the alphabet and the PRNG seed. ^[r2:fcn.0040d4b0]
- OpenCTI label
dropped-by-phorpiex: This sample was dropped by Phorpiex infrastructure, but the binary itself is not a Phorpiex downloader. It is a second-stage payload with its own toolchain (MSVC 14.12 vs. the Delphi/VCL or MinGW builders seen in Phorpiex droppers). Attribution to a specific family is pending. ^[metadata.json] - GUI subsystem: Declares
Windows GUIbut shows no window-creation logic in the decompiled entry path. The USER32/GDI32 imports are likely decoys or minimal scaffolding for the subsystem requirement. ^[file.txt]
How To Mess With It (Homelab Replication)
- Toolchain: Install Visual Studio 2017 15.5+ (MSVC 14.12) or VS 2019/2022 with v141 toolset.
- Compile a stub that:
- Walks PEB InMemoryOrderModuleList.
- Computes export hashes (e.g., DJB2 or CRC32) for
VirtualAlloc,CreateThread,InternetOpenA. - Stores encrypted pointers in a
.dataslot array. - Uses
cpuidleaf 1 / leaf 7 checks for VM gating. - Implements the LCG
seed = (seed * 0x19660d + 0x3c6ef35f) & 0x7ffffff.
- String obfuscation: Embed the alphabet as XOR-NOT encrypted DWORDs. Build C2 URLs at runtime by indexing the alphabet with PRNG output.
- Verification: Run
capa <reproducer.exe>— should match the same anti-analysis and runtime-resolution capabilities seen in this sample's (failed) capa run.
Deployable Signatures
YARA Rule
rule unattributed_136b5750_pe32_xor_not_loader
{
meta:
description = "PE32 MSVC 14.12 reflective loader with XOR-NOT string crypto and PEB-walking API resolution"
author = "PacketPursuit"
date = "2026-07-29"
sha256 = "136b57507a3cfe1fc39cf6973869dca7e871e91d63f283c77380fc33298d6d51"
strings:
$xor_not_key = { 3D FF 5F 03 10 } // cmp eax, 0x10035fff
$xor_not_op = { 81 31 FF 5F 03 10 } // xor dword ptr [ecx], 0x10035fff
$lcg_mul = { 0D 66 19 00 00 } // 0x19660d
$lcg_inc = { 35 3C EF C6 03 } // 0x3c6ef35f
$lcg_mask = { 25 FF FF FF 07 } // and eax, 0x7ffffff
$alphabet_1 = { 41 BB BF EA } // encrypted "ABCD"
$alphabet_2 = { 45 E6 BB A7 } // encrypted "EFGH"
$alphabet_3 = { 49 EA B7 A3 } // encrypted "IJKL"
$post_wide = { 50 A0 B3 EF 53 A0 A8 EF 00 A0 FC EF } // encrypted "POST" wide fragments
condition:
uint16(0) == 0x5A4D and
uint32(uint32(0x3C) + 0x18) == 0x10B and // PE32
3 of ($xor_not_*) and
2 of ($lcg_*) and
2 of ($alphabet_*) and
$post_wide
}
Behavioral Fingerprint
This binary loads with a minimal import table (GDI32, USER32, KERNEL32 GUI functions only). Within the first 5 seconds of execution, it walks the PEB InMemoryOrderModuleList to resolve
VirtualAlloc,CreateThread,InternetOpen, and cryptographic APIs by hash. It allocates RWX memory, copies a decrypted payload into it, and spawns a file-system enumeration thread (FindFirstFilewith"*"wildcard) alongside a network thread that assembles an HTTP POST request. The POST body is encrypted with a session key imported viaCryptImportKey. C2 domain and User-Agent are generated at runtime using a seeded LCG PRNG and a base-62 alphabet table. If executed inside a VM, CPUID leaf 1 ECX[31] or leaf 7 EBX[18] hypervisor bits cause altered code paths or early termination.
IOCs
| Indicator | Value | Notes |
|---|---|---|
| SHA-256 | 136b57507a3cfe1fc39cf6973869dca7e871e91d63f283c77380fc33298d6d51 |
Primary |
| SHA-1 | 5d12d573caddd78d39ef56deaf9afe44636ae19b |
.text section only |
| MD5 (full) | 991090a0e1f6d5368522abda010b3fba |
.data section |
| Compilation | Sep 9 2022 01:27:01 UTC | Timestamp 0x631A9665 |
| Linker | 14.12 | VS 2017 15.5+ |
| TLSH | C3E37D21F612D0B3C87718F13736B1B2F39E8D2C29A56907DAD80F99BC658236F05997 |
Entropy-high .data |
| XOR Key | 0x10035fff |
Used for string + pointer encryption |
| LCG multiplier | 0x19660d |
PRNG constant |
| LCG increment | 0x3c6ef35f |
PRNG constant |
| Anti-VM | CPUID leaf 1 ECX[31], leaf 7 EBX[18], RDTSC rotate-13 | Static detection targets |
| Pseudo-import region | 0x425000–0x425fff (.data VA) |
Decrypted at runtime |
Detection Signatures
| ATT&CK Technique | Implementation |
|---|---|
| T1055 — Process Injection | Reflective PE loader: VirtualAlloc → write → VirtualProtect → thread creation ^[r2:fcn.00406668] |
| T1071.001 — Application Layer Protocol: Web Protocols | HTTP POST C2 with encrypted body; WinInet API resolution ^[r2:fcn.0040cfcc] |
| T1027 — Obfuscated Files or Information | XOR-NOT encrypted strings, base-62 alphabet encoding, runtime C2 URL generation ^[r2:fcn.00401240] ^[r2:fcn.0040d4b0] |
| T1497.001 — Virtualization/Sandbox Evasion: System Checks | CPUID hypervisor-bit checks (leaf 1 ECX[31], leaf 7 EBX[18]) ^[r2:fcn.004010bc] |
| T1497.002 — Virtualization/Sandbox Evasion: User Activity Based | RDTSC differential timing gate ^[r2:fcn.004010bc] |
| T1083 — File and Directory Discovery | Recursive "*" enumeration via FindFirstFile / FindNextFile ^[r2:fcn.00407468] |
| T1573.001 — Encrypted Channel: Symmetric Cryptography | CryptEncrypt / CryptDecrypt for C2 payload body ^[r2:fcn.0040782c] |
| T1105 — Ingress Tool Transfer | Downloader / payload retrieval via HTTP POST response handling ^[r2:fcn.0040782c] |
References
- OpenCTI artifact:
6d13c7ca-593e-4afd-9785-c7531b1b038a, labels:dropped-by-phorpiex,exe,malware-bazaar^[metadata.json] - Related wiki pages: unattributed, peb-walking-api-resolution
- Phorpiex entity page (for dropper linkage, not payload family): phorpiex
Provenance
Analysis produced from static triage inputs (file.txt, pefile.txt, exiftool.json, metadata.json, rabin2-info.txt, yara.txt, ssdeep.txt, tlsh.txt) and radare2 decompilation (analysis level 3) of the binary at <sample 136b57507a3c.bin>. CAPA and floss failed due to missing signature database and incorrect CLI invocation, respectively. CAPE dynamic analysis skipped — no Windows guest available. All behavioral claims are statically inferred and marked accordingly.