如何使用Java 8 ASM解决此方法重命名问题

时间:2019-09-27 20:31:52

标签: java-8 rename obfuscation java-bytecode-asm

最近,我用Java用ASM编写了一个混淆器,并想重命名类,方法和字段。但是问题是,代码也不能正常工作,我也不知道如何解决该问题。问题是,如果我混淆了jar,则该类中的每个方法都会重命名,但是有时(并非每次)都不会重命名一些代码,因此该jar无法执行。例如

public abstract class ColorThread implements Runnable
{

    @Getter
    private final String name;

    @Getter
    private Thread thread;

    public ColorThread(final String name) {
        this.name = name;

        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }

    @Override
    public void run() {
        throw new NotOverriddenException("The Thread \"" + getName() + "\" is not overwritten.");
    }

    /**
     * This method interrupts the running thread.
     */
    public void close() {
        this.getThread().interrupt();
    }

    public void start() { //<- Method gets renamed e.g "⢍⢖⣕⠟⡨⠣"
        this.thread = new Thread(this, this.getName());
        thread.start();
    }

}

因此该类变得模糊不清,但后来在其他代码中调用:

final ConnectThread connectThread = new ConnectThread();
connectThread.start(); // <- this line

具有connectThread.start()的行;未重命名为“ connectThread。⢍⢖⣕⠟⡨⠣();”。如果我使用另一个扩展ColorThread的类,例如ReceiveThread,start方法在这段代码中被重命名。

如果我制造了混淆器,我每次都会为这个问题而苦苦挣扎,因此我结束了该项目。但是现在我想问一下是否有人可以帮助我。抱歉,这篇漫长的帖子,但是我想提供解决问题所需的一切。

该项目在Java 1.8.0_161上运行,并将ASM-All作为依赖项。

要读取一个罐子,我使用此方法。它将所有类存储在ArrayList中:

try (final JarFile jarFile = new JarFile(inputFile)) {
            final Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();

            while (jarEntryEnumeration.hasMoreElements()) {
                final JarEntry jarEntry = jarEntryEnumeration.nextElement();

                if (jarEntry.isDirectory())
                    continue;

                final byte[] bytes = this.readInputStream(jarFile.getInputStream(jarEntry));

                if (jarEntry.getName().endsWith(".class")) {
                    if (jarEntry.getName().endsWith("module-info.class"))
                        continue;

                    final ClassNode classNode = new ClassNode();
                    //                  new ClassReader(bytes).accept(classNode, ClassReader.EXPAND_FRAMES | ClassReader.SKIP_DEBUG);
                    new ClassReader(bytes).accept(classNode, ClassReader.EXPAND_FRAMES);
                    this.classes.add(classNode);
                } else {
                    if (jarEntry.getName().contains("MANIFEST.MF"))
                        continue;

                    this.files.put(jarEntry.getName(), bytes);
                }
            }

            this.manifest = jarFile.getManifest();
        } catch (final Exception ex) {
            ex.printStackTrace();
        }

此后,我使用转换系统来重命名方法:

@Override
    public void transform(final ArrayList<ClassNode> classes, final HashMap<String, byte[]> files) {
        final String mainClass = this.getJarResources().getManifest().getMainAttributes().getValue("Main-Class").replace(".", "/");

        final HashMap<String, String> methodNames = new HashMap<>();

        for (final ClassNode classNode : classes) {
            for (final Object methodObj : classNode.methods) {
                if (!(methodObj instanceof MethodNode))
                    continue;

                final MethodNode methodNode = (MethodNode) methodObj;
                if (methodNode.name.equals("<init>"))
                    continue;
                if (methodNode.name.equals(mainClass) || methodNode.name.equals("main"))
                    continue;

                methodNames.put(classNode.name + "." + methodNode.name + methodNode.desc, this.generateString(6));
            }
        }

        this.remapClasses(classes, methodNames);
    }

remap方法如下:

public void remapClasses(final ArrayList<ClassNode> classes, final HashMap<String, String> remappedNames) {
        final SimpleRemapper simpleRemapper = new SimpleRemapper(remappedNames);

        for (int index = 0; index < classes.size(); index++) {
            final ClassNode realNode = classes.get(index);
            final ClassNode copyNode = new ClassNode();

            final ClassRemapper classRemapper = new ClassRemapper(copyNode, simpleRemapper);
            realNode.accept(classRemapper);

            classes.set(index, copyNode);
        }
    }

最后我写文件:

public void writeFile() {
        try (final JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(this.outputFile), this.manifest)) {
            for (final ClassNode classNode : this.classes) {
                final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                classNode.accept(writer);

                jarOutputStream.putNextEntry(new JarEntry(classNode.name + ".class"));
                jarOutputStream.write(writer.toByteArray());
                jarOutputStream.closeEntry();
            }

            for (final Map.Entry<String, byte[]> file : this.files.entrySet()) {
                final String filePath = file.getKey();

                if(filePath.endsWith(".kotlin_module") || filePath.contains("maven") || filePath.contains("3rd-party-licenses"))
                    continue;

                jarOutputStream.putNextEntry(new JarEntry(filePath));
                jarOutputStream.write(file.getValue());
                jarOutputStream.closeEntry();
            }
        } catch (final Exception ex) {
            ex.printStackTrace();
        }
    }

0 个答案:

没有答案