typeanalysisfamilyunclassified-pe32plusconfidencelowpecompilerevasionanti-debugunclassifiedmsvccpptls-callback
SHA-256: 0b6a849a68a48f7301c3459a7771378e458e2d5debce9376be350784c61b72b7

unclassified-pe32plus: 0b6a849a — 5.9 MB MSVC C++ PE32+ with TLS callbacks, empty C2 footprint

Executive Summary

OpenCTI and MalwareBazaar co-labeled this sample hippamsascom / sunwukong. Structural analysis shows this is a false-positive attribution. The binary diverges from confirmed hippamsascom siblings on every build/RE axis: 12× larger .text, standard populated IAT (not empty), no semantic export obfuscation, no masquerade version info, C++ STL runtime, and a TLS callback array with 8 entries. Static IOCs are absent; no C2 strings, no certificate artifacts, no registry or mutex indicators. CAPE detonation skipped (no Windows guest). Family classification remains unclassified-pe32plus.

What It Is

Property Value
SHA-256 0b6a849a68a48f7301c3459a7771378e458e2d5debce9376be350784c61b72b7
File name s_folder.exe ^[triage.json]
Type PE32+ executable (GUI) x86-64, 7 sections ^[file.txt]
Size 5,889,536 bytes (^5.6 MB) ^[triage.json]
Compiled Wed May 6 15:42:39 2026 UTC ^[rabin2-info.txt]
Linker MSVC 14.41 (VS 2022) ^[exiftool.json]
Language C++ (MSVC CRT + STL exception framework) ^[strings.txt:1431-1437] ^[rabin2-info.txt]
Signed No ^[rabin2-info.txt]
Stripped No (debug symbol absent, but unwind pdata present) ^[rabin2-info.txt]
capa Failed — missing signature database ^[capa.txt]
CAPE Skipped — no Windows machine available ^[dynamic-analysis.md]

Section inventory:

Section RVA Raw size Entropy Notes
.text 0x1000 0x4f7200 5.32 5.2 MB — massive; minimal recoverable strings ^[pefile.txt:77-95]
.rdata 0x4f9000 0x48400 6.03 Import name table, C++ RTTI, TLS directory ^[pefile.txt:97-115]
.data 0x542000 0xE00 2.01 Tiny ^[pefile.txt:117-135]
.pdata 0x549000 0x2400 5.67 Exception unwind data ^[pefile.txt:137-155]
.fptable 0x54c000 0x200 0.00 Function pointer table (all nulls per raw dump) ^[pefile.txt:157-175]
.reloc 0x54d000 0x800 4.97 Base relocation data ^[pefile.txt:177-195]
.rsrc 0x54e000 0x5a800 3.42 6 icon groups; no RT_VERSION ^[pefile.txt:197-215]

Key divergence from hippamsascom cluster: confirmed siblings (e.g. 0c9e772d, 9a3c18be) are ~400–500 KB, have empty IATs, semantic-jargon-export-obfuscation, masquerade VS_VERSIONINFO (Emard LLC / Littel LLC), and custom PEB-walking API resolution. This sample has a standard import table, no exports, no masquerade, and a standard MSVC CRT entry. ^[pefile.txt:268-358], ^[sample 0c9e772d/pefile.txt]

How It Works

Static-only inference — no CAPE runtime data, no floss/capa.

  1. TLS callbacks fire before CRT main. The PE contains a IMAGE_DIRECTORY_ENTRY_TLS with AddressOfCallBacks = 0x1404f9338 ^[pefile.txt:247-248]. Eight callback entries are present in the array ^[dump_tls.py]. This is a known evasion/anti-debug vector: code executes before analyst breakpoints can be set on CRT main ^[techniques/tls-callback-anti-analysis].

  2. Standard MSVC C++ CRT startup. Entry point (0x1404e8cb4) follows the post-VS-2015 pattern: sub rsp, 0x28call __security_init_cookiejmp __scrt_common_main_seh ^[r2:entry0]. The latter initializes the CRT heap, exception chain, and locale, then dispatches to the actual main stub at 0x1402effa0 with args (argc=0, argv=ImageBase, penv). This is normal toolchain output, not a custom self-loader.

  3. Minimal static strings despite 5.2 MB .text. Only ~1,500 ASCII strings were recovered ^[strings.txt]. This is extremely low for a binary of this size. Possibilities:

    • Heavy string encryption or runtime decoding (common in malware)
    • Large statically-linked C++ codebase with few human-readable literals
    • Most API names resolved dynamically via LoadLibraryA / GetProcAddress (both imported in IAT) ^[pefile.txt:279,281]
  4. .fptable section at zero entropy. The 0x200-byte .fptable is all nulls in the on-disk image ^[pefile.txt:157-175]. At runtime this region is RWX (0xC0000040). It may serve as a scratchpad for resolved function pointers or unpacked payload segments.

  5. No external library strings found. Scans for Qt, Boost, OpenSSL, Python, Chromium, Node.js, game engine, crypto library, and network stack signatures returned negative except a single 2-byte V8 hit (insufficient to claim V8 embedding) ^[check_pe_library.py].

