DEX文件解析--5、dex方法原型解析

一、前言

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


二、DEX文件中的方法原型

    1、关于dex文件中方法原型的解析,需要知道怎么解析出字符串和类的类型,不明白的可以看我前几篇的解析。DEX文件中的方法原型定义了一个方法的返回值类型和参数类型,例如一个方法返回值为void,参数类型为int,那么在dex文件中该方法原型表示为V(I)(smaliV表示voidI表示int)。在dex文件头部中,关于方法原型有两处,第一处位于0x48处,用4个字节定义了方法原型的数量,在0x4C处用4个字节定义了方法原型的偏移地址,如下所示:

3.png

    2、在上面我们知道了方法原型的起始偏移地址,接下来我们根据这个偏移地址找到方法原型,同样的,跟解析类的类型比较类似,一个方法原型所占字节数为12个字节,第一个字节到第四个字节表示了定义方法原型的字符串,这四个字节按小端序存储,读取出来为在字符串列表的索引,例如一个方法原型返回值为void,参数为boolean,那么定义该方法原型的字符串即为VZ;第5个字节到第八个字节表示该方法原型的返回值类型,读取出来的值为前面解析出来的类的类型列表的索引;第8个字节到第十二给字节表示该方法原型的参数,读取出来为一组地址,通过该地址可以找到该方法原型的参数,跳转到该地址去,首先看前4个字节,前四个字节按照小端序存储,读取出来的值为该方法原型参数的个数,接着根据参数个数,读取具体的参数类型,每个参数类型占2个字节,这两个字节读取出来的值为前面解析出来的类的类型列表的索引,如下所示:

4.png

5.png


三、解析代码

 运行环境:我电脑环境为python3.6
 运行截图:

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 parseProtold(f,typeList,stringList):
    f.seek(0x48)
    protoldSizeTmp = f.read(4)
    protoldSize = byte2int(protoldSizeTmp)
    print('[+] protold size ==> ',end='')
    print(protoldSize)
    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
            print(f'[-] protold[{i}] ==> ',end='')
            print(AllString + ' : ',end='')
            print(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))
        print(f'[-] protold[{i}] ==> ',end='')
        print(AllString + ' : ',end='')
        print(changeDisplay(returnString) + '(',end='')
        param = ','.join(paramTmp)
        print(param + ')')

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)
    parseProtold(f,typeList,strList)
    f.close()

四、相关链接以及样本代码下载加总结

  1、总结:没啥可以总结的,就是代码写的比较丑,大佬勿喷!!!

  2、smali数据格式参考链接:https://blog.csdn.net/ls0111/article/details/76228068

  3、样本及代码下载链接:
    百度网盘:https://pan.baidu.com/s/1dF-V7oSoXv_shYw7GlP84A,提取码:wzdu

原文地址:https://www.cnblogs.com/aWxvdmVseXc0/p/12713171.html