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
应该增加个下载确认,不小心点着就下载了
emmm你可以跟管理人员建议一波 010有没有中文版?? 感谢楼主教学{:301_1004:} 来了,谢谢楼主 感谢分享 高手如云 谢谢楼主分享
页:
[1]