dex2jar源码解析----smail转dex
SmaliCmd用来将smail文件转换为dex文件
smail转dex用到了一个ANTLR 语言识别的一个工具 (ANother Tool for Language Recognition ) 来识别 smail语法,它定义了一个smail的文法文件
这里的Smail.g4就是定义的文法文件,使用的是g4版本,另外几个java文件是根据文法文件自动生成的
这是文法文件的一部分
关于ANTLR我也不是特别了解,主要是知道它在这里的用途就是识别smalil文件中的关键字,词法等
我们主要分析smail转为dex实现过程
@Syntax(cmd = "d2j-smali", syntax = "[options] [--] [<smali-file>|folder]*", desc = "assembles a set of smali files into a dex file", onlineHelp = "https://sourceforge.net/p/dex2jar/wiki/Smali")
public class SmaliCmd extends BaseCmd {@Opt(opt = "x", longOpt = "allow-odex-instructions", hasArg = false, description = "[not impl] allow odex instructions to be compiled into the dex file. Only a few instructions are supported - the ones that can exist in a dead code path and not cause dalvik to reject the class")private boolean allowOdexInstructions;@Opt(opt = "a", longOpt = "api-level", description = "[not impl] The numeric api-level of the file to generate, e.g. 14 for ICS. If not specified, it defaults to 14 (ICS).", argName = "API_LEVEL")private int apiLevel = 14;@Opt(opt = "v", longOpt = "version", hasArg = false, description = "prints the version then exits")private boolean showVersionThenExits;@Opt(opt = "o", longOpt = "output", description = "the name of the dex file that will be written. The default is out.dex", argName = "FILE")private Path output;@Opt(opt = "-", hasArg = false, description = "read smali from stdin")private boolean readSmaliFromStdin;public static void main(String[] args) {new SmaliCmd().doMain(args);}@Overrideprotected void doCommandLine() throws Exception {if (showVersionThenExits) {System.out.println("smali 1.4.2p (https://sourceforge.net/p/dex2jar)");System.out.println("Copyright (c) 2009-2013 Panxiaobo (pxb1988@gmail.com)");System.out.println("Apache license (http://www.apache.org/licenses/LICENSE-2.0)");return;}if (!readSmaliFromStdin && remainingArgs.length < 1) {System.err.println("ERRPR: no file to process");return;}if (output == null) {output = new File("out.dex").toPath();}Smali smali = new Smali();DexFileWriter fw = new DexFileWriter();DexFileVisitor fv = new DexFileVisitor(fw) {@Overridepublic void visitEnd() {// intercept the call to super}};if (readSmaliFromStdin) {smali.smaliFile("<stdin>", System.in, fv);System.err.println("smali <stdin> -> " + output);}for (String s : remainingArgs) {Path file = new File(s).toPath();if (!Files.exists(file)) {System.err.println("skip " + file + ", it is not a dir or a file");} else {System.err.println("smali " + s + " -> " + output);smali.smali(file, fv);}}fw.visitEnd();byte[] data = fw.toByteArray();Files.write(output, data);}
}
这里定义了一些选项,然后主要是里面的doCommandLine方法
定义了一个Smali 和DexFileWriter
然后主要是调用了smail的smail方法
public static void smali(Path base, final DexFileVisitor dfv) throws IOException {if (Files.isDirectory(base)) {Files.walkFileTree(base, new SimpleFileVisitor<Path>() {@Overridepublic FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {Path fn = dir.getFileName();if (fn != null && fn.toString().startsWith(".")) {return FileVisitResult.SKIP_SUBTREE;}return super.preVisitDirectory(dir, attrs);}@Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {smaliFile(file, dfv);return super.visitFile(file, attrs);}});} else if (Files.isRegularFile(base)) {smaliFile(base, dfv);}}
这里不是目录,走else分支
public static void smaliFile(Path path, DexFileVisitor dcv) throws IOException {try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {ANTLRInputStream is = new ANTLRInputStream(reader);is.name = path.toString();smali0(dcv, is);}}
这里读书smail文件中的内容,并以它构造一个ANTLRInputStream流,然后调用smali0
private static void smali0(DexFileVisitor dcv, CharStream is) throws IOException {SmaliLexer lexer = new SmaliLexer(is);CommonTokenStream ts = new CommonTokenStream(lexer);SmaliParser parser = new SmaliParser(ts);for (SmaliParser.SFileContext ctx : parser.sFiles().sFile()) {AntlrSmaliUtil.acceptFile(ctx, dcv);}}
用 ANTLRInputStream流 构造词法分析器 lexer,词法分析的作用是产生记号,用词法分析器 lexer 构造一个记号流 tokens,然后再使用 tokens 构造语法分析器 parser
这里解析出来的SmaliParser.SFileContext我们就可以当作以哥smail的文件上下文,包含了该smail文件的相关信息
public static void acceptFile(SmaliParser.SFileContext ctx, DexFileVisitor dexFileVisitor) {DexClassVisitor dexClassVisitor;String className = Utils.unEscapeId(ctx.className.getText());//获取类名int access = collectAccess(ctx.sAccList());//访问标志List<SmaliParser.SSuperContext> superContexts = ctx.sSuper();//超类String superClass = null;if (superContexts.size() > 0) {superClass = Utils.unEscapeId(superContexts.get(superContexts.size() - 1).name.getText());}List<SmaliParser.SInterfaceContext> itfs = ctx.sInterface();//接口String[] interfaceNames = null;if (itfs.size() > 0) {interfaceNames = new String[itfs.size()];for (int i = 0; i < itfs.size(); i++) {interfaceNames[i] = Utils.unEscapeId(itfs.get(i).name.getText());}}dexClassVisitor = dexFileVisitor.visit(access, className, superClass, interfaceNames);//根据前面的信息访问类List<SmaliParser.SSourceContext> sources = ctx.sSource();if (sources.size() > 0) {dexClassVisitor.visitSource(Utils.unescapeStr(sources.get(sources.size() - 1).src.getText()));}acceptAnnotations(ctx.sAnnotation(), dexClassVisitor);//访问注解acceptField(ctx.sField(), className, dexClassVisitor);//访问字段acceptMethod(ctx.sMethod(), className, dexClassVisitor);//访问方法dexClassVisitor.visitEnd();}
AntlrSmaliUtil提供了一些访问类,字段等的静态方法
这里会根据前面的词法解析获取类的一些基本信息,然后调用DexFileVisitor的visitor访问这个类,并返回一个DexClassVisitor,我们看下visitor的实现
public DexClassVisitor visit(int access_flags, String className, String superClass, String[] interfaceNames) {if (visitor == null) {return null;}return visitor.visit(access_flags, className, superClass, interfaceNames);}
@Overridepublic DexClassVisitor visit(int accessFlag, String name,String superClass, String[] itfClass) {ClassDefItem defItem = cp.putClassDefItem(accessFlag, name, superClass,itfClass);return new ClassWriter(defItem, cp);}
public ClassDefItem putClassDefItem(int accessFlag, String name, String superClass, String[] itfClass) {TypeIdItem type = uniqType(name);if (classDefs.containsKey(type)) {throw new DexWriteException("dup clz: " + name);}ClassDefItem classDefItem = new ClassDefItem();classDefItem.accessFlags = accessFlag;classDefItem.clazz = type;if (superClass != null) {classDefItem.superclazz = uniqType(superClass);}if (itfClass != null && itfClass.length > 0) {classDefItem.interfaces = putTypeList(Arrays.asList(itfClass));}classDefs.put(type, classDefItem);return classDefItem;}
创建了一个ClassDefItem并添加到classDefs,以ClassDefItem创建了一个ClassWriter并返回。
回到acceptFile
接下来以前面创建的ClassWriter为参数,分别调用visitSource ,acceptAnnotations acceptField acceptMethod 并最终调用它的visitEnd
@Overridepublic void visitSource(String file) {defItem.sourceFile = cp.uniqString(file);}
我们看acceptField
public static void acceptField(SmaliParser.SFieldContext ctx, String className, DexClassVisitor dexClassVisitor) {Field field;Token fieldObj = ctx.fieldObj;//获取fileld Tokenif (fieldObj.getType() == SmaliLexer.FIELD_FULL) {field = Utils.parseFieldAndUnescape(fieldObj.getText());} else {field = Utils.parseFieldAndUnescape(className, fieldObj.getText());//返回一个Field}int access = collectAccess(ctx.sAccList());//access标志Object value = null;SmaliParser.SBaseValueContext vctx = ctx.sBaseValue();if (vctx != null) {value = parseBaseValue(vctx);}DexFieldVisitor dexFieldVisitor = dexClassVisitor.visitField(access, field, value);//访问fieldif (dexFieldVisitor != null) {acceptAnnotations(ctx.sAnnotation(), dexFieldVisitor);//访问注解dexFieldVisitor.visitEnd();}}
public static Field parseFieldAndUnescape(String owner, String part) throws RuntimeException {int x = part.indexOf(':');if (x < 0) {throw new RuntimeException();}return new Field(owner, unEscapeId(part.substring(0, x)), unEscapeId(part.substring(x + 1)));}
调用class的visitField访问字段
public DexFieldVisitor visitField(int accessFlags, Field field, Object value) {final ClassDataItem.EncodedField encodedField = new ClassDataItem.EncodedField();encodedField.accessFlags = accessFlags;encodedField.field = cp.uniqField(field);if (value != null) {encodedField.staticValue = EncodedValue.wrap(cp.wrapEncodedItem(value));}if (0 != (ACC_STATIC & accessFlags)) { // is staticdataItem.staticFields.add(encodedField);} else {dataItem.instanceFields.add(encodedField);}return new FieldWriter(encodedField, cp);}
这里帮字段添加到staticValue 或instanceFields 然后新建一个FieldWriter返回
然后再看acceptMethod
private static void acceptMethod(List<SmaliParser.SMethodContext> sMethodContexts, String className, DexClassVisitor dexClassVisitor) {if (dexClassVisitor == null || sMethodContexts == null || sMethodContexts.size() == 0) {return;}for (SmaliParser.SMethodContext ctx : sMethodContexts) {acceptMethod(ctx, className, dexClassVisitor);}}
public static void acceptMethod(SmaliParser.SMethodContext ctx, String className, DexClassVisitor dexClassVisitor) {Method method;Token methodObj = ctx.methodObj;//获取方法对象if (methodObj.getType() == SmaliLexer.METHOD_FULL) {method = Utils.parseMethodAndUnescape(methodObj.getText());} else {// PARTmethod = Utils.parseMethodAndUnescape(className, methodObj.getText());//解析成一个Method对象}int access = collectAccess(ctx.sAccList());//access标志boolean isStatic = 0 != (access & DexConstants.ACC_STATIC);DexMethodVisitor dexMethodVisitor = dexClassVisitor.visitMethod(access, method);//访问方法if (dexMethodVisitor != null) {acceptAnnotations(ctx.sAnnotation(), dexMethodVisitor);//注解int ins = Utils.methodIns(method, isStatic);int totalRegisters = findTotalRegisters(ctx, ins);if (totalRegisters < 0) {totalRegisters = ins;}M m = new M(method, totalRegisters, ins, isStatic);acceptParameter(ctx.sParameter(), m, dexMethodVisitor);//访问参数acceptCode(ctx, m, dexMethodVisitor);//访问代码dexMethodVisitor.visitEnd();}}
调用parseMethodAndUnescape解析一个Method
public static Method parseMethodAndUnescape(String owner, String part) throws RuntimeException {int x = part.indexOf('(');if (x < 0) {throw new RuntimeException();}int y = part.indexOf(')', x);if (y < 0) {throw new RuntimeException();}String methodName = unEscapeId(part.substring(0, x));//方法名String[] params = toTypeList(part.substring(x + 1, y));//参数for (int i = 0; i < params.length; i++) {params[i] = unEscapeId(params[i]);}String ret = unEscapeId(part.substring(y + 1));//返回值return new Method(owner, methodName, params, ret);}
然后调用visitMethod访问方法
@Overridepublic DexMethodVisitor visitMethod(int accessFlags, Method method) {final ClassDataItem.EncodedMethod encodedMethod = new ClassDataItem.EncodedMethod();encodedMethod.accessFlags = accessFlags;encodedMethod.method = cp.uniqMethod(method);if (0 != (accessFlags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR))) {dataItem.directMethods.add(encodedMethod);} else {dataItem.virtualMethods.add(encodedMethod);}return new MethodWriter(encodedMethod, method,0 != (accessFlags & ACC_STATIC), cp);}
根据方法的类型添加到virtualMethods或 directMethods,然后返回了一个MethodWriter
回到acceptMethod
Utils.methodIn计算方法参数的大小,非静态方法要加1
findTotalRegisters计算寄存器数量,主要是计算局部变量,参数的个数
private static int findTotalRegisters(SmaliParser.SMethodContext ctx, int ins) {int totalRegisters = -1;List<SmaliParser.SInstructionContext> instructionContexts = ctx.sInstruction();for (SmaliParser.SInstructionContext instructionContext : instructionContexts) {ParserRuleContext parserRuleContext = (ParserRuleContext) instructionContext.getChild(0);if (parserRuleContext != null) {int ruleIndex = parserRuleContext.getRuleIndex();if (ruleIndex == SmaliParser.RULE_fregisters) {totalRegisters = parseInt(((SmaliParser.FregistersContext) parserRuleContext).xregisters.getText());break;} else if (ruleIndex == SmaliParser.RULE_flocals) {totalRegisters = ins + parseInt(((SmaliParser.FlocalsContext) parserRuleContext).xlocals.getText());break;}}}return totalRegisters;}
新建了一个M类
M(Method method, int totals, int ins, boolean isStatic) {this.locals = totals - ins;this.total = totals;String paramTypes[] = method.getParameterTypes();paramNames = new String[paramTypes.length];map = new int[ins];int start = 0;if (!isStatic) {map[start] = -1;start++;}for (int i = 0; i < paramTypes.length; i++) {char t = paramTypes[i].charAt(0);map[start++] = i;if (t == 'J' || t == 'D') {map[start++] = i;}}}
acceptParameter解析参数
private static void acceptParameter(List<SmaliParser.SParameterContext> sParameterContexts, M m, DexMethodVisitor dexMethodVisitor) {if (sParameterContexts == null || sParameterContexts.size() == 0 || dexMethodVisitor == null) {return;}boolean hasParam = false;boolean hasParamter = false;for (SmaliParser.SParameterContext ctx : sParameterContexts) {if (ctx.param != null) {hasParam = true;}if (ctx.parameter != null) {hasParamter = true;}}if (hasParam && hasParamter) {throw new RuntimeException("cant mix use .param and .parameter on method");}for (int i = 0; i < sParameterContexts.size(); i++) {SmaliParser.SParameterContext ctx = sParameterContexts.get(i);int index;if (ctx.param != null) {index = m.regToParamIdx(m.pareReg(ctx.r.getText()));} else {index = i;}if (ctx.name != null) {m.setNameByIdx(index, unescapeStr(ctx.name.getText()));}List<SmaliParser.SAnnotationContext> annotationContexts = ctx.sAnnotation();if (annotationContexts.size() > 0) {acceptAnnotations(annotationContexts, dexMethodVisitor.visitParameterAnnotation(index));}}}
acceptCode访问代码
private static void acceptCode(SmaliParser.SMethodContext ctx, final M m, DexMethodVisitor dexMethodVisitor) {if (ctx == null || dexMethodVisitor == null) {return;}final DexCodeVisitor dexCodeVisitor = dexMethodVisitor.visitCode();//返回一个DexCodeVisitorif (dexCodeVisitor == null) {return;}final SmaliCodeVisitor scv = new SmaliCodeVisitor(dexCodeVisitor);//新建一个SmaliCodeVisitor,具体的转换是由它来执行的final DexDebugVisitor dexDebugVisitor = scv.visitDebug();final List<SmaliParser.SInstructionContext> instructionContexts = ctx.sInstruction();final SmaliBaseVisitor v = new SmaliBaseVisitor() {//新建一个SmaliBaseVisitor,实现了各种访问方法把smail指令转换为dex指令@Overridepublic Object visitFregisters(SmaliParser.FregistersContext ctx) {return null;}@Overridepublic Object visitFlocals(SmaliParser.FlocalsContext ctx) {return null;}@Overridepublic Object visitFline(SmaliParser.FlineContext ctx) {if (dexDebugVisitor != null) {//行号相关,是否调试DexLabel dexLabel = new DexLabel();scv.visitLabel(dexLabel);dexDebugVisitor.visitLineNumber(Utils.parseInt(ctx.line.getText()), dexLabel);}return null;}@Overridepublic Object visitFend(SmaliParser.FendContext ctx) {if (dexDebugVisitor != null) {DexLabel dexLabel = new DexLabel();scv.visitLabel(dexLabel);int reg = m.pareReg(ctx.r.getText());dexDebugVisitor.visitEndLocal(reg, dexLabel);}return null;}@Overridepublic Object visitFlocal(SmaliParser.FlocalContext ctx) {if (dexDebugVisitor != null) {DexLabel dexLabel = new DexLabel();scv.visitLabel(dexLabel);int reg = m.pareReg(ctx.r.getText());String name;String type;if (ctx.v1 != null) {Field fld = parseFieldAndUnescape("Lt;", ctx.v1.getText());name = fld.getName();type = fld.getType();} else if (ctx.v2 != null) {String txt = ctx.v2.getText();int i = findString(txt, 1, txt.length(), '\"');name = unescapeStr(txt.substring(0, i + 1));type = unEscapeId(txt.substring(i + 2));} else {if (ctx.name2 != null) {name = unescapeStr(ctx.name2.getText());} else {name = unEscapeId(ctx.name1.getText());}type = unEscapeId(ctx.type.getText());}String sig = ctx.sig == null ? null : unescapeStr(ctx.sig.getText());dexDebugVisitor.visitStartLocal(reg, dexLabel, name, type, sig);}return null;}@Overridepublic Object visitFrestart(SmaliParser.FrestartContext ctx) {if (dexDebugVisitor != null) {DexLabel dexLabel = new DexLabel();scv.visitLabel(dexLabel);int reg = m.pareReg(ctx.r.getText());dexDebugVisitor.visitRestartLocal(reg, dexLabel);}return null;}@Overridepublic Object visitFprologue(SmaliParser.FprologueContext ctx) {if (dexDebugVisitor != null) {DexLabel dexLabel = new DexLabel();scv.visitLabel(dexLabel);dexDebugVisitor.visitPrologue(dexLabel);}return null;}Map<String, DexLabel> labelMap = new HashMap<>();@Overridepublic Object visitSLabel(SmaliParser.SLabelContext ctx) {scv.visitLabel(getLabel(ctx.label.getText()));return null;}@Overridepublic Object visitFspareswitch(SmaliParser.FspareswitchContext ctx) {List<TerminalNode> ints = ctx.INT();List<TerminalNode> ts = ctx.LABEL();int cases[] = new int[ts.size()];DexLabel labels[] = new DexLabel[ts.size()];for (int i = 0; i < ts.size(); i++) {cases[i] = parseInt(ints.get(i).getSymbol().getText());labels[i] = getLabel(ts.get(i).getSymbol().getText());}scv.dSparseSwitch(cases, labels);return null;}@Overridepublic Object visitFarraydata(SmaliParser.FarraydataContext ctx) {int size = parseInt(ctx.size.getText());List<SmaliParser.SBaseValueContext> ts = ctx.sBaseValue();byte[] ps = new byte[ts.size()];for (int i = 0; i < ts.size(); i++) {ps[i] = ((Number) parseBaseValue(ts.get(i))).byteValue();}scv.dArrayData(size, ps);return null;}Op getOp(Token t) {return Utils.getOp(t.getText());}@Overridepublic Object visitF0x(SmaliParser.F0xContext ctx) {scv.visitStmt0R(getOp(ctx.op));return null;}@Overridepublic Object visitF0t(SmaliParser.F0tContext ctx) {scv.visitJumpStmt(getOp(ctx.op), 0, 0, getLabel(ctx.target.getText()));return null;}@Overridepublic Object visitF1x(SmaliParser.F1xContext ctx) {scv.visitStmt1R(getOp(ctx.op), m.pareReg(ctx.r1.getText()));return null;}@Overridepublic Object visitFconst(SmaliParser.FconstContext ctx) {Op op = getOp(ctx.op);int r = m.pareReg(ctx.r1.getText());Token cst = ctx.cst;switch (op) {case CONST_STRING:case CONST_STRING_JUMBO:scv.visitConstStmt(op, r, unescapeStr(cst.getText()));break;case CONST_CLASS:scv.visitConstStmt(op, r, new DexType(unEscapeId(cst.getText())));break;case CHECK_CAST:case NEW_INSTANCE:scv.visitTypeStmt(op, r, 0, unEscapeId(cst.getText()));break;case CONST_WIDE:scv.visitConstStmt(op, r, cst.getType() == SmaliLexer.INT ? ((long) parseInt(cst.getText())) : parseLong(cst.getText()));break;case CONST_WIDE_16: {long v;if (cst.getType() == SmaliLexer.LONG) {v = parseLong(cst.getText());} else {v = (short) parseInt(cst.getText());}scv.visitConstStmt(op, r, v);}break;case CONST_WIDE_32: {long v;if (cst.getType() == SmaliLexer.LONG) {v = parseLong(cst.getText());} else {v = parseInt(cst.getText());}scv.visitConstStmt(op, r, v);}break;case CONST_WIDE_HIGH16: {long v;if (cst.getType() == SmaliLexer.LONG) {v = parseLong(cst.getText());} else {v = (short) parseInt(cst.getText());v <<= 48;}scv.visitConstStmt(op, r, v);}break;case CONST:case CONST_4:case CONST_16: {int v = parseInt(cst.getText());scv.visitConstStmt(op, r, v);}break;case CONST_HIGH16: {int v = parseInt(cst.getText());v <<= 16;scv.visitConstStmt(op, r, v);}break;default:throw new RuntimeException();}return null;}@Overridepublic Object visitFf1c(SmaliParser.Ff1cContext ctx) {int r = m.pareReg(ctx.r1.getText());Field field = parseFieldAndUnescape(ctx.fld.getText());scv.visitFieldStmt(getOp(ctx.op), r, 0, field);return null;}@Overridepublic Object visitFt2c(SmaliParser.Ft2cContext ctx) {int r1 = m.pareReg(ctx.r1.getText());int r2 = m.pareReg(ctx.r2.getText());scv.visitTypeStmt(getOp(ctx.op), r1, r2, unEscapeId(ctx.type.getText()));return null;}@Overridepublic Object visitFf2c(SmaliParser.Ff2cContext ctx) {int r1 = m.pareReg(ctx.r1.getText());int r2 = m.pareReg(ctx.r2.getText());scv.visitFieldStmt(getOp(ctx.op), r1, r2, parseFieldAndUnescape(ctx.fld.getText()));return null;}@Overridepublic Object visitF2x(SmaliParser.F2xContext ctx) {int r1 = m.pareReg(ctx.r1.getText());int r2 = m.pareReg(ctx.r2.getText());scv.visitStmt2R(getOp(ctx.op), r1, r2);return null;}@Overridepublic Object visitF3x(SmaliParser.F3xContext ctx) {int r1 = m.pareReg(ctx.r1.getText());int r2 = m.pareReg(ctx.r2.getText());int r3 = m.pareReg(ctx.r3.getText());scv.visitStmt3R(getOp(ctx.op), r1, r2, r3);return null;}@Overridepublic Object visitFt5c(SmaliParser.Ft5cContext ctx) {Op op = getOp(ctx.op);List<TerminalNode> ts = ctx.REGISTER();int rs[] = new int[ts.size()];for (int i = 0; i < ts.size(); i++) {rs[i] = m.pareReg(ts.get(i).getSymbol().getText());}scv.visitFilledNewArrayStmt(op, rs, unEscapeId(ctx.type.getText()));return null;}@Overridepublic Object visitFm5c(SmaliParser.Fm5cContext ctx) {Op op = getOp(ctx.op);List<TerminalNode> ts = ctx.REGISTER();int rs[] = new int[ts.size()];for (int i = 0; i < ts.size(); i++) {rs[i] = m.pareReg(ts.get(i).getSymbol().getText());}scv.visitMethodStmt(op, rs, parseMethodAndUnescape(ctx.method.getText()));return null;}@Overridepublic Object visitFmrc(SmaliParser.FmrcContext ctx) {if (ctx.rstart != null) {int start = m.pareReg(ctx.rstart.getText());int end = m.pareReg(ctx.rend.getText());int size = end - start + 1;int rs[] = new int[size];for (int i = 0; i < size; i++) {rs[i] = start + i;}scv.visitMethodStmt(getOp(ctx.op), rs, parseMethodAndUnescape(ctx.method.getText()));} else {scv.visitMethodStmt(getOp(ctx.op), new int[0], parseMethodAndUnescape(ctx.method.getText()));}return null;}@Overridepublic Object visitFtrc(SmaliParser.FtrcContext ctx) {if (ctx.rstart != null) {int start = m.pareReg(ctx.rstart.getText());int end = m.pareReg(ctx.rend.getText());int size = end - start + 1;int rs[] = new int[size];for (int i = 0; i < size; i++) {rs[i] = start + i;}scv.visitFilledNewArrayStmt(getOp(ctx.op), rs, unEscapeId(ctx.type.getText()));} else {scv.visitFilledNewArrayStmt(getOp(ctx.op), new int[0], unEscapeId(ctx.type.getText()));}return null;}@Overridepublic Object visitF31t(SmaliParser.F31tContext ctx) {scv.visitF31tStmt(getOp(ctx.op), m.pareReg(ctx.r1.getText()), getLabel(ctx.label.getText()));return null;}@Overridepublic Object visitF1t(SmaliParser.F1tContext ctx) {scv.visitJumpStmt(getOp(ctx.op), m.pareReg(ctx.r1.getText()), 0, getLabel(ctx.label.getText()));return null;}@Overridepublic Object visitF2t(SmaliParser.F2tContext ctx) {scv.visitJumpStmt(getOp(ctx.op), m.pareReg(ctx.r1.getText()), m.pareReg(ctx.r2.getText()), getLabel(ctx.label.getText()));return null;}@Overridepublic Object visitF2sb(SmaliParser.F2sbContext ctx) {scv.visitStmt2R1N(getOp(ctx.op), m.pareReg(ctx.r1.getText()), m.pareReg(ctx.r2.getText()), parseInt(ctx.lit.getText()));return null;}@Overridepublic Object visitFpackageswitch(SmaliParser.FpackageswitchContext ctx) {int start = parseInt(ctx.start.getText());List<TerminalNode> ts = ctx.LABEL();DexLabel labels[] = new DexLabel[ts.size()];for (int i = 0; i < ts.size(); i++) {labels[i] = getLabel(ts.get(i).getSymbol().getText());}scv.dPackedSwitch(start, labels);return null;}@Overridepublic Object visitFcache(SmaliParser.FcacheContext ctx) {scv.visitTryCatch(getLabel(ctx.start.getText()), getLabel(ctx.end.getText()),new DexLabel[]{getLabel(ctx.handle.getText())},new String[]{unEscapeId(ctx.type.getText())});return null;}@Overridepublic Object visitFcacheall(SmaliParser.FcacheallContext ctx) {scv.visitTryCatch(getLabel(ctx.start.getText()), getLabel(ctx.end.getText()),new DexLabel[]{getLabel(ctx.handle.getText())},new String[]{null});return null;}DexLabel getLabel(String name) {DexLabel dexLabel = labelMap.get(name);if (dexLabel == null) {dexLabel = new DexLabel();labelMap.put(name, dexLabel);}return dexLabel;}@Overridepublic Object visitFepiogue(SmaliParser.FepiogueContext ctx) {if (dexDebugVisitor != null) {DexLabel dexLabel = new DexLabel();scv.visitLabel(dexLabel);dexDebugVisitor.visitEpiogue(dexLabel);}return null;}};scv.visitRegister(m.total);if (dexDebugVisitor != null) {for (int i = 0; i < m.paramNames.length; i++) {String name = m.paramNames[i];if (name != null) {dexDebugVisitor.visitParameterName(i, name);}}}for (SmaliParser.SInstructionContext instructionContext : instructionContexts) {ParserRuleContext parserRuleContext = (ParserRuleContext) instructionContext.getChild(0);parserRuleContext.accept(v);}scv.visitEnd();}private static int findTotalRegisters(SmaliParser.SMethodContext ctx, int ins) {int totalRegisters = -1;List<SmaliParser.SInstructionContext> instructionContexts = ctx.sInstruction();for (SmaliParser.SInstructionContext instructionContext : instructionContexts) {ParserRuleContext parserRuleContext = (ParserRuleContext) instructionContext.getChild(0);if (parserRuleContext != null) {int ruleIndex = parserRuleContext.getRuleIndex();if (ruleIndex == SmaliParser.RULE_fregisters) {totalRegisters = parseInt(((SmaliParser.FregistersContext) parserRuleContext).xregisters.getText());break;} else if (ruleIndex == SmaliParser.RULE_flocals) {totalRegisters = ins + parseInt(((SmaliParser.FlocalsContext) parserRuleContext).xlocals.getText());break;}}}return totalRegisters;}
代码都在这里了,这里
1、调用DexMethodVisitor的visitor先获取一个DexCodeVisitor,
2、以他构建一个SmaliCodeVisitor,指令的转换都是由SmaliCodeVisitor完成的
3、调用ctx.sInstruction()获取所有指令
4、构建一个SmaliBaseVisitor,它里面对Debug做了一些处理,具体的指令转换还是由上面的SmaliCodeVisitor完成
5、对所有的指令,调用ParserRuleContext的accept并以前面的SmaliBaseVisitor为参数解析该指令
再看下SmaliCodeVisitor
public class SmaliCodeVisitor extends DexCodeNode {public SmaliCodeVisitor(DexCodeVisitor visitor) {super(visitor);}@Overridepublic void visitConstStmt(Op op, int ra, Object value) {switch (op) {case CONST_WIDE_16: {if(value instanceof Integer) {short v = ((Number) value).shortValue();super.visitConstStmt(op, ra, (long) v);} else {super.visitConstStmt(op, ra, value);}}break;case CONST_WIDE_HIGH16: {if(value instanceof Integer) {short v = ((Number) value).shortValue();super.visitConstStmt(op, ra, ((long) v) << 48);} else {super.visitConstStmt(op, ra, value);}}break;case CONST_WIDE_32: {if(value instanceof Integer) {int v = ((Number) value).intValue();super.visitConstStmt(op, ra, (long) v);} else {super.visitConstStmt(op, ra, value);}}break;case CONST_HIGH16: {int v = ((Number) value).intValue();if(0 != (v & 0xFFff0000)){super.visitConstStmt(op, ra, v);} else {super.visitConstStmt(op, ra, v << 16);}}break;default:super.visitConstStmt(op, ra, value);break;}}public static class ArrayDataStmt extends DexStmtNode {int length;byte[] objs;public ArrayDataStmt(int length, byte[] obj) {super(null);this.length = length;this.objs = obj;}@Overridepublic void accept(DexCodeVisitor cv) {}}public static class PackedSwitchStmt extends DexStmtNode {int firstCase;DexLabel[] labels;public PackedSwitchStmt(int reg, DexLabel[] labels) {super(null);this.firstCase = reg;this.labels = labels;}@Overridepublic void accept(DexCodeVisitor cv) {}}public static class SparseSwitchStmt extends DexStmtNode {int[] cases;DexLabel labels[];public SparseSwitchStmt(int[] cases, DexLabel[] labels) {super(null);this.cases = cases;this.labels = labels;}@Overridepublic void accept(DexCodeVisitor cv) {}}private List<DexStmtNode> needCareStmts = new ArrayList<DexStmtNode>();@Overridepublic void visitEnd() {if (super.visitor != null) {super.accept(super.visitor);}needCareStmts = null;stmts = null;tryStmts = null;super.visitEnd();}private void addCare(DexStmtNode stmt) {needCareStmts.add(stmt);super.add(stmt);}/* package */void dArrayData(int length, byte[] obj) {addCare(new ArrayDataStmt(length, obj));}void dPackedSwitch(int first, DexLabel[] labels) {addCare(new PackedSwitchStmt(first, labels));}/* package */void dSparseSwitch(int[] cases, DexLabel[] labels) {addCare(new SparseSwitchStmt(cases, labels));}int findLabelIndex(DexLabel label) {int labelIndex = -1;for (int i = 0; i < needCareStmts.size(); i++) {DexStmtNode s = needCareStmts.get(i);if (s instanceof DexLabelStmtNode) {DexLabelStmtNode ss = (DexLabelStmtNode) s;if (ss.label == label) {labelIndex = i;}}}return labelIndex;}/* package */void visitF31tStmt(final Op op, final int reg, final DexLabel label) {add(new DexStmtNode(op) {@Overridepublic void accept(DexCodeVisitor cv) {int labelIndex = findLabelIndex(label);if (labelIndex < 0 || labelIndex >= needCareStmts.size()) {throw new RuntimeException("can't find label for " + op + " " + label);}switch (op) {case PACKED_SWITCH:PackedSwitchStmt packedSwitchStmt = (PackedSwitchStmt) needCareStmts.get(labelIndex + 1);cv.visitPackedSwitchStmt(op, reg, packedSwitchStmt.firstCase, packedSwitchStmt.labels);break;case SPARSE_SWITCH:SparseSwitchStmt sparseSwitchStmt = (SparseSwitchStmt) needCareStmts.get(labelIndex + 1);cv.visitSparseSwitchStmt(op, reg, sparseSwitchStmt.cases, sparseSwitchStmt.labels);break;case FILL_ARRAY_DATA:ArrayDataStmt arrayDataStmt = (ArrayDataStmt) needCareStmts.get(labelIndex + 1);Object v;byte[] vs = arrayDataStmt.objs;switch (arrayDataStmt.length) {case 1: {v = vs;}break;case 2: {short[] vs1 = new short[vs.length / 2];for (int i = 0; i < vs1.length; i++) {vs1[i] = (short) ((vs[i * 2] & 0xFF) | ((vs[i * 2 + 1] & 0xFF) << 8));}v = vs1;}break;case 4: {int[] vs1 = new int[vs.length / 4];for (int i = 0; i < vs1.length; i++) {int base = i * 4;vs1[i] = (vs[base + 0] & 0xFF) | ((vs[base + 1] & 0xFF) << 8)| ((vs[base + 2] & 0xFF) << 16) | ((vs[base + 3] & 0xFF) << 24);}v = vs1;}break;case 8: {long[] vs1 = new long[vs.length / 8];for (int i = 0; i < vs1.length; i++) {int base = i * 8;int a = ((vs[base + 0] & 0xFF) << 0) | ((vs[base + 1] & 0xFF) << 8)| ((vs[base + 2] & 0xFF) << 16) | ((vs[base + 3] & 0xFF) << 24);int b = ((vs[base + 4] & 0xFF) << 0) | ((vs[base + 5] & 0xFF) << 8)| ((vs[base + 6] & 0xFF) << 16) | ((vs[base + 7] & 0xFF) << 24);vs1[i] = (((long) b) << 32) | a;}v = vs1;}break;default:throw new RuntimeException();}cv.visitFillArrayDataStmt(Op.FILL_ARRAY_DATA, reg, v);break;default:throw new RuntimeException();}}});}@Overridepublic void visitLabel(final DexLabel label) {addCare(new DexLabelStmtNode(label));}
}
它继承于DexCodeNode,实现了转换dex指令
最后调用visitEnd,这里是一个方法里面的code访问完了
@Overridepublic void visitEnd() {if (super.visitor != null) {super.accept(super.visitor);}needCareStmts = null;stmts = null;tryStmts = null;super.visitEnd();}
public void accept(DexCodeVisitor v) {if (tryStmts != null) {for (TryCatchNode n : tryStmts) {n.accept(v);}}if (debugNode != null) {DexDebugVisitor ddv = v.visitDebug();if (ddv != null) {debugNode.accept(ddv);ddv.visitEnd();}}if (totalRegister >= 0) {//使用的寄存器v.visitRegister(this.totalRegister);}for (DexStmtNode n : stmts) {//对于所有指令,调用accept 并传递前面的DexCodeVisitorn.accept(v);}}
当一个文件访问完毕的时候,回到acceptFile,调用dexClassVisitor.visitEnd()
public void visitEnd() {if (dataItem != null && dataItem.getMemberSize() > 0) {cp.addClassDataItem(dataItem);defItem.classData = dataItem;}defItem.prepare(cp);}
public void prepare(ConstPool cp) {if (classData != null) {classData.prepare(cp);}preparteAnnotationsDirectoryItem(cp);prepareEncodedArrayItem(cp);}
这里会对解析的进行一些处理,以便于后面写文件
当所有的smail文件都访问完毕,我们回到doCommandLine,调用fw.toByteArray(),获取二进制数据
public byte[] toByteArray() {// init structure for writingbuildMapListItem();//构建各个字段、Head等// place all item into file, we can know the size nowfinal int size = place();ByteBuffer buffer = ByteBuffer.allocate(size);DataOut out = new ByteBufferOut(buffer);if (DEBUG) {out = wrapDumpOut(out);}// write itwrite(out);if (size != buffer.position()) {throw new RuntimeException("generated different file size, planned " + size + ", but is " + buffer.position());}// update the CRC/ sha1 checksum in dex headerupdateChecksum(buffer, size);//更新crc和sha1return buffer.array();}
这里主要看下buildMapListItem
void buildMapListItem() {// begin ===========// satisfy 'bool DexFileVerifier::CheckMap()' on art/runtime/dex_file_verifier.cc// make sure the items are not emptyif (cp.classDefs.isEmpty()) {//类System.err.println("WARN: no classdef on the dex");}if (cp.methods.isEmpty()) {//方法cp.uniqMethod("Ljava/lang/Object;", "<init>", new String[0], "V");}if (cp.fields.isEmpty()) {//字段cp.uniqField("Ljava/lang/System;", "out", "Ljava/io/PrintStream;");}if (cp.protos.isEmpty()) {//ProtoId结构cp.uniqProto(new String[0], "V");}if (cp.types.isEmpty()) {//类型cp.uniqType("V");}if (cp.strings.isEmpty()) {//字符串cp.uniqString("V");}// end ===========mapItem = new MapListItem();headItem = new HeadItem();//头SectionItem<HeadItem> headSection = new SectionItem<>(SectionType.TYPE_HEADER_ITEM);headSection.items.add(headItem);SectionItem<MapListItem> mapSection = new SectionItem<MapListItem>(SectionType.TYPE_MAP_LIST);mapSection.items.add(mapItem);SectionItem<StringIdItem> stringIdSection = new SectionItem<>(SectionType.TYPE_STRING_ID_ITEM, cp.strings.values());SectionItem<TypeIdItem> typeIdSection = new SectionItem<>(SectionType.TYPE_TYPE_ID_ITEM, cp.types.values());SectionItem<ProtoIdItem> protoIdSection = new SectionItem<>(SectionType.TYPE_PROTO_ID_ITEM, cp.protos.values());SectionItem<FieldIdItem> fieldIdSection = new SectionItem<>(SectionType.TYPE_FIELD_ID_ITEM, cp.fields.values());SectionItem<MethodIdItem> methodIdSection = new SectionItem<>(SectionType.TYPE_METHOD_ID_ITEM, cp.methods.values());SectionItem<ClassDefItem> classDefSection = new SectionItem<>(SectionType.TYPE_CLASS_DEF_ITEM, cp.buildSortedClassDefItems());SectionItem<TypeListItem> typeListSection = new SectionItem<>(SectionType.TYPE_TYPE_LIST, cp.typeLists.values());SectionItem<AnnotationSetRefListItem> annotationSetRefListItemSection = new SectionItem<>(SectionType.TYPE_ANNOTATION_SET_REF_LIST,cp.annotationSetRefListItems.values());SectionItem<AnnotationSetItem> annotationSetSection = new SectionItem<>(SectionType.TYPE_ANNOTATION_SET_ITEM,cp.annotationSetItems.values());SectionItem<ClassDataItem> classDataItemSection = new SectionItem<>(SectionType.TYPE_CLASS_DATA_ITEM, cp.classDataItems);SectionItem<CodeItem> codeItemSection = new SectionItem<>(SectionType.TYPE_CODE_ITEM, cp.codeItems);SectionItem<StringDataItem> stringDataItemSection = new SectionItem<>(SectionType.TYPE_STRING_DATA_ITEM, cp.stringDatas);SectionItem<DebugInfoItem> debugInfoSection = new SectionItem<>(SectionType.TYPE_DEBUG_INFO_ITEM, cp.debugInfoItems);SectionItem<AnnotationItem> annotationItemSection = new SectionItem<>(SectionType.TYPE_ANNOTATION_ITEM, cp.annotationItems.values());SectionItem<EncodedArrayItem> encodedArrayItemSection = new SectionItem<>(SectionType.TYPE_ENCODED_ARRAY_ITEM, cp.encodedArrayItems);SectionItem<AnnotationsDirectoryItem> annotationsDirectoryItemSection = new SectionItem<>(SectionType.TYPE_ANNOTATIONS_DIRECTORY_ITEM,cp.annotationsDirectoryItems);{headItem.mapSection = mapSection;//设置头headItem.stringIdSection = stringIdSection;headItem.typeIdSection = typeIdSection;headItem.protoIdSection = protoIdSection;headItem.fieldIdSection = fieldIdSection;headItem.methodIdSection = methodIdSection;headItem.classDefSection = classDefSection;}List<SectionItem<?>> dataSectionItems = new ArrayList<>();{dataSectionItems.add(mapSection); // data sectiondataSectionItems.add(typeListSection);// data sectiondataSectionItems.add(annotationSetRefListItemSection);// data// sectiondataSectionItems.add(annotationSetSection);// data section// make codeItem Before classDataItemdataSectionItems.add(codeItemSection);// data sectiondataSectionItems.add(classDataItemSection);// data sectiondataSectionItems.add(stringDataItemSection);// data sectiondataSectionItems.add(debugInfoSection);// data sectiondataSectionItems.add(annotationItemSection);// data sectiondataSectionItems.add(encodedArrayItemSection);// data sectiondataSectionItems.add(annotationsDirectoryItemSection);// data// section}List<SectionItem<?>> items = mapItem.items;{items.add(headSection);items.add(stringIdSection);items.add(typeIdSection);items.add(protoIdSection);items.add(fieldIdSection);items.add(methodIdSection);items.add(classDefSection);items.addAll(dataSectionItems);}// cp is useless now since all value are copied nowcp.clean();cp = null;}
主要是根据前面的解析结果,和dex文件的结构设置相关字段的值。
到这里smail转换为dex就已经完成了。跟前面的其他转换相比,主要是这里用到了一个词法解析器,而前面的都是自己在代码中实现解析的。
dex2jar源码解析----smail转dex相关推荐
- dex2jar源码解析----dex转smail
我们以BaksmaliTest为例,查看一下Dex转smail的流程 public class BaksmaliTest {@Testpublic void t() throws Exception ...
- dex2jar源码解析----解析dex文件一
Dex2jar命令在Dex2jarCmd.java文件中 public static void main(String... args) {new Dex2jarCmd().doMain(args); ...
- replugin源码解析之replugin-plugin-gradle(插件的gradle插件)
前言 replugin-plugin-gradle 是 RePlugin 插件框架中提供给replugin插件用的gradle插件,是一种动态编译方案实现. 主要在插件应用的编译期,基于Transfo ...
- 免Root 实现App加载Xposed插件的工具Xpatch源码解析(一)
前言 Xpatch是一款免Root实现App加载Xposed插件的工具,可以非常方便地实现App的逆向破解(再也不用改smali代码了),源码也已经上传到Github上,欢迎各位Fork and St ...
- Alibaba-AndFix Bug热修复框架原理及源码解析
小憩之后,继续为你解读AndFix热修复框架,呵呵. 上一篇Alibaba-AndFix Bug热修复框架的使用已经介绍了AndFix的使用,这篇主要介绍AndFix原理以及源码解析. AndFix原 ...
- ARouter 源码解析(1.5.2 版本)
文章目录 1.简介 2.ARouter 配置与基本用法 2.1 依赖引入与配置 2.2 基本用法 3.ARouter 编译时原理分析 4.ARouter 源码解析 4.1 ARouter 源码主要代码 ...
- Multidex记录三:源码解析
个人博客地址 http://dandanlove.com/ Multidex记录一:介绍和使用 Multidex记录二:缺陷&解决 Multidex记录三:源码解析 记录Multidex源码解 ...
- 谷歌BERT预训练源码解析(二):模型构建
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...
- 谷歌BERT预训练源码解析(三):训练过程
目录 前言 源码解析 主函数 自定义模型 遮蔽词预测 下一句预测 规范化数据集 前言 本部分介绍BERT训练过程,BERT模型训练过程是在自己的TPU上进行的,这部分我没做过研究所以不做深入探讨.BE ...
- 谷歌BERT预训练源码解析(一):训练数据生成
目录 预训练源码结构简介 输入输出 源码解析 参数 主函数 创建训练实例 下一句预测&实例生成 随机遮蔽 输出 结果一览 预训练源码结构简介 关于BERT,简单来说,它是一个基于Transfo ...
最新文章
- 利用BP神经网络教计算机识别语音特征信号(代码部分SL)
- cuda runtime error (59) : device-side assert triggered when running transfer_learning_
- Ubuntu LXC
- flink中写入行存储、列存储时设定checkpoint多久时间间隔另存一个新文件
- 淘宝Tprofiler工具实现分析
- jeval 公式_几款公式解析工具的比较
- 电商ERP软件、订单管理系统、库存管理系统
- 【React】react实现前端播放m3u8格式视频
- Root原理分析及防Root新思路
- android保存裁剪图片,Android选择图片并裁剪,无法保存经过裁剪的图片
- autocad怎么用计算机,职称计算机AutoCAD实用技巧
- Python中filter筛选函数匿名参数问题
- 【C#】打印机ZPL指令打印图片,将图片转成十六进制指令
- 你会选择逃离北上广回小城市写代码吗?
- 你怎么保存微博中喜欢的视频
- 无显示器主机配置服务器
- 励志名言名句,2018励志名言名句大全
- vc常见问题108问-很有资料价值
- 江苏省南京市2021年电子信息申报通知(中、高级)
- c语言引用性间接变量,c语言取地址和间接引用
热门文章
- Topaz Adjust AI(HDR渲染滤镜) v1.0.0直装破解版
- linux支持hd610显卡吗,奔腾G4560核显怎么样且HD610相当于什么级别的显卡?
- 迷你迅雷 vs. QQ旋风
- AutoCAD输出矢量图
- RouterOS 端口映射
- Python SQLite3 教程
- SQL 2008升级SQL 2008 R2完全教程或者10 00 1600升级10 50 1600
- python监测网页变化_Python利用Last-Modified实现监控网页变化
- 2008年17款远程控制软件大比拼
- 正二十面体的各个面位置点