new execute stuff

This commit is contained in:
Adam
2015-05-31 16:03:37 -04:00
parent 1e34e0ec66
commit 0fcbcd262c
205 changed files with 2977 additions and 1451 deletions

View File

@@ -1,42 +0,0 @@
package info.sigterm.deob.execution;
import java.util.Arrays;
public class ArrayInstance extends ObjectInstanceBase
{
private Object[] array;
public ArrayInstance(Path path, ClassInstance type, int len)
{
super(path, type);
this.array = new Object[len];
}
private ArrayInstance(ArrayInstance other, Path path, ClassInstance type)
{
super(path, type);
this.array = Arrays.copyOf(other.array, other.array.length);
}
public void put(Object obj, int idx)
{
array[idx] = obj;
}
public Object get(int idx)
{
return array[idx];
}
public int getLength()
{
return array.length;
}
@Override
public ObjectInstanceBase dup(Path path, ClassInstance type)
{
return new ArrayInstance(this, path, type);
}
}

View File

@@ -1,62 +0,0 @@
package info.sigterm.deob.execution;
import info.sigterm.deob.ClassFile;
import info.sigterm.deob.Field;
import info.sigterm.deob.Fields;
import info.sigterm.deob.attributes.AttributeType;
import info.sigterm.deob.attributes.Attributes;
import info.sigterm.deob.attributes.ConstantValue;
import info.sigterm.deob.pool.NameAndType;
import java.util.ArrayList;
public class ClassInstance
{
private Path path;
private ClassFile clazz;
private ArrayList<StaticFieldInstance> fields = new ArrayList<StaticFieldInstance>();
public ClassInstance(Path path, ClassFile clazz)
{
this.path = path;
this.clazz = clazz;
/* initialize static fields */
Fields fields = clazz.getFields();
for (Field field : fields.getFields())
if ((field.getAccessFlags() & Field.ACC_STATIC) != 0)
{
Attributes attributes = field.getAttributes();
ConstantValue cv = (ConstantValue) attributes.findType(AttributeType.CONSTANT_VALUE);
StaticFieldInstance fi = new StaticFieldInstance(this, field, cv);
this.fields.add(fi);
}
}
protected ClassInstance(Path path, ClassInstance other)
{
this.path = path;
this.clazz = other.clazz;
for (StaticFieldInstance f : other.fields)
this.fields.add(new StaticFieldInstance(other, f));
}
public Path getPath()
{
return path;
}
public ClassFile getClassFile()
{
return clazz;
}
public StaticFieldInstance findStaticField(NameAndType nat)
{
for (StaticFieldInstance f : fields)
if (f.getField().getName().equals(nat.getName()) && f.getField().getType().equals(nat.getDescriptorType()))
return f;
return null;
}
}

View File

@@ -7,69 +7,30 @@ import info.sigterm.deob.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
public class Execution
{
private ClassGroup group;
private ArrayList<Path> paths = new ArrayList<Path>(); // paths of execution
private HashMap<Method, HashSet<Integer>> visited = new HashMap<Method, HashSet<Integer>>();
//protected HashSet<Method> methods = new HashSet<Method>();
public List<Frame> frames = new ArrayList<>();
public Execution(ClassGroup group)
{
this.group = group;
}
public void run(ClassFile cf, Method method, Object... args)
public int run()
{
Path p = new Path(this);
ClassInstance instance = p.getClassInstance(cf);
ObjectInstance object = p.createObject(instance);
int fcount = 0;
int count = 1;
p.invoke(method, object);
while (!paths.isEmpty())
while (!frames.isEmpty())
{
p = paths.remove(0);
++count;
try
{
System.out.println("Resuming path with " + paths.size() + " remaining");
p.resume();
}
catch (Exception ex)
{
ex.printStackTrace();
}
Frame frame = frames.remove(0);
System.out.println("Executing frame " + frame);
++fcount;
frame.execute();
}
System.out.println("Done " + count + " paths");
}
public void addPath(Path p)
{
paths.add(p);
}
public boolean visit(Method m)
{
if (visited.containsKey(m))
return false;
visited.put(m, new HashSet<Integer>());
return true;
}
public boolean visit(Method m, int pc)
{
HashSet<Integer> map = visited.get(m);
if (map == null || !map.contains(pc))
{
map.add(pc);
return true;
}
return false;
return fcount;
}
}

View File

