Ceci est l'article sur le 17ème jour du "Calendrier de l'Avent Java 2016".
Certains disent
Vous pouvez le démarrer à partir d'un chargeur qui crypte le fichier de classe Java, le déchiffre et le charge dynamiquement. Étant donné que le fichier de classe lui-même est chiffré, vous ne devriez pas pouvoir afficher le code simplement en le plaçant dans le décrypteur.
De manière appropriée comme ça
private Key key;// KeyGenerator#generateKey()De manière appropriée
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 lit la classe principale du plug-in dans le fichier de configuration (plugin.yml) et la réécrit dans celle de la classe loader
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();
}
Du fichier à la chaîne d'octets
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();
}
Décryptage (la clé est lue par ObjectInputStream)
private byte[] decrypt(byte[] bytes, Key key) {
    byte[] inByte = null;
    try {
        if (key == null) throw new IllegalArgumentException("La clé est nulle.");
        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;
}
Lisez bien la chaîne d'octets en utilisant la réflexion La méthode defineClass0 de ClassLoader est importante
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);
}
Étant donné que chaque plug-in est géré par le nom du package + le nom de la classe sur le serveur, s'il existe au moins deux plug-ins difficiles à décompiler en raison d'un conflit entre le nom du package du chargeur et le nom de la classe, le deuxième plug-in et les suivants seront chargés. Échouer. Il n'y a aucun problème lors de la distribution en tant que programme Java autonome.
Le programme de décompilation difficile JAR (provisoire) a le code source du chargeur dans le corps principal, et le nom du paquet est changé dynamiquement afin qu'il soit compilé à chaque fois. L'argument de getTask a été décidé par essais et erreurs, et il devrait fonctionner normalement.
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();
    }
}
Le point qu'un JDK est nécessaire pour compiler le chargeur lors de l'exécution d'une décompilation difficile. (Est-il possible de se passer de JDK en modifiant directement le fichier de classe?) Même si les grandes entreprises deviennent open source, qu'en est-il de cacher le code source en premier lieu?
Recommended Posts