吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4509|回复: 6
收起左侧

[Android 原创] DEX文件解析--6、dex文件字段和方法定义解析

  [复制链接]
windy_ll 发表于 2020-4-18 19:57
本帖最后由 windy_ll 于 2022-4-1 20:32 编辑

一、前言

   前几篇文章链接:
      DEX文件解析---1、dex文件头解析
      DEX文件解析---2、Dex文件checksum(校验和)解析
      DEX文件解析--3、dex文件字符串解析
      DEX文件解析--4、dex类的类型解析
      DEX文件解析--5、dex方法原型解析  

    PS:阅读之前,最好知道关于dex文件字符串、类的类型以及方法原型是怎么解析出来的!!!  


二、Dex文件中的字段

    1、在dex文件头中,关于字段(ps:字段可以简单理解成定义的变量或者常量)相关的信息有8个字节,在0x50~0x53这四个字节,按小端序存储这dex文件中的字段数量,在0x54~0x57这四个字节,存储这读取字段的起始偏移地址,如下所示:  

  

    2、根据上面的字段起始偏移地址,我们可以找到字段,表示一个字段需要用八个字节,其中,前两个字节为我们在前面解析出来类的类型列表的索引,通过该索引找到的类的类型表示该字段在该类中被定义的(ps:我是这么理解的,如有不对,还请纠正);第三个字节和第四个字节,也是类的类型列表的索引,表示该字段的类型,例如我们在java某个类中定义了一个变量int a,那么我们此处解析出来的字段类型就是int;最后四个字节,则是我们前面解析出来字符串列表的索引,通过该索引找到的字符串表示字段的,例如我们定义了一个变量String test;,那么我们在这里解析出来的就是test,如下图所示:  

  

解析代码运行截图:  

  

  

解析代码:  

import binascii
import os
import sys

def byte2int(bs):
    tmp = bytearray(bs)
    tmp.reverse()
    rl = bytes(tmp)
    rl = str(binascii.b2a_hex(rl),encoding='UTF-8')
    rl = int(rl,16)
    return rl

def getStringsCount(f):
    f.seek(0x38)
    stringsId = f.read(4)
    count = byte2int(stringsId)
    return count

def getStringByteArr(f,addr):
    byteArr = bytearray()
    f.seek(addr + 1)
    b = f.read(1)
    b = str(binascii.b2a_hex(b),encoding='UTF-8')
    b = int(b,16)
    index = 2
    while b != 0:
        byteArr.append(b)
        f.seek(addr + index)
        b = f.read(1)
        b = str(binascii.b2a_hex(b),encoding='UTF-8')
        b = int(b,16)
        index = index + 1
    return byteArr

def BytesToString(byteArr):
    try:
        bs = bytes(byteArr)
        stringItem = str(bs,encoding='UTF-8')
        return stringItem
    except:
        pass

def getAddress(addr):
    address = bytearray(addr)
    address.reverse()
    address = bytes(address)
    address = str(binascii.b2a_hex(address),encoding='UTF-8')
    address = int(address,16)
    return address

def getStrings(f,stringAmount):
    stringsList = []
    f.seek(0x3c)
    stringOff = f.read(4)
    Off = getAddress(stringOff)
    f.seek(Off)
    for i in range(stringAmount):
        addr = f.read(4)
        address = getAddress(addr)
        byteArr = getStringByteArr(f,address)
        stringItem = BytesToString(byteArr)
        stringsList.append(stringItem)
        Off = Off + 4
        f.seek(Off)
    return stringsList

def getTypeAmount(f):
    f.seek(0x40)
    stringsId = f.read(4)
    count = byte2int(stringsId)
    return count

def getTypeItem(f,count,strLists):
    typeList = []
    f.seek(0x44)
    type_ids_off = f.read(4)
    type_off = byte2int(type_ids_off)
    f.seek(type_off)
    for i in range(count):
        typeIndex = f.read(4)
        typeIndex = byte2int(typeIndex)
        typeList.append(strLists[typeIndex])
        type_off = type_off + 0x04
        f.seek(type_off)
    return typeList

def changeDisplay(viewString):
    display = ''
    if viewString == 'V':
        display = 'void'
    elif viewString == 'Z':
        display = 'boolean'
    elif viewString == 'B':
        display = 'byte'
    elif viewString == 'S':
        display = 'short'
    elif viewString == 'C':
        display = 'char'
    elif viewString == 'I':
        display = 'int'
    elif viewString == 'J':
        display = 'long'
    elif viewString == 'F':
        display = 'float'
    elif viewString == 'D':
        display = 'double'
    elif viewString[0:1] == 'L':
        display = viewString[1:-1]
    elif viewString[0:1] == '[':
        if viewString[1:2] == 'L':
            display = viewString[2:-1] + '[]'
        else:
            if viewString[1:] == 'Z':
                display = 'boolean[]'
            elif viewString[1:] == 'B':
                display = 'byte[]'
            elif viewString[1:] == 'S':
                display = 'short[]'
            elif viewString[1:] == 'C':
                display = 'char[]'
            elif viewString[1:] == 'I':
                display = 'int[]'
            elif viewString[1:] == 'J':
                display = 'long[]'
            elif viewString[1:] == 'F':
                display = 'float[]'
            elif viewString[1:] == 'D':
                display = 'double[]'
            else:
                display = ''
    else:
        display = ''
    return display

def parserField(f,stringList,typelist):
    fieldList = []
    f.seek(0x50)
    fieldSize = byte2int(f.read(4))
    print('[+] field size ==> ',end='')
    print(fieldSize)
    fieldAddr = byte2int(f.read(4))
    for i in range(fieldSize):
        fieldStr = ''
        f.seek(fieldAddr)
        classIdx = typelist[byte2int(f.read(2))]
        f.seek(fieldAddr + 2)
        typeIdx = typelist[byte2int(f.read(2))]
        f.seek(fieldAddr + 4)
        nameIdx = stringList[byte2int(f.read(4))]
        fieldAddr += 8
        fieldStr = changeDisplay(typeIdx) + ' ' + changeDisplay(classIdx) + '.' + nameIdx
        fieldList.append(fieldStr)

    k = 0

    for fieldItem in fieldList:
        print(f'[-] fieldId[{k}] ==> ',end='')
        print(fieldItem)
        k += 1

if __name__ == '__main__':
    filename = str(os.path.join(sys.path[0])) + '\\1.dex'
    f = open(filename,'rb',True)
    stringsCount = getStringsCount(f)
    strList = getStrings(f,stringsCount)
    typeCount = getTypeAmount(f)
    typeList = getTypeItem(f,typeCount,strList)
    parserField(f,strList,typeList)
    f.close()

三、Dex文件中的方法定义

    1、在dex文件头中,关于方法定义的信息同样是八个字节,分别位于0x58处和0x5c处。在0x58处的四个字节,指明了dex文件中方法定义的数量,在0x5c处的四个字节,表明了dex文件中的方法定义的起始地址(ps:都是以小端序存储的),如下图所示:  

  

    2、在上面的一步以及找到了方法定义的起始地址,跟字段类似的,一个方法定义也需要八个字节。其中,在前两个字节,以小端序存储着解析出来的类的类型列表的索引,表示该方法属于哪个类;第三个字节和第四个字节,以小端序存储这解析出来的方法原型列表的索引,通过该索引值找到的方法原型声明了该方法的返回值类型和参数类型;最后四个字节则以小端序存储着前面解析出来的字符串列表的索引,声明了该方法的方法名。如下图所示:  

  

解析代码运行截图:  

  

  

解析代码:  

import binascii
import os
import sys

def byte2int(bs):
    tmp = bytearray(bs)
    tmp.reverse()
    rl = bytes(tmp)
    rl = str(binascii.b2a_hex(rl),encoding='UTF-8')
    rl = int(rl,16)
    return rl

def getStringsCount(f):
    f.seek(0x38)
    stringsId = f.read(4)
    count = byte2int(stringsId)
    return count

def getStringByteArr(f,addr):
    byteArr = bytearray()
    f.seek(addr + 1)
    b = f.read(1)
    b = str(binascii.b2a_hex(b),encoding='UTF-8')
    b = int(b,16)
    index = 2
    while b != 0:
        byteArr.append(b)
        f.seek(addr + index)
        b = f.read(1)
        b = str(binascii.b2a_hex(b),encoding='UTF-8')
        b = int(b,16)
        index = index + 1
    return byteArr

def BytesToString(byteArr):
    try:
        bs = bytes(byteArr)
        stringItem = str(bs,encoding='UTF-8')
        return stringItem
    except:
        pass

def getAddress(addr):
    address = bytearray(addr)
    address.reverse()
    address = bytes(address)
    address = str(binascii.b2a_hex(address),encoding='UTF-8')
    address = int(address,16)
    return address

def getStrings(f,stringAmount):
    stringsList = []
    f.seek(0x3c)
    stringOff = f.read(4)
    Off = getAddress(stringOff)
    f.seek(Off)
    for i in range(stringAmount):
        addr = f.read(4)
        address = getAddress(addr)
        byteArr = getStringByteArr(f,address)
        stringItem = BytesToString(byteArr)
        stringsList.append(stringItem)
        Off = Off + 4
        f.seek(Off)
    return stringsList

def getTypeAmount(f):
    f.seek(0x40)
    stringsId = f.read(4)
    count = byte2int(stringsId)
    return count

def getTypeItem(f,count,strLists):
    typeList = []
    f.seek(0x44)
    type_ids_off = f.read(4)
    type_off = byte2int(type_ids_off)
    f.seek(type_off)
    for i in range(count):
        typeIndex = f.read(4)
        typeIndex = byte2int(typeIndex)
        typeList.append(strLists[typeIndex])
        type_off = type_off + 0x04
        f.seek(type_off)
    return typeList

