Skip to content

Commit

Permalink
fix Android support
Browse files Browse the repository at this point in the history
  • Loading branch information
radioegor146 committed Feb 5, 2024
1 parent 7f3b795 commit 909e99c
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 45 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# native-obfuscator
Java .class to .cpp converter for use with JNI

Currently fully supports only Java 8. Java 9+ support is entirely experimental
Currently fully supports only Java 8. Java 9+ and Android support is entirely experimental

Warning: blacklist/whitelist usage is recommended because this tool slows down code significantly (like do not obfuscate full Minecraft .jar)

Expand Down Expand Up @@ -64,8 +64,8 @@ Transpiles .jar file into .cpp files and generates output .jar file
Directory for dependent libraries
-p, --platform=<platform>
Target platform: hotspot - standard standalone
HotSpot JRE, std_java - java standard (as for
Android)
HotSpot JRE, std_java - java standard, android -
for Android builds (w/o DefineClass)
--plain-lib-name=<libraryName>
Plain library name for LoaderPlain
-V, --version Print version information and exit.
Expand All @@ -83,9 +83,10 @@ Transpiles .jar file into .cpp files and generates output .jar file

`-p <platform>` - JVM platform to run library on

Two options are available:
- hotspot: will use HotSpot JVM internals and should work with most obfuscators (even with stack trace checking as well)
- std_java: will use only minor JVM internals that are also available on Android. Use only this option if you want to run your library on Android
Three options are available:
- `hotspot`: will use HotSpot JVM internals and should work with most obfuscators (even with stack trace checking as well)
- `std_java`: will use only minor JVM internals that must be available on all JVMs
- `android`: use this method when building library for Android. Will use no JVM internals, as well as no DefineClass for hidden methods (obfuscators that rely on stack for string/name obfuscator will not work due to the fact that some methods will not be hidden)

`-a` - enable annotation processing

Expand Down
2 changes: 1 addition & 1 deletion obfuscator/src/main/java/by/radioegor146/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private static class NativeObfuscatorRunner implements Callable<Integer> {
private String customLibraryDirectory;

@CommandLine.Option(names = {"-p", "--platform"}, defaultValue = "hotspot",
description = "Target platform: hotspot - standard standalone HotSpot JRE, std_java - java standard (as for Android)")
description = "Target platform: hotspot - standard standalone HotSpot JRE, std_java - java standard, android - for Android builds (w/o DefineClass)")
private Platform platform;

@CommandLine.Option(names = {"-a", "--annotations"}, description = "Use annotations to ignore/include native obfuscation")
Expand Down
84 changes: 48 additions & 36 deletions obfuscator/src/main/java/by/radioegor146/NativeObfuscator.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ public void process(Path inputJarPath, Path outputDir, List<Path> inputLibs,
cMakeBuilder.addMainFile("string_pool.hpp");
cMakeBuilder.addMainFile("string_pool.cpp");

if (platform == Platform.HOTSPOT) {
cMakeBuilder.addFlag("USE_HOTSPOT");
}

MainSourceBuilder mainSourceBuilder = new MainSourceBuilder();

File jarFile = inputJarPath.toAbsolutePath().toFile();
Expand Down Expand Up @@ -299,48 +303,56 @@ public void process(Path inputJarPath, Path outputDir, List<Path> inputLibs,
}
});