Decompiled Behavior

Entry point (0x1404e8cb4) — disassembly via radare2 ^[r2:entry0]:

sub rsp, 0x28
call fcn.1404e93a0        ; __security_init_cookie
add rsp, 0x28
jmp loc.0x1404e8b40       ; __scrt_common_main_seh

__scrt_common_main_seh sets up IsProcessorFeaturePresent, IsDebuggerPresent, QueryPerformanceCounter, then calls the user main stub at 0x1402effa0 with arguments argc=0, argv=0x140000000, penv=0x140543220 ^[r2:entry0].

The main stub (0x1402effa0) could not be reached for full decompilation within analysis timeouts; the function is inside the 5.2 MB .text morass. radare2 level-2 analysis timed out, and Ghidra remained in queued state throughout. Xrefs from entry0 into .text suggest a monolithic codebase with little modular structure.

Notable imported APIs (standard IAT, only KERNEL32.dll):

  • IsDebuggerPresent — T1622 debugger detection ^[pefile.txt:299]
  • VirtualProtect — runtime page protection changes ^[pefile.txt:349]
  • LoadLibraryA, GetProcAddress — dynamic API resolution for non-KERNEL32 DLLs ^[pefile.txt:279,281]
  • CreateFileW, WriteFile, FindFirstFileExW, FindNextFileW — filesystem interaction ^[pefile.txt]

No ADVAPI32, USER32, SHELL32, WS2_32, or WININET imports are present in the static IAT. This is unusual for a 5.9 MB binary and reinforces the hypothesis that most higher-level APIs are resolved dynamically at runtime.

C2 Infrastructure

  • Static IOCs: None. No domains, IPs, URLs, mutex names, named pipes, or registry keys were recovered from strings, resources, or headers.
  • Dynamic inference: Not available — CAPE detonation skipped (no Windows guest) ^[dynamic-analysis.md].
  • Conclusion: C2 is either fully runtime-resolved (via dynamic API resolution + encrypted config), or this sample is not primarily network-communicative in its current form. The 5.2 MB .text could contain payload logic that only activates under specific conditions.

Interesting Tidbits

  • Absent masquerade — every confirmed hippamsascom sibling carries a fabricated VS_VERSIONINFO (company name, product name, original filename). This samples .rsrc has 6 embedded icons but no RT_VERSION block at all ^[pefile.txt:360+], ^[check_pe.py].
  • Unsigned — confirmed siblings carry fabricated Authenticode (Emard LLC, Hane Group, Littel LLC intermediate CAs). This sample shows signed: false ^[rabin2-info.txt].
  • No exports — hippamsascom employs semantic-jargon-export-obfuscation (330 names → 19 unique RVAs). This sample has a zero-length export directory ^[pefile.txt:219-221].
  • C++ exception frameworkstd::bad_alloc, std::length_error, std::out_of_range, std::logic_error, std::bad_exception, std::bad_array_new_length, std::exception, std::type_info ^[strings.txt:1431-1437]. None of the confirmed hippamsascom siblings show STL runtime strings.
  • fptable section — unique among corpus samples observed to date. The zero-entropy RWX scratchpad is empty on disk and writable at runtime ^[pefile.txt:157-175].
  • TLS callback count — 8 entries is well above the standard MSVC output (0–2). This is a red flag for anti-analysis logic.

How To Mess With It (Homelab Replication)

Goal: Reproduce a MSVC C++ PE32+ with TLS callbacks and minimal IAT to understand pre-main execution timing.

Toolchain: Visual Studio 2022 (v14.3x), x64 Release, C++17.

Steps:

  1. Create a new C++ Console App in VS 2022.
  2. In any .cpp, add a TLS callback:
#pragma comment(linker, "/INCLUDE:__tls_used")
#pragma data_seg(".tls")
#pragma data_seg(".tls$ZZZ")
#pragma data_seg()

#pragma section(".CRT$XLB", read)
extern "C" __declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK TlsCallback =
    [](PVOID hModule, DWORD Reason, PVOID Reserved) {
        if (Reason == DLL_PROCESS_ATTACH) {
            // Pre-main code here
            OutputDebugStringA("TLS callback fired\n");
        }
    };
  1. Build with /GS and /guard:cf enabled (defaults in Release).
  2. Verify with rabin2 -I your.exe — look for havecode true, canary true, pic true.
  3. Dump TLS callbacks: python3 -c "import pefile; pe = pefile.PE('your.exe'); print([hex(c) for c in pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks])".

What you will learn: TLS callbacks execute before main() — before most EDR user-mode hooks are in place. Malware uses this gap to set up hooks, decrypt config, or terminate if a debugger is detected.

