Dies ist der Artikel am 17. Tag von "Java Adventskalender 2016".
Manche sagen
Sie können es von einem Loader aus starten, der die Java-Klassendatei verschlüsselt, entschlüsselt und dynamisch lädt. Da die Klassendatei selbst verschlüsselt ist, sollten Sie den Code nicht nur durch Einfügen in den Entschlüsseler anzeigen können.
Passend dazu
private Key key;// KeyGenerator#generateKey()Passend
void encrypt(File file) {
byte[] inByte = null;
try {
inByte = FileUtils.readFileToByteArray(file);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(inByte);
FileUtils.writeByteArrayToFile(file, encrypted);
decryptTest(file, key, inByte);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
Spigot liest die Hauptklasse des Plug-Ins aus der Konfigurationsdatei (plugin.yml) und schreibt sie in die der Loader-Klasse um
Yaml yaml = new Yaml();
try {
String str = FileUtils.readFileToString(new File(appTmpDir, "plugin.yml"), "utf-8");
Map map = yaml.loadAs(str, Map.class);
map.put("main", getPackageName(getMainClass()) + ".PluginLoader");
str = yaml.dumpAsMap(map);
FileUtils.writeStringToFile(new File(appTmpDir, "plugin.yml"), str, "utf-8");
} catch (IOException e) {
e.printStackTrace();
}
Von Datei zu Byte-String
private byte[] read(InputStream inputStream) {
byte[] buf = new byte[1024];
int len;
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
while ((len = bufferedInputStream.read(buf)) > 0) {
byteArrayOutputStream.write(buf, 0, len);
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return byteArrayOutputStream.toByteArray();
}
Entschlüsselung (Schlüssel wird von ObjectInputStream gelesen)
private byte[] decrypt(byte[] bytes, Key key) {
byte[] inByte = null;
try {
if (key == null) throw new IllegalArgumentException("Schlüssel ist null.");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
inByte = cipher.doFinal(bytes);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return inByte;
}
Lesen Sie die Byte-Zeichenfolge mithilfe von Reflektion gut durch Die defineClass0-Methode von ClassLoader ist wichtig
private void loadClass(byte[] bytes, String name) throws ClassFormatError {
try {
String packageName = name.replaceAll("/", ".").substring(0, name.length() - 6);
Method define0Method = ClassLoader.class.getDeclaredMethod("defineClass0", new Class[]{String.class, byte[].class, int.class, int.class, ProtectionDomain.class});
define0Method.setAccessible(true);
Class loadedClass = (Class) define0Method.invoke(getClassLoader(), packageName, bytes, 0, bytes.length, null);
if (packageName.equals(mainClassName)) {
this.mainClass = loadedClass;
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof ClassFormatError) {
throw (ClassFormatError) cause;
}
}
stub.add(name);
}
Da jedes Plug-In vom Paketnamen + Klassennamen auf dem Server verwaltet wird, werden das zweite und die nachfolgenden Plug-Ins geladen, wenn zwei oder mehr Plug-Ins aufgrund eines Konflikts zwischen dem Namen des Loader-Pakets und dem Klassennamen schwer zu dekompilieren sind. Scheitern. Es gibt kein Problem bei der Verteilung als eigenständiges Java-Programm.
Das schwierige JAR-Dekompilierungsprogramm (vorläufig) enthält den Quellcode des Loaders im Hauptteil, und der Paketname wird dynamisch geändert, sodass er jedes Mal kompiliert wird. Das Argument von getTask wurde durch Ausprobieren entschieden und sollte normal funktionieren.
private void addLoader(String packageName, File target, File bukkitJar) {
try {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
String pluginLoaderJava = FileUtils.readFileToString(new File(ClassLoader.getSystemResource("PluginLoader.java").getFile()), "utf-8");
JavaFileObject file = new JavaSourceFromString("PluginLoader", pluginLoaderJava.replace("{{package}}", "package " + packageName + ";"));
String[] compileOptions = new String[]{"-d", target.getAbsolutePath(), "-classpath", bukkitJar.getAbsolutePath()};
Iterable<String> compilationOption = Arrays.asList(compileOptions);
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
JavaCompiler.CompilationTask task = compiler.getTask(
null,
null,
null,
compilationOption,
null,
compilationUnits);
task.call();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
Der Punkt, an dem ein JDK erforderlich ist, um den Loader zu kompilieren, wenn eine schwierige Dekompilierung ausgeführt wird. (Kann auf JDK verzichtet werden, indem die Klassendatei direkt geändert wird?) Auch wenn große Unternehmen zu Open Source werden, wie wäre es, den Quellcode überhaupt zu verstecken?
Recommended Posts