for (ClassNode hiddenClass : hiddenMethodsPool.getClasses()) {
String hiddenClassFileName = "data_" + Util.escapeCppNameString(hiddenClass.name.replace('/', '_'));
if (platform == Platform.ANDROID) {
for (ClassNode hiddenClass : hiddenMethodsPool.getClasses()) {
ClassWriter classWriter = new SafeClassWriter(metadataReader, Opcodes.ASM7 | ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
hiddenClass.accept(classWriter);
Util.writeEntry(out, hiddenClass.name + ".class", classWriter.toByteArray());
}
} else {
for (ClassNode hiddenClass : hiddenMethodsPool.getClasses()) {
String hiddenClassFileName = "data_" + Util.escapeCppNameString(hiddenClass.name.replace('/', '_'));

cMakeBuilder.addClassFile("output/" + hiddenClassFileName + ".hpp");
cMakeBuilder.addClassFile("output/" + hiddenClassFileName + ".cpp");
cMakeBuilder.addClassFile("output/" + hiddenClassFileName + ".hpp");
cMakeBuilder.addClassFile("output/" + hiddenClassFileName + ".cpp");

mainSourceBuilder.addHeader(hiddenClassFileName + ".hpp");
mainSourceBuilder.registerDefine(stringPool.get(hiddenClass.name), hiddenClassFileName);
mainSourceBuilder.addHeader(hiddenClassFileName + ".hpp");
mainSourceBuilder.registerDefine(stringPool.get(hiddenClass.name), hiddenClassFileName);

ClassWriter classWriter = new SafeClassWriter(metadataReader, Opcodes.ASM7 | ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
hiddenClass.accept(classWriter);
byte[] rawData = classWriter.toByteArray();
List<Byte> data = new ArrayList<>(rawData.length);
for (byte b : rawData) {
data.add(b);
}
ClassWriter classWriter = new SafeClassWriter(metadataReader, Opcodes.ASM7 | ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
hiddenClass.accept(classWriter);
byte[] rawData = classWriter.toByteArray();
List<Byte> data = new ArrayList<>(rawData.length);
for (byte b : rawData) {
data.add(b);
}

if (debug != null) {
Util.writeEntry(debug, hiddenClass.name + ".class", rawData);
}
if (debug != null) {
Util.writeEntry(debug, hiddenClass.name + ".class", rawData);
}

try (BufferedWriter hppWriter = Files.newBufferedWriter(cppOutput.resolve(hiddenClassFileName + ".hpp"))) {
hppWriter.append("#include \"../native_jvm.hpp\"\n\n");
hppWriter.append("#ifndef ").append(hiddenClassFileName.toUpperCase()).append("_HPP_GUARD\n\n");
hppWriter.append("#define ").append(hiddenClassFileName.toUpperCase()).append("_HPP_GUARD\n\n");
hppWriter.append("namespace native_jvm::data::__ngen_").append(hiddenClassFileName).append(" {\n");
hppWriter.append(" const jbyte* get_class_data();\n");
hppWriter.append(" const jsize get_class_data_length();\n");
hppWriter.append("}\n\n");
hppWriter.append("#endif\n");
}
try (BufferedWriter hppWriter = Files.newBufferedWriter(cppOutput.resolve(hiddenClassFileName + ".hpp"))) {
hppWriter.append("#include \"../native_jvm.hpp\"\n\n");
hppWriter.append("#ifndef ").append(hiddenClassFileName.toUpperCase()).append("_HPP_GUARD\n\n");
hppWriter.append("#define ").append(hiddenClassFileName.toUpperCase()).append("_HPP_GUARD\n\n");
hppWriter.append("namespace native_jvm::data::__ngen_").append(hiddenClassFileName).append(" {\n");
hppWriter.append(" const jbyte* get_class_data();\n");
hppWriter.append(" const jsize get_class_data_length();\n");
hppWriter.append("}\n\n");
hppWriter.append("#endif\n");
}

try (BufferedWriter cppWriter = Files.newBufferedWriter(cppOutput.resolve(hiddenClassFileName + ".cpp"))) {
cppWriter.append("#include \"").append(hiddenClassFileName).append(".hpp\"\n\n");
cppWriter.append("namespace native_jvm::data::__ngen_").append(hiddenClassFileName).append(" {\n");
cppWriter.append(" static const jbyte class_data[").append(String.valueOf(data.size())).append("] = { ");
cppWriter.append(data.stream().map(String::valueOf).collect(Collectors.joining(", ")));
cppWriter.append("};\n");
cppWriter.append(" static const jsize class_data_length = ").append(String.valueOf(data.size())).append(";\n\n");
cppWriter.append(" const jbyte* get_class_data() { return class_data; }\n");
cppWriter.append(" const jsize get_class_data_length() { return class_data_length; }\n");
cppWriter.append("}\n");
try (BufferedWriter cppWriter = Files.newBufferedWriter(cppOutput.resolve(hiddenClassFileName + ".cpp"))) {
cppWriter.append("#include \"").append(hiddenClassFileName).append(".hpp\"\n\n");
cppWriter.append("namespace native_jvm::data::__ngen_").append(hiddenClassFileName).append(" {\n");
cppWriter.append(" static const jbyte class_data[").append(String.valueOf(data.size())).append("] = { ");
cppWriter.append(data.stream().map(String::valueOf).collect(Collectors.joining(", ")));
cppWriter.append("};\n");
cppWriter.append(" static const jsize class_data_length = ").append(String.valueOf(data.size())).append(";\n\n");
cppWriter.append(" const jbyte* get_class_data() { return class_data; }\n");
cppWriter.append(" const jsize get_class_data_length() { return class_data_length; }\n");
cppWriter.append("}\n");
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion obfuscator/src/main/java/by/radioegor146/Platform.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

public enum Platform {
HOTSPOT,
STD_JAVA
STD_JAVA,
ANDROID
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ private static void processIndy(ClassNode classNode, MethodNode methodNode,


switch (platform) {
case ANDROID:
case STD_JAVA: {
Type[] bsmArguments = Type.getArgumentTypes(invokeDynamicInsnNode.bsm.getDesc());
int targetArgLength = bsmArguments.length - 3;
Expand Down Expand Up @@ -256,6 +257,7 @@ private static void processIndy(ClassNode classNode, MethodNode methodNode,
}
break;
}
case ANDROID:
case STD_JAVA: {
invokeInstructions.add(new InsnNode(Opcodes.SWAP)); // 2
invokeInstructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class CMakeFilesBuilder {

private final String projectName;
private final List<String> classFiles;
private final List<String> mainFiles;
private final List<String> flags;

public CMakeFilesBuilder(String projectName) {
this.projectName = projectName;
classFiles = new ArrayList<>();
mainFiles = new ArrayList<>();
flags = new ArrayList<>();
}

public void addClassFile(String classFile) {
Expand All @@ -25,12 +28,18 @@ public void addMainFile(String mainFile) {
mainFiles.add(mainFile);
}

public void addFlag(String flag) {
flags.add(flag);
}

public String build() {
String template = Util.readResource("sources/CMakeLists.txt");
return Util.dynamicFormat(template, Util.createMap(
"classfiles", String.join(" ", classFiles),
"mainfiles", String.join(" ", mainFiles),
"projectname", projectName
"projectname", projectName,
"definitions", flags.stream().map(flag -> String.format("-D%s=1", flag))
.collect(Collectors.joining(" "))
));
}
}
2 changes: 2 additions & 0 deletions obfuscator/src/main/resources/sources/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ include_directories(${JNI_INCLUDE_DIRS})

set(CLASS_FILES $classfiles)
set(MAIN_FILES $mainfiles)
add_definitions($definitions)

add_library($projectname SHARED ${CLASS_FILES} ${MAIN_FILES})
6 changes: 6 additions & 0 deletions obfuscator/src/main/resources/sources/native_jvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ namespace native_jvm::utils {
jmethodID init_cause_method;
jclass methodhandles_lookup_class;
jmethodID lookup_init_method;
#ifdef USE_HOTSPOT
jclass methodhandle_natives_class;
jmethodID link_call_site_method;
bool is_jvm11_link_call_site;
#endif

void init_utils(JNIEnv *env) {
jclass clazz = env->FindClass("[Z");
Expand Down Expand Up @@ -102,6 +104,7 @@ namespace native_jvm::utils {
if (env->ExceptionCheck())
return;

#ifdef USE_HOTSPOT
jclass _methodhandle_natives_class = env->FindClass("java/lang/invoke/MethodHandleNatives");
if (env->ExceptionCheck())
return;
Expand All @@ -119,8 +122,10 @@ namespace native_jvm::utils {
if (env->ExceptionCheck())
return;
}
#endif
}

#ifdef USE_HOTSPOT
jobject link_call_site(JNIEnv *env, jobject caller_obj, jobject bootstrap_method_obj,
jobject name_obj, jobject type_obj, jobject static_arguments, jobject appendix_result) {
if (is_jvm11_link_call_site) {
Expand All @@ -130,6 +135,7 @@ namespace native_jvm::utils {
return env->CallStaticObjectMethod(methodhandle_natives_class, link_call_site_method, caller_obj,
bootstrap_method_obj, name_obj, type_obj, static_arguments, appendix_result);
}
#endif

template <>
jarray create_array_value<1>(JNIEnv *env, jint size) {
Expand Down
2 changes: 2 additions & 0 deletions obfuscator/src/main/resources/sources/native_jvm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ namespace native_jvm::utils {
return result_array;
}

#ifdef USE_HOTSPOT
jobject link_call_site(JNIEnv *env, jobject caller_obj, jobject bootstrap_method_obj,
jobject name_obj, jobject type_obj, jobject static_arguments, jobject appendix_result);
#endif

jclass find_class_wo_static(JNIEnv *env, jobject classloader, jstring class_name);

Expand Down

0 comments on commit 909e99c

Please sign in to comment.