windy_ll 发表于 2020-4-16 15:18

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

本帖最后由 windy_ll 于 2022-4-1 20:35 编辑


# 一、前言

**   前几篇文章链接:**
**    (https://www.52pojie.cn/thread-1057245-1-1.html)**
**    (https://www.52pojie.cn/thread-1070218-1-1.html)**
**    (https://www.52pojie.cn/thread-1148568-1-1.html)**
**    (https://www.52pojie.cn/thread-1151528-1-1.html)**

---

# 二、DEX文件中的方法原型

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

![](https://cdn.jsdelivr.net/gh/windy-purple/blog_picture_bed//dex_protold/3.png)

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

![](https://cdn.jsdelivr.net/gh/windy-purple/blog_picture_bed//dex_protold/4.png)

![](https://cdn.jsdelivr.net/gh/windy-purple/blog_picture_bed//dex_protold/5.png)

---

# 三、解析代码

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

![](https://cdn.jsdelivr.net/gh/windy-purple/blog_picture_bed//dex_protold/1.png)

![](https://cdn.jsdelivr.net/gh/windy-purple/blog_picture_bed//dex_protold/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)
            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 == 'L':
            display = viewString
      elif viewString == '[':
            if viewString == 'L':
                display = viewString + '[]'
            else:
                if 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[]'
                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
            protoldAddr += 4
            f.seek(protoldAddr)
            returnString = typeList
            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
                  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)) + '\\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**

Rx0 发表于 2020-4-16 15:32

应该增加个下载确认,不小心点着就下载了

windy_ll 发表于 2020-4-16 15:33

Rx0 发表于 2020-4-16 15:32
应该增加个下载确认,不小心点着就下载了

emmm你可以跟管理人员建议一波

芽衣 发表于 2020-4-16 16:17

010有没有中文版??

小天使xx 发表于 2020-4-16 23:02

感谢楼主教学{:301_1004:}

15295828305 发表于 2020-4-17 07:50

来了,谢谢楼主

Windows10 发表于 2020-4-17 13:10

感谢分享

jiabode520 发表于 2020-4-17 18:14

高手如云

BUG606 发表于 2020-7-25 21:51

谢谢楼主分享
页: [1]
查看完整版本: DEX文件解析--5、dex方法原型解析