Skip to content

Commit

Permalink
Stringer 3.1.3 Decrypter (java-deobfuscator#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThisTestUser authored and samczsun committed Oct 2, 2017
1 parent 6c4cc43 commit 57f1467
Show file tree
Hide file tree
Showing 10 changed files with 1,081 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package com.javadeobfuscator.deobfuscator.analyzer;

import java.util.ArrayList;
import java.util.List;

import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes;
import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type;
import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.AbstractInsnNode;
import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodInsnNode;
import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode;
import com.javadeobfuscator.deobfuscator.utils.Utils;

/**
* This class is used to retrieve where a method's args begin/end.
* Somtimes, obfuscators don't like to put a method's args right before its method that uses it.
* This analyzer can only run backwards, because running forwards doesn't work.
* @author ThisTestUser
*/
public class ArgsAnalyzer
{
private final MethodNode method;

/**
* Which index do we start at?
*/
private final int startIndex;

/**
* How many arguments do we need?
*/
private final int argSize;

public ArgsAnalyzer(MethodNode method, int startIndex, int argSize)
{
this.method = method;
this.startIndex = startIndex;
this.argSize = argSize;
}

/**
* Performs an arg lookup.
* @return The list of nodes as the arguments, in order.
*/
public List<AbstractInsnNode> lookupArgs()
{
//Reverse bytecode analysis
List<AbstractInsnNode> stack = new ArrayList<>();
int neededNodes = 0;
//This isn't complete, fix as new errors come
for(int i = startIndex; i >= 0; i--)
{
AbstractInsnNode node = method.instructions.get(i);
if(Utils.isNumber(node))
{
stack.add(0, node);
if(neededNodes > 0)
{
neededNodes--;
stack.remove(0);
}
if(stack.size() >= argSize && neededNodes == 0)
{
if(stack.size() > argSize)
throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected "
+ argSize + " !");
return stack;
}
}else if(node.getOpcode() == Opcodes.ACONST_NULL
|| node.getOpcode() == Opcodes.LDC)
{
stack.add(0, node);
if(neededNodes > 0)
{
neededNodes--;
stack.remove(0);
}
if(stack.size() >= argSize && neededNodes == 0)
{
if(stack.size() > argSize)
throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected "
+ argSize + " !");
return stack;
}
}else if(node.getOpcode() >= Opcodes.ILOAD
&& node.getOpcode() <= Opcodes.ALOAD)
{
stack.add(0, node);
if(neededNodes > 0)
{
neededNodes--;
stack.remove(0);
}
if(stack.size() >= argSize && neededNodes == 0)
{
if(stack.size() > argSize)
throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected "
+ argSize + " !");
return stack;
}
}else if(node.getOpcode() >= Opcodes.ISTORE
&& node.getOpcode() <= Opcodes.ASTORE)
neededNodes++;
else if(node.getOpcode() == Opcodes.INVOKEVIRTUAL
|| node.getOpcode() == Opcodes.INVOKESPECIAL
|| node.getOpcode() == Opcodes.INVOKEINTERFACE)
{
//Note that we add one for the instance
MethodInsnNode cast = (MethodInsnNode)node;
neededNodes += Type.getArgumentTypes(cast.desc).length + 1;
if(Type.getReturnType(cast.desc).getReturnType().getSort() > 0
&& Type.getReturnType(cast.desc).getReturnType().getSort() < 11)
{
stack.add(0, node);
if(neededNodes > 0)
{
neededNodes--;
stack.remove(0);
}
if(stack.size() >= argSize && neededNodes == 0)
{
if(stack.size() > argSize)
throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected "
+ argSize + " !");
return stack;
}
}
}else if(node.getOpcode() == Opcodes.INVOKESTATIC)
{
MethodInsnNode cast = (MethodInsnNode)node;
neededNodes += Type.getArgumentTypes(cast.desc).length;
if(Type.getReturnType(cast.desc).getReturnType().getSort() > 0
&& Type.getReturnType(cast.desc).getReturnType().getSort() < 11)
{
stack.add(0, node);
if(neededNodes > 0)
{
neededNodes--;
stack.remove(0);
}
if(stack.size() >= argSize && neededNodes == 0)
{
if(stack.size() > argSize)
throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected "
+ argSize + " !");
return stack;
}
}
}else if(node.getOpcode() == Opcodes.GETSTATIC || node.getOpcode() == Opcodes.GETFIELD)
{
if(node.getOpcode() == Opcodes.GETFIELD)
neededNodes++;
stack.add(0, node);
if(neededNodes > 0)
{
neededNodes--;
stack.remove(0);
}
if(stack.size() >= argSize && neededNodes == 0)
{
if(stack.size() > argSize)
throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected "
+ argSize + " !");
return stack;
}
}else if(node.getOpcode() == Opcodes.PUTFIELD || node.getOpcode() == Opcodes.PUTSTATIC)
{
if(node.getOpcode() == Opcodes.PUTFIELD)
neededNodes++;
neededNodes++;
}else if(node.getOpcode() == Opcodes.INVOKEDYNAMIC)
{
//InvokeDynamic adds to the stack
stack.add(0, node);
if(neededNodes > 0)
{
neededNodes--;
stack.remove(0);
}
if(stack.size() >= argSize && neededNodes == 0)
{
if(stack.size() > argSize)
throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected "
+ argSize + " !");
return stack;
}
}else if(node.getOpcode() == Opcodes.NEW)
{
if(node.getNext() != null
&& node.getNext().getOpcode() == Opcodes.DUP)
{
stack.add(0, node);
if(neededNodes > 0)
{
neededNodes--;
stack.remove(0);
}
if(stack.size() >= argSize && neededNodes == 0)
{
if(stack.size() > argSize)
throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected "
+ argSize + " !");
return stack;
}
}
stack.add(0, node);
if(neededNodes > 0)
{
neededNodes--;
stack.remove(0);
}
if(stack.size() >= argSize && neededNodes == 0)
{
if(stack.size() > argSize)
throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected "
+ argSize + " !");
return stack;
}
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@

