DEX文件解析 - 2.Dex文件checksum(校验和)解析
一、checksum介绍
checksum(校验和)是DEX位于文件头部的一个信息,用来判断DEX文件是否损坏或者被篡改,它位于头部的0x08
偏移地址处,占用4个字节,采用小端序存储。
在DEX文件中,采用Adler-32
校验算法计算出校验和,将DEX文件从0x0C
处开始读取到文件结束,将读取到的字节数组使用Adler-32校验算法
计算出结果即是校验和即checksum字段!!!
二、Adler-32算法
Adler-32
算法如下步骤实现:
- 定义两个变量
varA
、varB
,其中varA
初始化为1,varB
初始化为0。 - 读取字节数组的一个字节(假设该字节变量名为
byte
),计算varA = (varA + byte) mod 65521
,然后可以计算出varB = (varA + varB) mod 65521
。 - 重复步骤2,直到字节数组全部读取完毕,得到最终
varA
、varB
两个变量的结果。 - 根据第三步得到的
varA
、varB
两个变量,可得到最终校验和checksum =(varB << 16)+ varA
。
官方wiki:https://en.wikipedia.org/wiki/Adler-32
三、Python实现Adler-32算法
先给出Dex文件头部信息以及代码跑出的结果
代码如下
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 calc_checksum(self):
# checksum(校验和)是DEX位于文件头部的一个信息,
# 用来判断DEX文件是否损坏或者被篡改,它位于头部的0x08
# 偏移地址处,占用4个字节,采用小端序存储。
# 在DEX文件中,采用Adler-32校验算法计
# 算出校验和,将DEX文件从0x0C处开始读取到文件结束,将读取
# 到的字节数组使用Adler-32校验算法计算出结果即是校验和即checksum字段!!!
# Adler-32算法如下步骤实现:
# 1. 定义两个变量varA、varB,其中varA初始化为1,varB初始化为0。
# 2. 读取字节数组的一个字节(假设该字节变量名为byte),计算varA = (varA + byte) mod 65521,然后可以计算出varB = (varA + varB) mod 65521。
# 3. 重复步骤2,直到字节数组全部读取完毕,得到最终varA、varB两个变量的结果。
# 4. 根据第三步得到的varA、varB两个变量,可得到最终校验和checksum =(varB << 16)+ varA。
f = self.f
f.seek(0x0c)
VarA = 1
VarB = 0
flag = 0
CheckSum = 0
while True:
srcBytes = []
for i in range(1024): # 一次只读1024个字节,防止内存占用过大
ch = f.read(1)
if not ch: # 如果读取到末尾,设置标识符,然后退出读取循环
flag = 1
break
else:
ch = binascii.b2a_hex(ch) # 将字节转为int类型,然后添加到数组中
ch = str(ch, encoding='utf-8')
ch = int(ch, 16)
srcBytes.append(ch)
varList = self._CalculationVar(srcBytes, VarA, VarB)
VarA = varList[0]
VarB = varList[1]
if flag == 1:
CheckSum = self._getCheckSum(VarA, VarB)
CheckSum = hex(CheckSum)
break
return CheckSum
def _toInt(self, data):
tmp = bytearray(data)
tmp.reverse()
return int(binascii.b2a_hex(tmp), 16)
def _CalculationVar(self, srcByte, vara,varb):
varA = vara
varB = varb
icount = 0
listAB = []
while icount < len(srcByte):
varA = (varA + srcByte[icount]) % 65521
varB = (varB + varA) % 65521
icount += 1
listAB.append(varA)
listAB.append(varB)
return listAB
def _getCheckSum(self, varA, varB):
Output = (varB << 16) + varA
return Output
dex = DexParser('./classes.dex')
print('Checksum', dex.calc_checksum())