typeanalysisfamilyphorpiexconfidencemediummalware-familyloaderpeparasitic-infectorphorpiexruntime-api-resolutionregistry-evasionmsvc
SHA-256: d69d4497aa86ebf8e32ed15ecb21a3bf37aefdd23e517646680d62a718772647

phorpiex: 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. .text entropy 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:

  1. Resolves the drive letter via QueryDosDeviceW ^[strings.txt:71]
  2. 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]
  3. Recursively walks directories via FindFirstFileW / FindNextFileW
  4. Matches *.exe files via PathMatchSpecW ^[strings.txt:18]
  5. Lower-cases each path via CharLowerW before 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:

  1. Appends a new section named .zero (Characteristics 0x60000000 = executable+readable) via fcn.004022d0 ^[r2:fcn.004022d0]
  2. 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.
  3. Patches the victim's entry point in the PE header (AddressOfEntryPoint) to point into the new .zero section, using ImageBase alignment arithmetic ^[r2:fcn.00402530]
  4. Sets ImageBase = 0xdead as an infection marker ^[r2:fcn.00402530]
  5. 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:

  1. Resolves APIs via PEB walking + export-name hashing — it manually walks the PEB InMemoryOrderModuleList, iterates each module's export table, hashes every exported name with fcn.00401920 / fcn.00401a70, and caches the pointer when the hash matches a hardcoded constant ^[r2:fcn.00401bc0]
  2. Resolves urlmon.dll by hash, then loads it via the resolved LoadLibraryW equivalent
  3. Constructs %appdata%\windrx.txt via stack-allocated wide-character string building (character-by-character to avoid static string references in the shellcode) ^[r2:fcn.004010f0]
  4. Downloads a payload using the resolved URLDownloadToFileW (hash 0x7a3a310) — 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 module
  • 0xc4b4a94d → second API
  • 0x7a3a310 → URLDownloadToFileW (inferred from context)
  • 0x165d9659 → URLDownloadToFileW parameter setup
  • 0xeae447bb → 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

  1. 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]
  2. Trampoline via ntdll.dll copy. Before patching the victim PE, the infector copies the shellcode into ntdll.dll address space (0x60000000 access, .zero section 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]
  3. 0xdead infection sentinel. Using ImageBase = 0xdead is a crude but effective infection marker that survives PE rebuilds and is unlikely to collide with legitimate software. ^[r2:fcn.00402530]
  4. 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]
  5. Registry evasion via NoDrives compliance. By respecting existing NoDrives settings, 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)

  1. 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).
  2. Key APIs: SHLWAPI.dll for path manipulation, KERNEL32.dll for file mapping, ADVAPI32.dll for registry reads.
  3. Infection logic recipe:
    • Walk GetLogicalDrives() → QueryDosDeviceW() for each visible drive
    • Skip blacklisted directory names (case-insensitive via CharLowerW)
    • For each *.exe, memory-map via CreateFileMappingA+MapViewOfFile
    • Validate MZ/PE, check ImageBase != 0xdead
    • Append a new section header with Name=.zero, VirtualSize=shellcode_size, Characteristics=0x60000000
    • Update NumberOfSections, fix SizeOfImage, patch AddressOfEntryPoint
    • Copy shellcode (position-independent, compiled with /GS- and no relocs)
    • Flush, unmap, truncate, close
  4. Shellcode recipe:
    • Position-independent x86 code using EBP-relative stack frames
    • PEB base via fs:[0x30] (x86) or gs:[0x60] (x64)
    • Walk InMemoryOrderModuleList, hash exports with a simple djb2 or ROR variant
    • Match hardcoded 32-bit hash constants
    • Call resolved URLDownloadToFileW with a stack-built URL
  5. Verification: infected PE should have 6 sections (including .zero), ImageBase = 0xdead, and entry point in the .zero section RVA range. strings on the victim should reveal TWIZTPEINF.

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

Provenance

  • file.txt — file(1) output
  • exiftool.json — ExifTool PE metadata
  • pefile.txt — pefile.py structured dump
  • strings.txt — plain strings extraction
  • rabin2-info.txt — radare2 binary header summary
  • yara.txt — YARA scan (PE_File_Generic only, no family rule)
  • binwalk.txt — embedded artefact scan (plain PE, no overlay)
  • floss.txt — FLOSS failed with argument-parsing error; no decoded strings
  • capa.txt — capa failed (missing signatures directory)
  • Dynamic: CAPE skipped — no Windows guest available.
  • radare2 analysis at depth 3 (73 functions recovered), decompilation via pdc backend.