import javax.xml.bind.DatatypeConverter;

import javax.xml.bind.DatatypeConverter;

public class JVMMethodProvider extends MethodProvider {
@SuppressWarnings("serial")
//@formatter:off
Expand Down Expand Up @@ -121,6 +119,9 @@ public class JVMMethodProvider extends MethodProvider {
put("java/util/Arrays", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("asList([Ljava/lang/Object;)Ljava/util/List;", (targetObject, args, context) -> Arrays.asList(args.get(0).as(Object[].class)));
}});
put("java/util/ArrayList", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("<init>()V", (targetObject, args, context) -> new ArrayList());
}});
put("java/lang/String", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("<init>([CII)V", (targetObject, args, context) -> {
expect(targetObject, "java/lang/String");
Expand Down Expand Up @@ -253,11 +254,18 @@ public class JVMMethodProvider extends MethodProvider {
}});
put("java/lang/Class", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("forName(Ljava/lang/String;)Ljava/lang/Class;", (targetObject, args, context) -> new JavaClass(args.get(0).as(String.class), context));
put("getDeclaredConstructor([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getDeclaredConstructor(toJavaClass(args.get(0).as(Object[].class))));
put("getDeclaredConstructors()[Ljava/lang/reflect/Constructor;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getDeclaredConstructors());
put("getConstructor([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getConstructor(toJavaClass(args.get(0).as(Object[].class))));
put("getConstructors()[Ljava/lang/reflect/Constructor;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getConstructors());
put("getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getDeclaredMethod(args.get(0).as(String.class), toJavaClass(args.get(1).as(Object[].class))));
put("getDeclaredMethods()[Ljava/lang/reflect/Method;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getDeclaredMethods());
put("getMethods()[Ljava/lang/reflect/Method;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getDeclaredMethods());//FIXME: Temporary patch
put("getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getMethod(args.get(0).as(String.class), toJavaClass(args.get(1).as(Object[].class))));
put("getMethods()[Ljava/lang/reflect/Method;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getMethods());
put("getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getDeclaredField(args.get(0).as(String.class)));
put("getDeclaredFields()[Ljava/lang/reflect/Field;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getDeclaredFields());
put("getField(Ljava/lang/String;)Ljava/lang/reflect/Field;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getField(args.get(0).as(String.class)));
put("getFields()[Ljava/lang/reflect/Field;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getFields());
put("getClassLoader()Ljava/lang/ClassLoader;", (targetObject, args, context) -> null);
put("getName()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getName());
put("getSimpleName()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getSimpleName());
Expand Down Expand Up @@ -305,7 +313,7 @@ public class JVMMethodProvider extends MethodProvider {
return null;
});

put("newInstance([Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(JavaConstructor.class)); // FIXME
put("newInstance([Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(JavaConstructor.class).newInstance(context, args.get(0).as(Object[].class)));
}});
put("java/lang/reflect/Method", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("getName()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(JavaMethod.class).getName());
Expand Down Expand Up @@ -358,6 +366,17 @@ public class JVMMethodProvider extends MethodProvider {
return null;
});
}});
put("java/lang/invoke/MutableCallSite", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("<init>(Ljava/lang/invoke/MethodHandle;)V", (targetObject, args, context) -> {
expect(targetObject, "java/lang/invoke/MutableCallSite");
targetObject.initialize(args.get(0).value());
return null;
});
put("setTarget(Ljava/lang/invoke/MethodHandle;)V", (targetObject, args, context) -> {
return null;
});
put("getTarget()Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> targetObject.value());
}});
put("java/lang/System", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("currentTimeMillis()J", (targetObject, args, context) -> System.currentTimeMillis());
put("arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V", (targetObject, args, context) -> {
Expand Down Expand Up @@ -435,6 +454,14 @@ public class JVMMethodProvider extends MethodProvider {
return null;
});
put("put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(HashMap.class).put(args.get(0), args.get(1)));
put("isEmpty()Z", (targetObject, args, context) -> targetObject.as(HashMap.class).isEmpty());
}});
put("java/util/HashSet", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("<init>()V", (targetObject, args, context) -> {
expect(targetObject, "java/util/HashSet");
targetObject.initialize(new HashSet<>());
return null;
});
}});
put("java/util/LinkedList", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("<init>()V", (targetObject, args, context) -> {
Expand Down Expand Up @@ -490,6 +517,7 @@ public class JVMMethodProvider extends MethodProvider {
// Javax
put("javax/xml/bind/DatatypeConverter", new HashMap<String, Function3<JavaValue, List<JavaValue>, Context, Object>>() {{
put("parseBase64Binary(Ljava/lang/String;)[B", (targetObject, args, context) -> DatatypeConverter.parseBase64Binary(args.get(0).as(String.class)));
put("parseHexBinary(Ljava/lang/String;)[B", (targetObject, args, context) -> DatatypeConverter.parseHexBinary(args.get(0).as(String.class)));
}});

// Sun
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ public MappedMethodProvider(Map<String, WrappedClassNode> classpath) {

public boolean canInvokeMethod(String className, String methodName, String methodDesc, JavaValue targetObject, List<JavaValue> args, Context context) {
WrappedClassNode wrappedClassNode = classpath.get(className);
return wrappedClassNode != null;
if(wrappedClassNode == null)
return false;
MethodNode methodNode = wrappedClassNode.classNode.methods.stream().filter(mn -> mn.name.equals(methodName) && mn.desc.equals(methodDesc)).findFirst().orElse(null);
return methodNode != null;
}

@Override
Expand Down
Loading

0 comments on commit 57f1467

Please sign in to comment.