@@ -1,39 +0,0 @@
package info.sigterm.deob.execution;
import info.sigterm.deob.Field;
public class FieldInstance
{
private ObjectInstance object;
private Field field;
private Object value;
public FieldInstance(ObjectInstance object, Field field, Object value)
{
this.object = object;
this.field = field;
this.value = value;
}
protected FieldInstance(ObjectInstance object, FieldInstance other)
{
this.object = object;
this.field = other.field;
this.value = other.value;
}
public Field getField()
{
return field;
}
public Object getValue()
{
return value;
}
public void setValue(Object obj)
{
value = obj;
}
}

View File

@@ -1,47 +1,84 @@
package info.sigterm.deob.execution;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import info.sigterm.deob.Method;
import info.sigterm.deob.attributes.Code;
import info.sigterm.deob.attributes.code.Exception;
import info.sigterm.deob.attributes.code.Instruction;
import info.sigterm.deob.attributes.code.Instructions;
import info.sigterm.deob.attributes.code.instructions.LookupSwitch;
import info.sigterm.deob.attributes.code.instructions.TableSwitch;
import info.sigterm.deob.pool.NameAndType;
public class Frame
{
private Path path;
private Execution execution;
private Method method;
boolean executing = true;
private boolean executing = true;
private int pc;
private Stack stack;
private Variables variables;
private List<InstructionContext> instructions = new ArrayList<>(); // instructions executed in this frame
private Map<Instruction, Instruction> visited; // shared
public Frame(Path path, Method method)
public Frame(Execution execution, Method method)
{
Code code = method.getCode();
this.path = path;
this.execution = execution;
this.method = method;
Code code = method.getCode();
stack = new Stack(code.getMaxStack());
variables = new Variables(code.getMaxLocals());
visited = new HashMap<>();
// initialize LVT
int pos = 0;
if (!method.isStatic())
variables.set(pos++, new VariableContext(null, new Type(method.getMethods().getClassFile().getName())));
NameAndType nat = method.getNameAndType();
for (int i = 0; i < nat.getNumberOfArgs(); ++i)
{
variables.set(pos, new VariableContext(null, new Type(nat.getDescriptor().getTypeOfArg(i)).toStackType()));
pos += nat.getDescriptor().getTypeOfArg(i).getSlots();
}
}
protected Frame(Path path, Frame other)
protected Frame(Frame other)
{
this.path = path;
this.execution = other.execution;
this.method = other.method;
this.executing = other.executing;
this.pc = other.pc;
this.stack = new Stack(other.stack);
this.variables = new Variables(other.variables);
this.visited = other.visited;
}
public Path getPath()
public Frame dup()
{
return path;
Frame other = new Frame(this);
execution.frames.add(other);
return other;
}
public void stop()
{
executing = false;
}
public void throwException(Type type)
{
executing = false; // XXX
}
public Method getMethod()
@@ -58,12 +95,17 @@ public class Frame
{
return stack;
}
public Variables getVariables()
{
return variables;
}
public void addInstructionContext(InstructionContext i)
{
instructions.add(i);
}
public void execute()
{
Instructions ins = method.getCode().getInstructions();
@@ -90,16 +132,11 @@ public class Frame
System.err.println("Frame stack (grows downward):");
while (stack.getSize() > 0)
{
Instruction stacki = stack.getIns();
Object obj = stack.pop();
if (obj != null)
System.err.println(" " + obj + " (class " + obj.getClass().getName() + ") pushed by instruction " + stacki + " at pc " + stacki.getPc());
else
System.err.println(" " + obj + " pushed by instruction " + stacki + " at pc " + stacki.getPc());
StackContext stacki = stack.pop();
System.err.println(stacki);
}
System.err.println("end of stack");
ex.printStackTrace();
//System.exit(-1);
throw ex;
}
@@ -114,39 +151,42 @@ public class Frame
}
}
public void resume()
private void doJump(Instruction from, Instruction to)
{
execute();
visited.put(from, to);
}
public void skip()
private boolean hasJumped(Instruction from, Instruction to)
{
/* for resume, skip current ins? */
Instructions ins = method.getCode().getInstructions();
Instruction i = ins.findInstruction(pc);
pc += i.getLength();
}
private void checkLoop()
{
if (!this.getPath().getExecution().visit(method, pc))
{
System.out.println("Ending frame " + this);
executing = false;
}
Instruction i = visited.get(from);
if (from instanceof TableSwitch || from instanceof LookupSwitch) // XXX magic instructions which jump to multiple different places
if (i != null)
return true;
assert i == null || i == to;
return i == to;
}
public void jump(int offset)
{
assert offset != 0;
pc += offset;
checkLoop();
jumpAbsolute(pc + offset);
}
public void jumpAbsolute(int pc)
{
Instruction from = method.getCode().getInstructions().findInstruction(this.pc);
Instruction to = method.getCode().getInstructions().findInstruction(pc);
assert from != null;
assert to != null;
if (hasJumped(from, to))
{
executing = false;
return;
}
doJump(from, to);
this.pc = pc;
checkLoop();
}
public Collection<Exception> getExceptionHandlers()

