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

本文介绍了如何使用SmaliCmd将smali文件转换为dex文件,涉及ANTLR工具对smali语法的识别,以及解析过程中Smali、DexFileWriter、DexMethodVisitor和SmaliCodeVisitor等组件的角色。详细阐述了从词法解析到方法和字段处理的整个转换流程。

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);
    }

    @Override
    protected 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) {
            @Override
            public 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>() {
                @Override
                public 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);
                }

                @Override
                public 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);
    }
  @Override
    public 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

 @Override
    public 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 Token
        if (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);//访问field
        if (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 static
            dataItem.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 {// PART
            method = 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访问方法

 @Override
    public 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();//返回一个DexCodeVisitor
        if (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指令
            @Override
            public Object visitFregisters(SmaliParser.FregistersContext ctx) {
                return null;
            }

            @Override
            public Object visitFlocals(SmaliParser.FlocalsContext ctx) {
                return null;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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<>();

            @Override
            public Object visitSLabel(SmaliParser.SLabelContext ctx) {
                scv.visitLabel(getLabel(ctx.label.getText()));
                return null;
            }

            @Override
            public 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;
            }

            @Override
            public 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());
            }

            @Override
            public Object visitF0x(SmaliParser.F0xContext ctx) {
                scv.visitStmt0R(getOp(ctx.op));
                return null;
            }

            @Override
            public Object visitF0t(SmaliParser.F0tContext ctx) {
                scv.visitJumpStmt(getOp(ctx.op), 0, 0, getLabel(ctx.target.getText()));
                return null;
            }

            @Override
            public Object visitF1x(SmaliParser.F1xContext ctx) {
                scv.visitStmt1R(getOp(ctx.op), m.pareReg(ctx.r1.getText()));
                return null;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public Object visitF31t(SmaliParser.F31tContext ctx) {
                scv.visitF31tStmt(getOp(ctx.op), m.pareReg(ctx.r1.getText()), getLabel(ctx.label.getText()));
                return null;
            }

            @Override
            public Object visitF1t(SmaliParser.F1tContext ctx) {
                scv.visitJumpStmt(getOp(ctx.op), m.pareReg(ctx.r1.getText()), 0, getLabel(ctx.label.getText()));
                return null;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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;
            }

            @Override
            public 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);
    }

    @Override
    public 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;
        }

        @Override
        public 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;
        }

        @Override
        public 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;
        }

        @Override
        public void accept(DexCodeVisitor cv) {
        }
    }

    private List<DexStmtNode> needCareStmts = new ArrayList<DexStmtNode>();

    @Override
    public 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) {

            @Override
            public 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();
                }
            }
        });
    }

    @Override
    public void visitLabel(final DexLabel label) {
        addCare(new DexLabelStmtNode(label));
    }
}

它继承于DexCodeNode,实现了转换dex指令


最后调用visitEnd,这里是一个方法里面的code访问完了

  @Override
    public 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 并传递前面的DexCodeVisitor
            n.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 writing
        buildMapListItem();//构建各个字段、Head等

        // place all item into file, we can know the size now
        final int size = place();

        ByteBuffer buffer = ByteBuffer.allocate(size);
        DataOut out = new ByteBufferOut(buffer);

        if (DEBUG) {
            out = wrapDumpOut(out);
        }
        // write it
        write(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 header
        updateChecksum(buffer, size);//更新crc和sha1

        return buffer.array();
    }

这里主要看下buildMapListItem

 void buildMapListItem() {

        // begin ===========
        // satisfy 'bool DexFileVerifier::CheckMap()' on art/runtime/dex_file_verifier.cc
        // make sure the items are not empty
        if (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 section
            dataSectionItems.add(typeListSection);// data section
            dataSectionItems.add(annotationSetRefListItemSection);// data
            // section
            dataSectionItems.add(annotationSetSection);// data section
            // make codeItem Before classDataItem
            dataSectionItems.add(codeItemSection);// data section
            dataSectionItems.add(classDataItemSection);// data section
            dataSectionItems.add(stringDataItemSection);// data section
            dataSectionItems.add(debugInfoSection);// data section
            dataSectionItems.add(annotationItemSection);// data section
            dataSectionItems.add(encodedArrayItemSection);// data section
            dataSectionItems.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 now
        cp.clean();
        cp = null;
    }


主要是根据前面的解析结果,和dex文件的结构设置相关字段的值。

到这里smail转换为dex就已经完成了。跟前面的其他转换相比,主要是这里用到了一个词法解析器,而前面的都是自己在代码中实现解析的。


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值