一步一步学习Allatori解密 (三)如何找到加密字符串解密

接着之前的,我们今天学习如何找到被加密的字符串,打开混淆的类的字节码,我们发现被加密的字符串是这样的,LDC是加密的字符串,之后INVOKESTATIC是执行解密的方法调用
在这里插入图片描述

我们可以用asm获取到方法描述为(Ljava/lang/String;)Ljava/lang/String;的静态方法,然后结合之前判断是否是加密方法的程序就可以找到这个被加密的字符串,然后我们执行解密方法就可以获取到原始字符串了。
我们编写如下代码,获取加密的字符串。
在这里插入图片描述

执行一下,我们找到了加密的字符串,调用解密方法获得了原始字符串
在这里插入图片描述

我们把原始代码修改一下,多加几个字符试试
在这里插入图片描述
混淆之后
在这里插入图片描述

加密字符串增加了,我们再次运行解密程序
在这里插入图片描述在这里插入图片描述

可以看到解密多个字符串没问题了,但是问题又来了,这么多字符串解密完,我怎么知道这是解密什么地方的字符串?下次我们讲如何把解密之后的字符串放到原始位置。

完整代码如下:

package org.example;

import org.apache.commons.io.IOUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.analysis.*;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.ListIterator;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import static org.objectweb.asm.Opcodes.*;

public class FindStringEncryption {
    public static void main(String[] args) {
        File jar = new File("D:\\work\\allatoriDecrypt\\obfuscator\\allatoriDecrypt.jar");
        JarFile inputJar = null;
        try {
            inputJar = new JarFile(jar);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        JarFile finalInputJar = inputJar;
        inputJar.stream().forEach(entry -> {
            if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
                return;
            }
            byte[] src = null;
            try {
                src = IOUtils.toByteArray(finalInputJar.getInputStream(entry));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            ClassReader classReader = new ClassReader(src);
            ClassNode classNode = new ClassNode();
            classReader.accept(classNode, ClassReader.SKIP_DEBUG);

            for (MethodNode method : classNode.methods) {
                Frame<SourceValue>[] frames;
                try {
                    frames = new Analyzer<>(new SourceInterpreter()).analyze(classNode.name, method);
                } catch (AnalyzerException e) {
                    continue;
                }
                ListIterator<AbstractInsnNode> iterator = method.instructions.iterator();
                while (iterator.hasNext()){
                    AbstractInsnNode node = iterator.next();
                    if(!(node instanceof LineNumberNode) && !(node instanceof FrameNode) && !(node instanceof LabelNode)){
                        if (node.getOpcode() != Opcodes.INVOKESTATIC) { //如果不是静态方法
                            continue;
                        }
                        MethodInsnNode m = (MethodInsnNode) node;
                        if (!m.desc.equals("(Ljava/lang/Object;)Ljava/lang/String;") && !m.desc.equals("(Ljava/lang/String;)Ljava/lang/String;")) {
                            continue;
                        }
                        String targetClass = m.owner;

                        Frame<SourceValue> f = frames[method.instructions.indexOf(m)];
                        if (f.getStack(f.getStackSize() - 1).insns.size() != 1) {
                            continue;
                        }
                        AbstractInsnNode insn = f.getStack(f.getStackSize() - 1).insns.iterator().next();
                        if (insn.getOpcode() != Opcodes.LDC) {
                            continue;
                        }
                        LdcInsnNode ldc = (LdcInsnNode) insn;
                        if (!(ldc.cst instanceof String)) {
                            continue;
                        }
                        try {
                            ClassNode targetClassNode = lookupClass(jar, targetClass + ".class");
                            MethodNode decrypterNode = targetClassNode.methods.stream()
                                    .filter(mn -> mn.name.equals(m.name) && mn.desc.equals(m.desc))
                                    .findFirst().orElse(null);
                            if (decrypterNode == null || decrypterNode.instructions.getFirst() == null) {
                                continue;
                            }
                            if (isAllatoriMethod(decrypterNode)) {
                                System.out.println("找到加密字符串 类:" + targetClassNode.name + "方法:" + decrypterNode.name + "加密字符串:" + ldc.cst);
                                Class clazz = Class.forName(targetClassNode.name.replaceAll("/","."));
                                Object obj = clazz.newInstance();
                                Method decrypterMethod = clazz.getDeclaredMethod(decrypterNode.name, String.class);
                                Object invoke = decrypterMethod.invoke(obj, ldc.cst);
                                System.out.println("解密字符串:"+invoke);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

    public static ClassNode lookupClass(File file, String name) throws IOException {
        ZipFile zipIn = new ZipFile(file);
        Enumeration<? extends ZipEntry> entries = zipIn.entries();

        while (entries.hasMoreElements()) {
            ZipEntry ent = entries.nextElement();
            if (ent.getName().endsWith(".class") && name.equals(ent.getName())) {
                byte[] src = null;
                try {
                    src = IOUtils.toByteArray(zipIn.getInputStream(ent));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                ClassReader classReader = new ClassReader(src);
                ClassNode classNode = new ClassNode();
                classReader.accept(classNode, ClassReader.SKIP_DEBUG);
                return classNode;
            }
        }
        return null;
    }

    private static boolean isAllatoriMethod(MethodNode decryptorNode) {
        boolean isAllatori = true;
        isAllatori = isAllatori && containsInvokeVirtual(decryptorNode, "java/lang/String", "charAt", "(I)C");
        isAllatori = isAllatori && containsInvokeVirtual(decryptorNode, "java/lang/String", "length", "()I");
        isAllatori = isAllatori && containsInvokeSpecial(decryptorNode, "java/lang/String", "<init>", null);
        isAllatori = isAllatori && countOccurencesOf(decryptorNode, IXOR) > 2;
        isAllatori = isAllatori && countOccurencesOf(decryptorNode, NEWARRAY) > 0;
        return isAllatori;
    }

    public static boolean containsInvokeVirtual(MethodNode methodNode, String owner, String name, String desc) {
        for (AbstractInsnNode insn : methodNode.instructions) {
            if (isInvokeVirtual(insn, owner, name, desc)) {
                return true;
            }
        }
        return false;
    }

    public static boolean isInvokeVirtual(AbstractInsnNode insn, String owner, String name, String desc) {
        if (insn == null) {
            return false;
        }
        if (insn.getOpcode() != INVOKEVIRTUAL) {
            return false;
        }
        MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
        return (owner == null || methodInsnNode.owner.equals(owner)) &&
                (name == null || methodInsnNode.name.equals(name)) &&
                (desc == null || methodInsnNode.desc.equals(desc));
    }

    public static boolean containsInvokeSpecial(MethodNode methodNode, String owner, String name, String desc) {
        for (AbstractInsnNode insn : methodNode.instructions) {
            if (isInvokeSpecial(insn, owner, name, desc)) {
                return true;
            }
        }
        return false;
    }

    public static boolean isInvokeSpecial(AbstractInsnNode insn, String owner, String name, String desc) {
        if (insn == null) {
            return false;
        }
        if (insn.getOpcode() != INVOKESPECIAL) {
            return false;
        }
        MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
        return (owner == null || methodInsnNode.owner.equals(owner)) &&
                (name == null || methodInsnNode.name.equals(name)) &&
                (desc == null || methodInsnNode.desc.equals(desc));
    }

    public static int countOccurencesOf(MethodNode methodNode, int opcode) {
        int i = 0;
        for (AbstractInsnNode insnNode : methodNode.instructions) {
            if (insnNode.getOpcode() == opcode) {
                i++;
            }
        }
        return i;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值