← Back to list

Analysis of an Loader (Ethereum Loader)

Shubho57 · 2026-04-20 02:14 · 1 claps · 10.8 min read
#loader #etherum #malware-analysis #threat-hunting
Open on Medium ↗
Wiki topics: CRY · Crypto & Web3 🔒 · Cybersecurity

Analysis of an Loader (Ethereum Loader)

Summary

  1. The DLL file is highly malicious and performs a lot of different evasion techniques
  2. It also shows how the file is communicating via the Ethereum tokens
  3. The file also shows it is masquerading the actual VMWare instance
  4. Multiple DLL files spawning
  5. It also changes the registry modifications

Analysis of the file

Fig 1 — File information (taken from strairwell file analysis)

Fig 1 — File information (taken from strairwell file analysis)

A sophisticated loader malware — a DLL designed to load and execute a secondary malicious payload.

Key Malicious Behaviors Identified

Deception & Evasion

  • Fake metadata claiming to be from both VMware and HP simultaneously (a red flag)
  • Embeds an unsigned PE file inside itself to hide the real payload
  • Uses IsAttached to detect if it's being debugged

Persistence (survives reboots)

  • Modifies the Windows Registry Run key
  • Creates a scheduled task via schtasks.exe that runs at logon with highest privileges

C2 Communication (Command & Control)

  • Uses Ethereum JSON-RPC calls (eth_call) — meaning it uses the blockchain as a covert channel to receive instructions, bypassing traditional network security

Living-off-the-Land (LotL)

  • Abuses legitimate Windows tools like rundll32.exe and schtasks.exe to avoid detection

Data Exfiltration

  • Can send/receive JSON payloads to external servers (machine name, username, etc.)

Fig 2 — persistent beaconing loop

Fig 2 — persistent beaconing loop

**Sleep(GetTickCount() % 0x898 + 0x320)**

  • Sleeps for a randomized duration
  • GetTickCount() % 0x898 = random value between 0–2199ms
  • + 0x320 = adds a fixed 800ms minimum
  • Total sleep: roughly 0.8 to 3 seconds
  • This is a classic sandbox evasion / timing technique — making behavior unpredictable

**sub_2365b1022()**

  • Calls another function (likely the main malicious payload — C2 beacon, data exfil, etc.)

**while(true) { Sleep(0x7fffffff) }**

  • If the above function returns (which it shouldn’t), it enters an infinite sleep loop
  • Sleeps ~24 days continuously
  • This effectively freezes the thread permanently as a failsafe

Fig 3 — disguised as a legitimate COM server

Fig 3 — disguised as a legitimate COM server

This shows the standard DLL export functions — but with suspicious/malicious behavior hidden inside them.

1. _start (DllMain equivalent)

c

int64_t _start(HMODULE arg1, int32_t arg2)
  • Only executes when arg2 == 1 → means **DLL_PROCESS_ATTACH** (when DLL is first loaded)
  • **DisableThreadLibraryCalls()** — stops notifications for thread creation/deletion, reduces noise, common in malware to stay quiet
  • **GetModuleFileNameW()** — gets the DLL's own file path on disk, stores it at data_2365b5020
  • This is likely used later for self-referencing, copying, or persistence

2. DllGetClassObject

c

int64_t DllGetClassObject(int64_t, int64_t, int64_t* arg3)
  • Sets *arg3 = 0 (nulls out the class pointer)
  • Returns 0x80040111 = **REGDB_E_CLASSNOTREG** (COM error — class not registered)
  • This is a stub/fake COM implementation — makes the DLL look like a legitimate COM server while doing nothing real

3. DllCanUnloadNow

c

int64_t DllCanUnloadNow() __pure
  • Always returns 1 = "yes, unload me"
  • But combined with the __noreturn beaconing loop seen earlier, it never actually unloads
  • This is contradictory behavior — another deception technique

4. DllRegisterServer

c

