mirror of
https://github.com/pxb1988/dex2jar.git
synced 2025-02-17 03:37:38 +00:00
parent
80a81b317f
commit
62eba635fd
@ -292,7 +292,10 @@ public class DecryptStringCmd extends BaseCmd {
|
|||||||
// convert ir to m3
|
// convert ir to m3
|
||||||
MethodNode m3 = new MethodNode();
|
MethodNode m3 = new MethodNode();
|
||||||
m3.tryCatchBlocks = new ArrayList<>();
|
m3.tryCatchBlocks = new ArrayList<>();
|
||||||
new IR2JConverter(true).convert(irMethod, m3);
|
new IR2JConverter()
|
||||||
|
.ir(irMethod)
|
||||||
|
.asm(m3)
|
||||||
|
.convert();
|
||||||
|
|
||||||
// copy back m3 to m
|
// copy back m3 to m
|
||||||
m.maxLocals = -1;
|
m.maxLocals = -1;
|
||||||
|
@ -34,24 +34,44 @@ import com.googlecode.dex2jar.ir.stmt.Stmt.ST;
|
|||||||
import org.objectweb.asm.*;
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@SuppressWarnings("incomplete-switch")
|
@SuppressWarnings("incomplete-switch")
|
||||||
public class IR2JConverter implements Opcodes {
|
public class IR2JConverter implements Opcodes {
|
||||||
|
|
||||||
|
public static final int MAX_FILL_ARRAY_BYTES = 500;
|
||||||
private boolean optimizeSynchronized = false;
|
private boolean optimizeSynchronized = false;
|
||||||
|
Dex2Asm.ClzCtx clzCtx;
|
||||||
|
IrMethod ir;
|
||||||
|
MethodVisitor asm;
|
||||||
public IR2JConverter() {
|
public IR2JConverter() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IR2JConverter(boolean optimizeSynchronized) {
|
public IR2JConverter optimizeSynchronized(boolean optimizeSynchronized) {
|
||||||
super();
|
|
||||||
this.optimizeSynchronized = optimizeSynchronized;
|
this.optimizeSynchronized = optimizeSynchronized;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void convert(IrMethod ir, MethodVisitor asm) {
|
public IR2JConverter clzCtx(Dex2Asm.ClzCtx clzCtx) {
|
||||||
|
this.clzCtx = clzCtx;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IR2JConverter ir(IrMethod ir) {
|
||||||
|
this.ir = ir;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IR2JConverter asm(MethodVisitor asm) {
|
||||||
|
this.asm = asm;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void convert() {
|
||||||
mapLabelStmt(ir);
|
mapLabelStmt(ir);
|
||||||
reBuildInstructions(ir, asm);
|
reBuildInstructions(ir, asm);
|
||||||
reBuildTryCatchBlocks(ir, asm);
|
reBuildTryCatchBlocks(ir, asm);
|
||||||
@ -229,15 +249,45 @@ public class IR2JConverter implements Opcodes {
|
|||||||
} else {
|
} else {
|
||||||
elementType = "I";
|
elementType = "I";
|
||||||
}
|
}
|
||||||
int iastoreOP = getOpcode(elementType, IASTORE);
|
boolean genBig = false;
|
||||||
accept(e2.getOp1(), asm);
|
try {
|
||||||
for (int i = 0; i < arraySize; i++) {
|
if (this.clzCtx != null
|
||||||
asm.visitInsn(DUP);
|
&& "BSIJ".contains(elementType)) {
|
||||||
asm.visitLdcInsn(i);
|
|
||||||
asm.visitLdcInsn(Array.get(arrayData, i));
|
byte[] data = toLittleEndianArray(arrayData);
|
||||||
asm.visitInsn(iastoreOP);
|
|
||||||
|
if (data != null && data.length > MAX_FILL_ARRAY_BYTES) {
|
||||||
|
accept(e2.getOp1(), asm);
|
||||||
|
asm.visitLdcInsn(0);
|
||||||
|
constLargeArray(asm, data, elementType);
|
||||||
|
asm.visitLdcInsn(0);
|
||||||
|
asm.visitLdcInsn(arraySize);
|
||||||
|
|
||||||
|
asm.visitMethodInsn(Opcodes.INVOKESTATIC,
|
||||||
|
"java/lang/System",
|
||||||
|
"arraycopy",
|
||||||
|
"(Ljava/lang/Object;ILjava/lang/Object;II)V",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
genBig = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// any exception, revert to normal
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!genBig) {
|
||||||
|
int iastoreOP = getOpcode(elementType, IASTORE);
|
||||||
|
accept(e2.getOp1(), asm);
|
||||||
|
for (int i = 0; i < arraySize; i++) {
|
||||||
|
asm.visitInsn(DUP);
|
||||||
|
asm.visitLdcInsn(i);
|
||||||
|
asm.visitLdcInsn(Array.get(arrayData, i));
|
||||||
|
asm.visitInsn(iastoreOP);
|
||||||
|
}
|
||||||
|
asm.visitInsn(POP);
|
||||||
}
|
}
|
||||||
asm.visitInsn(POP);
|
|
||||||
} else {
|
} else {
|
||||||
FilledArrayExpr filledArrayExpr = (FilledArrayExpr) e2.getOp2();
|
FilledArrayExpr filledArrayExpr = (FilledArrayExpr) e2.getOp2();
|
||||||
int arraySize = filledArrayExpr.ops.length;
|
int arraySize = filledArrayExpr.ops.length;
|
||||||
@ -248,15 +298,47 @@ public class IR2JConverter implements Opcodes {
|
|||||||
} else {
|
} else {
|
||||||
elementType = "I";
|
elementType = "I";
|
||||||
}
|
}
|
||||||
int iastoreOP = getOpcode(elementType, IASTORE);
|
|
||||||
accept(e2.getOp1(), asm);
|
boolean genBig = false;
|
||||||
for (int i = 0; i < arraySize; i++) {
|
try {
|
||||||
asm.visitInsn(DUP);
|
if (this.clzCtx != null
|
||||||
asm.visitLdcInsn(i);
|
&& "BSIJ".contains(elementType)
|
||||||
accept(filledArrayExpr.ops[i], asm);
|
&& isConstant(filledArrayExpr.ops)) {
|
||||||
asm.visitInsn(iastoreOP);
|
// create a 500-len byte array, may cause 'Method code too large!'
|
||||||
|
// convert it to a base64 decoding
|
||||||
|
byte[] data = collectDataAsByteArray(filledArrayExpr.ops, elementType);
|
||||||
|
if (data != null && data.length > MAX_FILL_ARRAY_BYTES) {
|
||||||
|
accept(e2.getOp1(), asm);
|
||||||
|
asm.visitLdcInsn(0);
|
||||||
|
constLargeArray(asm, data, elementType);
|
||||||
|
asm.visitLdcInsn(0);
|
||||||
|
asm.visitLdcInsn(arraySize);
|
||||||
|
|
||||||
|
asm.visitMethodInsn(INVOKESTATIC,
|
||||||
|
"java/lang/System",
|
||||||
|
"arraycopy",
|
||||||
|
"(Ljava/lang/Object;ILjava/lang/Object;II)V",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
genBig = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// any exception, revert to normal
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!genBig) {
|
||||||
|
int iastoreOP = getOpcode(elementType, IASTORE);
|
||||||
|
accept(e2.getOp1(), asm);
|
||||||
|
for (int i = 0; i < arraySize; i++) {
|
||||||
|
asm.visitInsn(DUP);
|
||||||
|
asm.visitLdcInsn(i);
|
||||||
|
accept(filledArrayExpr.ops[i], asm);
|
||||||
|
asm.visitInsn(iastoreOP);
|
||||||
|
}
|
||||||
|
asm.visitInsn(POP);
|
||||||
}
|
}
|
||||||
asm.visitInsn(POP);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -390,6 +472,35 @@ public class IR2JConverter implements Opcodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void constLargeArray(MethodVisitor asm, byte[] data, String elementType) {
|
||||||
|
String cst = hexEncode(data);
|
||||||
|
if (cst.length() > 65535) { // asm have the limit
|
||||||
|
asm.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
|
||||||
|
asm.visitInsn(Opcodes.DUP);
|
||||||
|
asm.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
|
||||||
|
|
||||||
|
for (int i = 0; i < cst.length(); i += 65500) {
|
||||||
|
int a = Math.min(65500, cst.length() - i);
|
||||||
|
asm.visitLdcInsn(cst.substring(i, i + a));
|
||||||
|
asm.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder",
|
||||||
|
"append",
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/StringBuilder;",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
asm.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder",
|
||||||
|
"toString",
|
||||||
|
"()Ljava/lang/String;",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
asm.visitLdcInsn(cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
asm.visitMethodInsn(Opcodes.INVOKESTATIC, toInternal(this.clzCtx.classDescriptor),
|
||||||
|
this.clzCtx.buildHexDecodeMethodName(elementType), "(Ljava/lang/String;)[" + elementType, false);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isLocalWithIndex(Value v, int i) {
|
private static boolean isLocalWithIndex(Value v, int i) {
|
||||||
return v.vt == VT.LOCAL && ((Local) v)._ls_index == i;
|
return v.vt == VT.LOCAL && ((Local) v)._ls_index == i;
|
||||||
}
|
}
|
||||||
@ -556,7 +667,7 @@ public class IR2JConverter implements Opcodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void accept(Value value, MethodVisitor asm) {
|
private void accept(Value value, MethodVisitor asm) {
|
||||||
|
|
||||||
switch (value.et) {
|
switch (value.et) {
|
||||||
case E0:
|
case E0:
|
||||||
@ -595,10 +706,17 @@ public class IR2JConverter implements Opcodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void reBuildEnExpression(EnExpr value, MethodVisitor asm) {
|
public static String hexEncode(byte[] data) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : data) {
|
||||||
|
sb.append(String.format("%02x", b & 0xFF));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reBuildEnExpression(EnExpr value, MethodVisitor asm) {
|
||||||
if (value.vt == VT.FILLED_ARRAY) {
|
if (value.vt == VT.FILLED_ARRAY) {
|
||||||
FilledArrayExpr fae = (FilledArrayExpr) value;
|
FilledArrayExpr fae = (FilledArrayExpr) value;
|
||||||
reBuildE1Expression(Exprs.nNewArray(fae.type, Exprs.nInt(fae.ops.length)), asm);
|
|
||||||
String tp1 = fae.valueType;
|
String tp1 = fae.valueType;
|
||||||
int xastore = IASTORE;
|
int xastore = IASTORE;
|
||||||
String elementType = null;
|
String elementType = null;
|
||||||
@ -607,6 +725,25 @@ public class IR2JConverter implements Opcodes {
|
|||||||
xastore = getOpcode(elementType, IASTORE);
|
xastore = getOpcode(elementType, IASTORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.clzCtx != null
|
||||||
|
&& elementType != null
|
||||||
|
&& "BSIJ".contains(elementType)
|
||||||
|
&& isConstant(fae.ops)) {
|
||||||
|
|
||||||
|
byte[] data = collectDataAsByteArray(fae.ops, elementType);
|
||||||
|
if (data != null && data.length > MAX_FILL_ARRAY_BYTES) {
|
||||||
|
constLargeArray(asm, data, elementType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// any exception, revert to normal
|
||||||
|
}
|
||||||
|
|
||||||
|
reBuildE1Expression(Exprs.nNewArray(fae.type, Exprs.nInt(fae.ops.length)), asm);
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < fae.ops.length; i++) {
|
for (int i = 0; i < fae.ops.length; i++) {
|
||||||
if (fae.ops[i] == null)
|
if (fae.ops[i] == null)
|
||||||
continue;
|
continue;
|
||||||
@ -720,6 +857,91 @@ public class IR2JConverter implements Opcodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static byte[] collectDataAsByteArray(Value[] ops, String t) {
|
||||||
|
switch (t) {
|
||||||
|
case "B": {
|
||||||
|
byte[] d = new byte[ops.length];
|
||||||
|
for (int i = 0, opsLength = ops.length; i < opsLength; i++) {
|
||||||
|
Value op = ops[i];
|
||||||
|
Constant cst = (Constant) op;
|
||||||
|
d[i] = ((Number) cst.value).byteValue();
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
case "S": {
|
||||||
|
short[] d = new short[ops.length];
|
||||||
|
for (int i = 0, opsLength = ops.length; i < opsLength; i++) {
|
||||||
|
Value op = ops[i];
|
||||||
|
Constant cst = (Constant) op;
|
||||||
|
d[i] = ((Number) cst.value).shortValue();
|
||||||
|
}
|
||||||
|
return toLittleEndianArray(d);
|
||||||
|
}
|
||||||
|
case "I": {
|
||||||
|
int[] d = new int[ops.length];
|
||||||
|
for (int i = 0, opsLength = ops.length; i < opsLength; i++) {
|
||||||
|
Value op = ops[i];
|
||||||
|
Constant cst = (Constant) op;
|
||||||
|
d[i] = ((Number) cst.value).intValue();
|
||||||
|
}
|
||||||
|
return toLittleEndianArray(d);
|
||||||
|
}
|
||||||
|
case "J": {
|
||||||
|
long[] d = new long[ops.length];
|
||||||
|
for (int i = 0, opsLength = ops.length; i < opsLength; i++) {
|
||||||
|
Value op = ops[i];
|
||||||
|
Constant cst = (Constant) op;
|
||||||
|
d[i] = ((Number) cst.value).longValue();
|
||||||
|
}
|
||||||
|
return toLittleEndianArray(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] toLittleEndianArray(Object d) {
|
||||||
|
if (d instanceof byte[]) {
|
||||||
|
return (byte[]) d;
|
||||||
|
} else if (d instanceof short[]) {
|
||||||
|
return toLittleEndianArray((short[]) d);
|
||||||
|
} else if (d instanceof int[]) {
|
||||||
|
return toLittleEndianArray((int[]) d);
|
||||||
|
} else if (d instanceof long[]) {
|
||||||
|
return toLittleEndianArray((long[]) d);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] toLittleEndianArray(long[] d) {
|
||||||
|
ByteBuffer b = ByteBuffer.allocate(d.length *8);
|
||||||
|
b.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
b.asLongBuffer().put(d);
|
||||||
|
return b.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] toLittleEndianArray(int[] d) {
|
||||||
|
ByteBuffer b = ByteBuffer.allocate(d.length *4);
|
||||||
|
b.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
b.asIntBuffer().put(d);
|
||||||
|
return b.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] toLittleEndianArray(short[] d) {
|
||||||
|
ByteBuffer b = ByteBuffer.allocate(d.length *2);
|
||||||
|
b.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
b.asShortBuffer().put(d);
|
||||||
|
return b.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isConstant(Value[] ops) {
|
||||||
|
for (Value op : ops) {
|
||||||
|
if (op.vt != VT.CONSTANT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static void box(String provideType, String expectedType, MethodVisitor asm) {
|
private static void box(String provideType, String expectedType, MethodVisitor asm) {
|
||||||
if(provideType.equals(expectedType)){
|
if(provideType.equals(expectedType)){
|
||||||
return;
|
return;
|
||||||
@ -848,7 +1070,7 @@ public class IR2JConverter implements Opcodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void reBuildE1Expression(E1Expr e1, MethodVisitor asm) {
|
private void reBuildE1Expression(E1Expr e1, MethodVisitor asm) {
|
||||||
accept(e1.getOp(), asm);
|
accept(e1.getOp(), asm);
|
||||||
switch (e1.vt) {
|
switch (e1.vt) {
|
||||||
case STATIC_FIELD: {
|
case STATIC_FIELD: {
|
||||||
@ -924,7 +1146,7 @@ public class IR2JConverter implements Opcodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void reBuildE2Expression(E2Expr e2, MethodVisitor asm) {
|
private void reBuildE2Expression(E2Expr e2, MethodVisitor asm) {
|
||||||
String type = e2.op2.valueType;
|
String type = e2.op2.valueType;
|
||||||
accept(e2.op1, asm);
|
accept(e2.op1, asm);
|
||||||
if ((e2.vt == VT.ADD || e2.vt == VT.SUB) && e2.op2.vt == VT.CONSTANT) {
|
if ((e2.vt == VT.ADD || e2.vt == VT.SUB) && e2.op2.vt == VT.CONSTANT) {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.googlecode.d2j.dex;
|
package com.googlecode.d2j.dex;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import com.googlecode.d2j.converter.Dex2IRConverter;
|
import com.googlecode.d2j.converter.Dex2IRConverter;
|
||||||
@ -14,7 +16,19 @@ import com.googlecode.dex2jar.ir.ts.*;
|
|||||||
import com.googlecode.dex2jar.ir.ts.array.FillArrayTransformer;
|
import com.googlecode.dex2jar.ir.ts.array.FillArrayTransformer;
|
||||||
|
|
||||||
public class Dex2Asm {
|
public class Dex2Asm {
|
||||||
|
public static class ClzCtx {
|
||||||
|
public String classDescriptor;
|
||||||
|
public String hexDecodeMethodNamePrefix;
|
||||||
|
|
||||||
|
public String buildHexDecodeMethodName(String x) {
|
||||||
|
if (hexDecodeMethodNamePrefix == null) {
|
||||||
|
byte[] d = new byte[4];
|
||||||
|
new Random().nextBytes(d);
|
||||||
|
hexDecodeMethodNamePrefix = "$d2j$hex$" + IR2JConverter.hexEncode(d);
|
||||||
|
}
|
||||||
|
return hexDecodeMethodNamePrefix + "$decode_" + x;
|
||||||
|
}
|
||||||
|
}
|
||||||
protected static class Clz {
|
protected static class Clz {
|
||||||
public int access;
|
public int access;
|
||||||
public Clz enclosingClass;
|
public Clz enclosingClass;
|
||||||
@ -440,17 +454,42 @@ public class Dex2Asm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (classNode.methods != null) {
|
if (classNode.methods != null) {
|
||||||
|
ClzCtx clzCtx = new ClzCtx();
|
||||||
|
clzCtx.classDescriptor = classNode.className;
|
||||||
for (DexMethodNode methodNode : classNode.methods) {
|
for (DexMethodNode methodNode : classNode.methods) {
|
||||||
convertMethod(classNode, methodNode, cv);
|
convertMethod(classNode, methodNode, cv, clzCtx);
|
||||||
|
}
|
||||||
|
if (clzCtx.hexDecodeMethodNamePrefix != null) {
|
||||||
|
addHexDecodeMethod(cv, clzCtx.hexDecodeMethodNamePrefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cv.visitEnd();
|
cv.visitEnd();
|
||||||
}
|
}
|
||||||
|
private void addHexDecodeMethod(ClassVisitor outCV, String hexDecodeMethodNameBase) {
|
||||||
public void convertCode(DexMethodNode methodNode, MethodVisitor mv) {
|
// the .data is a class file compiled from res.Hex
|
||||||
|
try (InputStream is = Dex2Asm.class.getResourceAsStream("/d2j_hex_decode_stub.data")) {
|
||||||
|
ClassReader cr = new ClassReader(is);
|
||||||
|
cr.accept(new ClassVisitor(Opcodes.ASM5) {
|
||||||
|
@Override
|
||||||
|
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||||
|
if (name.startsWith("decode")) {
|
||||||
|
return outCV.visitMethod(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
|
||||||
|
hexDecodeMethodNameBase + "$" + name,
|
||||||
|
desc, signature, exceptions
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return super.visitMethod(access, name, desc, signature, exceptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, ClassReader.EXPAND_FRAMES);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("fail to add hex.decode", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void convertCode(DexMethodNode methodNode, MethodVisitor mv, ClzCtx clzCtx) {
|
||||||
IrMethod irMethod = dex2ir(methodNode);
|
IrMethod irMethod = dex2ir(methodNode);
|
||||||
optimize(irMethod);
|
optimize(irMethod);
|
||||||
ir2j(irMethod, mv);
|
ir2j(irMethod, mv, clzCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void convertDex(DexFileNode fileNode, ClassVisitorFactory cvf) {
|
public void convertDex(DexFileNode fileNode, ClassVisitorFactory cvf) {
|
||||||
@ -546,7 +585,7 @@ public class Dex2Asm {
|
|||||||
return ele;
|
return ele;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void convertMethod(DexClassNode classNode, DexMethodNode methodNode, ClassVisitor cv) {
|
public void convertMethod(DexClassNode classNode, DexMethodNode methodNode, ClassVisitor cv, ClzCtx clzCtx) {
|
||||||
|
|
||||||
MethodVisitor mv = collectBasicMethodInfo(methodNode, cv);
|
MethodVisitor mv = collectBasicMethodInfo(methodNode, cv);
|
||||||
|
|
||||||
@ -593,7 +632,7 @@ public class Dex2Asm {
|
|||||||
if ((NO_CODE_MASK & methodNode.access) == 0) { // has code
|
if ((NO_CODE_MASK & methodNode.access) == 0) { // has code
|
||||||
if (methodNode.codeNode != null) {
|
if (methodNode.codeNode != null) {
|
||||||
mv.visitCode();
|
mv.visitCode();
|
||||||
convertCode(methodNode, mv);
|
convertCode(methodNode, mv, clzCtx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,8 +663,13 @@ public class Dex2Asm {
|
|||||||
return clz;
|
return clz;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ir2j(IrMethod irMethod, MethodVisitor mv) {
|
public void ir2j(IrMethod irMethod, MethodVisitor mv, ClzCtx clzCtx) {
|
||||||
new IR2JConverter(false).convert(irMethod, mv);
|
new IR2JConverter()
|
||||||
|
.optimizeSynchronized(false)
|
||||||
|
.clzCtx(clzCtx)
|
||||||
|
.ir(irMethod)
|
||||||
|
.asm(mv)
|
||||||
|
.convert();
|
||||||
mv.visitMaxs(-1, -1);
|
mv.visitMaxs(-1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,15 +689,16 @@ public class Dex2Asm {
|
|||||||
T_agg.transform(irMethod);
|
T_agg.transform(irMethod);
|
||||||
T_multiArray.transform(irMethod);
|
T_multiArray.transform(irMethod);
|
||||||
T_voidInvoke.transform(irMethod);
|
T_voidInvoke.transform(irMethod);
|
||||||
T_type.transform(irMethod);
|
|
||||||
{
|
{
|
||||||
// https://github.com/pxb1988/dex2jar/issues/477
|
// https://github.com/pxb1988/dex2jar/issues/477
|
||||||
// dead code found in unssa, clean up
|
// dead code found in unssa, clean up
|
||||||
T_deadCode.transform(irMethod);
|
T_deadCode.transform(irMethod);
|
||||||
T_removeLocal.transform(irMethod);
|
T_removeLocal.transform(irMethod);
|
||||||
T_removeConst.transform(irMethod);
|
T_removeConst.transform(irMethod);
|
||||||
T_unssa.transform(irMethod);
|
|
||||||
}
|
}
|
||||||
|
T_type.transform(irMethod);
|
||||||
|
T_unssa.transform(irMethod);
|
||||||
T_trimEx.transform(irMethod);
|
T_trimEx.transform(irMethod);
|
||||||
T_ir2jRegAssign.transform(irMethod);
|
T_ir2jRegAssign.transform(irMethod);
|
||||||
}
|
}
|
||||||
|
@ -124,12 +124,12 @@ public class Dex2jar {
|
|||||||
};
|
};
|
||||||
|
|
||||||
new ExDex2Asm(exceptionHandler) {
|
new ExDex2Asm(exceptionHandler) {
|
||||||
public void convertCode(DexMethodNode methodNode, MethodVisitor mv) {
|
public void convertCode(DexMethodNode methodNode, MethodVisitor mv, ClzCtx clzCtx) {
|
||||||
if ((readerConfig & DexFileReader.SKIP_CODE) != 0 && methodNode.method.getName().equals("<clinit>")) {
|
if ((readerConfig & DexFileReader.SKIP_CODE) != 0 && methodNode.method.getName().equals("<clinit>")) {
|
||||||
// also skip clinit
|
// also skip clinit
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
super.convertCode(methodNode, mv);
|
super.convertCode(methodNode, mv, clzCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -169,8 +169,13 @@ public class Dex2jar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void ir2j(IrMethod irMethod, MethodVisitor mv) {
|
public void ir2j(IrMethod irMethod, MethodVisitor mv, ClzCtx clzCtx) {
|
||||||
new IR2JConverter(0 != (V3.OPTIMIZE_SYNCHRONIZED & v3Config)).convert(irMethod, mv);
|
new IR2JConverter()
|
||||||
|
.optimizeSynchronized(0 != (V3.OPTIMIZE_SYNCHRONIZED & v3Config))
|
||||||
|
.clzCtx(clzCtx)
|
||||||
|
.ir(irMethod)
|
||||||
|
.asm(mv)
|
||||||
|
.convert();
|
||||||
}
|
}
|
||||||
}.convertDex(fileNode, cvf);
|
}.convertDex(fileNode, cvf);
|
||||||
|
|
||||||
|
@ -32,12 +32,12 @@ public class ExDex2Asm extends Dex2Asm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void convertCode(DexMethodNode methodNode, MethodVisitor mv) {
|
public void convertCode(DexMethodNode methodNode, MethodVisitor mv, ClzCtx clzCtx) {
|
||||||
MethodVisitor mw = AsmBridge.searchMethodWriter(mv);
|
MethodVisitor mw = AsmBridge.searchMethodWriter(mv);
|
||||||
MethodNode mn = new MethodNode(Opcodes.ASM5, methodNode.access, methodNode.method.getName(),
|
MethodNode mn = new MethodNode(Opcodes.ASM5, methodNode.access, methodNode.method.getName(),
|
||||||
methodNode.method.getDesc(), null, null);
|
methodNode.method.getDesc(), null, null);
|
||||||
try {
|
try {
|
||||||
super.convertCode(methodNode, mn);
|
super.convertCode(methodNode, mn, clzCtx);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
if (exceptionHandler == null) {
|
if (exceptionHandler == null) {
|
||||||
throw new DexException(ex, "fail convert code for %s", methodNode.method);
|
throw new DexException(ex, "fail convert code for %s", methodNode.method);
|
||||||
|
BIN
dex-translator/src/main/resources/d2j_hex_decode_stub.data
Normal file
BIN
dex-translator/src/main/resources/d2j_hex_decode_stub.data
Normal file
Binary file not shown.
@ -296,9 +296,9 @@ public abstract class TestUtils {
|
|||||||
// 1. convert to .class
|
// 1. convert to .class
|
||||||
Dex2Asm dex2Asm = new Dex2Asm() {
|
Dex2Asm dex2Asm = new Dex2Asm() {
|
||||||
@Override
|
@Override
|
||||||
public void convertCode(DexMethodNode methodNode, MethodVisitor mv) {
|
public void convertCode(DexMethodNode methodNode, MethodVisitor mv, ClzCtx clzCtx) {
|
||||||
try {
|
try {
|
||||||
super.convertCode(methodNode, mv);
|
super.convertCode(methodNode, mv, clzCtx);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
BaksmaliDumper d = new BaksmaliDumper();
|
BaksmaliDumper d = new BaksmaliDumper();
|
||||||
try {
|
try {
|
||||||
|
65
dex-translator/src/test/java/res/Hex.java
Normal file
65
dex-translator/src/test/java/res/Hex.java
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package res;
|
||||||
|
|
||||||
|
import java.nio.*;
|
||||||
|
|
||||||
|
public class Hex {
|
||||||
|
public static long[] decode_J(String src) {
|
||||||
|
byte[] d = decode_B(src);
|
||||||
|
ByteBuffer b = ByteBuffer.wrap(d);
|
||||||
|
b.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
LongBuffer s = b.asLongBuffer();
|
||||||
|
long[] data = new long[d.length / 8];
|
||||||
|
s.get(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
public static int[] decode_I(String src) {
|
||||||
|
byte[] d = decode_B(src);
|
||||||
|
ByteBuffer b = ByteBuffer.wrap(d);
|
||||||
|
b.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
IntBuffer s = b.asIntBuffer();
|
||||||
|
int[] data = new int[d.length / 4];
|
||||||
|
s.get(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static short[] decode_S(String src) {
|
||||||
|
byte[] d = decode_B(src);
|
||||||
|
ByteBuffer b = ByteBuffer.wrap(d);
|
||||||
|
b.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
ShortBuffer s = b.asShortBuffer();
|
||||||
|
short[] data = new short[d.length / 2];
|
||||||
|
s.get(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decode_B(String src) {
|
||||||
|
char[] d = src.toCharArray();
|
||||||
|
byte[] ret = new byte[src.length() / 2];
|
||||||
|
for (int i = 0; i < ret.length; i++) {
|
||||||
|
char h = d[2 * i];
|
||||||
|
char l = d[2 * i + 1];
|
||||||
|
int hh = 0;
|
||||||
|
if (h >= '0' && h <= '9') {
|
||||||
|
hh = h - '0';
|
||||||
|
} else if (h >= 'a' && h <= 'f') {
|
||||||
|
hh = h - 'a' + 10;
|
||||||
|
} else if (h >= 'A' && h <= 'F') {
|
||||||
|
hh = h - 'A' + 10;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
int ll = 0;
|
||||||
|
if (l >= '0' && l <= '9') {
|
||||||
|
ll = h - '0';
|
||||||
|
} else if (l >= 'a' && l <= 'f') {
|
||||||
|
ll = h - 'a' + 10;
|
||||||
|
} else if (l >= 'A' && l <= 'F') {
|
||||||
|
ll = h - 'A' + 10;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
d[i] = (char) ((hh << 4) | ll);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
.class Lcode.Large;
|
.class Lcode/Large;
|
||||||
|
.super Lcode/LargeS;
|
||||||
|
|
||||||
.method private static constructor <clinit>()V
|
.method private static constructor <clinit>()V
|
||||||
.catchall { :L2 .. :L3 } :L0
|
.catchall { :L2 .. :L3 } :L0
|
Loading…
x
Reference in New Issue
Block a user