View File

@@ -0,0 +1,42 @@
package info.sigterm.deob.execution;
import java.util.ArrayList;
import java.util.List;
import info.sigterm.deob.attributes.code.Instruction;
public class InstructionContext
{
private Instruction ins;
private Frame frame;
private List<StackContext> pops = new ArrayList<>();
private List<VariableContext> reads = new ArrayList<>(); // lvt reads
public InstructionContext(Instruction i, Frame f)
{
ins = i;
frame = f;
}
public void pop(StackContext... ctx)
{
for (StackContext c : ctx)
pops.add(c);
}
public void read(VariableContext... ctx)
{
for (VariableContext c : ctx)
reads.add(c);
}
public Instruction getInstruction()
{
return ins;
}
public List<StackContext> getPops()
{
return pops;
}
}

View File

@@ -1,57 +0,0 @@
package info.sigterm.deob.execution;
import info.sigterm.deob.Field;
import info.sigterm.deob.Fields;
import info.sigterm.deob.attributes.AttributeType;
import info.sigterm.deob.attributes.Attributes;
import info.sigterm.deob.attributes.ConstantValue;
import info.sigterm.deob.pool.NameAndType;
import java.util.ArrayList;
public class ObjectInstance extends ObjectInstanceBase
{
private ArrayList<FieldInstance> fields = new ArrayList<FieldInstance>();
public ObjectInstance(Path path, ClassInstance type)
{
super(path, type);
/* create fields */
Fields fields = type.getClassFile().getFields();
for (Field field : fields.getFields())
{
if ((field.getAccessFlags() & Field.ACC_STATIC) != 0)
continue;
Attributes attributes = field.getAttributes();
ConstantValue cv = (ConstantValue) attributes.findType(AttributeType.CONSTANT_VALUE);
FieldInstance fi = new FieldInstance(this, field, cv != null ? cv.getValue().getObject() : null);
this.fields.add(fi);
}
}
private ObjectInstance(ObjectInstance other, Path path, ClassInstance type)
{
super(path, type);
/* copy fields */
for (FieldInstance field : other.fields)
this.fields.add(new FieldInstance(this, field));
}
public FieldInstance getField(NameAndType nat)
{
for (FieldInstance f : fields)
if (f.getField().getName().equals(nat.getName()) && f.getField().getType().equals(nat.getDescriptorType()))
return f;
return null;
}
@Override
public ObjectInstanceBase dup(Path path, ClassInstance type)
{
return new ObjectInstance(this, path, type);
}
}

View File

@@ -1,21 +0,0 @@
package info.sigterm.deob.execution;
public abstract class ObjectInstanceBase
{
private Path path;
private ClassInstance type;
public ObjectInstanceBase(Path path, ClassInstance type)
{
this.path = path;
this.type = type;
}
public ClassInstance getType()
{
return type;
}
public abstract ObjectInstanceBase dup(Path path, ClassInstance type);
}

View File

