-
Notifications
You must be signed in to change notification settings - Fork 55
/
XposedFridaBridge.js
413 lines (342 loc) · 17.5 KB
/
XposedFridaBridge.js
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/*
Frida Xposed Bridge
by Monkeylord
License: MIT
Load Xposed Bridge&Modules though Frida.
原理:通过Frida加载XposedBridge.jar,同时通过Frida Java Hook来实现Xposed API。随后模拟Xposed初始化,并加载插件,然后再模拟应用启动。
*/
var typeTranslation = {
"Z":"java.lang.Boolean",
"B":"java.lang.Byte",
"S":"java.lang.Short",
"I":"java.lang.Integer",
"J":"java.lang.Long",
"F":"java.lang.Float",
"D":"java.lang.Double"
}
var XposedClassFactory = null
function implementXposedAPI(){
// Implement ZygoteService API
var ZygoteService = XposedClassFactory.use("de.robv.android.xposed.services.ZygoteService")
ZygoteService.checkFileAccess.implementation = function(){
console.log("[API Call] checkFileAccess", " filename:", arguments[0])
return true
}
ZygoteService.statFile.implementation = function(){
console.log("[API Call] statFile", " filename:", arguments[0])
return null
}
ZygoteService.readFile.overload('java.lang.String').implementation = function(){
console.log("[API Call] readFile", " filename:", arguments[0])
return null
}
// Implement XposedBridge API
var XposedBridge = XposedClassFactory.use("de.robv.android.xposed.XposedBridge")
XposedBridge.runtime.value = 2 // Art
XposedBridge.hadInitErrors.implementation=function(){
console.log("[API Call] hadInitErrors")
return false
}
XposedBridge.getStartClassName.implementation=function(){
console.log("[API Call] getStartClassName")
// TODO
return ""
}
XposedBridge.getRuntime.implementation=function(){
console.log("[API Call] getRuntime")
// 1 = Dalvik, 2 = Art
return 2
}
XposedBridge.startsSystemServer.implementation=function(){
console.log("[API Call] startsSystemServer")
// TODO
return false
}
XposedBridge.getXposedVersion.implementation=function(){
console.log("[API Call] getXposedVersion")
return 82
}
XposedBridge.initXResourcesNative.implementation=function(){
console.log("[API Call] initXResourcesNative")
// Disable Resource Hook
// TODO: implement Resource Hook
return false
}
XposedBridge.hookMethodNative.implementation=function(javaReflectedMethod, jobject, jint, javaAdditionalInfo){
console.log("[API Call] hookMethodNative", javaReflectedMethod.getDeclaringClass().getName(), javaReflectedMethod.getName())
// 这里来的,可能是Method,也可能是Constructor
// 在7.0里不能直接getClass,用$className替代
var refMethod = Java.use(javaReflectedMethod.$className)
var method = Java.cast(javaReflectedMethod, refMethod)
// 创建GlobalRef,不然再次调用时可能就是野指针了
//javaReflectedMethod.$h = Java.vm.getEnv().newGlobalRef(javaReflectedMethod.$h)
//javaAdditionalInfo.$h = Java.vm.getEnv().newGlobalRef(javaAdditionalInfo.$h)
javaReflectedMethod = Java.retain(javaReflectedMethod)
javaAdditionalInfo = Java.retain(javaAdditionalInfo)
// 拿基本信息
// Frida中Method Hook和Constructor Hook方式不同,所以要区分
var clazz = method.getDeclaringClass().getName()
var mtdname = (javaReflectedMethod.$className=="java.lang.reflect.Constructor")? "$init": method.getName()
var overload = method.getParameterTypes().map(function(clz){return clz.getName()})
var fridaMethod = Java.use(clazz)[mtdname].overload.apply(Java.use(clazz)[mtdname], overload)
fridaMethod.implementation = function(){
console.log("handleHookedMethod", javaReflectedMethod.getDeclaringClass().getName(), javaReflectedMethod.getName())
var isInstanceMethod = fridaMethod.type == 3 // 3 = Instance Method
var thisObject = null
if (isInstanceMethod)
thisObject = this
var args = arguments
var jarr = Object.keys(arguments).map(function(key){return args[key]})
fridaMethod.argumentTypes.forEach(function(type,index){
if(type.type != "pointer")jarr[index] = Java.use(typeTranslation[type.name]).valueOf(jarr[index])
else{
var env = Java.vm.getEnv()
jarr[index] = Java.classFactory._getType("java.lang.Object").fromJni(type.toJni(jarr[index], env),env, false)
}
})
try{
var xposedResult = XposedBridge.handleHookedMethod(javaReflectedMethod, jint, javaAdditionalInfo, thisObject, Java.array("Ljava.lang.Object;", jarr))
/*
Frida-java在这里有Bug,手动解决数组对象问题
*/
var env = Java.vm.getEnv()
var retType = fridaMethod._p[4]
var hhmRetType = XposedBridge.handleHookedMethod.overloads[0]._p[4]
if(xposedResult==null)return null
if(retType.type != "pointer"){
var value
var basicObj = Java.cast(xposedResult,Java.use(typeTranslation[retType.name]))
switch(retType.name){
case "Z":
value = basicObj.booleanValue();break;
case "B":
value = basicObj.byteValue();break;
case "S":
value = basicObj.shortValue();break;
case "I":
value = basicObj.intValue();break;
case "J":
value = basicObj.longValue();break;
case "F":
value = basicObj.floatValue();break;
case "D":
value = basicObj.doubleValue();break;
}
return value
}else{
return retType.fromJni(hhmRetType.toJni(xposedResult, env), env, false)
}
}catch(e){
console.log("Exception: ", e)
//throw e
}
}
}
XposedBridge.setObjectClassNative.implementation=function(javaObj, javaClazz){
console.log("[API Call] setObjectClassNative", javaObj, javaClazz)
Java.cast(javaObj, javaClazz)
}
XposedBridge.dumpObjectNative.implementation=function(){
console.log("[API Call] dumpObjectNative")
return undefined
}
XposedBridge.cloneToSubclassNative.implementation=function(javaObj, javaClazz){
console.log("[API Call] cloneToSubclassNative", javaObj, javaClazz)
return Java.cast(javaObj, javaClazz)
}
XposedBridge.removeFinalFlagNative.implementation=function(){
console.log("[API Call] removeFinalFlagNative")
// TODO: Remove final flag
// This is used by Resource Hook
// Reference: https://github.com/frida/frida-java-bridge/blob/master/lib/android.js#L1390
}
XposedBridge.invokeOriginalMethodNative.implementation = function(javaMethod, isResolved, jobjectArray, jclass, javaReceiver, javaArgs){
console.log("[API Call] invokeOriginalMethodNative", javaMethod)
var refMethod = Java.use(javaMethod.$className)
var method = Java.cast(javaMethod, refMethod)
var clazz = method.getDeclaringClass().getName()
var mtdname = method.getName()
var overload = method.getParameterTypes().map(function(clz){return clz.getName()})
var fridaMethod = Java.use(clazz)[mtdname].overload.apply(Java.use(clazz)[mtdname], overload)
var thisObject = (fridaMethod.type == 3)?Java.cast(javaReceiver, Java.use(clazz)):Java.use(clazz)
var jarr = javaArgs
// 不知道为什么结尾可能会多一个null,可能是ducktape问题?手动去掉。
jarr = jarr.slice(0, javaArgs.length)
fridaMethod.argumentTypes.forEach(function(type,index){
if(type.type!="pointer"){
//console.log("CAST: ",JSON.stringify(Object.keys(jarr[index])))
var value
var basicObj = Java.cast(jarr[index],Java.use(typeTranslation[type.name]))
switch(type.name){
case "Z":
value = basicObj.booleanValue();break;
case "B":
value = basicObj.byteValue();break;
case "S":
value = basicObj.shortValue();break;
case "I":
value = basicObj.intValue();break;
case "J":
value = basicObj.longValue();break;
case "F":
value = basicObj.floatValue();break;
case "D":
value = basicObj.doubleValue();break;
}
jarr[index]=value
}else{
var env = Java.vm.getEnv()
jarr[index] = type.fromJni(Java.classFactory._getType("java.lang.Object").toJni(jarr[index], env), env, false)
}
})
var result = null
try{
result = fridaMethod.apply(thisObject, jarr)
/*
这里有Frida的Bug,frida-java-bridge的对象处理中,数组并不被认为是Object,所以不能作为Object返回。
因为数组经过了转换,转换的代码在 https://github.com/frida/frida-java-bridge/blob/4aa88501d2c6c871ada1c696816ca6f7f2626d7b/lib/types.js#L396
而数组处理需要ArrayType来处理,ObjectType并不兼容它
需要手动解决这个问题,构造和析构对象。
*/
var env = Java.vm.getEnv()
var retType = fridaMethod._p[4]
var iomnRetType = XposedBridge.invokeOriginalMethodNative.overloads[0]._p[4]
if(retType.type != "pointer")return Java.use(typeTranslation[retType.name]).valueOf(result)
var rawResult = retType.toJni(result, env)
var tmpResult = iomnRetType.fromJni(rawResult, env, false)
result = tmpResult
}catch(e){
console.log(e)
throw e
}
return result || null
}
XposedBridge.closeFilesBeforeForkNative.implementation=function(){
console.log("[API Call] closeFilesBeforeForkNative")
// TODO
// Useless outside Zygote
}
XposedBridge.reopenFilesAfterForkNative.implementation=function(){
console.log("[API Call] reopenFilesAfterForkNative")
// TODO
// Useless outside Zygote
}
XposedBridge.invalidateCallersNative.implementation=function(){
console.log("[API Call] invalidateCallersNative")
// TODO:
// This is used in resource hook
}
}
function FrameworkInit(bridgePath, xposedPath){
var ActivityThread = Java.use("android.app.ActivityThread")
var apkClassloader = ActivityThread.currentActivityThread().peekPackageInfo(ActivityThread.currentApplication().getPackageName(), true).getClassLoader()
console.log("[XposedFridaBridge] Current Application Classloader: ",apkClassloader)
// 加载Xposed类
// Java.openClassFile(bridgePath).load()
var app = ActivityThread.currentApplication()
var DexClassLoader = Java.use("dalvik.system.DexClassLoader")
var codeCacheDir = (app.getCodeCacheDir) ? app.getCodeCacheDir().toString() : "/data/data/" + app.getPackageName() + "/code_cache"
console.log("[XposedFridaBridge] Code Cache Directory: ", codeCacheDir)
var XposedCL = DexClassLoader.$new(bridgePath, codeCacheDir, null, DexClassLoader.getSystemClassLoader());
XposedClassFactory = Java.ClassFactory.get(XposedCL)
console.log("[XposedFridaBridge] Xposed Classloader: ", XposedCL)
// 实现XposedBridge API
implementXposedAPI()
console.log("[XposedFridaBridge] XposedBridge successfully loaded\n")
// 开始处理初始化
console.log("[XposedFridaBridge] Initating Xposed Framework")
// 模拟XposedBridge.main
var XposedBridge = XposedClassFactory.use("de.robv.android.xposed.XposedBridge")
// XposedBridge.initXResources() //initXResource被放弃实现,转而通过对XposedBridge.jar二次打包实现,修改了android.content.res.XResource
XposedBridge.XPOSED_BRIDGE_VERSION.value = 82
XposedBridge.BOOTCLASSLOADER.value = XposedCL
XposedBridge.isZygote.value = true
var XposedInit = XposedClassFactory.use("de.robv.android.xposed.XposedInit")
XposedInit.BASE_DIR.value = xposedPath
console.log("[XposedFridaBridge] Static Attribute Set")
console.log("[XposedFridaBridge] Initating SELinuxHelper")
var SELinuxHelper = XposedClassFactory.use("de.robv.android.xposed.SELinuxHelper")
SELinuxHelper.initOnce()
SELinuxHelper.initForProcess(ActivityThread.currentApplication().getPackageName())
console.log("[XposedFridaBridge] hookResources")
XposedInit.hookResources()
console.log("[XposedFridaBridge] initForZygote")
XposedInit.initForZygote()
console.log("[XposedFridaBridge] Framework Initated\n")
console.log("[XposedFridaBridge] Load Modules")
XposedInit.loadModules()
console.log("[XposedFridaBridge] Xposed Framework Ready\n")
}
function triggerLoadPackage(){
var XposedBridge = XposedClassFactory.use("de.robv.android.xposed.XposedBridge")
var XCallback = XposedClassFactory.use("de.robv.android.xposed.callbacks.XCallback")
var LoadPackageParam = XposedClassFactory.use("de.robv.android.xposed.callbacks.XC_LoadPackage$LoadPackageParam")
console.log("[XposedFridaBridge] Preparing LoadPackageParam")
var ActivityThread = Java.use("android.app.ActivityThread")
var app = ActivityThread.currentApplication()
var thread = ActivityThread.currentActivityThread()
console.log(" [PackageName]", app.getPackageName())
console.log(" [ProcessName]", ActivityThread.currentPackageName())
var boundApplication = thread.mBoundApplication.value
console.log(" [boundApplication]", boundApplication)
var appInfo = boundApplication.appInfo.value
console.log(" [AppInfo]", appInfo)
//console.log(appInfo.packageName.value)
var compatInfo = boundApplication.compatInfo.value
console.log(" [compatInfo]", compatInfo)
var loadedApk = thread.getPackageInfoNoCheck(appInfo, compatInfo)
console.log(" [loadedApk]", loadedApk)
var classLoader = loadedApk.getClassLoader()
console.log(" [classLoader]", classLoader)
var lpparam = LoadPackageParam.$new(XposedBridge.sLoadedPackageCallbacks.value)
lpparam.packageName.value = app.getPackageName()
lpparam.processName.value = ActivityThread.currentPackageName()
lpparam.classLoader.value = loadedApk.getClassLoader()
lpparam.appInfo.value = loadedApk.getApplicationInfo()
lpparam.isFirstApplication.value = true
console.log("[XposedFridaBridge] LoadPackageParam Ready\n")
console.log("[XposedFridaBridge] Triggering Modules")
XCallback.callAll(lpparam)
}
// 启动
function startBridge(){
Java.performNow(function(){
// Java.deoptimizeEverything()
console.log("Xposed Frida Bridge\n by Monkeylord\n")
console.log("XposedBridge.jar Path:", "/data/local/tmp/XposedBridge.jar")
console.log("modules.list Path:", "/data/local/tmp/conf/modules.list")
console.log("\n")
console.log("[XposedFridaBridge] Start Loading Xposed")
// 获取当前应用Application
var ActivityThread = Java.use("android.app.ActivityThread")
var app = ActivityThread.currentApplication()
if(app!=null){
// 当前应用已加载,直接加载
// 初始化Framework
console.log("[XposedFridaBridge] Current Application: ", app.getPackageName())
FrameworkInit("/data/local/tmp/XposedBridge.jar", "/data/local/tmp/")
// 触发模块启动
console.log("[XposedFridaBridge] Triggering Modules Load")
triggerLoadPackage()
console.log("[XposedFridaBridge] Ready\n")
}else{
// Spawn方式启动,等待应用加载
console.log("[XposedFridaBridge] Application has not initialized, waiting.")
ActivityThread.handleBindApplication.implementation = function(appInfo){
// 注意:此处和Xposed加载顺序不同,Xposed在handleBindApplication前初始化模块
// 但由于目前实现需要其中的currentApplication,便利起见,在之后初始化模块
// TODO 使用其他方式替代currentApplication
this.handleBindApplication()
// 特定位置初始化Framework
app = ActivityThread.currentApplication()
console.log("[XposedFridaBridge] Current Application: ", app.getPackageName())
FrameworkInit("/data/local/tmp/XposedBridge.jar", "/data/local/tmp/")
// 触发模块启动
console.log("[XposedFridaBridge] Triggering Modules Load")
triggerLoadPackage()
console.log("[XposedFridaBridge] Ready\n")
}
}
})
}
setTimeout(startBridge, 10)