DEX文件解析 - 1. DEX文件头解析

一. DEX文件

什么是dex文件?

dex文件是在Android平台上可执行文件的一种文件类型。它的文件格式可以用下面的图片来概括。

dex文件.jpg

dex文件头一般固定为0x70字节大小,文件头包含了标志、版本号、校验码、sha-1签名以及其他一些方法、类的数量和偏移地址等信息。如下图所示:

dex文件头列表.png

二. dex文件头各字段解析

dex文件头包含以下各个字段:

  1. magic:包含了dex文件标识符以及版本,从0x00开始,长度为8个字节
  2. checksum:dex文件校验码,偏移量为:0x08,长度为4个字节。
  3. signature:dex sha-1签名,偏移量为0x0c,长度为20个字节
  4. file_szie:dex文件大小,偏移量为0x20,长度为4个字节
  5. header_size:dex文件头大小,偏移量为0x24,长度为4个字节,一般为0x70
  6. endian_tag:dex文件判断字节序是否交换,偏移量为0x28,长度为4个字节,一般情况下为0x78563412
  7. link_size:dex文件链接段大小,为0则表示为静态链接,偏移量为0x2c,长度为4个字节
  8. link_off:dex文件链接段偏移位置,偏移量为0x30,长度为4个字节
  9. map_off:dex文件中map数据段偏移位置,偏移位置为0x34,长度为4个字节
  10. string_ids_size:dex文件包含的字符串数量,偏移量为0x38,长度为4个字节
  11. string_ids_off:dex文件字符串开始偏移位置,偏移量为0x3c,长度为4个字节
  12. type_ids_size:dex文件类数量,偏移量为0x40,长度为4个字节
  13. type_ids_off:dex文件类偏移位置,偏移量为0x44,长度为4个字节
  14. photo_ids_size:dex文件中方法原型数量,偏移量为0x48,长度为4个字节
  15. photo_ids_off:dex文件中方法原型偏移位置,偏移量为0x4c,长度为4个字节
  16. field_ids_size:dex文件中字段数量,偏移量为0x50,长度为4个字节
  17. field_ids_off:dex文件中字段偏移位置,偏移量为0x54,长度为4个字节
  18. method_ids_size:dex文件中方法数量,偏移量为0x58,长度为4个字节
  19. method_ids_off:dex文件中方法偏移位置,偏移量为0x5c,长度为4个字节
  20. class_defs_size:dex文件中类定义数量,偏移量为0x60,长度为4个字节
  21. class_defs_off:dex文件中类定义偏移位置,偏移量为0x64,长度为4个字节
  22. data_size:dex数据段大小,偏移量为0x68,长度为4个字节
  23. data_off:dex数据段偏移位置,偏移量为0x6c,长度为4个字节

三.dex文件头代码解析(python)

使用python的open函数打开dex二进制文件。使用seek函数移动文件指针。然后对照着偏移值和长度就可以做出来了

image-20210714160144882

四. dex文件头解析实现代码(python)

import binascii
import json