void DllRegisterServer() __noreturn
  • Sets a flag data_2365b5000 = 1
  • Marked __noreturnthis is likely where the malicious execution chain begins
  • Probably calls the beaconing/payload function seen in the previous screenshot

Fig 4 — C2 response token parser

Fig 4 — C2 response token parser

This is a string parsing/processing function — likely part of the C2 response parser or config decoder.

Step by Step Breakdown

1. Null checks

c

if (arg3 == 0) return
if (*arg3 == 0) return  // empty string check
  • Validates the input string isn’t null or empty before proceeding

2. Decryption/Decoding setup

c

sub_2365b1000(&var_38, "fDoY", 9, 0x37)
  • Calls a sub with hardcoded key **"fDoY"**, size 9, XOR constant 0x37
  • This strongly suggests XOR-based string decryption
  • 0x37 is the XOR key — a common simple obfuscation technique
  • var_38 is a 9-byte buffer that receives the decrypted result

3. Whitespace stripping

c

if (rsi_1 == 0x20)  // 0x20 = space character
    do
        rbx = &rbx[1]  // advance pointer
    while (*rbx == 0x20)
  • Skips leading spaces in the input string
  • Standard string trimming behavior

4. Character-by-character processing

c

char i = var_38[0]
if (i != 0)
    do { ... }
  • Iterates through the decrypted buffer
  • Processes each character in a loop

Fig 5 — extracting a hidden configuration

Fig 5 — extracting a hidden configuration

GetModuleVersion Function Analysis

This function is reading its own DLL’s version info from disk — likely for fingerprinting, C2 check-in, or anti-analysis purposes.

Step by Step

1. Get file version info size

c

dwLen = GetFileVersionInfoSizeW(&data_2365b5020, &lpdwHandle)
  • data_2365b5020 = the DLL's own file path (stored earlier in _start)
  • Gets the size of the version info block embedded in the PE file
  • If dwLen == 0 or arg2 <= 0x1freturn 0 (abort if too small)

2. Allocate heap memory

c

rax_1 = HeapAlloc(GetProcessHeap(), HEAP_NONE, dwLen)
  • Allocates a buffer on the heap to hold the version info
  • If allocation fails → return 0

3. Read version info

c

GetFileVersionInfoW(&data_2365b5020, 0, dwLen, rax_1)
  • Reads the actual version info from the DLL file into the allocated buffer4. Query specific version field

c

VerQueryValueW(pBlock: rax_1, lpSubBlock: &data_2365b2022, &lplpBuffer, &puLen)
  • data_2365b2022 is a hardcoded sub-block path (e.g. \StringFileInfo\040904b0\FileVersion)
  • Extracts a specific version string field

5. Read value at offset +0xc

c

rdx_1 = *(lplpBuffer_1 + 0xc)
  • Reads a value 12 bytes into the returned buffer
  • Likely extracting a specific field like ProductVersion or a hidden config value embedded in the version info

Fig 6 — pre-execution sanity check

Fig 6 — pre-execution sanity check

This function validates a file path — checking whether a file actually exists and extracting an attribute flag from it.

Step by Step

1. Null check

c

if (arg1 == 0)
    rdx = 0
  • If the pointer is null → return 0 (invalid)

2. Empty string check

c

if (*arg1 != 0)  // check first character isn't null terminator
  • If the string is empty → rdx stays 0, returns invalid

3. File existence check

c

rax_2 = GetFileAttributesW(arg1)
  • Calls GetFileAttributesW on the path
  • If file doesn’t exist, returns 0xFFFFFFFF (INVALID_FILE_ATTRIBUTES)

c

if (rax_2 != 0xffffffff)
  • Only proceeds if the file actually exists

4. Attribute bit extraction

c

rdx = (rax_2 u>> 4 ^ 1) & 1

This is the most interesting part — breaking it down:

  • rax_2 u>> 4 → right shifts attributes by 4 bits
  • ^ 1 → XOR with 1 (flips the lowest bit)
  • & 1 → isolates just that one bit