Deployable Signatures

YARA rule

rule Unclassified_LargeTLS_x64_MinimalStrings
{
    meta:
        description = "Large x64 MSVC binary with TLS callbacks, minimal IAT, no RT_VERSION"
        author = "pp-hermes"
        date = "2026-05-31"
        hash = "0b6a849a68a48f7301c3459a7771378e458e2d5debce9376be350784c61b72b7"
    strings:
        $mz = { 4D 5A }
        $pe = { 50 45 00 00 64 86 }
        $debug = "IsDebuggerPresent"
        $vprotect = "VirtualProtect"
        $std_ex1 = ".?AVbad_alloc@std@@"
        $std_ex2 = ".?AVexception@std@@"
    condition:
        $mz at 0 and
        $pe at (uint32(0x3C) + 4) and
        uint16(uint32(0x3C) + 0x14) == 0xF0 and  // OptionalHeader64
        filesize > 5MB and
        filesize < 7MB and
        pe.number_of_sections == 7 and
        pe.sections[0].name == ".text" and
        pe.sections[0].raw_size > 0x4A0000 and   // .text > 4.8 MB
        pe.sections[5].name == ".reloc" and
        pe.sections[6].name == ".rsrc" and
        pe.tls_callbacks > 0 and
        pe.tls_callbacks == 8 and
        for any i in (0..pe.number_of_sections - 1): (
            pe.sections[i].name == ".fptable"
        ) and
        all of ($debug, $vprotect) and
        2 of ($std_ex*)
}

Sigma rule (Process Creation — heuristic)

title: Suspicious Large PE with TLS Callbacks
description: Detects execution of unusually large x64 PE with pre-main TLS callback count > 2
logsource:
    category: process_creation
    product: windows
detection:
    selection:
        ParentImage|contains:
            - 'explorer.exe'
            - 'cmd.exe'
        CommandLine|contains:
            - 's_folder.exe'
    condition: selection
falsepositives:
    - Legitimate large C++ applications with complex TLS init
level: low

Note: The above Sigma is placeholder — the static-only sample provides no process-tree or command-line artifacts. A production rule would need runtime telemetry.

IOC list

Type Indicator Notes
SHA-256 0b6a849a68a48f7301c3459a7771378e458e2d5debce9376be350784c61b72b7 Primary
SHA-1 20240c0bdb53f2432d4de31b048cfb206e729027 ^[pefile.txt:93]
MD5 9efc1d267d047b1097273cc38cad7851 .text hash ^[pefile.txt:92]
File name s_folder.exe On-disk name ^[triage.json]
PE timestamp Wed May 6 15:42:39 2026 UTC Likely fabricated ^[pefile.txt:34]
Entry point 0x1404e8cb4 CRT startup pattern ^[pefile.txt:50]
TLS callbacks 0x1404f9338 → 8 entries Array VA ^[dump_tls.py]

Behavioral fingerprint statement

This sample is a PE32+ x64 MSVC C++ binary with an unusually large .text section (> 5 MB) and a near-empty ASCII string footprint (≈1,500 recoverable strings). The import table is restricted to KERNEL32.dll (61 functions) with IsDebuggerPresent and VirtualProtect imported explicitly. A .fptable RWX section of 512 bytes is present, along with a TLS callback array containing 8 entries — a count well above standard compiler output. No RT_VERSION resource, no export table, and no Authenticode signature. The entry point follows standard VS-2022 CRT startup via __security_init_cookie__scrt_common_main_sehmain(0, ImageBase, penv).

Detection Signatures

Capability / Technique ATT&CK ID Evidence Confidence
Debugger Detection T1622 IsDebuggerPresent in IAT ^[pefile.txt:299] Static — high
Obfuscated Files / Information T1027 5.2 MB .text with minimal strings; potential encrypted payload Static — medium
Software Packing T1027.002 .fptable RWX scratchpad; possible in-memory unpacking Static — low
Native API T1106 LoadLibraryA + GetProcAddress for dynamic resolution ^[pefile.txt:279,281] Static — high
Process Injection — inferred T1055 VirtualProtect + RWX .fptable + large .text suggest in-memory payload staging Static — low
Evade Detection T1497 TLS callbacks (8 entries) execute pre-main / pre-hook Static — medium

References

Provenance

Static analysis performed by pp-hermes on 2026-05-31. Tools used: file (file type), exiftool (metadata), pefile (PE structure), radare2 v5.9.8 (disassembly/decompilation), python3 + pefile library (PE resource/section / TLS parsing), strings (ASCII string extraction), ssdeep (fuzzy hash comparison). capa v7.0.1 failed due to missing rule signatures. floss failed with argument-parsing error. CAPE detonation skipped (no Windows guest available). Ghidra queued but analysis did not complete within session timeout. All observations are static-only where runtime data is absent.