'dex 038' support

This commit is contained in:
Bob Pan 2017-07-16 00:28:05 +08:00
parent dd9d7221e4
commit 6b08884da9
33 changed files with 1016 additions and 228 deletions

6
dex-ir/build.gradle Normal file
View File

@ -0,0 +1,6 @@
description = 'Dex Translator'
dependencies {
compile project(':dex-reader-api')
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2009-2017 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.dex2jar.ir.expr;
import com.googlecode.d2j.Proto;
import com.googlecode.dex2jar.ir.expr.Value.EnExpr;
public abstract class AbstractInvokeExpr extends EnExpr {
@Override
protected void releaseMemory() {
super.releaseMemory();
}
public abstract Proto getProto();
public AbstractInvokeExpr(VT type, Value[] args) {
super(type, args);
}
}

View File

@ -15,6 +15,9 @@
*/
package com.googlecode.dex2jar.ir.expr;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.MethodHandle;
import com.googlecode.d2j.Proto;
import com.googlecode.dex2jar.ir.expr.Value.VT;
public final class Exprs {
@ -188,6 +191,14 @@ public final class Exprs {
return new InvokeExpr(VT.INVOKE_VIRTUAL, regs, owner, name, argmentTypes, returnType);
}
public static InvokeCustomExpr nInvokeCustom(Value[] regs, String name, Proto proto, MethodHandle handle, Object[] bsmArgs) {
return new InvokeCustomExpr(VT.INVOKE_CUSTOM, regs , name, proto, handle, bsmArgs);
}
public static InvokePolymorphicExpr nInvokePolymorphic(Value[] regs, Proto proto, Method method) {
return new InvokePolymorphicExpr(VT.INVOKE_POLYMORPHIC, regs , proto, method);
}
public static BinopExpr nLCmp(Value a, Value b) {
return new BinopExpr(VT.LCMP, a, b, "J");
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2009-2017 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.dex2jar.ir.expr;
import com.googlecode.d2j.MethodHandle;
import com.googlecode.d2j.Proto;
import com.googlecode.dex2jar.ir.LabelAndLocalMapper;
public class InvokeCustomExpr extends AbstractInvokeExpr {
public String name;
public Proto proto;
public MethodHandle handle;
public Object[] bsmArgs;
@Override
protected void releaseMemory() {
name = null;
proto = null;
handle = null;
bsmArgs = null;
super.releaseMemory();
}
@Override
public Proto getProto() {
return proto;
}
public InvokeCustomExpr(VT type, Value[] args, String methodName, Proto proto, MethodHandle handle, Object[] bsmArgs) {
super(type, args);
this.proto = proto;
this.name = methodName;
this.handle = handle;
this.bsmArgs = bsmArgs;
}
@Override
public Value clone() {
return new InvokeCustomExpr(vt, cloneOps(), name, proto, handle, bsmArgs);
}
@Override
public Value clone(LabelAndLocalMapper mapper) {
return new InvokeCustomExpr(vt, cloneOps(mapper), name, proto, handle, bsmArgs);
}
@Override
public String toString0() {
StringBuilder sb = new StringBuilder();
sb.append("InvokeCustomExpr(....)");
return sb.toString();
}
}

View File

@ -15,6 +15,8 @@
*/
package com.googlecode.dex2jar.ir.expr;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.Proto;
import com.googlecode.dex2jar.ir.LabelAndLocalMapper;
import com.googlecode.dex2jar.ir.Util;
import com.googlecode.dex2jar.ir.expr.Value.EnExpr;
@ -22,60 +24,50 @@ import com.googlecode.dex2jar.ir.expr.Value.EnExpr;
/**
* Represent a method invocation expression. To represent a {@link VT#INVOKE_INTERFACE},{@link VT#INVOKE_SPECIAL} or
* {@link VT#INVOKE_VIRTUAL} the first element of ops is the owner object,To represent a {@link VT#INVOKE_NEW} or
* {@link VT#INVOKE_STATIC} all ops are arguments. The return type of {@link VT#INVOKE_NEW} is {@link #owner} instead of
* {@link #ret}
*
* {@link VT#INVOKE_STATIC} all ops are arguments. The return type of {@link VT#INVOKE_NEW} is owner instead of ret
*
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
* @version $Rev: 9fd8005bbaa4 $
* @see VT#INVOKE_INTERFACE
* @see VT#INVOKE_NEW
* @see VT#INVOKE_SPECIAL
* @see VT#INVOKE_STATIC
* @see VT#INVOKE_VIRTUAL
*
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
* @version $Rev: 9fd8005bbaa4 $
*/
public class InvokeExpr extends EnExpr {
public class InvokeExpr extends AbstractInvokeExpr {
/**
* argument type desc
*/
public String[] args;
public String name;
/**
* owner type desc
*/
public String owner;
/**
* owner type desc
*/
public String ret;
public Method method;
@Override
protected void releaseMemory() {
args = null;
ret = null;
owner = null;
name = null;
method = null;
super.releaseMemory();
}
@Override
public Proto getProto() {
return method.getProto();
}
public InvokeExpr(VT type, Value[] args, String ownerType, String methodName, String[] argmentTypes,
String returnType) {
String returnType) {
super(type, args);
this.ret = returnType;
this.name = methodName;
this.owner = ownerType;
this.args = argmentTypes;
this.method = new Method(ownerType, methodName, argmentTypes, returnType);
}
public InvokeExpr(VT type, Value[] args, Method method) {
super(type, args);
this.method = method;
}
@Override
public Value clone() {
return new InvokeExpr(vt, cloneOps(), owner, name, args, ret);
return new InvokeExpr(vt, cloneOps(), method);
}
@Override
public Value clone(LabelAndLocalMapper mapper) {
return new InvokeExpr(vt, cloneOps(mapper), owner, name, args, ret);
return new InvokeExpr(vt, cloneOps(mapper), method);
}
@Override
@ -83,10 +75,10 @@ public class InvokeExpr extends EnExpr {
StringBuilder sb = new StringBuilder();
if (super.vt == VT.INVOKE_NEW) {
sb.append("new ").append(Util.toShortClassName(owner)).append('(');
sb.append("new ").append(Util.toShortClassName(method.getOwner())).append('(');
} else {
sb.append(super.vt == VT.INVOKE_STATIC ? Util.toShortClassName(owner) : ops[0]).append('.')
.append(this.name).append('(');
sb.append(super.vt == VT.INVOKE_STATIC ? Util.toShortClassName(method.getOwner()) : ops[0]).append('.')
.append(this.method.getName()).append('(');
}
boolean first = true;
for (int i = (vt == VT.INVOKE_STATIC || vt == VT.INVOKE_NEW) ? 0 : 1; i < ops.length; i++) {
@ -100,4 +92,21 @@ public class InvokeExpr extends EnExpr {
sb.append(')');
return sb.toString();
}
public String getOwner() {
return method.getOwner();
}
public String getRet() {
return method.getReturnType();
}
public String getName() {
return method.getName();
}
public String[] getArgs() {
return method.getParameterTypes();
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2009-2017 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.dex2jar.ir.expr;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.Proto;
import com.googlecode.dex2jar.ir.LabelAndLocalMapper;
public class InvokePolymorphicExpr extends AbstractInvokeExpr {
public Proto proto;
public Method method;
@Override
protected void releaseMemory() {
method = null;
proto = null;
super.releaseMemory();
}
@Override
public Proto getProto() {
return proto;
}
public InvokePolymorphicExpr(VT type, Value[] args, Proto proto, Method method) {
super(type, args);
this.proto = proto;
this.method = method;
}
@Override
public Value clone() {
return new InvokePolymorphicExpr(vt, cloneOps(), proto, method);
}
@Override
public Value clone(LabelAndLocalMapper mapper) {
return new InvokePolymorphicExpr(vt, cloneOps(mapper), proto, method);
}
@Override
public String toString0() {
StringBuilder sb = new StringBuilder();
sb.append("InvokePolymorphicExpr(...)");
return sb.toString();
}
}

View File

@ -179,7 +179,7 @@ public abstract class Value implements Cloneable {
MAY_THROW), DCMPL(MAY_THROW), IDIV("/", CAN_THROW), LDIV("/", CAN_THROW), FDIV("/", MAY_THROW), DDIV("/", MAY_THROW), EQ("==", MAY_THROW), EXCEPTION_REF(0), FCMPG(
MAY_THROW), FCMPL(MAY_THROW), FIELD(CAN_THROW), FILLED_ARRAY(CAN_THROW), GE(">=", MAY_THROW), GT(">",
MAY_THROW), INSTANCE_OF(CAN_THROW), INVOKE_INTERFACE(CAN_THROW), //
INVOKE_NEW(CAN_THROW), INVOKE_SPECIAL(CAN_THROW), INVOKE_STATIC(CAN_THROW), INVOKE_VIRTUAL(CAN_THROW), LCMP(
INVOKE_NEW(CAN_THROW), INVOKE_SPECIAL(CAN_THROW), INVOKE_STATIC(CAN_THROW), INVOKE_VIRTUAL(CAN_THROW), INVOKE_CUSTOM(CAN_THROW), INVOKE_POLYMORPHIC(CAN_THROW), LCMP(
MAY_THROW), //
LE("<=", MAY_THROW), LENGTH(CAN_THROW), LOCAL(0), LT("<", MAY_THROW), MUL("*", MAY_THROW), NE("!=", MAY_THROW), NEG(
MAY_THROW), //

View File

@ -139,7 +139,7 @@ public class AggTransformer extends StatedTransformer {
boolean shouldExclude = false;
if (op.vt == Value.VT.INVOKE_STATIC) {
InvokeExpr ie = (InvokeExpr) op;
if (ie.name.equals("valueOf") && ie.owner.startsWith("Ljava/lang/") && ie.args.length == 1 && ie.args[0].length() == 1) {
if (ie.getName().equals("valueOf") && ie.getOwner().startsWith("Ljava/lang/") && ie.getArgs().length == 1 && ie.getArgs()[0].length() == 1) {
shouldExclude = true;
}
}
@ -318,7 +318,7 @@ public class AggTransformer extends StatedTransformer {
case En:
if (op.vt == Value.VT.INVOKE_STATIC) {
InvokeExpr ie = (InvokeExpr) op;
if (ie.name.equals("valueOf") && ie.owner.startsWith("Ljava/lang/") && ie.args.length == 1 && ie.args[0].length() == 1) {
if (ie.getName().equals("valueOf") && ie.getOwner().startsWith("Ljava/lang/") && ie.getArgs().length == 1 && ie.getArgs()[0].length() == 1) {
for (Value v : op.getOps()) {
if (!isLocationInsensitive(v)) {
return false;

View File

@ -41,10 +41,10 @@ public class MultiArrayTransformer extends StatedTransformer {
Value from = te.getOp();
if (from.vt == Value.VT.INVOKE_STATIC) {
InvokeExpr invokeExpr = (InvokeExpr) from;
if (invokeExpr.name.equals("newInstance")
&& invokeExpr.owner.equals("Ljava/lang/reflect/Array;")
&& invokeExpr.args.length == 2
&& invokeExpr.args[0].equals("Ljava/lang/Class;")) {
if (invokeExpr.getName().equals("newInstance")
&& invokeExpr.getOwner().equals("Ljava/lang/reflect/Array;")
&& invokeExpr.getArgs().length == 2
&& invokeExpr.getArgs()[0].equals("Ljava/lang/Class;")) {
Value arg0 = invokeExpr.getOps()[0];
String elementType = null;
if (arg0.vt == Value.VT.CONSTANT) {
@ -88,7 +88,7 @@ public class MultiArrayTransformer extends StatedTransformer {
}
if (elementType != null) {
Value dt = invokeExpr.getOps()[1];
if (invokeExpr.args[1].equals("I")) {
if (invokeExpr.getArgs()[1].equals("I")) {
if (te.type.equals("[" + elementType)) {
int d = 0;
while (elementType.charAt(d) == '[') {

View File

@ -98,14 +98,14 @@ public class NewTransformer implements Transformer {
InvokeExpr ie = findInvokeExpr(p, null);
if (ie != null) {
if ("<init>".equals(ie.name) && "V".equals(ie.ret)) {
if ("<init>".equals(ie.getName()) && "V".equals(ie.getRet())) {
Value[] orgOps = ie.getOps();
if (orgOps[0].vt == NEW) {
NewExpr newExpr = (NewExpr) ie.getOps()[0];
if (newExpr != null) {
Value[] nOps = new Value[orgOps.length - 1];
System.arraycopy(orgOps, 1, nOps, 0, nOps.length);
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.args, ie.owner);
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.getArgs(), ie.getOwner());
method.stmts.insertBefore(p, Stmts.nVoidInvoke(invokeNew));
it.remove();
}
@ -158,7 +158,7 @@ public class NewTransformer implements Transformer {
Value[] orgOps = ie.getOps();
Value[] nOps = new Value[orgOps.length - 1];
System.arraycopy(orgOps, 1, nOps, 0, nOps.length);
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.args, ie.owner);
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.getArgs(), ie.getOwner());
method.stmts.replace(obj.invokeStmt, Stmts.nAssign(obj.local, invokeNew));
}
}
@ -211,7 +211,7 @@ public class NewTransformer implements Transformer {
if (op.vt == INVOKE_SPECIAL) {
if (op.getOps().length >= 1) {
InvokeExpr ie = (InvokeExpr) op;
if ("<init>".equals(ie.name)) {
if ("<init>".equals(ie.getName())) {
Value thiz = op.getOps()[0];
if (thiz.vt == LOCAL) {
Local local = (Local) thiz;

View File

@ -917,20 +917,29 @@ public class TypeTransformer implements Transformer {
case INVOKE_SPECIAL:
case INVOKE_STATIC:
case INVOKE_VIRTUAL:
InvokeExpr ie = (InvokeExpr) enExpr;
String type = ie.vt == VT.INVOKE_NEW ? ie.owner : ie.ret;
case INVOKE_POLYMORPHIC:
case INVOKE_CUSTOM: {
AbstractInvokeExpr ice = (AbstractInvokeExpr) enExpr;
String type = ice.getProto().getReturnType();
provideAs(enExpr, type);
useAs(enExpr, type); // no one else will use it
int start = 0;
if (ie.vt != VT.INVOKE_STATIC && ie.vt != VT.INVOKE_NEW) {
start = 1;
useAs(vbs[0], ie.owner);
String argTypes[] = ice.getProto().getParameterTypes();
if (argTypes.length == vbs.length) {
for (int i = 0; i < vbs.length; i++) {
useAs(vbs[i], argTypes[i]);
}
} else if (argTypes.length + 1 == vbs.length) {
useAs(vbs[0], "L");
for (int i = 1; i < vbs.length; i++) {
useAs(vbs[i], argTypes[i - 1]);
}
} else {
throw new RuntimeException();
}
for (int i = 0; start < vbs.length; start++, i++) {
useAs(vbs[start], ie.args[i]);
}
break;
}
break;
case FILLED_ARRAY:
FilledArrayExpr fae = (FilledArrayExpr) enExpr;
for (Value vb : vbs) {

View File

@ -24,10 +24,6 @@ import java.util.Arrays;
* @version $Rev$
*/
public class Method {
/**
* descriptor of the method, this will build after {@link #getDesc()}.
*/
private String desc;
/**
* name of the method.
*/
@ -39,32 +35,24 @@ public class Method {
/**
* parameter types of the method, in TypeDescriptor format.
*/
private String[] parameterTypes;
private Proto proto;
/**
* return type of the method, in TypeDescriptor format.
*/
private String returnType;
public Proto getProto() {
return proto;
}
public Method(String owner, String name, String[] parameterTypes, String returnType) {
this.owner = owner;
this.name = name;
this.parameterTypes = parameterTypes;
this.returnType = returnType;
this.proto = new Proto(parameterTypes, returnType);
}
public Method(String owner, String name, Proto proto) {
this.owner = owner;
this.name = name;
this.proto = proto;
}
public String getDesc() {
if (desc == null) {
StringBuilder ps = new StringBuilder("(");
if (parameterTypes != null) {
for (String t : parameterTypes) {
ps.append(t);
}
}
ps.append(")").append(returnType);
desc = ps.toString();
}
return desc;
return proto.getDesc();
}
/**
@ -85,68 +73,38 @@ public class Method {
* @return the parameterTypes
*/
public String[] getParameterTypes() {
return parameterTypes;
return proto.getParameterTypes();
}
public String getReturnType() {
return returnType;
return proto.getReturnType();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Method method = (Method) o;
if (name != null ? !name.equals(method.name) : method.name != null) return false;
if (owner != null ? !owner.equals(method.owner) : method.owner != null) return false;
return proto.equals(method.proto);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((owner == null) ? 0 : owner.hashCode());
result = prime * result + Arrays.hashCode(parameterTypes);
result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (owner != null ? owner.hashCode() : 0);
result = 31 * result + proto.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Method other = (Method) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (owner == null) {
if (other.owner != null) {
return false;
}
} else if (!owner.equals(other.owner)) {
return false;
}
if (!Arrays.equals(parameterTypes, other.parameterTypes)) {
return false;
}
if (returnType == null) {
if (other.returnType != null) {
return false;
}
} else if (!returnType.equals(other.returnType)) {
return false;
}
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return this.getOwner() + "." + this.getName() + this.getDesc();

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2009-2017 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.d2j;
public class MethodHandle {
public static final int STATIC_PUT = 0x00;
public static final int STATIC_GET = 0x01;
public static final int INSTANCE_PUT = 0x02;
public static final int INSTANCE_GET = 0x03;
public static final int INVOKE_STATIC = 0x04;
public static final int INVOKE_INSTANCE = 0x05;
private int type;
private Field field;
private Method method;
public MethodHandle(int type, Field field) {
this.type = type;
this.field = field;
}
public MethodHandle(int type, Method method) {
this.type = type;
this.method = method;
}
public MethodHandle(int type, Field field, Method method) {
this.type = type;
this.field = field;
this.method = method;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodHandle that = (MethodHandle) o;
if (type != that.type) return false;
if (field != null ? !field.equals(that.field) : that.field != null) return false;
return method != null ? method.equals(that.method) : that.method == null;
}
@Override
public int hashCode() {
int result = type;
result = 31 * result + (field != null ? field.hashCode() : 0);
result = 31 * result + (method != null ? method.hashCode() : 0);
return result;
}
public int getType() {
return type;
}
public Field getField() {
return field;
}
public Method getMethod() {
return method;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2009-2017 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.d2j;
import java.util.Arrays;
public class Proto {
public Proto(String[] parameterTypes, String returnType) {
this.parameterTypes = parameterTypes;
this.returnType = returnType;
}
/**
* descriptor of the method, this will build after {@link #getDesc()}.
*/
private String desc;
/**
* parameter types of the method, in TypeDescriptor format.
*/
private String[] parameterTypes;
/**
* return type of the method, in TypeDescriptor format.
*/
private String returnType;
/**
* @return the parameterTypes
*/
public String[] getParameterTypes() {
return parameterTypes;
}
public String getReturnType() {
return returnType;
}
public String getDesc() {
if (desc == null) {
StringBuilder ps = new StringBuilder("(");
if (parameterTypes != null) {
for (String t : parameterTypes) {
ps.append(t);
}
}
ps.append(")").append(returnType);
desc = ps.toString();
}
return desc;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Proto proto = (Proto) o;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
if (!Arrays.equals(parameterTypes, proto.parameterTypes)) return false;
return returnType != null ? returnType.equals(proto.returnType) : proto.returnType == null;
}
@Override
public int hashCode() {
int result = Arrays.hashCode(parameterTypes);
result = 31 * result + (returnType != null ? returnType.hashCode() : 0);
return result;
}
}

View File

@ -19,9 +19,7 @@ package com.googlecode.d2j.node;
import java.util.ArrayList;
import java.util.List;
import com.googlecode.d2j.DexLabel;
import com.googlecode.d2j.Field;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.*;
import com.googlecode.d2j.node.insn.*;
import com.googlecode.d2j.reader.Op;
import com.googlecode.d2j.visitors.DexCodeVisitor;
@ -111,6 +109,16 @@ public class DexCodeNode extends DexCodeVisitor {
add(new MethodStmtNode(op, args, method));
}
@Override
public void visitMethodStmt(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object... bsmArgs) {
add(new MethodCustomStmtNode(op, args, name, proto, bsm, bsmArgs));
}
@Override
public void visitMethodStmt(Op op, int[] args, Method bsm, Proto proto) {
add(new MethodPolymorphicStmtNode(op, args, bsm, proto));
}
@Override
public void visitPackedSwitchStmt(final Op op, final int aA, final int first_case, final DexLabel[] labels) {
add(new PackedSwitchStmtNode(op, aA, first_case, labels));

View File

@ -1,5 +1,7 @@
package com.googlecode.d2j.node.analysis;
import com.googlecode.d2j.MethodHandle;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.node.insn.*;
import com.googlecode.d2j.reader.Op;
@ -259,18 +261,29 @@ public class DvmFrame<V> {
case INVOKE_STATIC_RANGE:
case INVOKE_STATIC:
case INVOKE_INTERFACE_RANGE:
case INVOKE_INTERFACE: {
case INVOKE_INTERFACE:
case INVOKE_CUSTOM:
case INVOKE_CUSTOM_RANGE:
case INVOKE_POLYMORPHIC:
case INVOKE_POLYMORPHIC_RANGE: {
int i = 0;
MethodStmtNode methodStmtNode = (MethodStmtNode) insn;
AbstractMethodStmtNode methodStmtNode = (AbstractMethodStmtNode) insn;
List<V> v;
Proto proto = methodStmtNode.getProto();
boolean isStatic = false;
if (insn.op == Op.INVOKE_STATIC || insn.op == Op.INVOKE_STATIC_RANGE) {
v = new ArrayList<>(methodStmtNode.method.getParameterTypes().length);
isStatic = true;
} else if (insn.op == Op.INVOKE_CUSTOM || insn.op == Op.INVOKE_CUSTOM_RANGE) {
isStatic = true;
}
if (isStatic) {
v = new ArrayList<>(proto.getParameterTypes().length);
} else {
v = new ArrayList<>(methodStmtNode.method.getParameterTypes().length + 1);
v = new ArrayList<>(proto.getParameterTypes().length + 1);
v.add(getReg(methodStmtNode.args[i++]));
}
for (String type : methodStmtNode.method.getParameterTypes()) {
for (String type : proto.getParameterTypes()) {
v.add(getReg(methodStmtNode.args[i]));
char t = type.charAt(0);
if (t == 'J' || t == 'D') {

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2009-2017 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.d2j.node.insn;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.reader.Op;
public abstract class AbstractMethodStmtNode extends DexStmtNode {
public final int[] args;
public AbstractMethodStmtNode(Op op, int[] args) {
super(op);
this.args = args;
}
public abstract Proto getProto();
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2009-2017 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.d2j.node.insn;
import com.googlecode.d2j.MethodHandle;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.reader.Op;
import com.googlecode.d2j.visitors.DexCodeVisitor;
public class MethodCustomStmtNode extends AbstractMethodStmtNode {
public final String name;
public final Proto proto;
public final MethodHandle bsm;
public final Object[] bsmArgs;
public MethodCustomStmtNode(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object[] bsmArgs) {
super(op, args);
this.proto = proto;
this.name = name;
this.bsm = bsm;
this.bsmArgs = bsmArgs;
}
@Override
public void accept(DexCodeVisitor cv) {
cv.visitMethodStmt(op, args, name, proto, bsm, bsmArgs);
}
@Override
public Proto getProto() {
return proto;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2009-2017 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.d2j.node.insn;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.reader.Op;
import com.googlecode.d2j.visitors.DexCodeVisitor;
public class MethodPolymorphicStmtNode extends AbstractMethodStmtNode {
public final Method method;
public final Proto proto;
public MethodPolymorphicStmtNode(Op op, int[] args, Method method, Proto proto) {
super(op, args);
this.method = method;
this.proto = proto;
}
@Override
public void accept(DexCodeVisitor cv) {
cv.visitMethodStmt(op, args, method, proto);
}
@Override
public Proto getProto() {
return proto;
}
}

View File

@ -1,16 +1,30 @@
/*
* Copyright (c) 2009-2017 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.d2j.node.insn;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.reader.Op;
import com.googlecode.d2j.visitors.DexCodeVisitor;
public class MethodStmtNode extends DexStmtNode {
public final int[] args;
public class MethodStmtNode extends AbstractMethodStmtNode {
public final Method method;
public MethodStmtNode(Op op, int[] args, Method method) {
super(op);
this.args = args;
super(op, args);
this.method = method;
}
@ -18,4 +32,9 @@ public class MethodStmtNode extends DexStmtNode {
public void accept(DexCodeVisitor cv) {
cv.visitMethodStmt(op, args, method);
}
@Override
public Proto getProto() {
return method.getProto();
}
}

View File

@ -29,6 +29,8 @@ public enum InstructionFormat {
// kFmt35ms(3), // [opt] invoke-virtual+super
kFmt3rc(3), // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
// kFmt3rms(3), // [opt] invoke-virtual+super/range
kFmt45cc(4), // op {vC,vD,vE,vF,vG}, meth@BBBB, proto@HHHH
kFmt4rcc(4), // op {vCCCC .. vNNNNN}, meth@BBBB, proto@HHHH
kFmt51l(5), // op vAA, #+BBBBBBBBBBBBBBBB
// kFmt35mi(3), // [opt] inline invoke
// kFmt3rmi(3), // [opt] inline invoke/range

View File

@ -10,5 +10,7 @@ package com.googlecode.d2j.reader;
kIndexFieldRef, // field reference index
kIndexInlineMethod, // inline method index (for inline linked methods)
kIndexVtableOffset, // vtable offset (for static linked methods)
kIndexFieldOffset // field offset (for static linked fields)
kIndexFieldOffset, // field offset (for static linked fields)
kIndexMethodAndProtoRef, // 038,
kIndexCallSiteRef, // 038,
};

View File

@ -117,6 +117,10 @@ public enum Op implements CFG {
SPUT_SHORT(0x6d, "sput-short", kFmt21c, kIndexFieldRef, kInstrCanContinue | kInstrCanThrow, false), //
INVOKE_VIRTUAL(0x6e, "invoke-virtual", kFmt35c, kIndexMethodRef, kInstrCanContinue | kInstrCanThrow | kInstrInvoke,
true), //
/**
* Behavior changed in 037, interface method is allowed
*/
INVOKE_SUPER(0x6f, "invoke-super", kFmt35c, kIndexMethodRef, kInstrCanContinue | kInstrCanThrow | kInstrInvoke,
true), //
INVOKE_DIRECT(0x70, "invoke-direct", kFmt35c, kIndexMethodRef, kInstrCanContinue | kInstrCanThrow | kInstrInvoke,
@ -242,6 +246,14 @@ public enum Op implements CFG {
SHL_INT_LIT8(0xe0, "shl-int/lit8", kFmt22b, kIndexNone, kInstrCanContinue, true), //
SHR_INT_LIT8(0xe1, "shr-int/lit8", kFmt22b, kIndexNone, kInstrCanContinue, true), //
USHR_INT_LIT8(0xe2, "ushr-int/lit8", kFmt22b, kIndexNone, kInstrCanContinue, true), //
INVOKE_POLYMORPHIC(0xfa, "invoke-polymorphic", kFmt45cc, kIndexMethodAndProtoRef, kInstrCanContinue | kInstrCanThrow
| kInstrInvoke, true), //
INVOKE_POLYMORPHIC_RANGE(0xfb, "invoke-polymorphic/range", kFmt4rcc, kIndexMethodAndProtoRef, kInstrCanContinue | kInstrCanThrow
| kInstrInvoke, true), //
INVOKE_CUSTOM(0xfc, "invoke-custom", kFmt35c, kIndexCallSiteRef, kInstrCanContinue | kInstrCanThrow
| kInstrInvoke, true), //
INVOKE_CUSTOM_RANGE(0xfd, "invoke-custom/range", kFmt3rc, kIndexCallSiteRef, kInstrCanContinue | kInstrCanThrow
| kInstrInvoke, true), //
BAD_OP(-1, "bad-opcode", null, kIndexNone, 0, false), //
;
public int opcode;

View File

@ -15,9 +15,7 @@
*/
package com.googlecode.d2j.visitors;
import com.googlecode.d2j.DexLabel;
import com.googlecode.d2j.Field;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.*;
import com.googlecode.d2j.reader.Op;
/**
@ -226,6 +224,29 @@ public class DexCodeVisitor {
}
}
/**
* <pre>
* OP_INVOKE_CUSTOM
* </pre>
*/
public void visitMethodStmt(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object... bsmArgs) {
if (visitor != null) {
visitor.visitMethodStmt(op, args, name, proto, bsm, bsmArgs);
}
}
/**
* <pre>
* OP_INVOKE_POLYMORPHIC
* </pre>
*
*/
public void visitMethodStmt(Op op, int[] args, Method bsm, Proto proto) {
if (visitor != null) {
visitor.visitMethodStmt(op, args, bsm, proto);
}
}
/**
* <pre>
* OP_MOVE*

View File

@ -5,6 +5,12 @@ import com.googlecode.d2j.visitors.DexFileVisitor;
import java.util.List;
public interface BaseDexFileReader {
int DEX_035 = 0x00303335;
int DEX_036 = 0x00303336;
int DEX_037 = 0x00303337;
int DEX_038 = 0x00303338;
int getDexVersion();
void accept(DexFileVisitor dv);

View File

@ -84,27 +84,30 @@ public class DexFileReader implements BaseDexFileReader {
static final int DBG_FIRST_SPECIAL = 0x0a;
static final int DBG_LINE_BASE = -4;
static final int DBG_LINE_RANGE = 15;
private static final int MAGIC_DEX = 0x0A786564 & 0x00FFFFFF;// hex for 'dex ', ignore the 0A
private static final int MAGIC_ODEX = 0x0A796564 & 0x00FFFFFF;// hex for 'dey ', ignore the 0A
private static final int MAGIC_035 = 0x00353330;
private static final int MAGIC_036 = 0x00363330;
private static final int ENDIAN_CONSTANT = 0x12345678;
private static final int VALUE_BYTE = 0;
private static final int VALUE_SHORT = 2;
private static final int VALUE_CHAR = 3;
private static final int VALUE_INT = 4;
private static final int VALUE_LONG = 6;
private static final int VALUE_FLOAT = 16;
private static final int VALUE_DOUBLE = 17;
private static final int VALUE_STRING = 23;
private static final int VALUE_TYPE = 24;
private static final int VALUE_FIELD = 25;
private static final int VALUE_METHOD = 26;
private static final int VALUE_ENUM = 27;
private static final int VALUE_ARRAY = 28;
private static final int VALUE_ANNOTATION = 29;
private static final int VALUE_NULL = 30;
private static final int VALUE_BOOLEAN = 31;
private static final int VALUE_BYTE = 0x00;
private static final int VALUE_SHORT = 0x02;
private static final int VALUE_CHAR = 0x03;
private static final int VALUE_INT = 0x04;
private static final int VALUE_LONG = 0x06;
private static final int VALUE_FLOAT = 0x10;
private static final int VALUE_DOUBLE = 0x11;
private static final int VALUE_METHOD_TYPE = 0x15;
private static final int VALUE_METHOD_HANDLE = 0x16;
private static final int VALUE_STRING = 0x17;
private static final int VALUE_TYPE = 0x18;
private static final int VALUE_FIELD = 0x19;
private static final int VALUE_METHOD = 0x1a;
private static final int VALUE_ENUM = 0x1b;
private static final int VALUE_ARRAY = 0x1c;
private static final int VALUE_ANNOTATION = 0x1d;
private static final int VALUE_NULL = 0x1e;
private static final int VALUE_BOOLEAN = 0x1f;
private static final int TYPE_CALL_SITE_ID_ITEM = 0x0007;
private static final int TYPE_METHOD_HANDLE_ITEM = 0x0008;
final ByteBuffer annotationSetRefListIn;
final ByteBuffer annotationsDirectoryItemIn;
final ByteBuffer annotationSetItemIn;
@ -121,11 +124,17 @@ public class DexFileReader implements BaseDexFileReader {
final ByteBuffer typeListIn;
final ByteBuffer stringDataIn;
final ByteBuffer debugInfoIn;
final ByteBuffer callSiteIdIn;
final ByteBuffer methodHandleIdIn;
final int string_ids_size;
final int type_ids_size;
final int proto_ids_size;
final int field_ids_size;
final int method_ids_size;
final private int class_defs_size;
final int call_site_ids_size;
final int method_handle_ids_size;
final int dex_version;
/**
* read dex from a {@link ByteBuffer}.
@ -134,8 +143,12 @@ public class DexFileReader implements BaseDexFileReader {
*/
public DexFileReader(ByteBuffer in) {
in.position(0);
in = in.asReadOnlyBuffer().order(ByteOrder.LITTLE_ENDIAN);
int magic = in.getInt() & 0x00FFFFFF;
in = in.asReadOnlyBuffer().order(ByteOrder.BIG_ENDIAN);
int magic = in.getInt() & 0xFFFFFF00;
final int MAGIC_DEX = 0x6465780A & 0xFFFFFF00;// hex for 'dex ', ignore the 0A
final int MAGIC_ODEX = 0x6465790A & 0xFFFFFF00;// hex for 'dey ', ignore the 0A
if (magic == MAGIC_DEX) {
;
} else if (magic == MAGIC_ODEX) {
@ -143,10 +156,12 @@ public class DexFileReader implements BaseDexFileReader {
} else {
throw new DexException("not support magic.");
}
int version = in.getInt() & 0x00FFFFFF;
if (version != MAGIC_035 && version != MAGIC_036) {
int version = in.getInt() >> 8;
if (version < 0 || version < DEX_035) {
throw new DexException("not support version.");
}
this.dex_version = version;
in.order(ByteOrder.LITTLE_ENDIAN);
// skip uint checksum
// and 20 bytes signature
@ -161,14 +176,15 @@ public class DexFileReader implements BaseDexFileReader {
// skip uint link_size
// and uint link_off
// and uint map_off
skip(in, 4 + 4 + 4);
skip(in, 4 + 4);
int map_off = in.getInt();
string_ids_size = in.getInt();
int string_ids_off = in.getInt();
type_ids_size = in.getInt();
int type_ids_off = in.getInt();
int proto_ids_size = in.getInt();
proto_ids_size = in.getInt();
int proto_ids_off = in.getInt();
field_ids_size = in.getInt();
int field_ids_off = in.getInt();
@ -178,12 +194,43 @@ public class DexFileReader implements BaseDexFileReader {
int class_defs_off = in.getInt();
// skip uint data_size data_off
int call_site_ids_off = 0;
int call_site_ids_size = 0;
int method_handle_ids_off = 0;
int method_handle_ids_size = 0;
if (dex_version > DEX_037) {
in.position(map_off);
int size = in.getInt();
for (int i = 0; i < size; i++) {
int type = in.getShort() & 0xFFFF;
in.getShort(); // unused;
int item_size = in.getInt();
int item_offset = in.getInt();
switch (type) {
case TYPE_CALL_SITE_ID_ITEM:
call_site_ids_off = item_offset;
call_site_ids_size = item_size;
break;
case TYPE_METHOD_HANDLE_ITEM:
method_handle_ids_off = item_offset;
method_handle_ids_size = item_size;
break;
default:
break;
}
}
}
this.call_site_ids_size = call_site_ids_size;
this.method_handle_ids_size = method_handle_ids_size;
stringIdIn = slice(in, string_ids_off, string_ids_size * 4);
typeIdIn = slice(in, type_ids_off, type_ids_size * 4);
protoIdIn = slice(in, proto_ids_off, proto_ids_size * 12);
fieldIdIn = slice(in, field_ids_off, field_ids_size * 8);
methoIdIn = slice(in, method_ids_off, method_ids_size * 8);
classDefIn = slice(in, class_defs_off, class_defs_size * 32);
callSiteIdIn = slice(in, call_site_ids_off, call_site_ids_size * 4);
methodHandleIdIn = slice(in, method_handle_ids_off, method_handle_ids_size * 8);
in.position(0);
annotationsDirectoryItemIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
@ -528,6 +575,11 @@ public class DexFileReader implements BaseDexFileReader {
}
}
@Override
public int getDexVersion() {
return dex_version;
}
/**
* equals to {@link #accept(DexFileVisitor, int)} with 0 as config
*
@ -626,47 +678,43 @@ public class DexFileReader implements BaseDexFileReader {
case VALUE_SHORT:
return new Short((short) readIntBits(in, b));
case VALUE_CHAR:
return new Character((char) readUIntBits(in, b));
case VALUE_INT:
return new Integer((int) readIntBits(in, b));
case VALUE_LONG:
return new Long(readIntBits(in, b));
case VALUE_CHAR:
return new Character((char) readUIntBits(in, b));
case VALUE_STRING:
return getString((int) readUIntBits(in, b));
case VALUE_FLOAT:
return Float.intBitsToFloat((int) (readFloatBits(in, b) >> 32));
case VALUE_DOUBLE:
return Double.longBitsToDouble(readFloatBits(in, b));
case VALUE_METHOD_TYPE:
return getProto((int) readUIntBits(in, b));
case VALUE_METHOD_HANDLE:
return getMethodHandle((int) readUIntBits(in, b));
case VALUE_NULL:
return null;
case VALUE_STRING:
return getString((int) readUIntBits(in, b));
case VALUE_BOOLEAN: {
return new Boolean(((b >> 5) & 0x3) != 0);
}
case VALUE_TYPE: {
int type_id = (int) readUIntBits(in, b);
return new DexType(getType(type_id));
}
case VALUE_ENUM: {
return getField((int) readUIntBits(in, b));
case VALUE_FIELD: {
int field_id = (int) readUIntBits(in, b);
return getField(field_id);
}
case VALUE_METHOD: {
int method_id = (int) readUIntBits(in, b);
return getMethod(method_id);
}
case VALUE_FIELD: {
int field_id = (int) readUIntBits(in, b);
return getField(field_id);
case VALUE_ENUM: {
return getField((int) readUIntBits(in, b));
}
case VALUE_ARRAY: {
return read_encoded_array(in);
@ -674,11 +722,37 @@ public class DexFileReader implements BaseDexFileReader {
case VALUE_ANNOTATION: {
return read_encoded_annotation(in);
}
case VALUE_NULL:
return null;
case VALUE_BOOLEAN: {
return new Boolean(((b >> 5) & 0x3) != 0);
}
default:
throw new DexException("Not support yet.");
}
}
private MethodHandle getMethodHandle(int i) {
methodHandleIdIn.position(i * 8);
int method_handle_type = methodHandleIdIn.getShort() & 0xFFFF;
methodHandleIdIn.getShort();//unused
int field_or_method_id = methodHandleIdIn.getShort() & 0xFFFF;
switch (method_handle_type) {
case MethodHandle.INSTANCE_GET:
case MethodHandle.INSTANCE_PUT:
case MethodHandle.STATIC_GET:
case MethodHandle.STATIC_PUT:
return new MethodHandle(method_handle_type, getField(field_or_method_id));
case MethodHandle.INVOKE_INSTANCE:
case MethodHandle.INVOKE_STATIC:
return new MethodHandle(method_handle_type, getMethod(field_or_method_id));
default:
throw new RuntimeException();
}
}
private void acceptClass(DexClassVisitor dcv, int source_file_idx, int annotations_off, int class_data_off,
int static_values_off, int config) {
if ((config & SKIP_DEBUG) == 0) {
@ -851,11 +925,7 @@ public class DexFileReader implements BaseDexFileReader {
return types;
}
private Method getMethod(int id) {
methoIdIn.position(id * 8);
int owner_idx = 0xFFFF & methoIdIn.getShort();
int proto_idx = 0xFFFF & methoIdIn.getShort();
int name_idx = methoIdIn.getInt();
private Proto getProto(int proto_idx) {
String[] parameterTypes;
String returnType;
@ -867,9 +937,15 @@ public class DexFileReader implements BaseDexFileReader {
returnType = getType(return_type_idx);
parameterTypes = getTypeList(parameters_off);
return new Proto(parameterTypes, returnType);
}
return new Method(getType(owner_idx), getString(name_idx), parameterTypes, returnType);
private Method getMethod(int id) {
methoIdIn.position(id * 8);
int owner_idx = 0xFFFF & methoIdIn.getShort();
int proto_idx = 0xFFFF & methoIdIn.getShort();
int name_idx = methoIdIn.getInt();
return new Method(getType(owner_idx), getString(name_idx), getProto(proto_idx));
}
private String getString(int id) {
@ -1197,6 +1273,15 @@ public class DexFileReader implements BaseDexFileReader {
idx = ushort(insns, u1offset + 2);
canContinue = idx < field_ids_size;
break;
case kIndexCallSiteRef:
idx = ushort(insns, u1offset + 2);
canContinue = idx < call_site_ids_size;
break;
case kIndexMethodAndProtoRef:
idx = ushort(insns, u1offset + 2);
int idx2 = ushort(insns, u1offset + 6);
canContinue = idx < method_ids_size && idx2 < proto_ids_size;
break;
default:
}
if (!canContinue) {
@ -1537,6 +1622,10 @@ public class DexFileReader implements BaseDexFileReader {
}
if (op.indexType == InstructionIndexType.kIndexTypeRef) {
dcv.visitFilledNewArrayStmt(op, regs, getType(b));
} else if (op.indexType == InstructionIndexType.kIndexCallSiteRef) {
Object[] callsite = getCallSite(b);
Object[] constArgs = Arrays.copyOfRange(callsite, 3, callsite.length);
dcv.visitMethodStmt(op, regs, (String) callsite[1], (Proto) callsite[2], (MethodHandle) callsite[0], constArgs);
} else {
dcv.visitMethodStmt(op, regs, getMethod(b));
}
@ -1552,11 +1641,50 @@ public class DexFileReader implements BaseDexFileReader {
}
if (op.indexType == InstructionIndexType.kIndexTypeRef) {
dcv.visitFilledNewArrayStmt(op, regs, getType(b));
} else if (op.indexType == InstructionIndexType.kIndexCallSiteRef) {
Object[] callsite = getCallSite(b);
Object[] constArgs = Arrays.copyOfRange(callsite, 3, callsite.length - 3);
dcv.visitMethodStmt(op, regs, (String) callsite[1], (Proto) callsite[2], (MethodHandle) callsite[0], constArgs);
} else {
dcv.visitMethodStmt(op, regs, getMethod(b));
}
}
break;
case kFmt45cc: {
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
int dc = ubyte(insns, u1offset + 4); // DC
int fe = ubyte(insns, u1offset + 5); // FE
int h = ushort(insns, u1offset + 6);
int regs[] = new int[a >> 4];
switch (a >> 4) {
case 5:
regs[4] = a & 0xF;// G
case 4:
regs[3] = 0xF & (fe >> 4);// F
case 3:
regs[2] = 0xF & (fe >> 0);// E
case 2:
regs[1] = 0xF & (dc >> 4);// D
case 1:
regs[0] = 0xF & (dc >> 0);// C
}
dcv.visitMethodStmt(op, regs, getMethod(b), getProto(h));
}
break;
case kFmt4rcc: {
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
c = ushort(insns, u1offset + 4);
int h = ushort(insns, u1offset + 6);
int regs[] = new int[a];
for (int i = 0; i < a; i++) {
regs[i] = c + i;
}
dcv.visitMethodStmt(op, regs, getMethod(b), getProto(h));
}
break;
case kFmt22x:
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
@ -1638,6 +1766,13 @@ public class DexFileReader implements BaseDexFileReader {
}
}
private Object[] getCallSite(int b) {
callSiteIdIn.position(b * 4);
int call_site_off = callSiteIdIn.getInt();
return read_encoded_array_item(call_site_off);
}
/**
* An entry in the resulting locals table
*/

View File

@ -70,6 +70,18 @@ public class MultiDexFileReader implements BaseDexFileReader {
}
}
@Override
public int getDexVersion() {
int max = DEX_035;
for (DexFileReader r : readers) {
int v = r.getDexVersion();
if (v > max) {
max = v;
}
}
return max;
}
@Override
public void accept(DexFileVisitor dv) {
accept(dv, 0);

View File

@ -18,10 +18,7 @@ package com.googlecode.d2j.util;
import java.util.HashMap;
import java.util.Map;
import com.googlecode.d2j.DexConstants;
import com.googlecode.d2j.DexLabel;
import com.googlecode.d2j.Field;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.*;
import com.googlecode.d2j.reader.Op;
import com.googlecode.d2j.visitors.DexCodeVisitor;
import com.googlecode.d2j.visitors.DexDebugVisitor;
@ -137,6 +134,16 @@ public class ASMifierCodeV extends DexCodeVisitor implements DexConstants {
m.s("code.visitJumpStmt(%s,%s,%s,%s);", op(op), a, b, v(label));
}
@Override
public void visitMethodStmt(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object... bsmArgs) {
m.s("code.visitMethodStmt(%s,%s,%s,%s,%s,%s);", op(op), Escape.v(args), Escape.v(name), Escape.v(proto), Escape.v(bsm), Escape.v(bsmArgs));
}
@Override
public void visitMethodStmt(Op op, int[] args, Method bsm, Proto proto) {
m.s("code.visitMethodStmt(%s,%s,%s,%s);", op(op), Escape.v(args), Escape.v(bsm), Escape.v(proto));
}
@Override
public void visitMethodStmt(Op op, int[] args, Method method) {
m.s("code.visitMethodStmt(%s,%s,%s);", op(op), Escape.v(args), Escape.v(method));

View File

@ -15,10 +15,7 @@
*/
package com.googlecode.d2j.util;
import com.googlecode.d2j.DexConstants;
import com.googlecode.d2j.DexType;
import com.googlecode.d2j.Field;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.*;
/**
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
@ -165,6 +162,29 @@ public class Escape implements DexConstants {
return String.format("new Method(%s,%s,%s,%s)", v(m.getOwner()), v(m.getName()), v(m.getParameterTypes()),
v(m.getReturnType()));
}
public static String v(Proto m) {
return String.format("new Proto(%s,%s)", v(m.getParameterTypes()), v(m.getReturnType()));
}
public static String v(MethodHandle m) {
switch (m.getType()) {
case MethodHandle.INSTANCE_GET:
return String.format("new MethodHandle(MethodHandle.INSTANCE_GET,%s)", v(m.getField()));
case MethodHandle.INSTANCE_PUT:
return String.format("new MethodHandle(MethodHandle.INSTANCE_PUT,%s)", v(m.getField()));
case MethodHandle.STATIC_GET:
return String.format("new MethodHandle(MethodHandle.STATIC_GET,%s)", v(m.getField()));
case MethodHandle.STATIC_PUT:
return String.format("new MethodHandle(MethodHandle.STATIC_PUT,%s)", v(m.getField()));
case MethodHandle.INVOKE_INSTANCE:
return String.format("new MethodHandle(MethodHandle.INVOKE_INSTANCE,%s)", v(m.getMethod()));
case MethodHandle.INVOKE_STATIC:
return String.format("new MethodHandle(MethodHandle.INVOKE_STATIC,%s)", v(m.getMethod()));
default:
throw new RuntimeException();
}
}
public static String v(String s) {
if (s == null) {
@ -255,6 +275,12 @@ public class Escape implements DexConstants {
if (obj instanceof Field) {
return v((Field) obj);
}
if (obj instanceof Proto) {
return v((Proto) obj);
}
if (obj instanceof MethodHandle) {
return v((MethodHandle) obj);
}
if (obj instanceof Integer) {
return " Integer.valueOf(" + obj + ")";

View File

@ -423,21 +423,21 @@ public class DecryptStringCmd extends BaseCmd {
if (op.vt == Value.VT.INVOKE_STATIC) {
InvokeExpr ie = (InvokeExpr) op;
MethodConfig key = DecryptStringCmd.this.key;
key.owner = ie.owner.substring(1, ie.owner.length() - 1);
key.name = ie.name;
key.desc = buildMethodDesc(ie.args, ie.ret);
key.owner = ie.getOwner().substring(1, ie.getOwner().length() - 1);
key.name = ie.getName();
key.desc = buildMethodDesc(ie.getArgs(), ie.getRet());
MethodConfig c = map.get(key);
if (c != null) {
try {
Method jmethod = c.jmethod;
if (ie.args.length != jmethod.getParameterTypes().length) {
if (ie.getArgs().length != jmethod.getParameterTypes().length) {
throw new RuntimeException();
}
Object args[] = new Object[ie.args.length];
Object args[] = new Object[ie.getArgs().length];
for (int i = 0; i < args.length; i++) {
args[i] = convertIr2Jobj(ie.getOps()[i], ie.args[i]);
args[i] = convertIr2Jobj(ie.getOps()[i], ie.getArgs()[i]);
}
if (verbose) {
System.out.println(" > calling " + jmethod + " with arguments " + v(args));

View File

@ -1115,15 +1115,44 @@ public class Dex2IRConverter {
}
return value;
case INVOKE_CUSTOM:
case INVOKE_CUSTOM_RANGE: {
Value[] vs = new Value[values.size()];
for (int i = 0; i < vs.length; i++) {
vs[i] = getLocal(values.get(i));
}
MethodCustomStmtNode n = (MethodCustomStmtNode) insn;
Value invoke = nInvokeCustom(vs, n.name, n.proto, n.bsm, n.bsmArgs);
if ("V".equals(n.getProto().getReturnType())) {
emit(nVoidInvoke(invoke));
return null;
} else {
return b(invoke);
}
}
case INVOKE_POLYMORPHIC:
case INVOKE_POLYMORPHIC_RANGE: {
Value[] vs = new Value[values.size()];
for (int i = 0; i < vs.length; i++) {
vs[i] = getLocal(values.get(i));
}
MethodPolymorphicStmtNode n = (MethodPolymorphicStmtNode) insn;
Value invoke = nInvokePolymorphic(vs, n.proto, n.method);
if ("V".equals(n.getProto().getReturnType())) {
emit(nVoidInvoke(invoke));
return null;
} else {
return b(invoke);
}
}
default:
Op op = insn.op;
Method method = ((MethodStmtNode) insn).method;
Value[] vs = new Value[values.size()];
for (int i = 0; i < vs.length; i++) {
vs[i] = getLocal(values.get(i));
}
Method method = ((MethodStmtNode) insn).method;
Value invoke = null;
switch (op) {
case INVOKE_VIRTUAL_RANGE:

View File

@ -16,7 +16,9 @@
*/
package com.googlecode.d2j.converter;
import com.googlecode.d2j.DexType;
import com.googlecode.d2j.asm.LdcOptimizeAdapter;
import com.googlecode.d2j.dex.Dex2Asm;
import com.googlecode.dex2jar.ir.IrMethod;
import com.googlecode.dex2jar.ir.Trap;
import com.googlecode.dex2jar.ir.expr.*;
@ -27,10 +29,7 @@ import com.googlecode.dex2jar.ir.expr.Value.VT;
import com.googlecode.dex2jar.ir.stmt.*;
import com.googlecode.dex2jar.ir.stmt.Stmt.E2Stmt;
import com.googlecode.dex2jar.ir.stmt.Stmt.ST;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.*;
import java.lang.reflect.Array;
import java.util.HashMap;
@ -635,13 +634,13 @@ public class IR2JConverter implements Opcodes {
asm.visitMultiANewArrayInsn(sb.toString(), value.ops.length);
break;
case INVOKE_NEW:
asm.visitTypeInsn(NEW, toInternal(((InvokeExpr) value).owner));
asm.visitTypeInsn(NEW, toInternal(((InvokeExpr) value).getOwner()));
asm.visitInsn(DUP);
// pass through
case INVOKE_INTERFACE:
case INVOKE_SPECIAL:
case INVOKE_STATIC:
case INVOKE_VIRTUAL:
case INVOKE_VIRTUAL: {
InvokeExpr ie = (InvokeExpr) value;
int i = 0;
if (value.vt != VT.INVOKE_STATIC && value.vt != VT.INVOKE_NEW) {
@ -651,7 +650,7 @@ public class IR2JConverter implements Opcodes {
for (int j = 0; i < value.ops.length; i++, j++) {
Value vb = value.ops[i];
accept(vb, asm);
insertI2x(vb.valueType, ie.args[j], asm);
insertI2x(vb.valueType, ie.getArgs()[j], asm);
}
int opcode;
@ -673,9 +672,36 @@ public class IR2JConverter implements Opcodes {
opcode = -1;
}
asm.visitMethodInsn(opcode, toInternal(ie.owner), ie.name,
buildMethodDesc(ie.vt == VT.INVOKE_NEW ? "V" : ie.ret, ie.args));
break;
asm.visitMethodInsn(opcode, toInternal(ie.getOwner()), ie.getName(),
buildMethodDesc(ie.vt == VT.INVOKE_NEW ? "V" : ie.getRet(), ie.getArgs()));
}
break;
case INVOKE_CUSTOM: {
InvokeCustomExpr ice = (InvokeCustomExpr) value;
String argTypes[] = ice.getProto().getParameterTypes();
Value[] vbs = ice.getOps();
if (argTypes.length == vbs.length) {
for (int i = 0; i < vbs.length; i++) {
Value vb = vbs[i];
accept(vb, asm);
insertI2x(vb.valueType, argTypes[i], asm);
}
} else if (argTypes.length + 1 == vbs.length) {
accept(vbs[0], asm);
for (int i = 1; i < vbs.length; i++) {
Value vb = vbs[i];
accept(vb, asm);
insertI2x(vb.valueType, argTypes[i - 1], asm);
}
} else {
throw new RuntimeException();
}
asm.visitInvokeDynamicInsn(ice.name, ice.proto.getDesc(), (Handle) Dex2Asm.convertConstantValue(ice.handle), Dex2Asm.convertConstantValues(ice.bsmArgs));
}
break;
case INVOKE_POLYMORPHIC: {
throw new RuntimeException("INVOKE_POLYMORPHIC"); // auto box/unbox ?
}
}
}

View File

@ -473,10 +473,7 @@ public class Dex2Asm {
}
}
}
Object value = fieldNode.cst;
if (value instanceof DexType) {
value = Type.getType(((DexType) value).desc);
}
Object value = convertConstantValue(fieldNode.cst);
final int FieldCleanFlag = ~DexConstants.ACC_DECLARED_SYNCHRONIZED;
FieldVisitor fv = cv.visitField(fieldNode.access & FieldCleanFlag, fieldNode.field.getName(),
fieldNode.field.getType(), signature, value);
@ -487,6 +484,47 @@ public class Dex2Asm {
fv.visitEnd();
}
public static Object[] convertConstantValues(Object[] v) {
Object[] copy = Arrays.copyOf(v, v.length);
for (int i = 0; i < copy.length; i++) {
Object ele = copy[i];
ele = convertConstantValue(ele);
copy[i] = ele;
}
return copy;
}
public static Object convertConstantValue(Object ele) {
if (ele instanceof DexType) {
ele = Type.getType(((DexType) ele).desc);
} else if (ele instanceof MethodHandle) {
Handle h = null;
MethodHandle mh = (MethodHandle) ele;
switch (mh.getType()) {
case MethodHandle.INSTANCE_GET:
h = new Handle(Opcodes.H_GETFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
break;
case MethodHandle.INSTANCE_PUT:
h = new Handle(Opcodes.H_PUTFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
break;
case MethodHandle.STATIC_GET:
h = new Handle(Opcodes.H_GETFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
break;
case MethodHandle.STATIC_PUT:
h = new Handle(Opcodes.H_PUTFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
break;
case MethodHandle.INVOKE_INSTANCE:
h = new Handle(Opcodes.H_INVOKEVIRTUAL, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc());
break;
case MethodHandle.INVOKE_STATIC:
h = new Handle(Opcodes.H_INVOKESTATIC, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc());
break;
}
ele = h;
}
return ele;
}
public void convertMethod(DexClassNode classNode, DexMethodNode methodNode, ClassVisitor cv) {
MethodVisitor mv = collectBasicMethodInfo(methodNode, cv);