Skip to content

Instantly share code, notes, and snippets.

@DavidBuchanan314
Last active July 27, 2025 12:58
Show Gist options
  • Save DavidBuchanan314/58635425eed2b3ce10f23c5b0c5f98af to your computer and use it in GitHub Desktop.
Save DavidBuchanan314/58635425eed2b3ce10f23c5b0c5f98af to your computer and use it in GitHub Desktop.
JNIC Reversing Notes https://jnic.dev/

I looked at a JAR file protected using JNIC, version jnic.dev v3.6.0. I haven't written a full-auto deobfuscater yet, but these notes should be useful for anyone reversing it.

The first layer is a LZMA2 compressed .dat file, from which a native library is extracted into a temp dir, and then loaded using System.load.

The sample I looked at had 4 different library versions (for different platforms/architectures), and the script I wrote to extract them looks like this:

import lzma

# from JNICLoader.java
offsets = { # (start, end)
	"lib_win_x86_64.dll": (0, 1413120),
	"lib_win_aarch64.dll": (1413120, 2123776),
	"lib_lin_x86_64.so": (2123776, 3487136),
	"lib_lin_aarch64.so": (3487136, 4192248),
}

lzma2_64M = [{
	"id": lzma.FILTER_LZMA2,
	"dict_size": 64 * 1024 * 1024,  # 64 MiB dictionary size (perhaps overkill)
}]

lzmaf = lzma.open("dev/jnic/lib/5f174765-6a65-4541-98b7-7620b554e31d.dat", format=lzma.FORMAT_RAW, filters=lzma2_64M)

for name, (start, end) in offsets.items():
	print(f"decompressing {name}")
	lzmaf.seek(start)
	with open(name, "wb") as outf:
		outf.write(lzmaf.read(end - start))

If you want to use this script, you'll need to adjust the offsets. Or you can just copy the file out of the temp dir...

On initialization (JNI_OnLoad), the native library uses a ChaCha20 variant to generate a keystream of length 0x1337b bytes, saving it into a buffer.

I haven't bothered replicating their ChaCha20 variant yet, I just dumped the keystream from memory. The keystream is used to obfuscate strings and other constants (through simple XORing).

Using Ghidra, I loaded the keystream into memory, pointed the pointer at it (in my case, this pointer was the first thing in the .bss section) and marked it as constant. Ghidra's decompiler does constant folding, thus deobfuscating the strings "for free".

The other native methods, with names formatted like Java_{classname}__00024jnicLoader, end with calls to JNIEnv->RegisterNatives. By inspecting the arguments to this function, you can map the rest of the native functions (which do not have symbol names) with their Java method names and call signatures. (I might automate this process, at some point)

@KorblFrank
Copy link

KorblFrank commented Aug 27, 2024

hey I found this mod for minecraft which seems to download a rat (remote access trojan) and its obfuscated with jnic I wonderd if you could help unobfuscating it attention malware : https://www.mediafire.com/file/a7yz8o3byhp4yvb/GeekbonesBenefit.jar/file

@glektarssza
Copy link

hey I found this mod for minecraft which seems to download a rat (remote access trojan) and its obfuscated with jnic I wonderd if you could help unobfuscating it attention malware : https://www.mediafire.com/file/a7yz8o3byhp4yvb/GeekbonesBenefit.jar/file

Hey @KorblFrank, if you want a hand I've unpacked the latest version of this of this mod's payload. Give me a poke.

@MrBreakNFix
Copy link

@glektarssza Any updates on this? I'm curious on how the process went!

@glektarssza
Copy link

@MrBreakNFix No updates beyond that I was able to unpack the payload and it's a bunch code that I have not made any further efforts to reverse engineer. Mostly due to losing my day job and needing to go job hunting which is consuming most of my time right now.

@micartey
Copy link

@glektarssza my condolence. I hope you find a new (and better!) job fast 😊

@glektarssza
Copy link

@micartey Many thanks!

@opale0
Copy link

opale0 commented May 4, 2025

hey ho! any further updates yet?

@TeamSyncOffical
Copy link

import lzma


lzma_filters = [{
    "id": lzma.FILTER_LZMA2,
    "dict_size": 64 * 1024 * 1024,
}]

input_file = "raw_payload.dat"
with open(input_file, "rb") as f:
    compressed_data = f.read()

decompressed_data = lzma.decompress(
    compressed_data,
    format=lzma.FORMAT_RAW,
    filters=lzma_filters
)

with open("jnic_decompressed.dat", "wb") as f:
    f.write(decompressed_data)

#Get this from JNICLoader.java (i think it was called ?)
offsets = {
    "lib_win_x86_64.dll": (0, 1413120),
    "lib_win_aarch64.dll": (1413120, 2123776),
    "lib_lin_x86_64.so": (2123776, 3487136),
    "lib_lin_aarch64.so": (3487136, 4192248),
}

for name, (start, end) in offsets.items():
    print(f"Extracting {name}")
    with open(name, "wb") as outf:
        outf.write(decompressed_data[start:end])

mz_offset = decompressed_data.find(b"MZ")
if mz_offset == -1:
    raise ValueError("No MZ header")

with open("dumped.dll", "wb") as f:
    f.write(decompressed_data[mz_offset:])

@TeamSyncOffical
Copy link

you get a readable dll output

@DavidBuchanan314
Copy link
Author

@TeamSyncOffical did an LLM write this?

@TeamSyncOffical
Copy link

@TeamSyncOffical did an LLM write this?

No? but i do tend to have a weird coding style

@McpeDX
Copy link

McpeDX commented Jul 22, 2025

@glektarssza
Copy link

glektarssza commented Jul 22, 2025

did not pay me for my work

@TeamSyncOffical If there's money involved and you're using terms like "skid" I'm going to assume you're also unprofessional enough to not have a contract of some kind in place before starting work on that sort of project. That one is on you.

@McpeDX
Copy link

McpeDX commented Jul 22, 2025

I don't trust that guy . If pay that guy. 50% change to scam me

@glektarssza
Copy link

@McpeDX I'm not getting involved in whatever beef you have with @TeamSyncOffical.

Keep it to other messaging platforms than GitHub Gist comments. This is not the place to have a conversation about a personal issue. If you two are going to duke it out, do it by email/instant messaging/carrier pigeon/whatever. I will start reporting continued comments as off topic/spam if you two continue.

Next time either of you do business, get a contract. Sign it. That way both sides can hold either side accountable if one side breaks the deal. Just some advice from somebody who has worked on such projects before. That's all I was trying to say.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment