美文网首页
Android 解析DEX文件

Android 解析DEX文件

作者: _凌浩雨 | 来源:发表于2018-06-30 10:56 被阅读35次

    1. DEX文件简介

    1). 基本格式
    Android DEX文件格式--非虫大神杰作.png dex-file-general-structure.png
    • Dex Header: header文件头
    • String Table: 字符串的索引
    • Type Table: 类型的索引
    • Proto Table: 方法原型的索引
    • Field Table: 域的索引
    • Method Table: 方法索引
    • Class Def Table: 类的定义区
    • Data Section: 数据区
    1). Dex Header

    dex文件里的header,描述.dex文件的文件信息,及其它各个区域的索引。

    /**
     * DEX 头部信息类型
     * 主要分为两部分:
     *  1). 魔数 + 签名 + 文件大小等信息
     *  2). 后面的各个数据结构的大小和偏移值,成对出现
     * 
     * struct DexHeader {
            u1  magic[8];           // includes version number
            u4  checksum;           // adler32 checksum 
            u1  signature[kSHA1DigestLen]; // SHA-1 hash
            u4  fileSize;           // length of entire file
            u4  headerSize;         // offset to start of next section
            u4  endianTag;
            u4  linkSize;
            u4  linkOff;
            u4  mapOff;
            u4  stringIdsSize;
            u4  stringIdsOff;
            u4  typeIdsSize;
            u4  typeIdsOff;
            u4  protoIdsSize;
            u4  protoIdsOff;
            u4  fieldIdsSize;
            u4  fieldIdsOff;
            u4  methodIdsSize;
            u4  methodIdsOff;
            u4  classDefsSize;
            u4  classDefsOff;
            u4  dataSize;
            u4  dataOff;
        };
     * 
     * @author mazaiting
     */
    public class HeaderType {
        /**8个字节,一般是常量,为使.dex文件能够被识别出来,必须出现在.dex文件的最开头位置
         *  数组的值一般可以转换为一个字符串: { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 } = "dex\n035\0"
         *  中间是一个 ‘\n' 符号 ,后面 035 是 Dex 文件格式的版本 。
         */
        public byte[] magic = new byte[8];
        /**文件校验码,使用alder32算法校验文件出去magic,checknum外余下所有文件区域用于检查文件错误*/
        public int checkSum;
        /**采用SHA-1算法hash出去magic,checknum和signature外余下所有的文件区域,用于唯一识别本文件*/
        public byte[] signAture = new byte[20];
        /**DEX文件的大小*/
        public int fileSize;
        /**header区域的大小,单位Byte,一般固定为0x70常量*/
        public int headerSize;
        /**大小端标签,标准.dex文件为小端,此项一般固定为0x12345678常量*/
        public int endianTag;
        /**链接数据的大小*/
        public int linkSize;
        /**链接数据的偏移值*/
        public int linkOff;
        /**map item的偏移地址,该item属于data区里的内容,值要大于等于dataOff的大小*/
        public int mapOff;
        /**DEX中用到的所有字符串内容的大小*/
        public int stringIdsSize;
        /**DEX中用到的所有字符串内容的偏移量*/
        public int stringIdsOff;
        /**DEX中类型数据结构的大小*/
        public int typeIdsSize;
        /**DEX中类型数据结构的偏移值*/
        public int typeIdsOff;
        /**DEX中的元数据信息数据结构的大小*/
        public int protoIdsSize;
        /**DEX中的元数据信息数据结构的偏移值*/
        public int protoIdsOff;
        /**DEX中字段信息数据结构的大小*/
        public int fieldIdsSize;
        /**DEX中字段信息数据结构的偏移值*/
        public int fieldIdsOff;
        /**DEX中方法信息数据结构的大小*/
        public int methodIdsSize;
        /**DEX中方法信息数据结构的偏移值*/
        public int methodIdsOff;
        /**DEX中的类信息数据结构的大小*/
        public int classDefsSize;
        /**DEX中的类信息数据结构的偏移值*/
        public int classDefsOff;
        /**DEX中数据区域的结构信息的大小*/
        public int dataSize;
        /**DEX中数据区域的结构信息的偏移值*/
        public int dataOff;
        
        @Override
        public String toString(){
            return "magic:"+Util.bytesToHexString(magic)+"\n"
                    + "checksum:"+checkSum + "\n"
                    + "siganature:"+Util.bytesToHexString(signAture) + "\n"
                    + "file_size:"+fileSize + "\n"
                    + "header_size:"+headerSize + "\n"
                    + "endian_tag:"+endianTag + "\n"
                    + "link_size:"+linkSize + "\n"
                    + "link_off:"+Util.bytesToHexString(Util.int2Byte(linkOff)) + "\n"
                    + "map_off:"+Util.bytesToHexString(Util.int2Byte(mapOff)) + "\n"
                    + "string_ids_size:"+stringIdsSize + "\n"
                    + "string_ids_off:"+Util.bytesToHexString(Util.int2Byte(stringIdsOff)) + "\n"
                    + "type_ids_size:"+typeIdsSize + "\n"
                    + "type_ids_off:"+Util.bytesToHexString(Util.int2Byte(typeIdsOff)) + "\n"
                    + "proto_ids_size:"+protoIdsSize + "\n"
                    + "proto_ids_off:"+Util.bytesToHexString(Util.int2Byte(protoIdsOff)) + "\n"
                    + "field_ids_size:"+fieldIdsSize + "\n"
                    + "field_ids_off:"+Util.bytesToHexString(Util.int2Byte(fieldIdsOff)) + "\n"
                    + "method_ids_size:"+methodIdsSize + "\n"
                    + "method_ids_off:"+Util.bytesToHexString(Util.int2Byte(methodIdsOff)) + "\n"
                    + "class_defs_size:"+classDefsSize + "\n"
                    + "class_defs_off:"+Util.bytesToHexString(Util.int2Byte(classDefsOff)) + "\n"
                    + "data_size:"+dataSize + "\n"
                    + "data_off:"+Util.bytesToHexString(Util.int2Byte(dataOff));
        }
    }
    
    图1.png
    2). String Table

    string_ids区索引.dex文件所有的字符串

    /**
     * 索引.dex文件所有的字符串
     * 
     * struct DexStringId {
            u4 stringDataOff;      // file offset to string_data_item
        };
     * @author mazaiting
     */
    public class StringIdsItem {
        /**字符串数据的偏移地址*/
        public int stringDataOff;
        
        /**
         * 获取当前类属性所占字节大小
         * @return
         */
        public static int getSize() {
            return 4;
        }
        
        @Override
        public String toString() {
            return Util.bytesToHexString(Util.int2Byte(stringDataOff));
        }
    }
    

    string_data_off是一个偏移地址,指向的数据结构为string_data_item.

    /**
     * StringIdsItem中stringDataOff指向的数据结构
     * struct string_data_item {
            uleb128 utf16_size;
            ubyte data;
        }
     * @author mazaiting
     */
    public class StringDataItem {
        /**LEB128 ( little endian base 128 ) 格式 ,是基于 1 个 Byte 的一种不定长度的
            编码方式 。若第一个 Byte 的最高位为 1 ,则表示还需要下一个 Byte 来描述 ,直至最后一个 Byte 的最高
            位为 0 。每个 Byte 的其余 Bit 用来表示数据*/
        public byte data;
        public List<Byte> utf16Size = new ArrayList<>();
    }
    
    3). Type Table

    主要描述dex中所有的类型,如类类型,基本类型等信息,type_ids区索引了dex文件里的所有数据类型,包括class类型,数据类型(array types) 和基本类型(primitive types).

    /**
     * DEX所有类型,基本类型等信息
     * type_ids 区索引了 dex 文件里的所有数据类型 ,包括 class 类型 ,数组类型(array types)和基本类型(primitive types)
     * 
     * struct DexTypeId {
        u4  descriptorIdx;      // index into stringIds list for type descriptor 
        };
     * @author mazaiting
     */
    public class TypeIdsItem {
        /**
         * ID索引
         */
        public int descriptorIdx;
        
        /**
         * 获取所属
         * @return
         */
        public static int getSize() {
            return 4;
        }
    }
    
    4). Proto Table

    proto是method prototype代表java语言里的一个method的原型

    /**
     * proto 的意思是 method prototype 代表 java 语言里的一个 method 的原型 。proto_ids 里的元素为 proto_id_item ,
     * 
     * struct DexProtoId {
            u4  shortyIdx;          // index into stringIds for shorty descriptor
            u4  returnTypeIdx;      // index into typeIds list for return type 
            u4  parametersOff;      // file offset to type_list for parameter types
        };
     *
     * @author mazaiting
     */
    public class ProtoIdsItem {
        /**值为一个string_ids的index号,用来说明该method原型*/
        public int shortyIdx;
        /**值为一个type_ids的index,表示该method原型的返回值类型*/
        public int returnTypeIdx;
        /**指定method原型的参数列表type_list,若method没有参数,则值为0. 参数的格式是type_list*/
        public int parametersOff;
        
        // 这个不是公共字段,而是为了存储方法原型中的参数类型名和参数个数
        public List<String> paramtersList = new ArrayList<>();
        public int paramterCount;
        
        /**
         * 获取当前类字节数
         * @return
         */
        public static int getSize() {
            return 4 + 4 + 4;
        }
        
        @Override
        public String toString() {
            return "shortyIdx:"+shortyIdx+",returnTypeIdx:"+returnTypeIdx+",parametersOff:"+parametersOff;
        }
    }
    
    5). Field Table

    field_ids区里面存放的是dex文件引用的所有field

    /**
     * field_ids区里存放的是dex文件引用的所有的field,本区元素格式是field_id_item
     * 
     * struct DexFieldId {
            u2  classIdx;           // index into typeIds list for defining class
            u2  typeIdx;            // index into typeIds for field type
            u4  nameIdx;            // index into stringIds for field name
        };
     * 
     * @author mazaiting
     */
    public class FieldIdsItem {
        /**
         * field所属的class类型,class_idx的值时type_ids的一个index,指向所属的类
         */
        public short classIdx;
        /**
         * field的类型,值是type_ids的一个index
         */
        public short typeIdx;
        /**
         * field的名称,它的值是string_ids的一个index
         */
        public int nameIdx;
        
        /**
         * 当前区域所占字节大小
         * @return
         */
        public static int getSize() {
            return 2 + 2 + 4;
        }
        
        @Override
        public String toString() {
            return "classIdx: " + classIdx + ",typeIdx: " + typeIdx + ",nameIdx: " + nameIdx;
        }
    }
    
    6). Method Table

    method_ids是索引区的最后一个条目,它索引了dex文件里的所有的method.

    /**
     * method_ids是索引区的最后一个条目,它索引了dex文件里的所有method.
     * method_ids的元素格式是method_id_item
     * 
     * struct DexMethodId {
            u2  classIdx;           // index into typeIds list for defining class
            u2  protoIdx;           // index into protoIds for method prototype
            u4  nameIdx;            // index into stringIds for method name
        };
     * 
     * @author mazaiting
     */
    public class MethodIdsItem {
        /**
         * method所属的class类型,class_idx的值是type_ids的一个index,必须指向一个class类型
         */
        public short classIdx;
        /**
         * method的原型,指向proto_ids的一个index
         */
        public short protoIdx;
        /**
         * method的名称,值为string_ids的一个index
         */
        public int nameIdx;
        
        public static int getSize() {
            return 2 + 2 + 4;
        }
    
        @Override
        public String toString(){
            return "classIdx: " + classIdx + ",protoIdx: " + protoIdx + ",nameIdx: " + nameIdx;
        }
    
    }
    
    7). Class Def Table

    class_defs区域里存放着class definitions,class的定义。它的结构较dex区要复杂,有的数据直接指向data区

    /**
     * class_defs区域里存放着class definitions,有些数据直接指向了data区
     * 
     * struct DexClassDef {
            u4  classIdx;           /* index into typeIds for this class
            u4  accessFlags;
            u4  superclassIdx;      /* index into typeIds for superclass
            u4  interfacesOff;      /* file offset to DexTypeList
            u4  sourceFileIdx;      /* index into stringIds for source file name
            u4  annotationsOff;     /* file offset to annotations_directory_item
            u4  classDataOff;       /* file offset to class_data_item
            u4  staticValuesOff;    /* file offset to DexEncodedArray
        };
     * 
     * @author mazaiting
     */
    public class ClassDefItem {
        /**
         * 描述具体的class类型,值是type_ids的一个index,值必须是一个class类型,不能是数组雷兴国或者基本类型
         */
        public int classIdx;
        /**
         * 描述class的访问类型,如public,final,static等
         */
        public int accessFlags;
        /**
         * 描述父类的类型,值必须是一个class类型,不能是数组雷兴国或者基本类型
         */
        public int superClassIdx;
        /**
         * 值为偏移地址,被指向的数据结构为type_list,class若没有interfaces,值为0
         */
        public int interfacesOff;
        /**
         * 表示源代码文件的信息,值为string_ids的一个index。若此项信息丢失,此项赋值为NO_INDEX=0xFFFFFFFF
         */
        public int sourceFileIdx;
        /**
         * 值为偏移地址,指向的内容是该class的注解,位置在data区,格式为annotations_directory_item,若没有此项,值为0
         */
        public int annotationsOff;
        /**
         * 值为偏移地址,指向的内容是该class的使用到的数据,位置在data区,格式为class_data_item。无偶没有此项,则值为0
         */
        public int classDataOff;
        /**
         * 值为偏移地址,指向data区里的一个列表,格式为encoded_array_item。若没有此项,值为0.
         */
        public int staticValueOff;
        
        /**
         * enum {
                ACC_PUBLIC       = 0x00000001,       // class, field, method, ic
                ACC_PRIVATE      = 0x00000002,       // field, method, ic
                ACC_PROTECTED    = 0x00000004,       // field, method, ic
                ACC_STATIC       = 0x00000008,       // field, method, ic
                ACC_FINAL        = 0x00000010,       // class, field, method, ic
                ACC_SYNCHRONIZED = 0x00000020,       // method (only allowed on natives)
                ACC_SUPER        = 0x00000020,       // class (not used in Dalvik)
                ACC_VOLATILE     = 0x00000040,       // field
                ACC_BRIDGE       = 0x00000040,       // method (1.5)
                ACC_TRANSIENT    = 0x00000080,       // field
                ACC_VARARGS      = 0x00000080,       // method (1.5)
                ACC_NATIVE       = 0x00000100,       // method
                ACC_INTERFACE    = 0x00000200,       // class, ic
                ACC_ABSTRACT     = 0x00000400,       // class, method, ic
                ACC_STRICT       = 0x00000800,       // method
                ACC_SYNTHETIC    = 0x00001000,       // field, method, ic
                ACC_ANNOTATION   = 0x00002000,       // class, ic (1.5)
                ACC_ENUM         = 0x00004000,       // class, field, ic (1.5)
                ACC_CONSTRUCTOR  = 0x00010000,       // method (Dalvik only)
                ACC_DECLARED_SYNCHRONIZED =
                                   0x00020000,       // method (Dalvik only)
                ACC_CLASS_MASK =
                    (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT
                            | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),
                ACC_INNER_CLASS_MASK =
                    (ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC),
                ACC_FIELD_MASK =
                    (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
                            | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM),
                ACC_METHOD_MASK =
                    (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
                            | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE
                            | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR
                            | ACC_DECLARED_SYNCHRONIZED),
            };
    
         */
        
        /**
         * 访问修饰符
         */
        public static final int 
            ACC_PUBLIC       = 0x00000001,       // class, field, method, ic
            ACC_PRIVATE      = 0x00000002,       // field, method, ic
            ACC_PROTECTED    = 0x00000004,       // field, method, ic
            ACC_STATIC       = 0x00000008,       // field, method, ic
            ACC_FINAL        = 0x00000010,       // class, field, method, ic
            ACC_SYNCHRONIZED = 0x00000020,       // method (only allowed on natives)
            ACC_SUPER        = 0x00000020,       // class (not used in Dalvik)
            ACC_VOLATILE     = 0x00000040,       // field
            ACC_BRIDGE       = 0x00000040,       // method (1.5)
            ACC_TRANSIENT    = 0x00000080,       // field
            ACC_VARARGS      = 0x00000080,       // method (1.5)
            ACC_NATIVE       = 0x00000100,       // method
            ACC_INTERFACE    = 0x00000200,       // class, ic
            ACC_ABSTRACT     = 0x00000400,       // class, method, ic
            ACC_STRICT       = 0x00000800,       // method
            ACC_SYNTHETIC    = 0x00001000,       // field, method, ic
            ACC_ANNOTATION   = 0x00002000,       // class, ic (1.5)
            ACC_ENUM         = 0x00004000,       // class, field, ic (1.5)
            ACC_CONSTRUCTOR  = 0x00010000,       // method (Dalvik only)
            ACC_DECLARED_SYNCHRONIZED =
                               0x00020000,       // method (Dalvik only)
            ACC_CLASS_MASK =
                (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT
                        | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),
            ACC_INNER_CLASS_MASK =
                (ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC),
            ACC_FIELD_MASK =
                (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
                        | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM),
            ACC_METHOD_MASK =
                (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
                        | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE
                        | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR
                        | ACC_DECLARED_SYNCHRONIZED);
    
        /**
         * 获取当前区域所占字节数
         * @return
         */
        public static int getSize() {
            return 4 * 8;
        }
        
        @Override
        public String toString(){
            return "classIdx: " + classIdx + ",accessFlags: " + accessFlags + ",superClassIdx: " + superClassIdx + 
                    ",iterfacesOff: " + interfacesOff + ",sourceFileIdx: " + sourceFileIdx + ",annotationsOff: " + 
                    annotationsOff + ",classDataOff: " + classDataOff + ",staticValueOff: " + staticValueOff;
        }
    
    }
    

    class_data_off指向data区里的class_data_itetm结构,class_data_item里存放着本class使用到的各种数据.

    /**
     * class_data_off指向data区里的class_data_item结构,class_data_item里存放着本class使用到的各种数据
     * 
     *  struct class_data_item  {
            uleb128 static_fields_size;
            uleb128 instance_fields_size;
            uleb128 direct_methods_size;
            uleb128 virtual_methods_size;
            encoded_field static_fields [ static_fields_size ];
            encoded_field instance_fields [ instance_fields_size ];
            encoded_method direct_methods [ direct_method_size ];
            encoded_method virtual_methods [ virtual_methods_size ];
        };
    
     * 
     * @author mazaiting
     */
    public class ClassDataItem {
        /**
         * 静态字段的大小
         */
        public int staticFieldsSize;
        /**
         * 实例化字段的大小
         */
        public int instanceFieldsSize;
        /**
         * 实现方法的大小
         */
        public int directMethodsSize;
        /**
         * 虚拟方法的大小
         */
        public int virtualMethodsSize;
        /**
         * 静态字段数组
         */
        public EncodeField[] staticFields;
        /**
         * 实例化字段数组
         */
        public EncodeField[] instanceFields;
        /**
         * 实现方法数组
         */
        public EncodeMethod[] directMethods;
        /**
         * 虚拟方法的数组
         */
        public EncodeMethod[] virtualMethods;
        
        @Override
        public String toString(){
            return "staticFieldsSize: " + staticFieldsSize + ",instanceFieldsSize: " + instanceFieldsSize
                    + ",directMethodsSize:" + directMethodsSize + ",virtualMethodsSize: " + virtualMethodsSize
                    + "\n" + getFieldsAndMethods();
        }
    
    
    
        private String getFieldsAndMethods() {
            StringBuilder sb = new StringBuilder();
            sb.append("staticFields:\n");
            for(int i=0;i<staticFields.length;i++){
                sb.append(staticFields[i]+"\n");
            }
            sb.append("instanceFields:\n");
            for(int i=0;i<instanceFields.length;i++){
                sb.append(instanceFields[i]+"\n");
            }
            sb.append("directMethods:\n");
            for(int i=0;i<directMethods.length;i++){
                sb.append(directMethods[i]+"\n");
            }
            sb.append("virtualMethods:\n");
            for(int i=0;i<virtualMethods.length;i++){
                sb.append(virtualMethods[i]+"\n");
            }
            return sb.toString();
        }
    
    }
    
    8). Data Section
    /**
     * 代码条目
     * 
     *  (1) 一个 .dex 文件被分成了 9 个区 ,其中有一个索引区叫做class_defs , 索引了 .dex 里面用到的 class ,以及对这个 class 的描述 。
     *  (2) class_defs 区 , 里面其实是class_def_item 结构 。这个结构里描述了 LHello; 的各种信息 ,诸如名称 ,superclass , access flag,
     *       interface 等 。class_def_item 里有一个元素 class_data_off , 指向data 区里的一个 class_data_item 结构 ,
     *       用来描述 class 使用到的各种数据 。自此以后的结构都归于 data区了 。
     *  (3) class_data_item 结构 ,里描述值着 class 里使用到的 static field , instance field , direct_method ,和 virtual_method 
     *      的数目和描述 。例子 Hello.dex 里 ,只有 2 个 direct_method , 其余的 field 和method 的数目都为 0 。描述 direct_method 的结构叫
     *      做 encoded_method ,是用来详细描述某个 method的 。
     *  (4) encoded_method 结构 ,描述某个 method 的 method 类型 , access flags 和一个指向 code_item的偏移地址 ,里面存放的是该 method 
     *      的具体实现 。
     *  (5) code_item ,结构里描述着某个 method 的具体实现 。
     * 
     * struct DexCode {
            u2  registersSize;
            u2  insSize;
            u2  outsSize;
            u2  triesSize;
            u4  debugInfoOff;       /* file offset to debug info stream
            u4  insnsSize;          /* size of the insns array, in u2 units
            u2  insns[1];
            /* followed by optional u2 padding
            /* followed by try_item[triesSize]
            /* followed by uleb128 handlersSize
            /* followed by catch_handler_item[handlersSize]
        };
     * 
     * @author mazaiting
     *
     */
    public class CodeItem {
        /**
         * 本段代码使用到的寄存器数目
         */
        public short registersSize;
        /**
         * method传入参数的数目
         */
        public short insSize;
        /**
         * 本段代码调用其他方法时需要的参数个数
         */
        public short outsSize;
        /**
         * try_item结构的个数
         */
        public short triesSize;
        /**
         * 偏移地址,指向本段代码的debug信息存放位置,是一个debug_info_item结构
         */
        public int debugInfoOff;
        /**
         * 指令列表的大小,以16-bit为单位。insns是instructions的缩写
         */
        public int insnsSize;
        /**
         * insns数组
         */
        public short[] insns;
        
        /**tries和handlers用于处理java中的exception*/
        
        @Override
        public String toString(){
            return "registersSize: " + registersSize + ",insSize: " + insSize + ",outsSize: " + outsSize + 
                    ",triesSize: " + triesSize + ",debugInfoOff: " + debugInfoOff + ",insnsSize: " + insnsSize 
                    + "\ninsns: " + getInsnsStr();
        }
        
        private String getInsnsStr(){
            StringBuilder sb = new StringBuilder();
            if (insns != null && insns.length > 0) {
                for(int i=0;i<insns.length;i++){
                    sb.append(Util.bytesToHexString(Util.short2Byte(insns[i]))+",");
                }   
            }
            
            return sb.toString();
        }
    }
    

    参考文章

    Android逆向之旅---解析编译之后的Dex文件格式

    代码下载

    相关文章

      网友评论

          本文标题:Android 解析DEX文件

          本文链接:https://www.haomeiwen.com/subject/yrftuftx.html