class DexParser:
    def __init__(self, dex_path):
        self.f = open(dex_path, 'rb')
        self.dexHeader = {
            'blog': 'www.zhuoyue360.com'
        }

    def init_header(self):
        f = self.f
        # 解析Magic
        f.seek(0x0, 0)
        magic = str(binascii.b2a_hex(f.read(8)), encoding='utf-8')
        self.dexHeader['magic'] = magic
        # 解析 checksum
        f.seek(0x8, 0)
        check_sum = bytearray(f.read(4))
        check_sum.reverse()
        check_sum = str(binascii.b2a_hex(check_sum), encoding='utf-8').upper()
        self.dexHeader['checksum_校验码'] = check_sum

        # 解析 sha1 signature
        f.seek(0xc, 0)
        signature = str(binascii.b2a_hex(f.read(20)), encoding='utf-8').upper()
        self.dexHeader['signature_签名'] = signature

        # 解析 uint file size
        f.seek(0x20)
        file_size = self._toInt(f.read(4))
        self.dexHeader['file_size_文件大小'] = file_size

        # 解析 header size
        f.seek(0x24, 0)
        header_size = self._toInt(f.read(4))

        self.dexHeader['header_size_文件头'] = header_size

        # endian_tag
        f.seek(0x28)
        endian_tag = bytearray(f.read(4))
        endian_tag.reverse()
        endian_tag = str(binascii.b2a_hex(endian_tag), encoding='utf-8').upper()

        self.dexHeader['endian_tag_字节序交换标志'] = endian_tag

        # link_size
        f.seek(0x2C)
        link_size = self._toInt(f.read(4))

        self.dexHeader['link_size_链接段大小'] = link_size

        # link off
        f.seek(0x30)
        link_off = self._toInt(f.read(4))
        self.dexHeader['link_off_链接段偏移位置'] = link_off

        # map_off
        f.seek(0x34)
        map_off = self._toInt(f.read(4))
        self.dexHeader['map_off_map数据偏移位置'] = map_off

        # string_ids_size
        f.seek(0x38)
        string_ids_size = self._toInt(f.read(4))
        self.dexHeader['string_ids_size_字符串列表字符串个数'] = string_ids_size

        # string_ids_off
        f.seek(0x3C)
        string_ids_off = self._toInt(f.read(4))
        self.dexHeader['string_ids_off_字符串列表表基地址'] = string_ids_off

        # type_ids_size
        f.seek(0x40)
        type_ids_size = self._toInt(f.read(4))
        self.dexHeader['type_ids_size_类型列表类型个数'] = type_ids_size

        # type_ids_off
        f.seek(0x44)
        type_ids_off = self._toInt(f.read(4))
        self.dexHeader['type_ids_off_类型列表基地址'] = type_ids_off

        # proto_ids_size
        f.seek(0x48)
        proto_ids_size = self._toInt(f.read(4))
        self.dexHeader['proto_ids_size_原型列表原型个数'] = proto_ids_size

        # proto_ids_off
        f.seek(0x4C)
        proto_ids_off = self._toInt(f.read(4))
        self.dexHeader['proto_ids_off_原型列表基地址'] = proto_ids_off

        # field_ids_size
        f.seek(0x50)
        field_ids_size = self._toInt(f.read(4))
        self.dexHeader['field_ids_size_字段列表字段个数'] = field_ids_size

        # field_ids_off
        f.seek(0x54)
        field_ids_off = self._toInt(f.read(4))
        self.dexHeader['field_ids_off_字段列表基地址'] = field_ids_off

        # method_ids_size
        f.seek(0x58)
        method_ids_size = self._toInt(f.read(4))
        self.dexHeader['method_ids_size_方法列表方法个数'] = method_ids_size

        # method_ids_off
        f.seek(0x5C)
        method_ids_off = self._toInt(f.read(4))
        self.dexHeader['method_ids_off_方法列表基地址'] = method_ids_off

        # class_defs_size
        f.seek(0x60)
        class_defs_size = self._toInt(f.read(4))
        self.dexHeader['class_defs_size_类定义列表中类的个数'] = class_defs_size

        # class_defs_off
        f.seek(0x64)
        class_defs_off = self._toInt(f.read(4))
        self.dexHeader['class_defs_off_类定义列表基地址'] = class_defs_off

        # data_size
        f.seek(0x68)
        data_size = self._toInt(f.read(4))
        self.dexHeader['data_size_数据段大小'] = data_size

        # data_off
        f.seek(0x6C)
        data_off = self._toInt(f.read(4))
        self.dexHeader['data_off_数据段基地址'] = data_off

        print(json.dumps(self.dexHeader, ensure_ascii=False, indent=2))

    def _toInt(self, data):
        tmp = bytearray(data)
        tmp.reverse()
        return int(binascii.b2a_hex(tmp), 16)


dex = DexParser('./classes.dex')
dex.init_header()

五. 参考链接

接单JS逆向/安卓逆向/小程序逆向 微信:yanjin2888