读取dex文件中的代码数据
java代码先javac转成class文件,然后用d8处理即可获得dex文件.
D8:Android 的 DEX 编译器 / desugar 工具,负责把 Java/Kotlin 编译出来的 .class(以及 Java 8+ 语言特性)转换成 .dex.
R8:Android 的 代码优化 + 压缩 + 混淆 + DEX 编译工具。它可以做 D8 做的编译工作,同时还会进行更激进的优化、shrinking、obfuscation.
若调用d8报错:

是因为d8和jdk/jre版本不匹配.
dex结构可以理解为:
header + index + data,header中包含了dex的基本信息和dex中各种类结构的偏移地址,index中存储了各类数据的地址或查询某类数据的索引(就是查第几个),data就是实际的数据区.数据读取过程可以简单地总结为:在header中找到所在结构的偏移地址,根据需要读取的数据类型获取对应的索引,然后在对应的index中找到索引对应的数据,获取偏移地址,读取最终的实际数据.
dex具体结构可学习:https://bbs.kanxue.com/thread-284995.htm#msg_header_h1_3
实现的dex读取器
import textwrap
import struct
path = "./classes.dex"
with open(path,"rb") as dex:
dex_file = dex.read()
def read_uleb128(data, offset):
result = shift = 0
while True:
byte = data[offset]
offset += 1
result |= (byte & 0x7f) << shift
if (byte & 0x80) == 0:
break
shift += 7
return result, offset
def getStrByIndex(dex, index):
stringid_length = 4
stringId_offset = struct.unpack_from("<I", dex, 60)[0]
offset = index*stringid_length+stringId_offset
dexString_offset = struct.unpack_from("<I", dex, offset)[0]
length, start_offset = read_uleb128(dex, dexString_offset)
string = dex[start_offset: start_offset+length].decode("utf-8")
return string
def ResolveMethodId(dex):
methodId_size = struct.unpack_from("<I", dex, 88)[0]
methodId_offset = struct.unpack_from("<I", dex, 92)[0]
methodId_length = 8
for i in range(0,methodId_size):
start_offset = methodId_offset+methodId_length*i
nameindex = struct.unpack_from("<I", dex, start_offset+4)[0]
name = getStrByIndex(dex, nameindex)
print(name)
def getClassData(dex):
classDef_num = struct.unpack_from("<I", dex, 96)[0]
classDef_offset = struct.unpack_from("<I", dex, 100)[0]
classDef_size = 32
for i in range(0,classDef_num):
start_offset = classDef_offset + i * classDef_size
data_offset = struct.unpack_from("<I", dex, start_offset+24)[0]
staticField_num,length1= read_uleb128(dex, data_offset)
instanceField_num,length2 = read_uleb128(dex, length1)
directMethod_num,length3 = read_uleb128(dex, length2)
virtualMethod_num,length4 = read_uleb128(dex, length3)
staticField_offset = length4
for j in range(0,staticField_num):
_,length1 = read_uleb128(dex, staticField_offset)
_,length2 = read_uleb128(dex, length1)
staticField_offset = length2
instanceField_offset = staticField_offset
for k in range(0,instanceField_num):
_,length1 = read_uleb128(dex, instanceField_offset)
_,length2 = read_uleb128(dex, length1)
instanceField_offset = length2
directMethod_offset = instanceField_offset
for i in range(0, directMethod_num):
t1,length1 = read_uleb128(dex,directMethod_offset)
t2,length2 = read_uleb128(dex,length1)
code_offset,length3 = read_uleb128(dex,length2)
insns_size = struct.unpack_from("<I", dex, code_offset+12)[0]
code = dex[code_offset+16: code_offset+16+insns_size*2]
print(code)
directMethod_offset = length3
virtualMethod_offset = directMethod_offset
for i in range(0, virtualMethod_num):
t1,length1 = read_uleb128(dex,virtualMethod_offset)
t2,length2 = read_uleb128(dex,length1)
code_offset,length3 = read_uleb128(dex,length2)
insns_size = struct.unpack_from("<I", dex, code_offset+12)[0]
code = dex[code_offset+16: code_offset+16+insns_size*2]
print(code)
virtualMethod_offset = length3
getClassData(dex_file)
注意,从code_item.insns读取到的代码数据是opcode(操作码)的二进制数据,不是直接的smali代码.
opcode以16byte为一个code unit且小端序存储,其中低八位是操作码,其余位则为寄存器编号 / 常量 / 偏移量等操作数.
把opcode转为smali,需要依照instruction format(指令格式):
首先,取一个code unit,查看其低八位,确定其对应的指令.
然后按照instruction format(这个是有规范定义的),确定接下来需要分析多少数据量,如何分析这些数据.
浙公网安备 33010602011771号