Skip to content

Instantly share code, notes, and snippets.

@JNNGL
Last active May 13, 2023 18:29
Show Gist options
  • Save JNNGL/b3b7b503d105e188681da20a01631e3a to your computer and use it in GitHub Desktop.
Save JNNGL/b3b7b503d105e188681da20a01631e3a to your computer and use it in GitHub Desktop.
JNI binding generation at runtime PoC
#include <stdio.h>
void hello() {
puts("Hello, world!");
}
import org.objectweb.asm.*;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.stream.Stream;
import static org.objectweb.asm.Opcodes.*;
public class Main {
public static void main(String[] args) throws Throwable {
ClassWriter cw = new ClassWriter(0);
cw.visit(49, ACC_PUBLIC + ACC_SUPER, "Test", null, "java/lang/Object", null);
// public static native void hello();
cw.visitMethod(ACC_PUBLIC + ACC_STATIC + ACC_NATIVE, "hello", "()V", null, null).visitEnd();
// public static void loadLibrary(String lib) { System.load(lib); }
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "loadLibrary", "(Ljava/lang/String;)V", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "load", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
cw.visitEnd();
Path directory = Files.createTempDirectory("test");
Files.write(directory.resolve("Test.class"), cw.toByteArray());
String bindingCode = """
#include <jni.h>
extern void hello();
JNIEXPORT void JNICALL Java_Test_hello(JNIEnv* env, jclass class) {
hello();
}""";
String targetLib = new File("hello.so").getAbsolutePath();
Files.writeString(directory.resolve("glue.c"), bindingCode);
ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", "gcc -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -Os -fPIC -shared glue.c -o binding.so " + targetLib);
processBuilder.environment().put("JAVA_HOME", System.getProperty("java.home"));
processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
processBuilder.directory(directory.toFile());
processBuilder.start().waitFor(); // TODO: Write native library directly in binary form
try (URLClassLoader classLoader = new URLClassLoader(new URL[]{directory.toUri().toURL()})) {
Class<?> nativeBinding = classLoader.loadClass("Test");
nativeBinding.getMethod("loadLibrary", String.class)
.invoke(null, directory.resolve("binding.so").toFile().getAbsolutePath());
nativeBinding.getMethod("hello").invoke(null);
}
try (Stream<Path> stream = Files.walk(directory)) {
stream.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::deleteOnExit);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment