接着之前的,我们今天学习如何找到被加密的字符串,打开混淆的类的字节码,我们发现被加密的字符串是这样的,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;
}
}