com.googlecode.d2j.smali.BaksmaliCodeDumper Maven / Gradle / Ivy
The newest version!
/*
* dex2jar - Tools to work with android .dex and java .class files
* Copyright (c) 2009-2014 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.smali;
import com.googlecode.d2j.*;
import com.googlecode.d2j.node.DexDebugNode;
import com.googlecode.d2j.reader.InstructionFormat;
import com.googlecode.d2j.reader.Op;
import com.googlecode.d2j.util.Out;
import com.googlecode.d2j.visitors.DexCodeVisitor;
import com.googlecode.d2j.visitors.DexDebugVisitor;
import java.util.*;
/*package*/class BaksmaliCodeDumper extends DexCodeVisitor {
private boolean useParameterRegisters;
private boolean useLocals;
private int nextLabelNumber;
private Out out;
final int startParamR;
final Set usedLabel;
final Map> debugLabelMap;
public BaksmaliCodeDumper(Out out, boolean useParameterRegisters, boolean useLocals, int nextLabelNumber,
int startParamR, Set usedLabel, Map> debugLabelMap) {
super();
this.out = out;
this.useParameterRegisters = useParameterRegisters;
this.useLocals = useLocals;
this.nextLabelNumber = nextLabelNumber;
this.startParamR = startParamR;
this.usedLabel = usedLabel;
this.debugLabelMap = debugLabelMap;
}
class PackedSwitchStmt {
int first_case;
DexLabel[] labels;
public PackedSwitchStmt(int first_case, DexLabel[] labels) {
super();
this.first_case = first_case;
this.labels = labels;
}
}
class SparseSwitchStmt {
int[] cases;
DexLabel[] labels;
public SparseSwitchStmt(int[] cases, DexLabel[] labels) {
super();
this.cases = cases;
this.labels = labels;
}
}
List> appendLast = new ArrayList<>();
String reg(int rdx) {
if (useParameterRegisters && rdx >= this.startParamR) {
return "p" + (rdx - this.startParamR);
}
return "v" + rdx;
}
@Override
public void visitFillArrayDataStmt(Op op, int ra, Object array) {
DexLabel dx = new DexLabel();
dx.displayName = "L" + nextLabelNumber++;
usedLabel.add(dx);
out.s("%s %s, %s", op.displayName, reg(ra), xLabel(dx));
appendLast.add(new AbstractMap.SimpleEntry(dx, array));
}
@SuppressWarnings("incomplete-switch")
@Override
public void visitConstStmt(Op op, int ra, Object value) {
switch (op) {
case CONST_WIDE_16: {
Long v = (Long) value;
value = (int) v.shortValue();
break;
}
case CONST_WIDE_HIGH16: {
Long v = (Long) value;
value = (int) ((short) (v >> 48));
break;
}
case CONST_WIDE_32: {
Long v = (Long) value;
value = (int) v.intValue();
break;
}
case CONST_HIGH16: {
Integer v = (Integer) value;
value = (int) v.intValue() >> 16;
break;
}
}
out.s("%s %s, %s", op.displayName, reg(ra), BaksmaliDumper.escapeValue(value));
super.visitConstStmt(op, ra, value);
}
@Override
public void visitEnd() {
for (Map.Entry e : this.appendLast) {
visitLabel(e.getKey());
Object v = e.getValue();
if (v instanceof SparseSwitchStmt) {
SparseSwitchStmt ss = (SparseSwitchStmt) v;
out.s(".sparse-switch");
out.push();
for (int i = 0; i < ss.cases.length; i++) {
out.s("%d -> %s", ss.cases[i], xLabel(ss.labels[i]));
}
out.pop();
out.s(".end sparse-switch");
} else if (v instanceof PackedSwitchStmt) {
PackedSwitchStmt ps = (PackedSwitchStmt) v;
out.s(".packed-switch %d", ps.first_case);
out.push();
for (DexLabel label : ps.labels) {
out.s(xLabel(label));
}
out.pop();
out.s(".end packed-switch");
} else {
Object array = e.getValue();
if (array instanceof byte[]) {
out.s(".array-data 1");
out.push();
byte[] vs = (byte[]) array;
for (int i = 0; i < vs.length; i++) {
out.s(BaksmaliDumper.escapeValue(vs[i]));
}
out.pop();
out.s(".end array-data");
} else if (array instanceof short[]) {
out.s(".array-data 2");
out.push();
short[] vs = (short[]) array;
for (int i = 0; i < vs.length; i++) {
short a = vs[i];
out.s("%s %s", BaksmaliDumper.escapeValue((byte) (a & 0xFF)),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 8))));
}
out.pop();
out.s(".end array-data");
} else if (array instanceof int[]) {
out.s(".array-data 4");
out.push();
int[] vs = (int[]) array;
for (int i = 0; i < vs.length; i++) {
int a = vs[i];
out.s("%s %s %s %s", BaksmaliDumper.escapeValue((byte) (a & 0xFF)),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 8))),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 16))),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 24))));
}
out.pop();
out.s(".end array-data");
} else if (array instanceof float[]) {
out.s(".array-data 4");
out.push();
float[] vs = (float[]) array;
for (int i = 0; i < vs.length; i++) {
int a = Float.floatToIntBits(vs[i]);
out.s("%s %s %s %s", BaksmaliDumper.escapeValue((byte) (a & 0xFF)),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 8))),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 16))),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 24))));
}
out.pop();
out.s(".end array-data");
} else if (array instanceof long[]) {
out.s(".array-data 8");
out.push();
long[] vs = (long[]) array;
for (int i = 0; i < vs.length; i++) {
long ttt = vs[i];
int a = (int) ttt;
int b = (int) (ttt >>> 32);
out.s("%s %s %s %s %s %s %s %s", BaksmaliDumper.escapeValue((byte) (a & 0xFF)),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 8))),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 16))),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 24))),
BaksmaliDumper.escapeValue((byte) (b & 0xFF)),
BaksmaliDumper.escapeValue((byte) (0xFF & (b >> 8))),
BaksmaliDumper.escapeValue((byte) (0xFF & (b >> 16))),
BaksmaliDumper.escapeValue((byte) (0xFF & (b >> 24))));
}
out.pop();
out.s(".end array-data");
} else if (array instanceof double[]) {
out.s(".array-data 8");
out.push();
double[] vs = (double[]) array;
for (int i = 0; i < vs.length; i++) {
long ttt = Double.doubleToLongBits(vs[i]);
int a = (int) ttt;
int b = (int) (ttt >>> 32);
out.s("%s %s %s %s %s %s %s %s", BaksmaliDumper.escapeValue((byte) (a & 0xFF)),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 8))),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 16))),
BaksmaliDumper.escapeValue((byte) (0xFF & (a >> 24))),
BaksmaliDumper.escapeValue((byte) (b & 0xFF)),
BaksmaliDumper.escapeValue((byte) (0xFF & (b >> 8))),
BaksmaliDumper.escapeValue((byte) (0xFF & (b >> 16))),
BaksmaliDumper.escapeValue((byte) (0xFF & (b >> 24))));
}
out.pop();
out.s(".end array-data");
}
}
}
}
@Override
public void visitFieldStmt(Op op, int a, int b, Field field) {
if (op.format == InstructionFormat.kFmt22c) {// iget,iput
out.s("%s %s, %s, %s", op.displayName, reg(a), reg(b), BaksmaliDumper.escapeField(field));
} else {
out.s("%s %s, %s", op.displayName, reg(a), BaksmaliDumper.escapeField(field));
}
}
@Override
public void visitFilledNewArrayStmt(Op op, int[] args, String type) {
if (args.length > 0) {
if (op.format == InstructionFormat.kFmt3rc) { // invoke-x/range
out.s("%s { %d .. %d }, %s", op.displayName, reg(args[0]), reg(args[args.length - 1]),
BaksmaliDumper.escapeType(type));
} else {
StringBuilder buff = new StringBuilder();
boolean first = true;
for (int i : args) {
if (first) {
first = false;
} else {
buff.append(", ");
}
buff.append(reg(i));
}
out.s("%s { %s }, %s", op.displayName, buff, BaksmaliDumper.escapeType(type));
}
} else {
out.s("%s { }, %s", op.displayName, BaksmaliDumper.escapeType(type));
}
}
@Override
public void visitJumpStmt(Op op, int a, int b, DexLabel label) {
if (op.format == InstructionFormat.kFmt21t || op.format == InstructionFormat.kFmt31t) {
out.s(op.displayName + " " + reg(a) + ", " + xLabel(label));
} else if (op.format == InstructionFormat.kFmt22t) {
out.s(op.displayName + " " + reg(a) + ", " + reg(b) + ", " + xLabel(label));
} else {
out.s(op.displayName + " " + xLabel(label));
}
}
DexDebugVisitor debugDumper = new DexDebugVisitor() {
@Override
public void visitStartLocal(int reg, DexLabel label, String name, String type, String signature) {
super.visitStartLocal(reg, label, name, type, signature);
if (signature == null) {
out.s(".local %s, %s:%s", reg(reg), BaksmaliDumper.escapeValue(name), type);
} else {
out.s(".local %s, %s:%s, %s", reg(reg), BaksmaliDumper.escapeValue(name), type, BaksmaliDumper.escapeValue(signature));
}
}
@Override
public void visitPrologue(DexLabel dexLabel) {
out.s(".prologue");
}
@Override
public void visitEpiogue(DexLabel dexLabel) {
out.s(".epiogue");
}
@Override
public void visitLineNumber(int line, DexLabel label) {
out.s(".line %d", line);
}
@Override
public void visitEndLocal(int reg, DexLabel label) {
out.s(".end local %s", reg(reg));
}
@Override
public void visitRestartLocal(int reg, DexLabel label) {
out.s(".restart local %s", reg(reg));
}
};
@Override
public void visitLabel(DexLabel label) {
if (usedLabel.contains(label)) {
out.s(xLabel(label));
}
List dOps = debugLabelMap.get(label);
if (dOps != null) {
for (DexDebugNode.DexDebugOpNode dOp : dOps) {
dOp.accept(debugDumper);
}
}
}
@Override
final public DexDebugVisitor visitDebug() {
return null;
}
@Override
public void visitMethodStmt(Op op, int[] args, Method method) {
if (args.length > 0) {
if (op.format == InstructionFormat.kFmt3rc) { // invoke-x/range
out.s("%s { %s .. %s }, %s", op.displayName, reg(args[0]), reg(args[args.length - 1]),
BaksmaliDumper.escapeMethod(method));
} else {
boolean first = true;
StringBuilder buff = new StringBuilder();
for (int i : args) {
if (first) {
first = false;
} else {
buff.append(", ");
}
buff.append(reg(i));
}
out.s("%s { %s }, %s", op.displayName, buff, BaksmaliDumper.escapeMethod(method));
}
} else {
out.s("%s { }, %s", op.displayName, BaksmaliDumper.escapeMethod(method));
}
}
@Override
public void visitMethodStmt(Op op, int[] args, Method method, Proto proto) {
if (args.length > 0) {
if (op.format == InstructionFormat.kFmt4rcc) { // invoke-x/range
out.s("%s { %s .. %s }, %s, %s", op.displayName, reg(args[0]), reg(args[args.length - 1]),
BaksmaliDumper.escapeMethod(method), BaksmaliDumper.escapeMethodDesc(proto));
} else {
boolean first = true;
StringBuilder buff = new StringBuilder();
for (int i : args) {
if (first) {
first = false;
} else {
buff.append(", ");
}
buff.append(reg(i));
}
out.s("%s { %s }, %s, %s", op.displayName, buff, BaksmaliDumper.escapeMethod(method),
BaksmaliDumper.escapeMethodDesc(proto));
}
} else {
out.s("%s { }, %s, %s", op.displayName, BaksmaliDumper.escapeMethod(method),
BaksmaliDumper.escapeMethodDesc(proto));
}
}
@Override
public void visitMethodStmt(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object... bsmArgs) {
StringBuilder sb = new StringBuilder();
sb.append("{ ").append( BaksmaliDumper.escapeValue(bsm)).append(", ").append(BaksmaliDumper.escapeValue(name)).append(", ").append(BaksmaliDumper.escapeMethodDesc(proto));
for(Object o: bsmArgs) {
sb.append(", ").append(BaksmaliDumper.escapeValue(o));
}
sb.append("}");
if (args.length > 0) {
if (op.format == InstructionFormat.kFmt3rc) { // invoke-x/range
out.s("%s { %s .. %s }, %s", op.displayName, reg(args[0]), reg(args[args.length - 1]),
sb);
} else {
boolean first = true;
StringBuilder buff = new StringBuilder();
for (int i : args) {
if (first) {
first = false;
} else {
buff.append(", ");
}
buff.append(reg(i));
}
out.s("%s { %s }, %s", op.displayName, buff, sb);
}
} else {
out.s("%s { }, %s", op.displayName, sb);
}
}
@Override
public void visitPackedSwitchStmt(Op op, int ra, int first_case, DexLabel[] labels) {
DexLabel dx = new DexLabel();
dx.displayName = "L" + nextLabelNumber++;
usedLabel.add(dx);
out.s(op.displayName + " " + reg(ra) + ", " + xLabel(dx));
appendLast.add(new AbstractMap.SimpleEntry(dx, new PackedSwitchStmt(first_case, labels)));
}
@Override
public void visitRegister(int total) {
if (useLocals) {
out.s(".locals %d", startParamR);
} else {
out.s(".registers %d", total);
}
}
@Override
public void visitSparseSwitchStmt(Op op, int ra, int[] cases, DexLabel[] labels) {
DexLabel dx = new DexLabel();
dx.displayName = "L" + nextLabelNumber++;
usedLabel.add(dx);
out.s(op.displayName + " " + reg(ra) + ", " + xLabel(dx));
appendLast.add(new AbstractMap.SimpleEntry(dx, new SparseSwitchStmt(cases, labels)));
}
@Override
public void visitStmt0R(Op op) {
if (op == Op.BAD_OP) {
out.s("%s # bad op", Op.NOP.displayName);
} else {
out.s(op.displayName);
}
}
@Override
public void visitStmt1R(Op op, int a) {
out.s(op.displayName + " " + reg(a));
}
@Override
public void visitStmt2R(Op op, int a, int b) {
out.s(op.displayName + " " + reg(a) + ", " + reg(b));
}
@Override
public void visitStmt2R1N(Op op, int a, int b, int content) {
out.s("%s %s, %s, %s", op.displayName, reg(a), reg(b), content);
}
@Override
public void visitStmt3R(Op op, int a, int b, int c) {
out.s("%s %s, %s, %s", op.displayName, reg(a), reg(b), reg(c));
}
@Override
public void visitTryCatch(DexLabel start, DexLabel end, DexLabel[] handler, String[] type) {
for (int i = 0; i < type.length; i++) {
String t = type[i];
if (t == null) {
out.s(".catchall { %s .. %s } %s", xLabel(start), xLabel(end), xLabel(handler[i]));
} else {
out.s(".catch %s { %s .. %s } %s", t, xLabel(start), xLabel(end), xLabel(handler[i]));
}
}
}
@Override
public void visitTypeStmt(Op op, int a, int b, String type) {
if (op.format == InstructionFormat.kFmt21c) {
out.s("%s %s, %s", op.displayName, reg(a), BaksmaliDumper.escapeType(type));
} else {
out.s("%s %s, %s, %s", op.displayName, reg(a), reg(b), BaksmaliDumper.escapeType(type));
}
}
String xLabel(DexLabel d) {
return ":" + d.displayName;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy