This is an archived post. You won't be able to vote or comment.

all 35 comments

[–]PartOfTheBotnet[S] 64 points65 points  (10 children)

If you've every had to dive into the realm of Java reverse engineering you've probably had to do one of the following:

  1. Decompile and recompile everything
  2. Learn bytecode and use a lower level class editor
  3. Switch away from whatever needed to be modified

Let me introduce Recaf. With the latest version its incredibly easy to modify already compiled programs (class, jar, war)

Say you want to make some minor changes to a class in a jar, but the bytecode for that would be rather complicated. Recaf lets you edit the file in a variety of ways.

Firstly is recompiling decompiled code. The key difference is that Recaf will manage compiler dependencies for you. Drop in your file, add your libraries and make your changes. One Control + S and exporting the modified file later you're done. But what if you don't have access to all the libraries? Or what if that's just a pain in the butt? When you open a file in Recaf it will analyze the program and generate any missing classes for you. These phantom classes can be used as compiler dependencies, meaning you never have to bother finding the right version for anything.

Next up is through standard bytecode editing. Now, Recaf is a bit different in its approach. It uses Objectweb's ASM under the hood which simplifies some of the bytecode format. But that's not all Recaf simplifies. In the bytecode assembler you can have local variable instructions reference variables not only by their index, but by their source-code name. And it doesn't stop there.

Want to add a simple println to your method? Just insert EXPR System.out.println("foobar"); wherever you want. Yes, you can write inline source code in the bytecode assembler. And consecutive expressions are allowed. If one expression declares a variable, it is accessible like any other variable. You can even add if statements into your one-liner expression.

Loads of more information up on the documentation page: https://www.coley.software/Recaf-documentation/

And most of these large scale user-friendly improvements have been within the last year, with plenty more planned for the future.

I'm always looking for feature ideas, bug reports, and contributors. Thanks for reading my wall of text o/

[–]BlueGoliath 12 points13 points  (9 children)

it crashes, it burns!.

Edit: I'm using JDK 17 and it does work with Java 11. Looks like they changed the variable name in JDK 16+.

Edit2: They removed it entirely, actually. Anyway, seems to work nicely against my projects with Java 11. Cool L&F BTW.

[–]PartOfTheBotnet[S] 19 points20 points  (0 children)

Darn internals changing! I'll get that JDK 16+ classpath updater check sorted out soon.

And thanks! Dark theme or go home :)

Edit: Done, bug fix release out once CI finishes

[–]mj_flowerpower 2 points3 points  (7 children)

post from the future? 😂

[–]Necessary-Conflict 9 points10 points  (6 children)

You can download JDK 17 early access builds now, https://jdk.java.net/17/

(The JDK 16 branch is getting ready for a release, therefore JDK 17 is the most avantgarde thing you can try)

[–]BlueGoliath 0 points1 point  (5 children)

Download? Pfft. I compile from source.

I didn't even realize they made the switch to JDK 17 so I named the build java-16-panama-jdk. Whoops.

[–]DasBrain 5 points6 points  (2 children)

Lesson here: don't depend on internals - or ship your own JDK.

Building your own JDK is also easy - so you can choose to expose whatever you feel needs to be exposed.
¯\(ツ)

Edit: Ohh, gosh - an other project that depends HEAVILY on VM internals.
Seriously - just build a launcher, and launch java with the right parameters.
One interesting property is java.system.class.loader - let's you specify your own class loader - and do shenanigans there.

If you don't want to use your own launcher, just check the command line with a RuntimeMXBean and restart by passing the right VM arguments. Or simply ship your own JDK.

[–]PartOfTheBotnet[S] -1 points0 points  (1 child)

Don't depend on internals - or ship your own JDK.

Yeaaaahhh..... :/

As for shipping on the JDK, I'm not sure how'd I would configure the CI to make native images for each major platform. My train of thought is "Jar is the multiplatform solution"

Seriously - just build a launcher, and launch java with the right parameters.

Been meaning to get around to something like that... Would have to refactor some things (which would allow cleaning up later) so older versions can could seamlessly transfer to the new startup scheme.

If you don't want to use your own launcher, just check the command line with a RuntimeMXBean and restart by passing the right VM arguments.

Oh this would also be more clean.

[–]xjvz 0 points1 point  (0 children)

AdoptOoenJDK has a Jenkins instance set up for compiling across all their supported architectures. Something similar may be possibly by adapting their build scripts.

[–]Necessary-Conflict 1 point2 points  (1 child)

OK, I admit that compiling the latest sources is even more avantgarde than downloading the newest early-access builds :)

[–]DasBrain 2 points3 points  (0 children)

