一、前言

   前几篇文章链接:

      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文件解析--6、dex文件字段和方法定义解析的更多相关文章

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

    一.前言    前几篇文章链接:     DEX文件解析---1.dex文件头解析     DEX文件解析---2.Dex文件checksum(校验和)解析     DEX文件解析--3.dex文件字 ...

  2. DEX、ODEX、OAT文件&Dalvik和ART虚拟机

    https://www.jianshu.com/p/389911e2cdfb https://www.jianshu.com/p/a468e714aca7 ODEX是安卓上的应用程序apk中提取出来的 ...

  3. plist文件、NSUserDefault 对文件进行存储的类、json格式解析

    ========================== 文件操作 ========================== Δ一 .plist文件 .plist文件是一个属性字典数组的一个文件: .plis ...

  4. 项目开发笔记-传单下发 名片替换 文件复制上传/html静态内容替换/json解析/html解析

    //////////////////////////// 注意: 此博客是个人工作笔记 非独立demo////////////////////////////////// .............. ...

  5. C语言解析WAV音频文件

    C语言解析WAV音频文件 代码地址: Github : https://github.com/CasterWx/c-wave-master 目录 前言 了解WAV音频文件 什么是二进制文件 WAV的二 ...

  6. 使用rdbtools工具来解析redis rdb文件

    工欲善其事必先利其器,日常工作中,好的工具能够高效的协助我们工作:今天介绍一款用来解析redis rdb文件的工具,非常好用.会之,受用无穷! 一.rdbtools工具介绍 源码地址:https:// ...

  7. Excelbatis-一个将excel文件读入成实体列表、将实体列表解析成excel文件的ORM框架,简洁易于配置、可扩展性好

    欢迎使用Excelbatis! github地址:https://github.com/log4leo/Excelbatis Excelbatis的优点 和spring天然结合,易于接入 xsd支持, ...

  8. 【文件处理】xml 文件 SAX解析

    SAX的全称是Simple APIs for XML,也即XML简单应用程序接口. 与DOM不同,SAX提供的访问模式是一种顺序模式,这是一种快速读写XML数据的方式. 当使用SAX分析器对XML文档 ...

  9. 【文件处理】xml 文件 DOM解析

    一.Java解析xml.解析xml四种方法.DOM.SAX.JDOM.DOM4j.XPath 此文针对其中的DOM方法具体展开介绍及代码分析 sax.dom是两种对xml文档进行解析的方法(没有具体实 ...

随机推荐

  1. 使用DragonFly进行智能镜像分发

    Dragonfly 是一款基于 P2P 的智能镜像和文件分发工具.它旨在提高文件传输的效率和速率,最大限度地利用网络带宽,尤其是在分发大量数据时,例如应用分发.缓存分发.日志分发和镜像分发. 在阿里巴 ...

  2. 为Promise添加finally方法支持,把小程序函数变成promise函数

    // 为Promise添加finally方法支持 Promise.prototype.finally = function (callback) {   let P = this.constructo ...

  3. Struts2 自定义拦截器时Action无法接收到参数

    问题:自定义拦截器,没有添加defaultStack导致Action无法接受到参数 解决办法: 方法一,添加defaultStack,然后在Action中引用 自定义的stack,其实defaultS ...

  4. spring 整合redis集群中使用@autowire无效问题的解决办法

    1.视频参考黑马32期宜立方商城第6课 redis对于的代码 我们先变向一个redis客户端的接口文件 package com.test; public interface JedisClient { ...

  5. JAVA环境配置(WIN10之64位)

    1.下载java开发工具包JDK,https://www.oracle.com/technetwork/java/javase/downloads/index.html进入首页, 点击下载页: 点击下 ...

  6. METS介绍

    METS介绍 首页  >  关于METS > METS介绍   医护英语水平考试(Medical English Test System, 以下简称:METS)是由教育部考试中心与中国国际 ...

  7. 部署rabbitMQ镜像集群实战测试

    部署rabbitMQ镜像集群 版本信息 rabbit MQ: 3.8.5 Erlang: 官方建议最低21.3 推荐22.x 这里用的是23 环境准备 主机规划 主机 节点 172.16.14.3 磁 ...

  8. Sql sever 声明变量,赋值变量

    语句: --声明变量DECLARE @idcard nvarchar () , @rowid nvarchar () --给变量赋值SELECT @idcard = '{0}', @rowid = ' ...

  9. 重学 Java 设计模式:实战观察者模式「模拟类似小客车指标摇号过程,监听消息通知用户中签场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 知道的越多不知道的就越多 编程开发这条路上的知识是无穷无尽的, ...

  10. 接口&&多态&&构造函数&&关键字

    day06 抽象类的局限性(与接口的区别) 抽象类可以定义非抽象方法,避免子类重复实现这些方法,提高代码重用性;接口只能包含抽象方法;jdk1.8之后接口可以包含默认方法. 一个类只能继承一个直接父类 ...