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相关推荐

  1. dex2jar源码解析----dex转smail

    我们以BaksmaliTest为例,查看一下Dex转smail的流程 public class BaksmaliTest {@Testpublic void t() throws Exception ...

  2. dex2jar源码解析----解析dex文件一

    Dex2jar命令在Dex2jarCmd.java文件中 public static void main(String... args) {new Dex2jarCmd().doMain(args); ...

  3. replugin源码解析之replugin-plugin-gradle(插件的gradle插件)

    前言 replugin-plugin-gradle 是 RePlugin 插件框架中提供给replugin插件用的gradle插件,是一种动态编译方案实现. 主要在插件应用的编译期,基于Transfo ...

  4. 免Root 实现App加载Xposed插件的工具Xpatch源码解析(一)

    前言 Xpatch是一款免Root实现App加载Xposed插件的工具,可以非常方便地实现App的逆向破解(再也不用改smali代码了),源码也已经上传到Github上,欢迎各位Fork and St ...

  5. Alibaba-AndFix Bug热修复框架原理及源码解析

    小憩之后,继续为你解读AndFix热修复框架,呵呵. 上一篇Alibaba-AndFix Bug热修复框架的使用已经介绍了AndFix的使用,这篇主要介绍AndFix原理以及源码解析. AndFix原 ...

  6. ARouter 源码解析(1.5.2 版本)

    文章目录 1.简介 2.ARouter 配置与基本用法 2.1 依赖引入与配置 2.2 基本用法 3.ARouter 编译时原理分析 4.ARouter 源码解析 4.1 ARouter 源码主要代码 ...

  7. Multidex记录三:源码解析

    个人博客地址 http://dandanlove.com/ Multidex记录一:介绍和使用 Multidex记录二:缺陷&解决 Multidex记录三:源码解析 记录Multidex源码解 ...

  8. 谷歌BERT预训练源码解析(二):模型构建

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...

  9. 谷歌BERT预训练源码解析(三):训练过程

    目录 前言 源码解析 主函数 自定义模型 遮蔽词预测 下一句预测 规范化数据集 前言 本部分介绍BERT训练过程,BERT模型训练过程是在自己的TPU上进行的,这部分我没做过研究所以不做深入探讨.BE ...

  10. 谷歌BERT预训练源码解析(一):训练数据生成

    目录 预训练源码结构简介 输入输出 源码解析 参数 主函数 创建训练实例 下一句预测&实例生成 随机遮蔽 输出 结果一览 预训练源码结构简介 关于BERT,简单来说,它是一个基于Transfo ...

最新文章

  1. 利用BP神经网络教计算机识别语音特征信号(代码部分SL)
  2. cuda runtime error (59) : device-side assert triggered when running transfer_learning_
  3. Ubuntu LXC
  4. flink中写入行存储、列存储时设定checkpoint多久时间间隔另存一个新文件
  5. 淘宝Tprofiler工具实现分析
  6. jeval 公式_几款公式解析工具的比较
  7. 电商ERP软件、订单管理系统、库存管理系统
  8. 【React】react实现前端播放m3u8格式视频
  9. Root原理分析及防Root新思路
  10. android保存裁剪图片,Android选择图片并裁剪,无法保存经过裁剪的图片
  11. autocad怎么用计算机,职称计算机AutoCAD实用技巧
  12. Python中filter筛选函数匿名参数问题
  13. 【C#】打印机ZPL指令打印图片,将图片转成十六进制指令
  14. 你会选择逃离北上广回小城市写代码吗?
  15. 你怎么保存微博中喜欢的视频
  16. 无显示器主机配置服务器
  17. 励志名言名句,2018励志名言名句大全
  18. vc常见问题108问-很有资料价值
  19. 江苏省南京市2021年电子信息申报通知(中、高级)
  20. c语言引用性间接变量,c语言取地址和间接引用

热门文章

  1. Topaz Adjust AI(HDR渲染滤镜) v1.0.0直装破解版
  2. linux支持hd610显卡吗,奔腾G4560核显怎么样且HD610相当于什么级别的显卡?
  3. 迷你迅雷 vs. QQ旋风
  4. AutoCAD输出矢量图
  5. RouterOS 端口映射
  6. Python SQLite3 教程
  7. SQL 2008升级SQL 2008 R2完全教程或者10 00 1600升级10 50 1600
  8. python监测网页变化_Python利用Last-Modified实现监控网页变化
  9. 2008年17款远程控制软件大比拼
  10. 正二十面体的各个面位置点