def changeDisplay(viewString):
    display = ''
    if viewString == 'V':
        display = 'void'
    elif viewString == 'Z':
        display = 'boolean'
    elif viewString == 'B':
        display = 'byte'
    elif viewString == 'S':
        display = 'short'
    elif viewString == 'C':
        display = 'char'
    elif viewString == 'I':
        display = 'int'
    elif viewString == 'J':
        display = 'long'
    elif viewString == 'F':
        display = 'float'
    elif viewString == 'D':
        display = 'double'
    elif viewString[0:1] == 'L':
        display = viewString[1:-1]
    elif viewString[0:1] == '[':
        if viewString[1:2] == 'L':
            display = viewString[2:-1] + '[]'
        else:
            if viewString[1:] == 'Z':
                display = 'boolean[]'
            elif viewString[1:] == 'B':
                display = 'byte[]'
            elif viewString[1:] == 'S':
                display = 'short[]'
            elif viewString[1:] == 'C':
                display = 'char[]'
            elif viewString[1:] == 'I':
                display = 'int[]'
            elif viewString[1:] == 'J':
                display = 'long[]'
            elif viewString[1:] == 'F':
                display = 'float[]'
            elif viewString[1:] == 'D':
                display = 'double[]'
            else:
                display = ''
    else:
        display = ''
    return display

def parseProtold(f,typeList,stringList):
    pList = []
    f.seek(0x48)
    protoldSizeTmp = f.read(4)
    protoldSize = byte2int(protoldSizeTmp)
    f.seek(0x4c)
    protoldAddr = byte2int(f.read(4))
    for i in range(protoldSize):
        f.seek(protoldAddr)
        AllString = stringList[byte2int(f.read(4))]
        protoldAddr += 4
        f.seek(protoldAddr)
        returnString = typeList[byte2int(f.read(4))]
        protoldAddr += 4
        f.seek(protoldAddr)
        paramAddr = byte2int(f.read(4))
        if paramAddr == 0:
            protoldAddr += 4
            pList.append(changeDisplay(returnString) + '()')
            continue
        f.seek(paramAddr)
        paramSize = byte2int(f.read(4))
        paramList = []
        if paramSize == 0:
            pass
        else:
            paramAddr = paramAddr + 4
            for k in range(paramSize):
                f.seek(paramAddr + (k * 2))
                paramString = typeList[byte2int(f.read(2))]
                paramList.append(paramString)
        protoldAddr += 4
        paramTmp = []
        for paramItem in paramList:
            paramTmp.append(changeDisplay(paramItem))
        param = changeDisplay(returnString) + '(' + ','.join(paramTmp) + ')'
        pList.append(param)
    return pList

def parserMethod(f,stringlist,typelist,protoldlist):
    methodlist = []
    f.seek(0x58)
    methodSize = byte2int(f.read(4))
    print('[+] method size ==> ',end='')
    print(methodSize)
    f.seek(0x5c)
    methodAddr = byte2int(f.read(4))
    for i in range(methodSize):
        f.seek(methodAddr)
        classIdx = typelist[byte2int(f.read(2))]
        f.seek(methodAddr + 2)
        protoldIdx = protoldlist[byte2int(f.read(2))]
        f.seek(methodAddr + 4)
        nameIdx = stringlist[byte2int(f.read(4))]
        tmp = protoldIdx.split('(',1)
        methodItem = str(tmp[0]) + ' ' + classIdx + '.' + nameIdx + '(' + str(tmp[1])
        methodlist.append(methodItem)
        print(f'[-] method[{i}] ==> ',end='')
        print(methodItem)
        methodAddr += 8

if __name__ == '__main__':
    filename = str(os.path.join(sys.path[0])) + '\\1.dex'
    f = open(filename,'rb',True)
    stringsCount = getStringsCount(f)
    strList = getStrings(f,stringsCount)
    typeCount = getTypeAmount(f)
    typeList = getTypeItem(f,typeCount,strList)
    protoldList = parseProtold(f,typeList,strList)
    parserMethod(f,strList,typeList,protoldList)
    f.close()

四、样本及代码下载链接和一些总结

  1、没什么好总结的,需要说明的是上面所有代码我电脑运行环境为python3.6。
  2、下载链接:
    百度网盘链接:https://pan.baidu.com/s/1qBgyy5b6Kw2GOmEY9idKqQ,提取码:u2ux

DEX文件字段和方法定义解析.zip

811.93 KB, 下载次数: 9, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 2威望 +2 吾爱币 +100 热心值 +2 收起 理由
miner123 + 1 热心回复!
qtfreet00 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

芽衣 发表于 2020-4-21 09:08
居然没人回复,帮暖 i_f25.png
NVIDIAVR 发表于 2020-5-2 08:41
秦时明月1 发表于 2020-8-6 09:31
以往 发表于 2020-8-29 23:05
学习了 感谢分享
正己 发表于 2022-4-1 22:51
windy_ll师傅是图床炸了吗?
 楼主| windy_ll 发表于 2022-4-2 08:48
正己 发表于 2022-4-1 22:51
windy_ll师傅是图床炸了吗?

图床炸了,刚迁完图床
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 12:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表