Are there any utilities that allow me to get a .class file and convert it into jvm bytecode?
There is a question for class decompilation, I do not want that, I want dissasembly. From the terminal.
I only find gui projects. https://github.com/java-decompiler/jd-gui I only want a simple utility
Given a .class object, there are some tools that help you dissasemble it.
One of those is javap
that ships with the JDK
using the -c
flag, let's see it in action.
$ javap -c Hello.class
Compiled from "Hello.java"
public class Hello {
public Hello();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello, world!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
Now this is all fine, except this is read only, which makes it hard to do edits, however since this is the official dissasembler it is a nice reference. There is another dissasembler for jasmin, which is a java assembler, a bit dated, written in C++ for windows. Assuming you have a c++ compiler (for example one distributed from msys2) This one does not require a JRE.
git clone https://github.com/masakioba/jhoja.git
cd jhoja
g++ jhoja.cpp -fpermissive -o jhoja.exe
We can then try
$ ./jhoja.exe /z/jvmstuff/Hello.class
;/* Class file Z:/jvmstuff/Hello.class version 50.0 */
.source Hello.java
.class public synchronized Hello
.super java/lang/Object
.method public <init>()V
.limit stack 1
.limit locals 1
aload_0
invokespecial java/lang/Object/<init>()V
return
.end method
.method public static main([Ljava/lang/String;)V
.limit stack 2
.limit locals 1
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "Hello, world!"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
return
.end method
This in turn let you assemble it back using jasmin.
There is yet another one, actually there are many jasm
program out there, this however requires JRE 17 to run, you could package it locally like this
# Check if ~/.local/bin and ~/.local/share/java exist, and create them if not
mkdir -p ~/.local/bin ~/.local/share/java
# Check if ~/.local/bin is in PATH
if ! echo "$PATH" | grep -q ":.*/\.local/bin"; then
# Append ~/.local/bin to .bashrc
echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc
# Append ~/.local/bin to the current PATH
export PATH="$PATH:$HOME/.local/bin"
fi
# Download jasm-cli-2.4.0-all.jar
wget -O ~/.local/share/java/jasm-cli-2.4.0-all.jar https://github.com/jumanji144/Jasm/releases/download/2.4.0/jasm-cli-2.4.0-all.jar
# Create the jasm script
cat > ~/.local/bin/jasm << 'EOF'
#!/bin/bash
java -jar ~/.local/share/java/jasm-cli-2.4.0-all.jar "$@"
EOF
chmod +x ~/.local/bin/jasm
# Test the jasm script
jasm --version
And test the output
$ jasm decompile /z/jvmstuff/Hello.class
.super java/lang/Object
.class public super Hello {
.method public <init> ()V {
parameters: { this },
code: {
A:
aload this
invokespecial java/lang/Object.<init> ()V
return
}
}
.method public static main ([Ljava/lang/String;)V {
parameters: { p0 },
code: {
A:
getstatic java/lang/System.out Ljava/io/PrintStream;
ldc "Hello, world!"
invokevirtual java/io/PrintStream.println (Ljava/lang/String;)V
B:
return
}
}
}
There is also a python dissasembler
git clone --depth=1 --single-branch --branch master https://github.com/Storyyeller/Krakatau.git
It works however with python 2.7 and 3 but not the modern one on windows. These will generate .j files
just use python Krakatau/dissasembler.py Hello.class
This is how they look
.version 50 0
.class public super Hello
.super java/lang/Object
.method public <init> : ()V
.code stack 1 locals 1
L0: aload_0
L1: invokespecial Method java/lang/Object <init> ()V
L4: return
L5:
.linenumbertable
L0 1
.end linenumbertable
.end code
.end method
.method public static main : ([Ljava/lang/String;)V
.code stack 2 locals 1
L0: getstatic Field java/lang/System out Ljava/io/PrintStream;
L3: ldc 'Hello, world!'
L5: invokevirtual Method java/io/PrintStream println (Ljava/lang/String;)V
L8: return
L9:
.linenumbertable
L0 3
L8 4
.end linenumbertable
.end code
.end method
.sourcefile 'Hello.java'
.end class
There are possibly other ways, but these are just some I have found, both 2nd and 3rd let you convert it to bytecode, the 3rd one includes both the assembler and dissasembler, and the 2nd one you need to get jasmin separately. The reason why there are so many assemblers is that Sun/Oracle never many any official ones. In case you want to inspect the class struct itself, https://ide.kaitai.io/ is a good start.
Edit: I wrote a small java dissasembler in JavaScript. You can run it as:
$ npx -p jvm_parser disassembler Hello.class
Or run it web.
async function processFile() {
const fileInput = document.getElementById('fileInput');
const outputDiv = document.getElementById('output');
if (fileInput.files.length === 0) {
outputDiv.textContent = 'Please select a file.';
return;
}
const file = fileInput.files[0];
const arrayBuffer = await file.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
try {
const parser = await import("https://esm.sh/jvm_parser");
const disassembled = await parser.getDisassembled(uint8Array);
outputDiv.textContent = disassembled;
} catch (error) {
outputDiv.textContent = `Error: ${error.message}`;
}
}
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
#output {
white-space: pre-wrap;
background-color: #f4f4f4;
padding: 10px;
border-radius: 5px;
margin-top: 20px;
}
<h1>JVM Parser File Input</h1>
<input type="file" id="fileInput" accept=".class">
<button onclick="processFile()">Process File</button>
<div id="output"></div>