@@ -1,174 +0,0 @@
package info.sigterm.deob.execution;
import info.sigterm.deob.ClassFile;
import info.sigterm.deob.Method;
import info.sigterm.deob.attributes.code.Exception;
import info.sigterm.deob.attributes.code.Instruction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
public class Path
{
private Execution execution;
private ArrayList<ClassInstance> classes = new ArrayList<ClassInstance>();
private ArrayList<ObjectInstanceBase> objects = new ArrayList<ObjectInstanceBase>();
private java.util.Stack<Frame> frames = new java.util.Stack<Frame>(); // current execution frames
public Path(Execution execution)
{
this.execution = execution;
}
private Path(Path other)
{
HashMap<ClassInstance, ClassInstance> classmap = new HashMap<ClassInstance, ClassInstance>();
this.execution = other.execution;
for (ClassInstance c : other.classes)
{
ClassInstance newclass = new ClassInstance(this, c);
classmap.put(c, newclass);
this.classes.add(newclass);
}
for (ObjectInstanceBase o : other.objects)
o.dup(this, classmap.get(o.getType()));
/* iteration order of a Stack is in reverse */
for (Frame f : other.frames)
frames.push(new Frame(this, f));
}
public Execution getExecution()
{
return execution;
}
public ClassInstance getClassInstance(ClassFile clazz)
{
for (ClassInstance cl : classes)
if (cl.getClassFile() == clazz)
return cl;
/* load parent */
ClassFile parent = clazz.getParent();
if (parent != null)
getClassInstance(parent);
ClassInstance cl = new ClassInstance(this, clazz);
classes.add(cl);
return cl;
}
public ObjectInstance createObject(ClassInstance type)
{
ObjectInstance obj = new ObjectInstance(this, type);
objects.add(obj);
return obj;
}
public ArrayInstance createArray(ClassInstance type, int len)
{
ArrayInstance arr = new ArrayInstance(this, type, len);
objects.add(arr);
return arr;
}
public Frame getCurrentFrame()
{
return frames.peek();
}
public Path dup()
{
Path other = new Path(this);
execution.addPath(other);
return other;
}
public void resume()
{
for (Frame f : frames)
{
/* top most is at the correct pc */
if (f == frames.peek())
break;
/* move pc past invoke function */
f.skip();
}
/* resume execution */
while (!frames.isEmpty())
{
Frame top = frames.peek();
top.resume();
if (!frames.isEmpty() && frames.peek() == top)
frames.pop(); // XXX throwing doesnt remove
}
}
public void invoke(Method method, Object... args)
{
if (!this.getExecution().visit(method))
return;
Frame f = new Frame(this, method);
Variables vars = f.getVariables();
for (int i = 0; i < args.length; ++i)
vars.set(i, args[i]);
frames.push(f);
System.out.println("Executing frame " + method.getName() + " " + method.getDescriptor());
f.execute();
if (frames.isEmpty() == false && frames.peek() == f)
System.err.println("Unpopped frame post execute");
}
public void returnFrame(Instruction i, Object value)
{
returnFrame();
Frame prevFrame = getCurrentFrame();
prevFrame.getStack().push(i, value);
}
public void returnFrame()
{
Frame currentFrame = frames.pop();
currentFrame.executing = false;
}
public void throwException(Instruction ins, ObjectInstance exception)
{
ArrayList<Exception> exceptions = new ArrayList<Exception>();
/* collect all existing exception handlers */
for (Frame f : frames)
{
Collection<Exception> handlers = f.getExceptionHandlers();
exceptions.addAll(handlers);
}
for (Exception handler : exceptions)
{
/* jump to handler */
Method handlerMethod = handler.getExceptions().getCode().getAttributes().getMethod();
Path other = this.dup();
/* walk up the frames until we find the one which holds the exception handler */
while (handlerMethod != other.getCurrentFrame().getMethod())
other.returnFrame();
/* handler pc is absolute from the beginning instruction */
other.getCurrentFrame().jumpAbsolute(handler.getHandlerPc());
}
/* this path stops executing */
for (Frame f : frames)
f.executing = false;
}
}

View File

