package uwu.narumi.deobfuscator.transformer.impl.custom;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import uwu.narumi.deobfuscator.Deobfuscator;
import uwu.narumi.deobfuscator.transformer.Transformer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CustomTrashString extends Transformer {
[USER=1367676]@override[/USER]
public void transform(Deobfuscator deobfuscator) throws Exception {
for (ClassNode classNode : deobfuscator.classes()) {
for (MethodNode methodNode : classNode.methods) {
Map<AbstractInsnNode, AbstractInsnNode> shouldReplace = new HashMap<>();
List<AbstractInsnNode> shouldRemove = new ArrayList<>();
for (AbstractInsnNode abstractInsnNode : methodNode.instructions.toArray()) {
if (abstractInsnNode.getOpcode() == Opcodes.LDC) {
LdcInsnNode ldc = (LdcInsnNode) abstractInsnNode;
Object ldcValue = ldc.cst;
List<MethodInsnNode> methodCalls = new ArrayList<>();
if (valid(classNode, abstractInsnNode, methodCalls)) {
shouldRemove.addAll(methodCalls);
shouldReplace.put(ldc, new LdcInsnNode(dec(classNode, ldcValue, methodCalls)));
}
}
}
shouldReplace.forEach((abstractInsnNode, abstractInsnNode2) -> methodNode.instructions.set(abstractInsnNode, abstractInsnNode2));
shouldRemove.forEach(methodNode.instructions::remove);
}
}
}
private boolean valid(ClassNode classNode, AbstractInsnNode currentNode, List<MethodInsnNode> calls) {
AbstractInsnNode nextNode = currentNode;
for (int i = 0; i < 6; i++) {
nextNode = nextNode.getNext();
if (!(nextNode instanceof MethodInsnNode methodInsn) || nextNode.getOpcode() != Opcodes.INVOKESTATIC) {
return false;
}
if (!methodInsn.owner.equals(classNode.name)) {
return false;
}
MethodNode method = classNode.methods.stream()
.filter(m -> m.name.equals(methodInsn.name) && m.desc.equals(methodInsn.desc))
.findFirst()
.orElse(null);
if (
method == null ||
(method.access & Opcodes.ACC_PRIVATE) == 0 ||
(method.access & Opcodes.ACC_STATIC) == 0 ||
!method.desc.endsWith(")Ljava/lang/String;")
) {
return false;
}
calls.add(methodInsn);
}
return true;
}
private String dec(ClassNode classNode, Object ldcValue, List<MethodInsnNode> methodCalls) {
String result = (String) ldcValue;
for (MethodInsnNode methodInsn : methodCalls) {
MethodNode decrypt = classNode.methods.stream()
.filter(m -> m.name.equals(methodInsn.name) && m.desc.equals(methodInsn.desc))
.findFirst()
.orElse(null);
if (decrypt != null) {
result = xueta(result, extract(decrypt));
}
}
return result;
}
private int extract(MethodNode methodNode) {
for (AbstractInsnNode insn : methodNode.instructions) {
if (insn instanceof LdcInsnNode ldcInsnNode) {
return (int) ldcInsnNode.cst;
}
}
return -1;
}
private String xueta(String encrypted, int key) {
StringBuilder stringBuilder = new StringBuilder();
int n = 0;
while (n < encrypted.length()) {
stringBuilder.append((char)(encrypted.charAt(n) ^ key));
++n;
}
return stringBuilder.toString();
}
}