WARNING: This repository contains decompiled malware source code. DO NOT compile or execute any of the code. It is preserved here strictly for research and documentation purposes.
This is a reverse engineering analysis of a malicious Minecraft Fabric mod that was distributed as a trojanized version of a legitimate mod. The original JAR (Hide_Item_Frame-1.0.32.jar) was received from a friend after their PC was compromised by it. The file is preserved in the source-jar/stage1/ directory.
The malware is a multi-stage loader disguised as "Hide Item Frame" - a Minecraft Fabric client mod. Stage 1 (this analysis) acts as a credential stealer and downloader for a Stage 2 payload, which is fetched at runtime from an attacker-controlled server resolved via the Ethereum blockchain.
- Decompilation: The original JAR (
source-jar/stage1/Hide_Item_Frame-1.0.32.jar) was put through Recaf using the Vineflower decompiler to obtain the raw Java source files. - Raw decompiled output: Placed in
stage1-fabricmc-mod/- class/method/field names remain as found in the JAR (obfuscated variable names, obfuscated string constants viaHelper.load()). - Manual deobfuscation: Placed in
stage1-fabricmc-mod-deobfuscated/- classes and methods have been renamed to reflect their true purpose:Entrypoint->MainExampleMixin->ExtraClassLoaderExampleMod->ModMainFabricAdapter->RPCHelperHelper->StageTwoLoader
- String decoder: The
decode/directory contains a standalone Java tool (Decode.java) written to offline-decode the individual obfuscated string constants that are decoded at runtime viaHelper.load(). The malware uses a custom cipher (S-box substitution + XOR + bit rotation with chained state) to hide all sensitive strings. To decode a string, theHelper.load(...)call parameters are copied into theDecode.javamain method, compiled, and run viadecode.bat. - Stage 2 decompilation: The Stage 2 JAR (
Module.jar) was also put through Recaf/Vineflower. The Vineflower option to hide synthetic fields (--decompile-inner) had to be disabled because the JNIC obfuscator marks certain real fields as synthetic (theACC_SYNTHETICflag), causing Vineflower to omit them from the output. The decompiled source is placed instage2-weedhack/.
The malware masquerades as a legitimate Fabric mod:
| Field | Value |
|---|---|
| Mod ID | grshiftcart |
| Display Name | "Hide Item Frame" |
| Version | 1.0.22 |
| Description | "Hide the frame if it contains an item" |
| Target Minecraft | 1.21.4 |
| Target Fabric Loader | >=0.17.3 |
| Java Requirement | >=21 |
| Environment | client |
| Legitimate entrypoint | cc.rshift.RShiftClient (the real mod's client class) |
| Malicious entrypoint | com.example.ExampleMod (registered under main) |
The fabric.mod.json lists both a real-looking client entrypoint (cc.rshift.RShiftClient) and the malicious com.example.ExampleMod under the main entrypoints. It also bundles seemingly legitimate LWJGL NanoVG native JARs in META-INF/jars/ to appear authentic.
A secondary fabric.api.json resource is bundled alongside, containing:
{"api_version": "2fbff058-173d-4ba3-adaf-95a757636fc5"}Presumably generated for each campaign/target as a UUID, it is read at runtime and sent to the C2 as a userId / campaign identifier - it is not a real Fabric API version field, the real Fabric loader does not even check for the existence of a fabric.api.json file.
The malware has two entrypoints that can invoke the Stage 2 loader:
When loaded as a Fabric mod inside Minecraft, onInitialize() executes and:
- Builds a JSON context object with
"executionEnvironment": "Fabric". - Harvests the player's Minecraft session: username, UUID, and access token.
- Resolves the C2 domain via Ethereum RPC (see below).
- Reads the campaign UUID from the bundled
fabric.api.json. - Immediately exfiltrates credentials via HTTP POST to
<C2_DOMAIN>/api/delivery/handler- this happens before Stage 2 is loaded. - Spawns a new thread that calls
StageTwoLoader.stageWithContext(context)to download and load the Stage 2 payload.
The JAR can also be run directly (outside Minecraft). When executed:
- Checks for the
--jwargument. If absent, relaunches itself viajavaw.exe(the windowless Java runtime) with--jw, then exits - this hides the console window on Windows. - Builds a context with
"executionEnvironment": "Fabric"(same string regardless of execution path). - Spawns a thread to invoke
StageTwoLoader.stageWithContext(context).
Note: In standalone mode, no Minecraft session data is available, so only the Stage 2 download occurs (no credential exfiltration).
All sensitive strings (URLs, JSON keys, field names, the RSA public key, etc.) are obfuscated using a custom runtime decoding function Helper.load(int[] d1, int[] d2, int k1, int k2). The algorithm:
- Interleaves two integer arrays (
d1,d2) into a single array - even indices fromd1, odd indices fromd2. - Generates a substitution box (S-box) via
sbox[i] = (i * 53 + 97) % 256and its inverse. - For each byte in the interleaved data, applies:
- Chained XOR: each value is XORed with the previous value (or
k2for the first). - Bit rotation: rotated right by a position-dependent shift
(idx * 5 + k1) % 8. - Inverse S-box substitution.
- Mask XOR: final XOR with a rolling mask derived from
k2, a stateful counter, and the position.
- Chained XOR: each value is XORed with the previous value (or
- The result is cast to
charand appended to build the final plaintext string.
All calls in this sample use constants k1=187, k2=67.
A standalone decoder is provided in decode/Decode.java to offline-decode any Helper.load() call without executing the malware. A complete mapping of all 57 call sites to their decoded plaintext strings is documented in MAPPINGS.md.
The malware uses an Ethereum smart contract to dynamically resolve its C2 domain, making it resilient to simple domain takedowns - the attacker can update the on-chain value to point to a new domain at any time.
- The
RPCHelper(obfuscated asFabricAdapter) contains a hardcoded list of 32 public Ethereum JSON-RPC endpoints (Llamarpc, Publicnode, Tenderly, Flashbots, DRPC, etc.). - It iterates through these endpoints and sends an
eth_callto contract address0x1280a841Fbc1F883365d3C83122260E0b2995B74(here it is on Etherscan.io) with function selector0xce6d41de. - The returned hex-encoded data is decoded (ABI string decoding: offset at bytes 0–32, length at bytes 32–64, UTF-8 string data at bytes 64+).
- The decoded string is in the format
<C2_URL>|<RSA_SIGNATURE>. - The RSA signature is verified against a hardcoded RSA-2048 public key (algorithm:
SHA256withRSA) before the URL is trusted. This prevents third parties from hijacking the C2 domain by writing to the contract - only the attacker's private key can produce a valid signature.
As of 2026-03-06T19:31:40Z (March 6th, 2026), the Ethereum contract resolves to:
https://whnewreceive.ru
The full list of Ethereum RPC endpoints the malware cycles through:
https://eth.llamarpc.comhttps://eth.api.onfinality.io/publichttps://rpc.eth.gateway.fmhttps://ethereum-rpc.publicnode.comhttps://eth.rpc.blxrbdn.comhttps://ethereum.rpc.subquery.network/publichttps://ethereum-json-rpc.stakely.iohttps://ethereum-public.nodies.apphttps://core.gashawk.io/rpchttps://mainnet.gateway.tenderly.cohttps://ethereum-mainnet.gateway.tatum.iohttps://eth1.lava.buildhttps://eth.meowrpc.comhttps://public-eth.nownodes.iohttps://rpc.mevblocker.io/fasthttps://rpc.mevblocker.io/norevertshttps://rpc.mevblocker.io/fullprivacyhttps://eth-mainnet.nodereal.io/v1/1659dfb40aa24bbb8153a677b98064d7https://eth-mainnet.public.blastapi.iohttps://ethereum.public.blockpi.network/v1/rpc/publichttps://eth-mainnet.rpcfast.com?api_key=xbhWBI1Wkguk8SNMu1bvvLurPGLXmgwYeC4S6g2H7WdwFigZSmPWVZRxrskEQwIfhttps://eth.drpc.orghttps://eth.blockrazor.xyzhttps://rpc.flashbots.net/fasthttps://gateway.tenderly.co/public/mainnethttps://rpc.flashbots.nethttps://rpc.fullsend.tohttps://eth.merkle.iohttps://api.zan.top/eth-mainnethttps://rpc.mevblocker.iohttps://endpoints.omniatech.io/v1/eth/mainnet/publichttps://1rpc.io/eth
Once the C2 domain is resolved, the malware downloads and loads a Stage 2 payload:
- Downloads a JAR file from
<C2_DOMAIN>/files/jar/modulevia HTTP GET. The served file is namedModule.jar. - Parses the downloaded JAR in-memory using
JarInputStream- it is never written to disk. - Separates entries into a
classMap(.classfiles) and aresourceMap(everything else). - Instantiates a custom
ClassLoader(ExtraClassLoader/ExampleMixin) that can load classes and resources from these in-memory maps. - Loads the class
dev.majanito.Mainfrom the downloaded JAR. - Reflectively invokes the method
initializeWeedhack(String contextJson)on a new instance, passing the collected context (execution environment, campaign UUID, and Minecraft credentials if available).
The Stage 2 payload is loaded entirely in-memory via a custom ClassLoader, leaving no artifacts on disk beyond the original JAR.
The following endpoint behaviors were observed through manual probing of the C2 server:
This is the credential exfiltration endpoint. Its behavior varies based on the request:
| Request | Response |
|---|---|
| Any method other than POST | 405 Method Not Allowed |
POST without Content-Type: application/json |
400 Bad Request |
POST with Content-Type: application/json and valid body |
200 OK with body {} |
A response of {} indicates the server successfully received and processed the exfiltrated data.
Returns the Stage 2 payload as a file download named Module.jar. This is the endpoint the malware calls to fetch the Stage 2 JAR for in-memory loading.
The Stage 2 payload has been obtained and decompiled. Its source is in stage2-weedhack/. The JAR is built with JNIC — a Java-to-native compiler that compiles Java bytecode into native code (C/C++ compiled to platform-specific DLLs). As a result, nearly every method body in the decompiled output is native — the actual logic lives in an embedded native library extracted at runtime.
JNIC requires the developer to select target platforms at compile time. This sample supports two platforms only:
| Platform | Architecture | .dat Byte Range |
|---|---|---|
| Windows x64 | x86_64 / amd64 |
0 – 516608 (≈504 KB) |
| Windows ARM64 | aarch64 |
516608 – 986112 (≈458 KB) |
If the platform is not one of these two, the loader throws UnsatisfiedLinkError("Platform not supported"). Linux and macOS are not supported.
The dev.jnic.kWGlIS package contains the JNIC runtime loader:
JNICLoader(which also doubles as a custom LZMAInputStreamdecompressor) reads the embedded resource/dev/jnic/lib/43072760-0388-4d20-83c3-edcc17b3391a.dat.- Based on
os.nameandos.arch, it selects the appropriate byte range from the.datfile and decompresses the native DLL. - The DLL is written to a temp file (
File.createTempFile("lib", null)) marked withdeleteOnExit(). System.load()loads the extracted DLL, making allnativemethods available.- Each class's
static {}initializer callsJNICLoader.init()followed by$jnicLoader()(and$jnicClinit()where applicable) to register its native methods.
The remaining single-letter classes in dev.jnic.kWGlIS (B, d, F, H, i, K, L, P, S, T, V, Y) implement the LZMA decompression algorithm used to decompress the native DLL from the .dat file.
| Class | Description |
|---|---|
dev.majanito.Main |
Entry point — initializeWeedhack(String) receives context JSON from Stage 1. Also has addDefenderExclusions() (Windows Defender evasion) and extractJnaNative(). |
dev.majanito.Elevator |
UAC bypass via CMSTP (Connection Manager Profile Installer) — a known Windows privilege escalation technique. Uses JNA to call SendMessage to simulate pressing Enter on the CMSTP elevation prompt. Creates .xdmf config files, can doSystem() to run arbitrary commands, and downloads an "elevator" payload to disk. |
dev.majanito.RPCHelper |
Same Ethereum RPC C2 resolution as Stage 1 — identical contract address, function selector, RSA public key, and signature verification. Redundant copy for standalone Stage 2 operation. |
dev.majanito.TelemetryHelper |
Data exfiltration — initTelemetry(String) likely sends stolen data to the C2. Has readAllAsString() and readAllBytes() helpers. |
dev.majanito.IMCL |
In-memory ClassLoader — identical pattern to Stage 1's ExtraClassLoader. Takes classDefinitions and resourceDefinitions maps for loading classes/resources without touching disk. Suggests Stage 2 may load a Stage 3. |
- All real logic is native — the Java source only shows method signatures. Full behavioral analysis requires reverse engineering the extracted DLLs (IDA Pro / Ghidra on the decompressed
.datcontents). - UAC bypass via CMSTP —
Elevatorusescmstp.exeto escalate privileges without a UAC prompt, a well-documented technique (T1218.003). - Windows Defender exclusions —
Main.addDefenderExclusions()suggests the malware modifies Defender settings (likely viaSet-MpPreference -ExclusionPath) after gaining elevated privileges. - JNA dependency — Stage 2 bundles JNA (
net.java.dev.jna) for Windows API interop (WinDef.HWND,WinDef.WPARAM,SendMessage). - Another ClassLoader —
IMCLmirrors Stage 1's in-memory class loading pattern, suggesting possible Stage 3 payload delivery. - Campaign UUID — The
.datresource UUID (43072760-0388-4d20-83c3-edcc17b3391a) may serve as a build/campaign identifier.
TODO — The native DLLs within the
.datfile have not yet been extracted or reverse-engineered. Full behavioral analysis of the JNIC-compiled methods requires disassembly of the platform-specific native code.
| Type | Value | Description |
|---|---|---|
| Ethereum Contract | 0x1280a841Fbc1F883365d3C83122260E0b2995B74 |
Smart contract storing the signed C2 domain |
| Ethereum Function Selector | 0xce6d41de |
Function called to retrieve the C2 URL |
| C2 Domain | whnewreceive.ru |
Resolved C2 domain (as of 2026-03-06, subject to change via on-chain update) |
| Credential Exfil Endpoint | https://whnewreceive.ru/api/delivery/handler |
POST endpoint for stolen Minecraft session data |
| Stage 2 Download Endpoint | https://whnewreceive.ru/files/jar/module |
GET endpoint serving the Stage 2 JAR payload |
| Type | Value | Description |
|---|---|---|
| Malicious JAR | Hide_Item_Frame-1.0.32.jar |
Original trojanized mod filename |
| Stage 2 JAR | Module.jar |
Stage 2 payload filename |
| Fabric Mod ID | grshiftcart |
Mod ID in fabric.mod.json |
| Mod Version | 1.0.22 |
Version string in fabric.mod.json |
| Malicious Entrypoint | com.example.ExampleMod |
Main entrypoint class in the JAR |
| Stage 2 Class | dev.majanito.Main |
Class loaded from the downloaded Stage 2 JAR |
| Stage 2 Method | initializeWeedhack(String) |
Reflectively invoked method in Stage 2 |
| Campaign UUID | 2fbff058-173d-4ba3-adaf-95a757636fc5 |
Hardcoded in fabric.api.json, sent to C2 |
| JNIC Resource UUID | 43072760-0388-4d20-83c3-edcc17b3391a |
.dat resource containing compressed native DLLs |
| CMSTP Config Extension | .xdmf |
Extension used for CMSTP UAC bypass configs |
| Temp DLL Pattern | lib*.tmp |
Temp file created by JNICLoader for native DLL |
| Type | Value | Description |
|---|---|---|
| RSA Public Key (Base64) | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmNzDf4737/iYWvscWg6vQg9dHa/yUchfQY9r5htNTLZ3ZDAbqrzN93I0ctZHa27oRnkpB7XpowI4NH8eIRmaMThggpTYRXzHzLvUjhyrFFPkIOo/HI1gZF5IV7/XmvYWqgEsSpxl0iesOUlaWO5A8QlTu0QLsZAzZtzZyLj/v1XbPT02rTvZkuRhE6nzpUR4GN3Jp4Bn8zQAWdFDe17PWZxOi19uUTMPzgFj9n3h7DprwBmE3fR7IMsbiFacAoSHfqkTpEwY7A8ArK1DQ1yJXPog/PQ4aTU9gU38WC20wtct796ImZiuRYdNWcSzHda5ZbvZdvpw6RHh0zQqGVhRQIDAQAB |
Used to verify the signed C2 domain from the Ethereum contract |
| Signing Algorithm | SHA256withRSA |
Signature verification algorithm |
| File | SHA-256 |
|---|---|
Hide_Item_Frame-1.0.32.jar (Stage 1) |
2c35516cc63322142b89ec6dd3feab357005f4390b276880d5a61d4fd8e76242 |
Module.jar (Stage 2) |
91b150fa49060acb1efd67cfe5e952580a36953d53c75491781cd8f0556e5508 |
- Fabric mod registering an entrypoint under the
com.examplepackage - HTTP POST to
/api/delivery/handlercontaining JSON with fields:username,uuid,accessToken eth_callJSON-RPC requests to Ethereum mainnet withdata: "0xce6d41de"andto: "0x1280a841..."across many public RPC endpoints- In-memory JAR loading via custom
ClassLoader(no Stage 2 artifacts on disk) - Relaunching via
javaw.exewith--jwflag (standalone execution path to hide console window) - Extraction of a native DLL to
%TEMP%from an embedded LZMA-compressed.datresource (Stage 2 JNIC loader) cmstp.exeexecution with custom.xdmfconfig files (Stage 2 UAC bypass)- Modification of Windows Defender exclusion paths (Stage 2 privilege escalation)
.
├── source-jar/
│ ├── stage1/
│ │ └── Hide_Item_Frame-1.0.32.jar # Original malicious JAR (do NOT execute)
│ └── stage2/
│ └── Module.jar # Stage 2 payload JAR (do NOT execute)
├── stage1-fabricmc-mod/ # Raw Recaf/Vineflower decompilation output (Stage 1)
│ └── src/main/java/com/example/
│ ├── ExampleMod.java # Malicious Fabric mod entrypoint
│ ├── Helper.java # String decoder + Stage 2 loader
│ ├── FabricAdapter.java # Ethereum RPC C2 resolver
│ ├── ExampleMixin.java # Custom in-memory ClassLoader
│ └── Entrypoint.java # Standalone JAR entrypoint
├── stage1-fabricmc-mod-deobfuscated/ # Manually deobfuscated & renamed version (Stage 1)
│ └── src/main/java/pog5/stage1/
│ ├── ModMain.java # → ExampleMod (deobfuscated)
│ ├── StageTwoLoader.java # → Helper (deobfuscated)
│ ├── RPCHelper.java # → FabricAdapter (deobfuscated)
│ ├── ExtraClassLoader.java # → ExampleMixin (deobfuscated)
│ └── Main.java # → Entrypoint (deobfuscated)
├── stage2-weedhack/ # Recaf/Vineflower decompilation output (Stage 2)
│ └── src/main/java/
│ ├── dev/majanito/
│ │ ├── Main.java # Stage 2 entry point (JNIC native)
│ │ ├── Elevator.java # UAC bypass via CMSTP
│ │ ├── RPCHelper.java # Ethereum RPC C2 resolver (copy of Stage 1)
│ │ ├── TelemetryHelper.java # Data exfiltration
│ │ └── IMCL.java # In-memory ClassLoader (possible Stage 3)
│ └── dev/jnic/kWGlIS/
│ ├── JNICLoader.java # JNIC native DLL extractor + LZMA decompressor
│ ├── B.java, d.java, F.java ... # LZMA algorithm implementation classes
│ └── lib/43072760-...391a.dat # Compressed native DLLs (win-x64 + win-aarch64)
├── decode/
│ ├── Decode.java # Standalone string decoder tool
│ └── decode.bat # Compile & run helper
├── MAPPINGS.md # Helper.load() string decode mappings
└── README.md # This file