DEX文件解析 - 1. DEX文件头解析
一. DEX文件
什么是dex文件?
dex文件是在Android平台上可执行文件的一种文件类型。它的文件格式可以用下面的图片来概括。
dex文件头一般固定为0x70字节大小,文件头包含了标志、版本号、校验码、sha-1签名以及其他一些方法、类的数量和偏移地址等信息。如下图所示:
二. dex文件头各字段解析
dex文件头包含以下各个字段:
- magic:包含了dex文件标识符以及版本,从0x00开始,长度为8个字节
- checksum:dex文件校验码,偏移量为:0x08,长度为4个字节。
- signature:dex sha-1签名,偏移量为0x0c,长度为20个字节
- file_szie:dex文件大小,偏移量为0x20,长度为4个字节
- header_size:dex文件头大小,偏移量为0x24,长度为4个字节,一般为0x70
- endian_tag:dex文件判断字节序是否交换,偏移量为0x28,长度为4个字节,一般情况下为0x78563412
- link_size:dex文件链接段大小,为0则表示为静态链接,偏移量为0x2c,长度为4个字节
- link_off:dex文件链接段偏移位置,偏移量为0x30,长度为4个字节
- map_off:dex文件中map数据段偏移位置,偏移位置为0x34,长度为4个字节
- string_ids_size:dex文件包含的字符串数量,偏移量为0x38,长度为4个字节
- string_ids_off:dex文件字符串开始偏移位置,偏移量为0x3c,长度为4个字节
- type_ids_size:dex文件类数量,偏移量为0x40,长度为4个字节
- type_ids_off:dex文件类偏移位置,偏移量为0x44,长度为4个字节
- photo_ids_size:dex文件中方法原型数量,偏移量为0x48,长度为4个字节
- photo_ids_off:dex文件中方法原型偏移位置,偏移量为0x4c,长度为4个字节
- field_ids_size:dex文件中字段数量,偏移量为0x50,长度为4个字节
- field_ids_off:dex文件中字段偏移位置,偏移量为0x54,长度为4个字节
- method_ids_size:dex文件中方法数量,偏移量为0x58,长度为4个字节
- method_ids_off:dex文件中方法偏移位置,偏移量为0x5c,长度为4个字节
- class_defs_size:dex文件中类定义数量,偏移量为0x60,长度为4个字节
- class_defs_off:dex文件中类定义偏移位置,偏移量为0x64,长度为4个字节
- data_size:dex数据段大小,偏移量为0x68,长度为4个字节
- data_off:dex数据段偏移位置,偏移量为0x6c,长度为4个字节
三.dex文件头代码解析(python)
使用python的open函数打开dex二进制文件。使用seek函数移动文件指针。然后对照着偏移值和长度就可以做出来了
四. 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()