Skip to content

Commit 909e99c

Browse files
committed
fix Android support
1 parent 7f3b795 commit 909e99c

File tree

9 files changed

+80
-45
lines changed

9 files changed

+80
-45
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# native-obfuscator
22
Java .class to .cpp converter for use with JNI
33

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

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

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

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

86-
Two options are available:
87-
- hotspot: will use HotSpot JVM internals and should work with most obfuscators (even with stack trace checking as well)
88-
- 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
86+
Three options are available:
87+
- `hotspot`: will use HotSpot JVM internals and should work with most obfuscators (even with stack trace checking as well)
88+
- `std_java`: will use only minor JVM internals that must be available on all JVMs
89+
- `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)
8990

9091
`-a` - enable annotation processing
9192

obfuscator/src/main/java/by/radioegor146/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ private static class NativeObfuscatorRunner implements Callable<Integer> {
4343
private String customLibraryDirectory;
4444

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

4949
@CommandLine.Option(names = {"-a", "--annotations"}, description = "Use annotations to ignore/include native obfuscation")

obfuscator/src/main/java/by/radioegor146/NativeObfuscator.java

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ public void process(Path inputJarPath, Path outputDir, List<Path> inputLibs,
131131
cMakeBuilder.addMainFile("string_pool.hpp");
132132
cMakeBuilder.addMainFile("string_pool.cpp");
133133

134+
if (platform == Platform.HOTSPOT) {
135+
cMakeBuilder.addFlag("USE_HOTSPOT");
136+
}
137+
134138
MainSourceBuilder mainSourceBuilder = new MainSourceBuilder();
135139

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

302-
for (ClassNode hiddenClass : hiddenMethodsPool.getClasses()) {
303-
String hiddenClassFileName = "data_" + Util.escapeCppNameString(hiddenClass.name.replace('/', '_'));
306+
if (platform == Platform.ANDROID) {
307+
for (ClassNode hiddenClass : hiddenMethodsPool.getClasses()) {
308+
ClassWriter classWriter = new SafeClassWriter(metadataReader, Opcodes.ASM7 | ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
309+
hiddenClass.accept(classWriter);
310+
Util.writeEntry(out, hiddenClass.name + ".class", classWriter.toByteArray());
311+
}
312+
} else {
313+
for (ClassNode hiddenClass : hiddenMethodsPool.getClasses()) {
314+
String hiddenClassFileName = "data_" + Util.escapeCppNameString(hiddenClass.name.replace('/', '_'));
304315

305-
cMakeBuilder.addClassFile("output/" + hiddenClassFileName + ".hpp");
306-
cMakeBuilder.addClassFile("output/" + hiddenClassFileName + ".cpp");
316+
cMakeBuilder.addClassFile("output/" + hiddenClassFileName + ".hpp");
317+
cMakeBuilder.addClassFile("output/" + hiddenClassFileName + ".cpp");
307318

308-
mainSourceBuilder.addHeader(hiddenClassFileName + ".hpp");
309-
mainSourceBuilder.registerDefine(stringPool.get(hiddenClass.name), hiddenClassFileName);
319+
mainSourceBuilder.addHeader(hiddenClassFileName + ".hpp");
320+
mainSourceBuilder.registerDefine(stringPool.get(hiddenClass.name), hiddenClassFileName);
310321

311-
ClassWriter classWriter = new SafeClassWriter(metadataReader, Opcodes.ASM7 | ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
312-
hiddenClass.accept(classWriter);
313-
byte[] rawData = classWriter.toByteArray();
314-
List<Byte> data = new ArrayList<>(rawData.length);
315-
for (byte b : rawData) {
316-
data.add(b);
317-
}
322+
ClassWriter classWriter = new SafeClassWriter(metadataReader, Opcodes.ASM7 | ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
323+
hiddenClass.accept(classWriter);
324+
byte[] rawData = classWriter.toByteArray();
325+
List<Byte> data = new ArrayList<>(rawData.length);
326+
for (byte b : rawData) {
327+
data.add(b);
328+
}
318329

319-
if (debug != null) {
320-
Util.writeEntry(debug, hiddenClass.name + ".class", rawData);
321-
}
330+
if (debug != null) {
331+
Util.writeEntry(debug, hiddenClass.name + ".class", rawData);
332+
}
322333

323-
try (BufferedWriter hppWriter = Files.newBufferedWriter(cppOutput.resolve(hiddenClassFileName + ".hpp"))) {
324-
hppWriter.append("#include \"../native_jvm.hpp\"\n\n");
325-
hppWriter.append("#ifndef ").append(hiddenClassFileName.toUpperCase()).append("_HPP_GUARD\n\n");
326-
hppWriter.append("#define ").append(hiddenClassFileName.toUpperCase()).append("_HPP_GUARD\n\n");
327-
hppWriter.append("namespace native_jvm::data::__ngen_").append(hiddenClassFileName).append(" {\n");
328-
hppWriter.append(" const jbyte* get_class_data();\n");
329-
hppWriter.append(" const jsize get_class_data_length();\n");
330-
hppWriter.append("}\n\n");
331-
hppWriter.append("#endif\n");
332-
}
334+
try (BufferedWriter hppWriter = Files.newBufferedWriter(cppOutput.resolve(hiddenClassFileName + ".hpp"))) {
335+
hppWriter.append("#include \"../native_jvm.hpp\"\n\n");
336+
hppWriter.append("#ifndef ").append(hiddenClassFileName.toUpperCase()).append("_HPP_GUARD\n\n");
337+
hppWriter.append("#define ").append(hiddenClassFileName.toUpperCase()).append("_HPP_GUARD\n\n");
338+
hppWriter.append("namespace native_jvm::data::__ngen_").append(hiddenClassFileName).append(" {\n");
339+
hppWriter.append(" const jbyte* get_class_data();\n");
340+
hppWriter.append(" const jsize get_class_data_length();\n");
341+
hppWriter.append("}\n\n");
342+
hppWriter.append("#endif\n");
343+
}
333344

334-
try (BufferedWriter cppWriter = Files.newBufferedWriter(cppOutput.resolve(hiddenClassFileName + ".cpp"))) {
335-
cppWriter.append("#include \"").append(hiddenClassFileName).append(".hpp\"\n\n");
336-
cppWriter.append("namespace native_jvm::data::__ngen_").append(hiddenClassFileName).append(" {\n");
337-
cppWriter.append(" static const jbyte class_data[").append(String.valueOf(data.size())).append("] = { ");
338-
cppWriter.append(data.stream().map(String::valueOf).collect(Collectors.joining(", ")));
339-
cppWriter.append("};\n");
340-
cppWriter.append(" static const jsize class_data_length = ").append(String.valueOf(data.size())).append(";\n\n");
341-
cppWriter.append(" const jbyte* get_class_data() { return class_data; }\n");
342-
cppWriter.append(" const jsize get_class_data_length() { return class_data_length; }\n");
343-
cppWriter.append("}\n");
345+
try (BufferedWriter cppWriter = Files.newBufferedWriter(cppOutput.resolve(hiddenClassFileName + ".cpp"))) {
346+
cppWriter.append("#include \"").append(hiddenClassFileName).append(".hpp\"\n\n");
347+
cppWriter.append("namespace native_jvm::data::__ngen_").append(hiddenClassFileName).append(" {\n");
348+
cppWriter.append(" static const jbyte class_data[").append(String.valueOf(data.size())).append("] = { ");
349+
cppWriter.append(data.stream().map(String::valueOf).collect(Collectors.joining(", ")));
350+
cppWriter.append("};\n");
351+
cppWriter.append(" static const jsize class_data_length = ").append(String.valueOf(data.size())).append(";\n\n");
352+
cppWriter.append(" const jbyte* get_class_data() { return class_data; }\n");
353+
cppWriter.append(" const jsize get_class_data_length() { return class_data_length; }\n");
354+
cppWriter.append("}\n");
355+
}
344356
}
345357
}
346358

obfuscator/src/main/java/by/radioegor146/Platform.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
public enum Platform {
44
HOTSPOT,
5-
STD_JAVA
5+
STD_JAVA,
6+
ANDROID
67
}

obfuscator/src/main/java/by/radioegor146/bytecode/IndyPreprocessor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ private static void processIndy(ClassNode classNode, MethodNode methodNode,
2525

2626

2727
switch (platform) {
28+
case ANDROID:
2829
case STD_JAVA: {
2930
Type[] bsmArguments = Type.getArgumentTypes(invokeDynamicInsnNode.bsm.getDesc());
3031
int targetArgLength = bsmArguments.length - 3;
@@ -256,6 +257,7 @@ private static void processIndy(ClassNode classNode, MethodNode methodNode,
256257
}
257258
break;
258259
}
260+
case ANDROID:
259261
case STD_JAVA: {
260262
invokeInstructions.add(new InsnNode(Opcodes.SWAP)); // 2
261263
invokeInstructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",

obfuscator/src/main/java/by/radioegor146/source/CMakeFilesBuilder.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@
44

55
import java.util.ArrayList;
66
import java.util.List;
7+
import java.util.stream.Collectors;
78

89
public class CMakeFilesBuilder {
910

1011
private final String projectName;
1112
private final List<String> classFiles;
1213
private final List<String> mainFiles;
14+
private final List<String> flags;
1315

1416
public CMakeFilesBuilder(String projectName) {
1517
this.projectName = projectName;
1618
classFiles = new ArrayList<>();
1719
mainFiles = new ArrayList<>();
20+
flags = new ArrayList<>();
1821
}
1922

2023
public void addClassFile(String classFile) {
@@ -25,12 +28,18 @@ public void addMainFile(String mainFile) {
2528
mainFiles.add(mainFile);
2629
}
2730

31+
public void addFlag(String flag) {
32+
flags.add(flag);
33+
}
34+
2835
public String build() {
2936
String template = Util.readResource("sources/CMakeLists.txt");
3037
return Util.dynamicFormat(template, Util.createMap(
3138
"classfiles", String.join(" ", classFiles),
3239
"mainfiles", String.join(" ", mainFiles),
33-
"projectname", projectName
40+
"projectname", projectName,
41+
"definitions", flags.stream().map(flag -> String.format("-D%s=1", flag))
42+
.collect(Collectors.joining(" "))
3443
));
3544
}
3645
}

obfuscator/src/main/resources/sources/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ include_directories(${JNI_INCLUDE_DIRS})
2828

2929
set(CLASS_FILES $classfiles)
3030
set(MAIN_FILES $mainfiles)
31+
add_definitions($definitions)
32+
3133
add_library($projectname SHARED ${CLASS_FILES} ${MAIN_FILES})

obfuscator/src/main/resources/sources/native_jvm.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ namespace native_jvm::utils {
1818
jmethodID init_cause_method;
1919
jclass methodhandles_lookup_class;
2020
jmethodID lookup_init_method;
21+
#ifdef USE_HOTSPOT
2122
jclass methodhandle_natives_class;
2223
jmethodID link_call_site_method;
2324
bool is_jvm11_link_call_site;
25+
#endif
2426

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

107+
#ifdef USE_HOTSPOT
105108
jclass _methodhandle_natives_class = env->FindClass("java/lang/invoke/MethodHandleNatives");
106109
if (env->ExceptionCheck())
107110
return;
@@ -119,8 +122,10 @@ namespace native_jvm::utils {
119122
if (env->ExceptionCheck())
120123
return;
121124
}
125+
#endif
122126
}
123127

128+
#ifdef USE_HOTSPOT
124129
jobject link_call_site(JNIEnv *env, jobject caller_obj, jobject bootstrap_method_obj,
125130
jobject name_obj, jobject type_obj, jobject static_arguments, jobject appendix_result) {
126131
if (is_jvm11_link_call_site) {
@@ -130,6 +135,7 @@ namespace native_jvm::utils {
130135
return env->CallStaticObjectMethod(methodhandle_natives_class, link_call_site_method, caller_obj,
131136
bootstrap_method_obj, name_obj, type_obj, static_arguments, appendix_result);
132137
}
138+
#endif
133139

134140
template <>
135141
jarray create_array_value<1>(JNIEnv *env, jint size) {

obfuscator/src/main/resources/sources/native_jvm.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ namespace native_jvm::utils {
7070
return result_array;
7171
}
7272

73+
#ifdef USE_HOTSPOT
7374
jobject link_call_site(JNIEnv *env, jobject caller_obj, jobject bootstrap_method_obj,
7475
jobject name_obj, jobject type_obj, jobject static_arguments, jobject appendix_result);
76+
#endif
7577

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

0 commit comments

Comments
 (0)