zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Idea配置注释
    mysql省市区数据表
    php实现汉字转拼音的类
    git push时如果不再弹出用户和密码的输入提示框该怎么办
    .gitignore 忽略特殊文件无效
    vmware中CentOS7网络设置教程详解
    Tp5中配置多模块开发
    安装tp6 并使用多应用模式
    ldf和mdf文件怎么还原到sqlserver数据库
    git本地仓库与远程仓库建立连接。
  • 原文地址:https://www.cnblogs.com/aWxvdmVseXc0/p/12713171.html
Copyright © 2011-2022 走看看