-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathdexDumper.py
More file actions
212 lines (180 loc) · 6.57 KB
/
dexDumper.py
File metadata and controls
212 lines (180 loc) · 6.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import os
import sys
import glob
import time
import frida
package_name = ""
def on_message(message, data):
if message['type'] == 'send':
if package_name == "":
return
dex_file_path = os.path.join(
os.getcwd(), package_name, message['payload'])
with open(dex_file_path, "wb") as f:
f.write(data)
jscode = """
const dexFilePtrSet = new Set();
const checksumSet = new Set();
const pointerSize = Process.pointerSize;
// 添加一些不需要dump的dex文件 如miui mediatek等
checksumSet.add(0x43174B52);
checksumSet.add(0x4480A281);
checksumSet.add(0x6186E805);
checksumSet.add(0x6BBD829E);
checksumSet.add(0x78CC0F74);
checksumSet.add(0x85DE015E);
checksumSet.add(0x8690AEE2);
checksumSet.add(0x93289A69);
checksumSet.add(0x9AB6A58A);
checksumSet.add(0x9ADDCFFD);
checksumSet.add(0xB214697F);
checksumSet.add(0xB5E8C542);
checksumSet.add(0xDD357319);
checksumSet.add(0xE7ECDFB6);
checksumSet.add(0xFB9676D7);
function dumpDexFileByBeginAndSize(begin, size) {
// 相同校验和只dump一次
const checksum = begin.add(8).readU32();
if (checksumSet.has(checksum)) {
return;
}
checksumSet.add(checksum);
console.log(`\\n[*] Found DexFile\\n- begin: ${begin}\\n- size: ${size}`);
const dexMagic = begin.readU32();
if (dexMagic !== 0x0A786564) { // dex
// 非dex文件打印一下头部信息
// 0x78656463 cdex ...
const headerHexDump = hexdump(begin, {
offset: 0,
length: 8,
header: true,
ansi: true
});
console.log(`[!] Not a dex file:\\n${headerHexDump}`);
return;
}
const filename = `${checksum.toString(16).toUpperCase().padStart(8, '0')}.dex`;
console.log(`=> Dump to ${filename}`);
const data = begin.readByteArray(size);
send(filename, data);
}
function dumpDexFileByPointer(dexFilePtr) {
// 每个地址只调用一次 不一定正确
// 有可能函数回填后地址相同 有需要的话关闭这里的判断
const addressString = dexFilePtr.toString();
if (dexFilePtrSet.has(addressString)) {
return;
}
dexFilePtrSet.add(addressString);
const begin = dexFilePtr.add(pointerSize).readPointer();
const size = dexFilePtr.add(pointerSize * 2).readUInt();
dumpDexFileByBeginAndSize(begin, size);
}
function main() {
Java.perform(doJavaHook);
// adb pull /apex/com.android.art/lib64/libart.so
const libart = Process.findModuleByName("libart.so");
const libartSymbols = Module.enumerateSymbols("libart.so");
// OpenCommon 在较新系统上未经测试 关闭下面这一段 不影响使用
// adb pull /apex/com.android.art/lib64/libdexfile.so
// libdexfile.so中hook OpenCommon函数
const libdexfile = Process.findModuleByName("libdexfile.so");
const libdexfileSymbols = Module.enumerateSymbols("libdexfile.so");
for (const symbol of libdexfileSymbols) {
if (symbol.name.includes("OpenCommon")) {
const targetFuncAddr = symbol.address;
console.log(`\\n[+] Function hooked\\n- name: ${symbol.name}\\n- offset: ${targetFuncAddr.sub(libdexfile.base)}`);
Interceptor.attach(targetFuncAddr, {
onEnter: function (args) {
// 名字包含 "OpenCommon" 的函数 可能不止一个
// 所以此处前两个参数未必是 begin 和 size
this.begin = ptr(args[0]);
this.size = args[1].toInt32();
},
onLeave: function (_retval) {
dumpDexFileByBeginAndSize(this.begin, this.size);
}
});
}
}
// libart.so
// RegisterDexFile函数 和 LoadMethod函数
for (const symbol of libartSymbols) {
if (!symbol.name.includes("DexFile")) {
continue;
}
if (!symbol.name.includes("ClassLinker")) {
continue;
}
// 下面两个函数就先不hook了
if (symbol.name.includes("RegisterDexFileLocked")) {
continue;
}
if (symbol.name.includes("RegisterDexFiles")) {
continue;
}
if (symbol.name.includes("RegisterDexFile")) {
const targetFuncAddr = symbol.address;
console.log(`\\n[+] Function hooked\\n- name: ${symbol.name}\\n- offset: ${targetFuncAddr.sub(libart.base)}`);
Interceptor.attach(targetFuncAddr, {
onEnter: function (args) {
const dexFilePtr = ptr(args[1]);
dumpDexFileByPointer(dexFilePtr);
},
});
}
if (symbol.name.includes("LoadMethod")) {
const targetFuncAddr = symbol.address;
console.log(`\\n[+] Function hooked\\n- name: ${symbol.name}\\n- offset: ${targetFuncAddr.sub(libart.base)}`);
Interceptor.attach(targetFuncAddr, {
onEnter: function (args) {
const dexFilePtr = ptr(args[1]);
dumpDexFileByPointer(dexFilePtr);
},
});
}
}
}
function doJavaHook() {
// hook下面这个两个Java层函数
const DexFile = Java.use("dalvik.system.DexFile");
DexFile.openDexFileNative.implementation = function (sourceName, outputName, flags, loader, elements) {
console.log(`\\n[*] dalvik.system.DexFile.openDexFileNative called\\n- sourceName: ${sourceName}`);
return this.openDexFileNative(sourceName, outputName, flags, loader, elements);
}
// const ByteBuffer = Java.use("java.nio.ByteBuffer");
DexFile.openInMemoryDexFilesNative.implementation = function (bufs, arrays, starts, ends, loader, elements) {
// TODO: 通过这里的arrays参数dump文件
console.log(`\\n[*] dalvik.system.DexFile.openInMemoryDexFilesNative called`);
return this.openInMemoryDexFilesNative(bufs, arrays, starts, ends, loader, elements);
}
// console.log("\\n[!] Java function hooked\\n- dalvik.system.DexPathList.$init()\\n+ dalvik.system.DexFile.openDexFileNative()");
console.log("\\n[+] Java function hooked\\n+ dalvik.system.DexFile.openDexFileNative()\\n+ dalvik.system.DexFile.openInMemoryDexFilesNative()");
}
setImmediate(main);
"""
# usage: python dexDumper.py com.example.app
if __name__ == '__main__':
try:
package_name = sys.argv[1]
except IndexError:
print("Usage: python dexDumper.py package_name")
sys.exit(1)
# 查看当前目录下是否有package_name文件夹,如果没有则创建
# 如果有则清空文件夹
if os.path.exists(package_name):
dex_files = glob.glob(os.path.join(package_name, '*.dex'))
for dex_file in dex_files:
os.remove(dex_file)
else:
os.makedirs(package_name)
device = frida.get_usb_device()
pid = device.spawn([package_name])
process = device.attach(pid)
script = process.create_script(jscode)
script.on('message', on_message)
print('Running DexDumper')
script.load()
time.sleep(2)
device.resume(pid)
sys.stdin.read()