Bit 4 of GetFileAttributesW = **FILE_ATTRIBUTE_DIRECTORY (0x10)**

  • So this returns 1 if it’s a file, 0 if it’s a directory

Fig 7 — API Wrapper Stubs Analysis

Fig 7 — API Wrapper Stubs Analysis

This pattern of wrapping standard APIs is used for several malicious reasons:

  1. Hook evasion — Security tools hook WriteFile, ReadFile etc. directly. By wrapping them, the malware can swap out the jump target at runtime to bypass hooks
  2. Dynamic resolution — The real API address can be swapped to a custom implementation without changing call sites throughout the code
  3. Stack trace obfuscation__tailcall removes evidence of the wrapper from call stacks, making forensic analysis harder
  4. Instrumentation point — One central place to add logging or conditional execution based on environment detection

Fig 8 — Payload Dropper & Loader Analysis

Fig 8 — Payload Dropper & Loader Analysis

Figure 1 — Dropping the Payload

1. Generate a stealthy file path

c

GetTempPathW(&buffer)  // gets C:\Users\...\AppData\Local\Temp\
QueryPerformanceCounter(&performanceCount)  // get unique timestamp
wsprintfW(&var_458, u"%s%08x.dll", &buffer, performanceCount)
  • Creates a randomly named DLL in the temp folder
  • Format: C:\Temp\a3f9b21c.dll (8 random hex chars)
  • Uses performance counter as entropy — filename is unpredictable, evades static detection

2. Create the file

c

CreateFileW(
    lpFileName: &var_458,
    dwDesiredAccess: 0x40000000,  // GENERIC_WRITE
    dwShareMode: FILE_SHARE_NONE,  // exclusive access
    lpSecurityAttributes: nullptr
)
  • Creates the dropped DLL with exclusive write access
  • No sharing — prevents other processes from reading while writing

Figure 2 — Loading & Executing the Payload

1. Free the heap buffer

c

HeapFree(GetProcessHeap(), HEAP_NONE, rax_2)
  • Cleans up after writing — evidence removal

2. Decrypt the library name

c

sub_2365b1000(&libFileName, "Z7D7T7X7E7R7R7", 0x16, 0x37)
  • XOR decrypts with key 0x37 (same as before!)
  • "Z7D7T7X7E7R7R7" decodes to something like **"ZXTXERR" or a DLL name**
  • Likely decrypts to a system DLL name being hijacked

3. Load the library

c

hModule = LoadLibraryW(&libFileName)
if (hModule == 0) return 0xffffffff
  • Loads the decrypted DLL into memory
  • Returns error if it fails

4. Decrypt and resolve the function

c

sub_2365b1000(&procName, "t{etERVCR~YDCVYTR", 0x11, 0x37)
rax_8 = GetProcAddress(hModule, &procName)
  • XOR decrypts "t{etERVCR~YDCVYTR" with key 0x37
  • Likely decodes to something like **"CreateRemoteThread"** or similar injection API
  • Dynamically resolves the function address at runtime

Fig 9 — XOR Decryption Engine

Fig 9 — XOR Decryption Engine

This is the core decryption function used throughout the entire malware — now fully revealed.

The Code

c

void sub_2365b1000(char* arg1, int64_t arg2, int32_t arg3, char arg4)
if (arg3 <= 0) return      // length check
int64_t i = 0
do {
    arg1[i] = arg4 ^ *(arg2 + i)   // XOR each byte
    i += 1
} while (arg3 != i)                 // loop until length reached

Parameters Decoded

ParameterRoleExample values seenarg1Output bufferwhere decrypted string goesarg2Encrypted input"Z7D7T7X7E7R7R7", "fDoY", "t{etERVCR~YDCVYTR"arg3Length0x16, 0x11, 9arg4XOR keyAlways 0x37

What It Does

A single-byte XOR loop:

output[i] = 0x37 XOR input[i]

