zoukankan      html  css  js  c++  java
  • DEX文件解析--6、dex文件字段和方法定义解析

    一、前言

       前几篇文章链接:
          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这四个字节,存储这读取字段的起始偏移地址,如下所示:

    5.png

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

    6.png

    解析代码运行截图:

    1.png

    2.png

    解析代码:

    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:都是以小端序存储的),如下图所示:

    7.png

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

    8.png

    解析代码运行截图:

    3.png

    4.png

    解析代码:

    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
        土豪通道:

  • 相关阅读:
    Add Binary
    Java笔记之String
    Java笔记之数组
    Merge Two Sorted Lists
    Remove Nth Node From End of List
    Longest Common Prefix
    Roman to Integer
    Palindrome Number
    Reverse Integer
    _cdel stdcall
  • 原文地址:https://www.cnblogs.com/aWxvdmVseXc0/p/12727731.html
Copyright © 2011-2022 走看看