d69d4497aa86ebf8e32ed15ecb21a3bf37aefdd23e517646680d62a718772647phorpiex: d69d4497 — TWIZTPEINF parasitic file infector, MSVC9 native PE appender
Executive Summary. A 22 KB MSVC9 x86 native binary that appends encrypted shellcode as a new .zero PE section to every *.exe found on removable and fixed drives, patches the victim's entry point to trampoline through ntdll.dll space, and hides infected drives via NoDrives registry manipulation. Static-only analysis (no CAPE Windows guest). OpenCTI labels it dropped-by-phorpiex; it shares the TWIZTPEINF infection marker and MSVC9 build fingerprint with sibling e0de4e3c.... This is the first parasitic infector (as opposed to downloader/spam-bot) observed in the Phorpiex campaign cluster.
What It Is
- File:
d69d4497aa86ebf8e32ed15ecb21a3bf37aefdd23e517646680d62a718772647.bin(22,528 bytes) - Type: PE32 executable (GUI) Intel 80386, 5 sections ^[file.txt]
- Toolchain: MSVC 9.0 (Visual Studio 2008), linker 9.0, MSVCR90.dll CRT ^[exiftool.json], ^[pefile.txt:44]
- Timestamp: 2026-05-29 11:32:30 UTC (same build day as Phorpiex business-app masquerade downloader
0371fbbf) ^[pefile.txt:34] - Family: Phorpiex (campaign umbrella). This sample is a parasitic file infector, distinct from the downloader/spam-bot morphs previously catalogued under the same label.
- Signing: Unsigned. No Authenticode, no security directory. ^[pefile.txt:192]
- Packing: None. Plain MSVC9 native code, no UPX, no crypter.
.textentropy 6.05, within normal range for compiled C++. ^[pefile.txt:92]
How It Works
Infection Marker & Mutex Gating
On launch, the binary sleeps 2 s, sets error mode, then creates a mutex named TWIZTPEINF ^[strings.txt:8], ^[r2:entry0]. If the mutex already exists (GetLastError() == ERROR_ALREADY_EXISTS, 0xB7), it exits immediately — single-instance gating to prevent re-infection loops.
It also checks for %appdata%\windrx.txt via PathFileExistsW ^[strings.txt:16], ^[r2:main]. If absent, it creates the file as a filesystem infection marker (empty, CreateFileW with GENERIC_WRITE) ^[r2:fcn.00402830].
Registry-Based Drive Hiding
Before spreading, the binary queries Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\NoDrives under both HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE ^[strings.txt:96], ^[r2:fcn.004029b0]. It merges the two DWORD values with GetLogicalDrives() to derive a drive-bitmask of visible drives. This is the drive-selection logic: drives that are NOT hidden by NoDrives are candidates for infection. In other words, the malware respects existing NoDrives configuration and only targets drives that Explorer already shows.
Implication: this is a deliberate evasion technique. If the victim already uses NoDrives to hide sensitive shares, the infector will skip them. It prefers external/removable drives (which are typically unhidden).
Drive Enumeration & Recursive .exe Search
For each visible drive (A: through Z:, starting at index 2), the binary:
- Resolves the drive letter via
QueryDosDeviceW^[strings.txt:71] - Skips drives whose root directory name matches a blacklist:
windows,winsxs,cache,boot,microsoft,system,programdata,program files,appdata,application data,intel,default,config,perflogs,recovery,drivers,prefetch,recycle,extend,msocache,temp^[strings.txt:117–131], ^[r2:fcn.00402a70] - Recursively walks directories via
FindFirstFileW/FindNextFileW - Matches
*.exefiles viaPathMatchSpecW^[strings.txt:18] - Lower-cases each path via
CharLowerWbefore comparison to ensure case-insensitive matching on case-sensitive filesystems ^[r2:fcn.004030a0]
PE Infection — The .zero Section Append
When an *.exe candidate is found, fcn.00402530 maps it via CreateFileMappingA + MapViewOfFile, validates MZ/PE signatures, and ensures it is either PE32 (Machine=0x14c) or PE32+ (Machine=0x8664) with Magic=0x10b or 0x20b respectively ^[r2:fcn.00402530].
Critical: the infector does not infect files that are already infected. It checks OptionalHeader.Magic plus the ImageBase field: if ImageBase == 0xdead, it skips — this is the infection sentinel. ^[r2:fcn.00402530]
If the file passes validation, the infector:
- Appends a new section named
.zero(Characteristics0x60000000= executable+readable) viafcn.004022d0^[r2:fcn.004022d0] - Copies its own shellcode into the new section. The shellcode is the body of
fcn.004010f0(the payload routine), extracted at runtime by subtracting two function pointers to get its size:size = (fcn.00401ef0) - (fcn.004010f0)^[r2:fcn.00402530]. This is a self-measurement trick: the shellcode region is bounded by two known function addresses. - Patches the victim's entry point in the PE header (
AddressOfEntryPoint) to point into the new.zerosection, usingImageBasealignment arithmetic ^[r2:fcn.00402530] - Sets
ImageBase = 0xdeadas an infection marker ^[r2:fcn.00402530] - Flushes the mapped view, unmaps, truncates the file to the new size, and closes handles ^[r2:fcn.00402530]
Payload Shellcode — fcn.004010f0
The appended shellcode is a position-independent routine that:
- Resolves APIs via PEB walking + export-name hashing — it manually walks the PEB
InMemoryOrderModuleList, iterates each module's export table, hashes every exported name withfcn.00401920/fcn.00401a70, and caches the pointer when the hash matches a hardcoded constant ^[r2:fcn.00401bc0] - Resolves
urlmon.dllby hash, then loads it via the resolvedLoadLibraryWequivalent - Constructs
%appdata%\windrx.txtvia stack-allocated wide-character string building (character-by-character to avoid static string references in the shellcode) ^[r2:fcn.004010f0] - Downloads a payload using the resolved
URLDownloadToFileW(hash0x7a3a310) — likely a stage-2 from the same C2 infrastructure used by other Phorpiex morphs ^[r2:fcn.004010f0]
The payload hash constants observed in fcn.00401bc0:
0x526e0dcd→ resolves to an API in the first loaded module0xc4b4a94d→ second API0x7a3a310→URLDownloadToFileW(inferred from context)0x165d9659→URLDownloadToFileWparameter setup0xeae447bb→ final call target
This is a custom hash-based PEB walker, distinct from the simpler name-based iteration seen in other Phorpiex samples (e.g., the 136b5750 reflective loader).
Thread Model
The main thread creates a worker thread (CreateThread) that runs the recursive file walker (fcn.00403130 → fcn.00402a70), then sleeps ~36 hours (0x2255100 ms ≈ 36.2 h) before exiting ^[r2:main]. This long sleep makes the process appear idle in sandboxes with short timeouts.
C2 Infrastructure
No static C2 URLs are present in the infector body. The C2 is runtime-resolved by the injected shellcode via the PEB-walker + URLDownloadToFileW path. The URL string is constructed on the stack inside fcn.004010f0 and not visible in static strings.
Attribution evidence: the TWIZTPEINF mutex string and the MSVC9 build fingerprint (linker 9.0, MSVCR90.dll, May 2026 timestamp) place this in the same campaign window as other Phorpiex samples. The sibling e0de4e3c... (also bearing TWIZTPEINF) was pulled from MalwareBazaar with the same dropped-by-phorpiex tag.
Interesting Tidbits
- Self-measuring shellcode extraction. The infector computes its payload size at runtime as
&fcn.00401ef0 - &fcn.004010f0. This avoids hardcoding a size and adapts if the shellcode is recompiled with different optimization. ^[r2:fcn.00402530] - Trampoline via ntdll.dll copy. Before patching the victim PE, the infector copies the shellcode into ntdll.dll address space (
0x60000000access,.zerosection name) and then copies from there into the victim. This may be an attempt to bypass memory-integrity scanners that flag writes to mapped PE files. ^[r2:fcn.00402160] 0xdeadinfection sentinel. UsingImageBase = 0xdeadis a crude but effective infection marker that survives PE rebuilds and is unlikely to collide with legitimate software. ^[r2:fcn.00402530]- No network imports in the host. The infector itself imports zero networking APIs. All download logic lives inside the position-independent shellcode that gets injected into victims. This splits the binary into a "clean" host (no obvious malicious imports) and a "dirty" payload (runtime-resolved). ^[pefile.txt:229]
- Registry evasion via
NoDrivescompliance. By respecting existingNoDrivessettings, the malware avoids drawing attention to already-hidden drives and focuses infection on visible external media. ^[r2:fcn.004029b0]
How To Mess With It (Homelab Replication)
- Toolchain: MSVC 9.0 (Visual Studio 2008) or compatible Windows SDK v6.1. Target
Win32,Subsystem:Windows,/MT(static CRT would remove MSVCR90 dependency; this sample uses dynamic CRT). - Key APIs:
SHLWAPI.dllfor path manipulation,KERNEL32.dllfor file mapping,ADVAPI32.dllfor registry reads. - Infection logic recipe:
- Walk
GetLogicalDrives()→QueryDosDeviceW()for each visible drive - Skip blacklisted directory names (case-insensitive via
CharLowerW) - For each
*.exe, memory-map viaCreateFileMappingA+MapViewOfFile - Validate MZ/PE, check
ImageBase != 0xdead - Append a new section header with Name=
.zero, VirtualSize=shellcode_size, Characteristics=0x60000000 - Update
NumberOfSections, fixSizeOfImage, patchAddressOfEntryPoint - Copy shellcode (position-independent, compiled with
/GS-and no relocs) - Flush, unmap, truncate, close
- Walk
- Shellcode recipe:
- Position-independent x86 code using EBP-relative stack frames
- PEB base via
fs:[0x30](x86) orgs:[0x60](x64) - Walk
InMemoryOrderModuleList, hash exports with a simple djb2 or ROR variant - Match hardcoded 32-bit hash constants
- Call resolved
URLDownloadToFileWwith a stack-built URL
- Verification: infected PE should have 6 sections (including
.zero),ImageBase = 0xdead, and entry point in the.zerosection RVA range.stringson the victim should revealTWIZTPEINF.
Deployable Signatures
YARA — Infector Detection (the dropper itself)
rule phorpiex_twiztpe_infector {
meta:
description = "Phorpiex TWIZTPEINF parasitic file infector dropper"
author = "packetpursuit"
date = "2026-08-27"
hash = "d69d4497aa86ebf8e32ed15ecb21a3bf37aefdd23e517646680d62a718772647"
strings:
$a = "TWIZTPEINF" wide ascii
$b = "windrx.txt" wide ascii
$c = "NoDrives" wide ascii
$d = { 53 6F 66 74 77 61 72 65 5C 4D 69 63 72 6F 73 6F 66 74 5C 57 69 6E 64 6F 77 73 5C 43 75 72 72 65 6E 74 56 65 72 73 69 6F 6E 5C 50 6F 6C 69 63 69 65 73 5C 45 78 70 6C 6F 72 65 72 } // Software\Microsoft\Windows\CurrentVersion\Policies\Explorer
$e = { 50 61 74 68 46 69 6C 65 45 78 69 73 74 73 57 } // PathFileExistsW
$f = { 50 61 74 68 43 6F 6D 62 69 6E 65 57 } // PathCombineW
$g = { 50 61 74 68 4D 61 74 63 68 53 70 65 63 57 } // PathMatchSpecW
condition:
uint16(0) == 0x5A4D and
filesize < 30KB and
all of ($a, $b, $c) and
4 of ($d, $e, $f, $g)
}
YARA — Infected-File Detection (post-infection victims)
rule twiztpe_infected_pe {
meta:
description = "PE file infected by TWIZTPEINF parasitic appender"
author = "packetpursuit"
date = "2026-08-27"
strings:
$zero = ".zero" ascii
$sentinel = { AD DE } // ImageBase = 0xdead (little-endian in optional header)
condition:
uint16(0) == 0x5A4D and
for any i in (0..pe.number_of_sections - 1): (
pe.sections[i].name == ".zero" and
pe.sections[i].characteristics & 0x60000000 == 0x60000000
) and
pe.optional_header.image_base == 0xdead
}
Sigma — NoDrives Registry Modification (behavioral)
title: NoDrives Registry Modification
description: Detects writes to NoDrives registry value, which may hide infected drives
logsource:
product: windows
service: sysmon
detection:
selection:
EventID: 13
TargetObject|contains: '\\Policies\\Explorer\\NoDrives'
condition: selection
falsepositives:
- Legitimate IT policy to hide network drives
level: medium
IOC List
| Indicator | Type | Notes |
|---|---|---|
d69d4497aa86ebf8e32ed15ecb21a3bf37aefdd23e517646680d62a718772647 |
SHA-256 | Infector dropper |
TWIZTPEINF |
Mutex | Infection gate |
%appdata%\windrx.txt |
File marker | Empty marker file |
0xdead |
PE sentinel | Infected files have ImageBase=0xdead |
.zero |
Section name | RX appended section in infected files |
Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\NoDrives |
Registry | Drive visibility query (read) |
Behavioral Fingerprint
This binary launches as a GUI-subsystem PE with no visible window, sleeps 2 seconds, creates a mutex named TWIZTPEINF, queries NoDrives under both HKCU and HKLM, then enumerates all visible drives recursively skipping system directories. For each *.exe found, it opens the file with GENERIC_READ|GENERIC_WRITE, maps it into memory, validates the PE header, appends a .zero section containing shellcode, patches the entry point, sets ImageBase=0xdead, and closes. The process then sleeps for ~36 hours before exiting. No network connections are initiated by the host process; all C2 activity is deferred to the shellcode injected into victim executables.
Detection Signatures
| ATT&CK ID | Name | Evidence |
|---|---|---|
| T1204.002 | User Execution: Malicious File | Spam-distributed PE (OpenCTI dropped-by-phorpiex) |
| T1059 | Command and Scripting Interpreter | Injected shellcode executes in victim process context |
| T1027.002 | Obfuscated Files or Information | Position-independent shellcode with API hash resolution |
| T1547.001 | Boot or Logon Autostart Execution: Registry Run | Indirect — infected PEs may achieve persistence via normal execution |
| T1497.001 | Virtualization/Sandbox Evasion: System Checks | NoDrives registry read to select target drives |
| T1564.001 | Hide Artifacts: Hidden Files and Directories | NoDrives registry manipulation to hide infected drives |
| T1485 | Data Destruction | Infected PEs are structurally modified (entry point patched, section appended) — not destructive per se, but integrity is compromised |
References
- OpenCTI artifact ID:
dfd58feb-e7ca-49a9-a7c3-5b265ed0e794 - MalwareBazaar:
dropped-by-phorpiexlabel - Sibling sample:
e0de4e3c9dee9877c78832ac9ebe3fbd2de45026896d6fc2a5ca12f6b588a29a(also containsTWIZTPEINF) - Related wiki: phorpiex, peb-walking-api-resolution, parasitic-pe-section-append, shellcode-self-injection-via-ntdll-copy
Provenance
file.txt— file(1) outputexiftool.json— ExifTool PE metadatapefile.txt— pefile.py structured dumpstrings.txt— plain strings extractionrabin2-info.txt— radare2 binary header summaryyara.txt— YARA scan (PE_File_Genericonly, no family rule)binwalk.txt— embedded artefact scan (plain PE, no overlay)floss.txt— FLOSS failed with argument-parsing error; no decoded stringscapa.txt— capa failed (missing signatures directory)- Dynamic: CAPE skipped — no Windows guest available.
- radare2 analysis at depth 3 (73 functions recovered), decompilation via
pdcbackend.