DEX文件解析 - 2.Dex文件checksum(校验和)解析

一、checksum介绍

checksum(校验和)是DEX位于文件头部的一个信息,用来判断DEX文件是否损坏或者被篡改,它位于头部的0x08偏移地址处,占用4个字节,采用小端序存储。

在DEX文件中,采用Adler-32校验算法计算出校验和,将DEX文件从0x0C处开始读取到文件结束,将读取到的字节数组使用Adler-32校验算法计算出结果即是校验和即checksum字段!!!

二、Adler-32算法

   Adler-32算法如下步骤实现:

  1. 定义两个变量varAvarB,其中varA初始化为1,varB初始化为0。
  2. 读取字节数组的一个字节(假设该字节变量名为byte),计算varA = (varA + byte) mod 65521,然后可以计算出varB = (varA + varB) mod 65521
  3. 重复步骤2,直到字节数组全部读取完毕,得到最终varAvarB两个变量的结果。
  4. 根据第三步得到的varAvarB两个变量,可得到最终校验和checksum =(varB << 16)+ varA

官方wiki:https://en.wikipedia.org/wiki/Adler-32

image-20210714164040788

三、Python实现Adler-32算法

    先给出Dex文件头部信息以及代码跑出的结果

image-20210714164129235

image-20210714164145463

代码如下

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())

四、参考

添加微信交流群, 联系微信:cjh-18888