Every encrypted string seen so far decrypts like:

'Z' ^ 0x37 = 'k'
't' ^ 0x37 = 'C'
'{' ^ 0x37 = 'L'

So "t{etERVCR~YDCVYTR" likely decodes to something like **"CreateRemoteThread"** or another sensitive API name.

Fig 10– C2 URL Construction Analysis

Fig 10– C2 URL Construction Analysis

This is the malware building its Command & Control (C2) URL from encrypted components — decrypting and assembling the pieces at runtime.

Step by Step

1. Decrypt string 1 — likely the protocol or subdomain

c

sub_2365b1000(&var_888, "d7N7D7C7R7Z7", 0x34, 0x37)
  • XOR decrypt with key 0x37
  • Length 0x34 (52 chars) — could be a domain or protocol prefix

2. Decrypt string 2 — likely the main domain/URL path

c

sub_2365b1000(&var_808, "z7V7Y7V7P7R7E7~7Y7D7C7V7Y7T7R7", 0x1e, 0x37)
  • Length 0x1e (30 chars)
  • Longer encrypted string — likely the main C2 domain or endpoint path

3. Decrypt string 3 — likely a file extension or suffix

c

sub_2365b1000(&var_8c8, "d7R7C7B7G7", 0xa, 0x37)
  • Length 0xa (10 chars) — short, likely **.json, `.php`**, or similar endpoint suffix

4. Assemble the full URL

c

wsprintfW(&param0, u"%s.%s", &var_888, &var_808)

5. Check config flag

c

if (data_2365b5000 == 0)
  • Checks the flag set earlier in DllRegisterServer
  • Gates whether to proceed with the C2 connection

Decrypting the Strings

Using key 0x37 on the visible encrypted strings:

python

key = 0x37
strings = ["d7N7D7C7R7Z7", "z7V7Y7V7P7R7E7~7Y7D7C7V7Y7T7R7", "d7R7C7B7G7"]
for s in strings:
    print(''.join(chr(ord(c) ^ key) for c in s))

The 7 characters (0x37 ^ 0x37 = 0x00) are actually null separators being used as delimiters between real characters — a clever way to store wide strings obfuscated.

Fig 11 — PE Version Info Strings — Fake Metadata Analysis

Fig 11 — PE Version Info Strings — Fake Metadata Analysis

1. Identity theft of VMware

  • Malware is impersonating a legitimate VMware runtime component
  • rtmext.dll sounds like a plausible VMware internal DLL name
  • Security analysts and EDR tools may whitelist or deprioritize files claiming to be VMware

2. Connection to GetModuleVersion

  • Remember the earlier function that read version info at offset +0xc?
  • It was reading this exact data
  • The 0xc offset likely points to a hidden config value embedded among these legitimate-looking fields

3. The 040904B0 locale block

  • 0409 = English (US), 04B0 = Unicode
  • Standard locale identifier — makes it look more legitimate

4. Imports confirm minimal footprint

  • Only imports KERNEL32.dll, USER32.dll, VERSION.dll
  • Small import table = less surface area for detection

Fig 12 — PE File Metadata & Overlay Analysis

Fig 12 — PE File Metadata & Overlay Analysis

n.dll (real dev name)

  • fake rtmext.dll VMware metadata bolted on
  • 15KB unknown payload in overlay (65% of file)
  • no certificate, no manifest
  • writable .bss section for runtime data ↓ Sophisticated, hand-crafted malware with deliberate identity deception

Fig 13 — malware’s network infrastructure and related malware family

Fig 13 — malware’s network infrastructure and related malware family

1. Active campaign — right now

  • Files dated April 8–14, 2026
  • All related, all low detection rates = zero-day or near-zero-day

2. Multiple components confirm modular malware

  • rtmext → loader (our sample)
  • payload.dll / managed.dll → secondary stages
  • datprov / svcctl → likely credential harvesting or lateral movement tools
  • PowerShell module → likely for persistence or reconnaissance

