Skip to content

Instantly share code, notes, and snippets.

@DavidBuchanan314
Last active October 13, 2025 13:29
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)

@zyaris0
Copy link

zyaris0 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.

@zyaris0
Copy link

zyaris0 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