@@ -3,50 +3,59 @@ package info.sigterm.deob.execution;
import java.util.Arrays;
import info.sigterm.deob.attributes.code.Instruction;
import info.sigterm.deob.pool.Method;
public class Stack
{
private int size;
private Object[] stack;
private Instruction[] ins;
private StackContext[] stack;
public Stack(int sz)
{
stack = new Object[sz];
ins = new Instruction[sz];
stack = new StackContext[sz];
}
protected Stack(Stack other)
{
this.size = other.size;
this.stack = Arrays.copyOf(other.stack, other.stack.length);
this.ins = Arrays.copyOf(other.ins, other.ins.length);
}
public void push(Instruction i, Object obj)
{
if (size == stack.length)
throw new RuntimeException("Stack overflow");
stack[size] = obj;
ins[size] = i;
++size;
}
public Instruction getIns()
private void printStack(StackContext ctx, int level)
{
if (size <= 0)
throw new RuntimeException("Stack underflow");
return ins[size - 1];
for (int i = 0; i < level; ++i)
System.err.print(" ");
System.err.println(ctx.getType().type + " pushed by " + ctx.getIns().getInstruction().getType().getName() + " at " + ctx.getIns().getInstruction().getPc());
for (StackContext c : ctx.getIns().getPops())
printStack(c, level + 2);
}
public Object pop()
public void push(StackContext i)
{
if (size == stack.length)
{
info.sigterm.deob.Method m = i.getIns().getInstruction().getInstructions().getCode().getAttributes().getMethod();
System.err.println("in " + m.getMethods().getClassFile().getName() + " method " + m.getNameAndType().getName());
for (int c = 0; c < stack.length; ++c)
printStack(stack[c], 0);
throw new RuntimeException("Stack overflow");
}
assert !i.getType().type.equals("V");
System.out.println("PUSH context " + i.getType().type + " from + " + i.getIns().getInstruction());
stack[size] = i;
++size;
}
public StackContext pop()
{
if (size <= 0)
throw new RuntimeException("Stack underflow");
System.out.println("POP");
if (size == 1)
System.out.println("STACK SIZE IS NOW ZERO");
return stack[--size];
}

View File

@@ -0,0 +1,35 @@
package info.sigterm.deob.execution;
public class StackContext
{
private InstructionContext ic; // instruction which pushed this
private Type type; // type of this
public StackContext(InstructionContext i, Type t)
{
ic = i;
type = t;
}
public StackContext(InstructionContext i, Class<?> c)
{
ic = i;
type = new Type(c.getCanonicalName());
}
public StackContext(InstructionContext i, info.sigterm.deob.pool.Class c)
{
ic = i;
type = new Type(c.getName());
}
public InstructionContext getIns()
{
return ic;
}
public Type getType()
{
return type;
}
}

View File

@@ -1,41 +0,0 @@
package info.sigterm.deob.execution;
import info.sigterm.deob.Field;
import info.sigterm.deob.attributes.ConstantValue;
public class StaticFieldInstance
{
private ClassInstance clazz;
private Field field;
private Object value;
public StaticFieldInstance(ClassInstance clazz, Field field, ConstantValue value)
{
this.clazz = clazz;
this.field = field;
if (value != null)
this.value = value.getValue().getObject();
}
protected StaticFieldInstance(ClassInstance clazz, StaticFieldInstance other)
{
this.clazz = clazz;
this.field = other.field;
this.value = other.value;
}
public Field getField()
{
return field;
}
public Object getValue()
{
return value;
}
public void setField(Object obj)
{
value = obj;
}
}

View File

@@ -0,0 +1,73 @@
package info.sigterm.deob.execution;
public class Type
{
public String type;
public Type(String type)
{
if (type.startsWith("["))
throw new IllegalStateException();
this.type = type;
}
public Type(info.sigterm.deob.signature.Type t)
{
String before = t.getType();
type = asmTypeToClass(t.getType());
for (int i = 0; i < t.getArrayDims(); ++i)
type = type + "[]";
System.out.println(before + " -> " + type);
}
public Type toStackType()
{
if (type.equals(byte.class.getCanonicalName()) || type.equals(char.class.getCanonicalName()) || type.equals(short.class.getCanonicalName())
|| type.equals(boolean.class.getCanonicalName()))
return new Type(int.class.getCanonicalName());
return this;
}
private static String asmTypeToClass(String type)
{
switch (type.toString())
{
case "B":
return byte.class.getCanonicalName();
case "C":
return char.class.getCanonicalName();
case "I":
return int.class.getCanonicalName();
case "S":
return short.class.getCanonicalName();
case "Z":
return boolean.class.getCanonicalName();
case "D":
return double.class.getCanonicalName();
case "F":
return float.class.getCanonicalName();
case "J":
return long.class.getCanonicalName();
default:
return type.replace("/", ".");
}
}
@Override
public boolean equals(Object other)
{
if (!(other instanceof Type))
return false;
Type t = (Type) other;
return type.equals(t.type);
}
public Type getSubtype()
{
if (!type.endsWith("[]"))
throw new IllegalStateException(type + " is not an array type");
return new Type(type.substring(0, type.length() - 2));
}
}

View File

@@ -0,0 +1,18 @@
package info.sigterm.deob.execution;
public class VariableContext
{
private InstructionContext ic;
private Type type;
public VariableContext(InstructionContext i, Type t)
{
ic = i;
type = t;
}
public Type getType()
{
return type;
}
}

View File

@@ -1,28 +1,28 @@
package info.sigterm.deob.execution;
import java.util.Arrays;
public class Variables
{
private Object[] variables;
public Variables(int sz)
{
variables = new Object[sz];
}
protected Variables(Variables other)
{
this.variables = Arrays.copyOf(other.variables, other.variables.length);
}
public void set(int index, Object value)
{
variables[index] = value;
}
public Object get(int index)
{
return variables[index];
}
}
package info.sigterm.deob.execution;
import java.util.Arrays;
public class Variables
{
private VariableContext[] variables;
public Variables(int sz)
{
variables = new VariableContext[sz];
}
protected Variables(Variables other)
{
this.variables = Arrays.copyOf(other.variables, other.variables.length);
}
public void set(int index, VariableContext value)
{
variables[index] = value;
}
public VariableContext get(int index)
{
return variables[index];
}
}