自己手工解密字符串很麻烦,要一个一个去执行,那么有没有方法可以用程序去解密呢?办法肯定是有的。接下来我们就一步一步去实现自动解密程序吧。
要实现自动解密,我们首先要先找到哪些方法是字符串解密方法。
分析一下这个加密方法,我们可以看到方法参数是String返回值是String,然后方法内部有些特殊的方法,比如charAt length还有^操作符等等这些特征。那我们就可以利用asm在字节码中找到这个加密方法。
方法写完,我们测试一下,看看是否能找到加密方法
执行方法,在控制台输出了方法名称,今天先讲到这里,下次我们讲一下获取到方法,那么如何去解密字符串呢。
完整代码如下:
package org.example;
import org.apache.commons.io.IOUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import java.io.IOException;
import java.util.jar.JarFile;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
public class FindAllatoriMethod {
public static void main(String[] args) {
JarFile inputJar = null;
try {
inputJar = new JarFile("D:\\work\\allatoriDecrypt\\obfuscator\\allatoriDecrypt.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) {
if(isAllatoriMethod(method)){
System.out.println(method.name);
}
}
});
}
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;
}
}