Analysis of an Loader (Ethereum Loader)
Analysis of an Loader (Ethereum Loader)

Summary
- The DLL file is highly malicious and performs a lot of different evasion techniques
- It also shows how the file is communicating via the Ethereum tokens
- The file also shows it is masquerading the actual VMWare instance
- Multiple DLL files spawning
- It also changes the registry modifications
Analysis of the file




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
IsAttachedto detect if it's being debugged
Persistence (survives reboots)
- Modifies the Windows Registry
Runkey - Creates a scheduled task via
schtasks.exethat 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.exeandschtasks.exeto avoid detection
Data Exfiltration
- Can send/receive JSON payloads to external servers (machine name, username, etc.)

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
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 atdata_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
__noreturnbeaconing 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
__noreturn— this 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
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"**, size9, XOR constant0x37 - This strongly suggests XOR-based string decryption
0x37is the XOR key — a common simple obfuscation techniquevar_38is 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
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 == 0orarg2 <= 0x1f→ return 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_2365b2022is 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
ProductVersionor a hidden config value embedded in the version info

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
GetFileAttributesWon 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
This pattern of wrapping standard APIs is used for several malicious reasons:
- Hook evasion — Security tools hook
WriteFile,ReadFileetc. directly. By wrapping them, the malware can swap out the jump target at runtime to bypass hooks - Dynamic resolution — The real API address can be swapped to a custom implementation without changing call sites throughout the code
- Stack trace obfuscation —
__tailcallremoves evidence of the wrapper from call stacks, making forensic analysis harder - Instrumentation point — One central place to add logging or conditional execution based on environment detection


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 key0x37 - Likely decodes to something like
**"CreateRemoteThread"** or similar injection API - Dynamically resolves the function address at runtime

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
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(¶m0, u"%s.%s", &var_888, &var_808)
- Combines the decrypted strings with a
**.** separator - Result: something like
[https://domain.endpoint/path](https://domain.endpoint/path)
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
1. Identity theft of VMware
- Malware is impersonating a legitimate VMware runtime component
rtmext.dllsounds 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
0xcoffset 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
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
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 stagesdatprov/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