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
这四个字节,存储这读取字段的起始偏移地址,如下所示:
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文件字段和方法定义解析的更多相关文章
- DEX文件解析--5、dex方法原型解析
一.前言 前几篇文章链接: DEX文件解析---1.dex文件头解析 DEX文件解析---2.Dex文件checksum(校验和)解析 DEX文件解析--3.dex文件字 ...
- DEX、ODEX、OAT文件&Dalvik和ART虚拟机
https://www.jianshu.com/p/389911e2cdfb https://www.jianshu.com/p/a468e714aca7 ODEX是安卓上的应用程序apk中提取出来的 ...
- plist文件、NSUserDefault 对文件进行存储的类、json格式解析
========================== 文件操作 ========================== Δ一 .plist文件 .plist文件是一个属性字典数组的一个文件: .plis ...
- 项目开发笔记-传单下发 名片替换 文件复制上传/html静态内容替换/json解析/html解析
//////////////////////////// 注意: 此博客是个人工作笔记 非独立demo////////////////////////////////// .............. ...
- C语言解析WAV音频文件
C语言解析WAV音频文件 代码地址: Github : https://github.com/CasterWx/c-wave-master 目录 前言 了解WAV音频文件 什么是二进制文件 WAV的二 ...
- 使用rdbtools工具来解析redis rdb文件
工欲善其事必先利其器,日常工作中,好的工具能够高效的协助我们工作:今天介绍一款用来解析redis rdb文件的工具,非常好用.会之,受用无穷! 一.rdbtools工具介绍 源码地址:https:// ...
- Excelbatis-一个将excel文件读入成实体列表、将实体列表解析成excel文件的ORM框架,简洁易于配置、可扩展性好
欢迎使用Excelbatis! github地址:https://github.com/log4leo/Excelbatis Excelbatis的优点 和spring天然结合,易于接入 xsd支持, ...
- 【文件处理】xml 文件 SAX解析
SAX的全称是Simple APIs for XML,也即XML简单应用程序接口. 与DOM不同,SAX提供的访问模式是一种顺序模式,这是一种快速读写XML数据的方式. 当使用SAX分析器对XML文档 ...
- 【文件处理】xml 文件 DOM解析
一.Java解析xml.解析xml四种方法.DOM.SAX.JDOM.DOM4j.XPath 此文针对其中的DOM方法具体展开介绍及代码分析 sax.dom是两种对xml文档进行解析的方法(没有具体实 ...
随机推荐
- 从新冠疫情出发,漫谈 Gossip 协议
众所周知周知,疫情仍然在全球各地肆虐.据最新数据统计,截至北京时间 2020-05-28,全球累计确诊 5698703 例,累计死亡 352282 例,累计治愈 2415237 例. 从上面的统计数据 ...
- Hexo快速构建个人小站-自定义域名和自定义主题(二)
背景交代: 在上一章<Hexo快速构建个人小站-Hexo初始化和将项目托管在Github(一)>中,我们已经成功的利用hexo初始化了博客项目,并托管在Github上,且通过Github的 ...
- 事务的特性ACID、隔离级别
1.事务特性ACID 1.1 事务的四大特性 1.原子性(Atomicity) 事务包装的一组sql,要么都执行成功,要么都失败.这些操作是不可分割的. 2.一致性(Consistency) 数据库的 ...
- IDEA SonarLint安装及使用
SonarLint插件安装IDEA菜单栏选择File->Settings,左边栏选择Plugins 在线安装选择Browse repositories,搜索Sonar,选择SonarLint进行 ...
- 小师妹学JVM之:JIT中的PrintCompilation
目录 简介 PrintCompilation 分析PrintCompilation的结果 总结 简介 上篇文章我们讲到了JIT中的LogCompilation,将编译的日志都收集起来,存到日志文件里面 ...
- 入门大数据---Python基础
前言 由于AI的发展,包括Python集成了很多计算库,所以淡入了人们的视野,成为一个极力追捧的语言. 首先概括下Python中文含义是蟒蛇,它是一个胶水语言和一个脚本语言,胶水的意思是能和多种语言集 ...
- python根据列表创建文件夹,拷贝指定文件
内容涉及:关键字定位,列表去重复,路径组装,文件夹创建,文件拷贝,字符串分割 list.txt的内容为包含关键字的文件路径,如:关键字 ’181‘ org/20190523/1/20190523201 ...
- Python实用笔记 (15)函数式编程——装饰器
什么函数可以被称为闭包函数呢?主要是满足两点:函数内部定义的函数:引用了外部变量但非全局变量. python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰 ...
- JVM垃圾回收概述
垃圾回收概述 什么是垃圾 什么是垃圾( Garbage) 呢? 垃圾是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾. 如果不及时对内存中的垃圾进行清理,那么,这些垃圾对象所占的内 ...
- NOIp (on line) 入门组 2020 总结
得分情况 : 估分: 100+30+30=160: 实际: 95+70+25=190: T1 : 题意: 有n块钱,买三种文具,分别为 a:7元.b:4元.c:3元,问怎么买能让n元钱全部用完,而且使 ...