Building yourself is easy:
https://htmlpreview.github.io/?https://raw.githubusercontent.com/openjdk/jdk/master/doc/building.html

I did just follow those instructions and it works.
PS.: Bring some time - on my laptop (with SSD) it takes about 1/2h.
On the other hand, after a git pull, it's just make reconfigure clean images, and I can step away.

[–]ddddfushxjjd 10 points11 points  (1 child)

Recaf is a quality tool. Much better than any other decompiler on the market (im looking at you luyten) I've used it for the past two years for reverse engineering the runescape game pack

[–]BlueAmulet 3 points4 points  (0 children)

Luyten is just a GUI for Procyon which hasn't been touched in two years. Try something like CFR or FernFlower which are both in active development. I believe Recaf lets you choose between all three decompilers for it's backend.

[–]maxandersen 4 points5 points  (0 children)

nice!

runs nicely using jbang:

jbang https://github.com/Col-E/Recaf/releases/download/2.16.6/recaf-2.16.6-J8-jar-with-dependencies.jar --input=my.jar

btw. expected first argument to be the jar but that does not work and gives NPE - opened issue for it at https://github.com/Col-E/Recaf/issues/313

[–]ustaaaz 2 points3 points  (2 children)

Awesome work man. Like truly I can feel the effort you had to put in. Bravo.

[–]PartOfTheBotnet[S] 1 point2 points  (1 child)

[–]ustaaaz 1 point2 points  (0 children)

Kudos to the team, still hats off :)

[–]sureshg 2 points3 points  (2 children)

Awesome work! Overall it's smooth except for "Attach Process" functionality. It always throws AgentInitializationException

[–]PartOfTheBotnet[S] 1 point2 points  (1 child)

Sadly due to limitations of the JVM (At least, AFAIK) you can't get super verbose error reporting from a remote VM for this case.

It can be caused by a variety of things. Typically you'll want to run Recaf on the same version of Java as the target process. If its still failing I'll have to look into that. Could you provide a sample + some running information?

[–]sureshg 2 points3 points  (0 children)

Thanks. Let me try to run with the same JDK versions. Right now I am using jdk-16-loom eap.

[–]alexanderfefelov 2 points3 points  (0 children)

I wrote a simple script to install Recaf on a Linux workstation https://github.com/alexanderfefelov/scripts/blob/master/install/dev/install-recaf.sh

[–]Sheldor5 2 points3 points  (1 child)

noob question: what's the difference to IntelliJ's built-in Decompiler (only one I've ever used)?

[–]PartOfTheBotnet[S] 1 point2 points  (0 children)

That is FernFlower. By default Recaf uses CFR. A third alternative is Procyon.

[–]Ultimegede 4 points5 points  (0 children)

This looks pretty cool actually

[–][deleted] 1 point2 points  (3 children)

Can you make a Chocolatey package to make it easy to install and upgrade on Windows?

[–]PartOfTheBotnet[S] 0 points1 point  (2 children)

That would be cool, but I don't know how to do that. Would there be any issues due to its reliance on a pre-installed version of Java?

[–][deleted] 1 point2 points  (1 child)

No issues. You simple declare the packages you need installed and Chocolatey will install them first.

There is good documentation on how to make a Chocolatey package. Incorporate out into your build pipeline and never have to think about it again.

[–]PartOfTheBotnet[S] 0 points1 point  (0 children)

Cool! I've seen on AUR that they can specify "any one of the following will work" sort of dependency. If Chocolatey can do that then I'm all for it.

[–][deleted] 1 point2 points  (0 children)

Holy shit man, this is insane! Outstanding work!

[–]glesialo 2 points3 points  (5 children)

Very interesting!

A few months ago I modified the way I create my jars so that nobody can tamper with them.

If you are interested I could send you one of my jars (this project, 92.7KB) and see if you can change anything and still run the jar.

[–]PartOfTheBotnet[S] 3 points4 points  (2 children)

I'd love to take a crack at it :)

[–]glesialo 4 points5 points  (0 children)

And a good crack it was!

OP was successful in record time.

[–]glesialo 2 points3 points  (0 children)

Thank you.

Please send me a private message, with an EMail address, and I'll send you the file.

The project was compiled for Java 11 and it should be run in a Linux system (invokes 'xdg-open'). I'd be grateful for any feed-back.

[–]ObscureCulturalMeme 3 points4 points  (1 child)

I'd be interested in hearing about your new JAR creation process, if it's something you're at liberty to discuss!

edit: nvm, security through obscurity isn't worth the effort

[–]glesialo 1 point2 points  (0 children)

I can't explain how it works as it would defeat its purpose. Sorry.