3. Infrastructure is bulletproof

  • Namecheap domain + Podaon SIA hosting
  • Cloudflare fronting to hide real C2
  • Domain has 0 detections despite 4 detections on the IP

4. This is an ongoing, targeted attack campaign with a full toolkit that is largely invisible to most security vendors.

Conclusion

This malware is a sophisticated, multi-stage loader that represents a serious and active threat. It combines multiple advanced techniques — fake VMware metadata, XOR-encrypted strings, blockchain-based C2 communication, and bulletproof hosting infrastructure — into a cohesive, well-engineered attack framework. With 0% prevalence in customer environments and detection rates as low as 1/71, this is either a highly targeted or brand-new threat. The campaign is clearly active as of April 2026, with multiple related components suggesting a full threat actor toolkit. The use of Ethereum JSON-RPC for C2 is particularly notable as it represents a significant evolution in command-and-control tradecraft designed to bypass traditional network controls.

MITRE ATT&CK

IOCs

YARA Rule

rule APT_Loader_rtmext_VMwareMasquerade { meta: description = “Detects sophisticated DLL loader masquerading as VMware rtmext.dll” author = “Subhankar H.” date = “2026–04–15” hash = “996db74a739c17a3d0ecd2f50cf523dfcbed497c27c1cb9f622f02519db” overlay_hash = “DD9091E73FB3B49C8514D18C96571E39048F60012EC191413FEB30D5B2BEDC38” severity = “CRITICAL” tlp = “RED”

strings: // Fake VMware metadata $meta1 = “rtmext.dll” wide $meta2 = “VMware Inc.” wide $meta3 = “Platform interface handler” wide $meta4 = “Service Controller” wide

// XOR encrypted string fragments (key 0x37) $enc1 = “d7N7D7C7R7Z7” $enc2 = “z7V7Y7V7P7R7E7~7Y7D7C7V7Y7T7R7” $enc3 = “d7R7C7B7G7” $enc4 = “Z7D7T7X7E7R7R7” $enc5 = “t{etERVCR~YDCVYTR” $enc6 = “fDoY”

// XOR decryption loop pattern (key 0x37) $xor_routine = { 80 F? 37 // XOR byte, 0x37 46 // INC ESI/counter 4? // DEC length counter 75 ?? // JNZ loop }

// Persistence strings $reg1 = “Software\Microsoft\Windows\CurrentVersion\Run” wide $sched = “schtasks.exe” wide

// Ethereum C2 indicator $eth1 = “ethcall” ascii $eth2 = “eth” ascii

// Anti-analysis $anti1 = “IsAttached” ascii

// LotL binaries $lotl1 = “rundll32.exe” wide $lotl2 = “schtasks.exe” wide

// Export name mismatch indicator $export_name = “n.dll” ascii

// Temp path DLL drop pattern $dropper = { 25 30 38 78 2E 64 6C 6C // “%08x.dll” }

condition: uint16(0) == 0x5A4D // MZ header (valid PE) and filesize < 500KB and ( // Core identity: fake VMware + encrypted strings (2 of ($meta) and 3 of ($enc)) or // Encryption routine + persistence ($xor_routine and 1 of ($reg1, $sched)) or // Blockchain C2 + anti-analysis (1 of ($eth) and $anti1) or // Full confidence: dropper + encrypted C2 + LotL ($dropper and 2 of ($enc) and 1 of ($lotl*)) ) }

Happy Reading!!!


메타데이터
post_id
e1330108f2bb
slug
analysis-of-an-loader-ethereum-loader-e1330108f2bb
url
https://medium.com/@shubhandrew/analysis-of-an-loader-ethereum-loader-e1330108f2bb
canonical_url
https://medium.com/@shubhandrew/analysis-of-an-loader-ethereum-loader-e1330108f2bb
author_url
https://medium.com/@shubhandrew
status
ok
fetched_at
2026-07-11 08:13:25