frida-fusion 0.1.14__tar.gz → 0.1.16__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of frida-fusion might be problematic. Click here for more details.
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/PKG-INFO +5 -2
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/README.md +4 -1
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/__meta__.py +2 -2
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/libs/helpers.js +41 -17
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/crypto/crypto.js +136 -14
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/crypto/crypto.py +30 -0
- frida_fusion-0.1.16/frida_fusion/modules/reflection/reflection-stalker.js +293 -0
- frida_fusion-0.1.16/frida_fusion/modules/reflection/reflection-stalker.py +206 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion.egg-info/PKG-INFO +5 -2
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion.egg-info/SOURCES.txt +2 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/LICENSE +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/__init__.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/__main__.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/args.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/config.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/exceptions.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/fusion.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/libs/__init__.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/libs/color.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/libs/database.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/libs/logger.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/libs/scriptlocation.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/module.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/__init__.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/android_setings/__init__.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/android_setings/settings.js +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/android_setings/settings.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/crypto/__init__.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/tls_unpinning/__init__.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/tls_unpinning/frida_multiple_unpinning.py +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion.egg-info/dependency_links.txt +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion.egg-info/entry_points.txt +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion.egg-info/requires.txt +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion.egg-info/top_level.txt +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/pyproject.toml +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/setup.cfg +0 -0
- {frida_fusion-0.1.14 → frida_fusion-0.1.16}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frida-fusion
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.16
|
|
4
4
|
Summary: Hook your mobile tests with Frida
|
|
5
5
|
Author-email: "Helvio Junior (M4v3r1ck)" <helvio_junior@hotmail.com>
|
|
6
6
|
Maintainer-email: "Helvio Junior (M4v3r1ck)" <helvio_junior@hotmail.com>
|
|
@@ -101,11 +101,14 @@ void fusion_printStackTrace();
|
|
|
101
101
|
# Print all methods of class 'name'
|
|
102
102
|
void fusion_printMethods(String name);
|
|
103
103
|
|
|
104
|
+
# Get value of a field inside an class instance
|
|
105
|
+
Object fusion_getFieldValue(Object obj, String fieldName);
|
|
106
|
+
|
|
104
107
|
# Wait until the class 'name' exists in memory to execute the callback function
|
|
105
108
|
void fusion_waitForClass(String name, CallbackFunction onReady)
|
|
106
109
|
|
|
107
110
|
# Conversions
|
|
108
|
-
|
|
111
|
+
String fusion_stringToBase64(String message);
|
|
109
112
|
String fusion_bytesToBase64(byte[] byteArray);
|
|
110
113
|
String fusion_encodeHex(byte[] byteArray);
|
|
111
114
|
```
|
|
@@ -66,11 +66,14 @@ void fusion_printStackTrace();
|
|
|
66
66
|
# Print all methods of class 'name'
|
|
67
67
|
void fusion_printMethods(String name);
|
|
68
68
|
|
|
69
|
+
# Get value of a field inside an class instance
|
|
70
|
+
Object fusion_getFieldValue(Object obj, String fieldName);
|
|
71
|
+
|
|
69
72
|
# Wait until the class 'name' exists in memory to execute the callback function
|
|
70
73
|
void fusion_waitForClass(String name, CallbackFunction onReady)
|
|
71
74
|
|
|
72
75
|
# Conversions
|
|
73
|
-
|
|
76
|
+
String fusion_stringToBase64(String message);
|
|
74
77
|
String fusion_bytesToBase64(byte[] byteArray);
|
|
75
78
|
String fusion_encodeHex(byte[] byteArray);
|
|
76
79
|
```
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
__version__ = '0.1.
|
|
1
|
+
__version__ = '0.1.16'
|
|
2
2
|
__title__ = "Frida Fusion"
|
|
3
3
|
__description__ = "📱 frida-fusion - runtime mobile exploration"
|
|
4
4
|
__url__ = "https://github.com/helviojunior/frida-fusion"
|
|
5
|
-
__build__ =
|
|
5
|
+
__build__ = 0xfe136e5
|
|
6
6
|
__author__ = "Helvio Junior (M4v3r1ck)"
|
|
7
7
|
__author_email__ = "helvio_junior@hotmail.com"
|
|
8
8
|
__license__ = "GPL-3.0"
|
|
@@ -128,22 +128,26 @@ function fusion_getCallerInfo() {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
function fusion_sendKeyValueData(module, items) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
var data = [];
|
|
131
|
+
try{
|
|
132
|
+
var st = fusion_getB64StackTrace();
|
|
134
133
|
|
|
135
|
-
|
|
136
|
-
for (let i = 0; i < items.length; i++) {
|
|
137
|
-
data = data.concat([{key: `${items[i].key}`, value:`${items[i].value}`}]);
|
|
138
|
-
}
|
|
134
|
+
var data = [];
|
|
139
135
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
stack_trace: st
|
|
145
|
-
}, null);
|
|
136
|
+
// Force as String
|
|
137
|
+
for (let i = 0; i < items.length; i++) {
|
|
138
|
+
data = data.concat([{key: `${items[i].key}`, value:`${items[i].value}`}]);
|
|
139
|
+
}
|
|
146
140
|
|
|
141
|
+
fusion_Send({
|
|
142
|
+
type: "key_value_data",
|
|
143
|
+
module: module,
|
|
144
|
+
data: data,
|
|
145
|
+
stack_trace: st
|
|
146
|
+
}, null);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
fusion_sendMessage("W", `Error: ${err}`)
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
function fusion_sendMessage(level, message){
|
|
@@ -160,7 +164,7 @@ function fusion_sendMessage(level, message){
|
|
|
160
164
|
message: b64Msg
|
|
161
165
|
}, null)
|
|
162
166
|
} catch (err) {
|
|
163
|
-
fusion_sendMessage("W", err)
|
|
167
|
+
fusion_sendMessage("W", `Error: ${err}`)
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
|
|
@@ -184,7 +188,7 @@ function fusion_sendMessageWithTrace(level, message){
|
|
|
184
188
|
message: b64Msg
|
|
185
189
|
}, null)
|
|
186
190
|
} catch (err) {
|
|
187
|
-
fusion_sendMessage("W", err)
|
|
191
|
+
fusion_sendMessage("W", `Error: ${err}`)
|
|
188
192
|
}
|
|
189
193
|
}
|
|
190
194
|
|
|
@@ -218,7 +222,7 @@ function fusion_getB64StackTrace(){
|
|
|
218
222
|
return b64Msg
|
|
219
223
|
|
|
220
224
|
} catch (err) {
|
|
221
|
-
fusion_sendMessage("W", err)
|
|
225
|
+
fusion_sendMessage("W", `Error: ${err}`)
|
|
222
226
|
return '';
|
|
223
227
|
}
|
|
224
228
|
}
|
|
@@ -256,12 +260,32 @@ function fusion_getClassName(obj)
|
|
|
256
260
|
// Se for algo não Java, apenas retorna tipo do JS
|
|
257
261
|
return typeof obj;
|
|
258
262
|
} catch (err) {
|
|
259
|
-
fusion_sendMessage("W", err)
|
|
263
|
+
fusion_sendMessage("W", `Error: ${err}`)
|
|
260
264
|
return '';
|
|
261
265
|
}
|
|
262
266
|
|
|
263
267
|
}
|
|
264
268
|
|
|
269
|
+
function fusion_getFieldValue(obj, fieldName) {
|
|
270
|
+
if (obj === null || obj === undefined) return "";
|
|
271
|
+
try {
|
|
272
|
+
var cls = obj.getClass();
|
|
273
|
+
while (cls != null) {
|
|
274
|
+
try {
|
|
275
|
+
var f = cls.getDeclaredField(fieldName);
|
|
276
|
+
f.setAccessible(true);
|
|
277
|
+
return f.get(obj);
|
|
278
|
+
} catch (e) {
|
|
279
|
+
cls = cls.getSuperclass();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
} catch (err) {
|
|
283
|
+
fusion_sendMessage("W", `Error: ${err}`)
|
|
284
|
+
return '';
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
265
289
|
function fusion_getReadableRange(p) {
|
|
266
290
|
try { p = ptr(p); } catch (_) { return null; }
|
|
267
291
|
const range = Process.findRangeByAddress(p); // não lança exceção
|
|
@@ -3,7 +3,8 @@ const CRYPTO_MODULES = {
|
|
|
3
3
|
KeyGenerator: true,
|
|
4
4
|
KeyPairGenerator: true,
|
|
5
5
|
SecretKeySpec: true,
|
|
6
|
-
MessageDigest:
|
|
6
|
+
MessageDigest: false,
|
|
7
|
+
KeyFactory: true,
|
|
7
8
|
SecretKeyFactory: true,
|
|
8
9
|
Signature: true,
|
|
9
10
|
Cipher: true,
|
|
@@ -12,7 +13,7 @@ const CRYPTO_MODULES = {
|
|
|
12
13
|
IvParameterSpec: true,
|
|
13
14
|
GCMParameterSpec: true,
|
|
14
15
|
PBEParameterSpec: true,
|
|
15
|
-
X509EncodedKeySpec: true
|
|
16
|
+
X509EncodedKeySpec: true,
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
setTimeout(function() {
|
|
@@ -144,6 +145,53 @@ setTimeout(function() {
|
|
|
144
145
|
|
|
145
146
|
}
|
|
146
147
|
|
|
148
|
+
if (CRYPTO_MODULES.KeyFactory) {
|
|
149
|
+
fusion_sendMessage('*', "Module attached: java.security.KeyFactory");
|
|
150
|
+
const keyFactory = Java.use("java.security.KeyFactory");
|
|
151
|
+
keyFactory.getInstance.overload("java.lang.String").implementation = function (arg0) {
|
|
152
|
+
fusion_sendKeyValueData("KeyFactory.getInstance", [
|
|
153
|
+
{key: "Algorithm", value: arg0}
|
|
154
|
+
]);
|
|
155
|
+
return this.getInstance(arg0);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
keyFactory.getInstance.overload("java.lang.String", "java.lang.String").implementation = function (arg0, arg1) {
|
|
159
|
+
fusion_sendKeyValueData("KeyFactory.getInstance", [
|
|
160
|
+
{key: "Algorithm", value: arg0},
|
|
161
|
+
{key: "Provider", value: arg1}
|
|
162
|
+
]);
|
|
163
|
+
return this.getInstance(arg0, arg1);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
keyFactory.getInstance.overload("java.lang.String", "java.security.Provider").implementation = function (arg0, arg1) {
|
|
167
|
+
fusion_sendKeyValueData("KeyFactory.getInstance", [
|
|
168
|
+
{key: "Algorithm", value: arg0},
|
|
169
|
+
{key: "Provider", value: arg1}
|
|
170
|
+
]);
|
|
171
|
+
return this.getInstance(arg0, arg1);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
keyFactory.generatePrivate.overload('java.security.spec.KeySpec').implementation = function (keySpec) {
|
|
176
|
+
fusion_sendKeyValueData("KeyFactory.generatePrivate", [
|
|
177
|
+
{key: "ClassType", value: fusion_getClassName(this)},
|
|
178
|
+
{key: "KeySpecClassType", value: fusion_getClassName(keySpec)},
|
|
179
|
+
{key: "Algorithm", value: this.getAlgorithm()},
|
|
180
|
+
{key: "Key", value: fusion_keyToBase64(keySpec)},
|
|
181
|
+
]);
|
|
182
|
+
return this.generatePrivate(keySpec);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
keyFactory.generatePublic.overload('java.security.spec.KeySpec').implementation = function (keySpec) {
|
|
186
|
+
fusion_sendKeyValueData("KeyFactory.generatePublic", [
|
|
187
|
+
{key: "ClassType", value: fusion_getClassName(this)},
|
|
188
|
+
{key: "KeySpecClassType", value: fusion_getClassName(keySpec)},
|
|
189
|
+
{key: "Algorithm", value: this.getAlgorithm()},
|
|
190
|
+
]);
|
|
191
|
+
return this.generatePublic(keySpec);
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
147
195
|
if (CRYPTO_MODULES.SecretKeyFactory) {
|
|
148
196
|
fusion_sendMessage('*', "Module attached: javax.crypto.SecretKeyFactory");
|
|
149
197
|
const secretKeyFactory = Java.use("javax.crypto.SecretKeyFactory");
|
|
@@ -281,26 +329,73 @@ setTimeout(function() {
|
|
|
281
329
|
}
|
|
282
330
|
|
|
283
331
|
cipher.getInstance.overload("java.lang.String").implementation = function (arg0) {
|
|
284
|
-
|
|
332
|
+
|
|
333
|
+
var data = [
|
|
285
334
|
{key: "Algorithm", value: arg0}
|
|
286
|
-
]
|
|
287
|
-
|
|
335
|
+
];
|
|
336
|
+
|
|
337
|
+
var instance = this.getInstance(arg0);
|
|
338
|
+
try{
|
|
339
|
+
data = data.concat([
|
|
340
|
+
{key: "HashCode", value: instance.hashCode().toString()},
|
|
341
|
+
]);
|
|
342
|
+
data = data.concat([
|
|
343
|
+
{key: "Algorithm", value: instance.getAlgorithm()}
|
|
344
|
+
]);
|
|
345
|
+
} catch (err1) {
|
|
346
|
+
fusion_sendError(err1)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
fusion_sendKeyValueData("cipher.getInstance", data);
|
|
350
|
+
return instance;
|
|
288
351
|
};
|
|
289
352
|
|
|
290
353
|
cipher.getInstance.overload("java.lang.String", "java.lang.String").implementation = function (arg0, arg1) {
|
|
291
|
-
|
|
354
|
+
|
|
355
|
+
var data = [
|
|
292
356
|
{key: "Algorithm", value: arg0},
|
|
293
357
|
{key: "Provider", value: arg1}
|
|
294
|
-
]
|
|
295
|
-
|
|
358
|
+
];
|
|
359
|
+
|
|
360
|
+
var instance = this.getInstance(arg0, arg1);
|
|
361
|
+
try{
|
|
362
|
+
data = data.concat([
|
|
363
|
+
{key: "HashCode", value: instance.hashCode().toString()},
|
|
364
|
+
]);
|
|
365
|
+
data = data.concat([
|
|
366
|
+
{key: "Algorithm", value: instance.getAlgorithm()}
|
|
367
|
+
]);
|
|
368
|
+
} catch (err1) {
|
|
369
|
+
fusion_sendError(err1)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
fusion_sendKeyValueData("cipher.getInstance", data);
|
|
373
|
+
return instance;
|
|
374
|
+
|
|
296
375
|
};
|
|
297
376
|
|
|
298
377
|
cipher.getInstance.overload("java.lang.String", "java.security.Provider").implementation = function (arg0, arg1) {
|
|
299
|
-
|
|
378
|
+
|
|
379
|
+
var data = [
|
|
300
380
|
{key: "Algorithm", value: arg0},
|
|
301
381
|
{key: "Provider", value: arg1}
|
|
302
|
-
]
|
|
303
|
-
|
|
382
|
+
];
|
|
383
|
+
|
|
384
|
+
var instance = this.getInstance(arg0, arg1);
|
|
385
|
+
try{
|
|
386
|
+
data = data.concat([
|
|
387
|
+
{key: "HashCode", value: instance.hashCode().toString()},
|
|
388
|
+
]);
|
|
389
|
+
data = data.concat([
|
|
390
|
+
{key: "Algorithm", value: instance.getAlgorithm()}
|
|
391
|
+
]);
|
|
392
|
+
} catch (err1) {
|
|
393
|
+
fusion_sendError(err1)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
fusion_sendKeyValueData("cipher.getInstance", data);
|
|
397
|
+
return instance;
|
|
398
|
+
|
|
304
399
|
};
|
|
305
400
|
|
|
306
401
|
cipher.doFinal.overload("[B").implementation = function (arg0) {
|
|
@@ -568,12 +663,39 @@ setTimeout(function() {
|
|
|
568
663
|
|
|
569
664
|
function fusion_keyToBase64(key){
|
|
570
665
|
if (key === null || key === undefined) return "IA==";
|
|
666
|
+
const cName = fusion_getClassName(key);
|
|
571
667
|
try{
|
|
668
|
+
|
|
669
|
+
try{
|
|
670
|
+
if (cName == "java.security.spec.RSAPrivateKeySpec" || (cName == "javax.crypto.spec.SecretKeySpec" && key.getAlgorithm() == "RSA")){
|
|
671
|
+
return {
|
|
672
|
+
classType: cName,
|
|
673
|
+
modulus: key.getModulus(),
|
|
674
|
+
privateExponent: key.getPrivateExponent(),
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
} catch (e1) {}
|
|
678
|
+
|
|
679
|
+
/*
|
|
680
|
+
const cName = fusion_getClassName(key);
|
|
681
|
+
|
|
682
|
+
if ("com.android.org.conscrypt.OpenSSLRSAPrivateKey" == cName) return "IA==";
|
|
683
|
+
|
|
684
|
+
fusion_sendMessageWithTrace("W", "fusion_keyToBase64\n" + fusion_getClassName(key));
|
|
685
|
+
|
|
686
|
+
if ("javax.crypto.spec.SecretKeySpec" == cName) {
|
|
687
|
+
var algo = key.getAlgorithm();
|
|
688
|
+
if (algo == "AES") return "IA==";
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
var tst = key.getEncoded();
|
|
692
|
+
fusion_sendMessageWithTrace("W", "fusion_keyToBase64\n" + fusion_getClassName(tst));
|
|
693
|
+
*/
|
|
572
694
|
|
|
573
|
-
return fusion_bytesToBase64(key.getEncoded())
|
|
695
|
+
return fusion_bytesToBase64(key.getEncoded());
|
|
574
696
|
|
|
575
697
|
} catch (err) {
|
|
576
|
-
fusion_sendMessage("W", err)
|
|
577
|
-
return
|
|
698
|
+
//fusion_sendMessage("W", `Error: ${err}`)
|
|
699
|
+
return fusion_stringToBase64(`Error getting key from class (${cName}): ${err}`);
|
|
578
700
|
}
|
|
579
701
|
}
|
|
@@ -457,6 +457,24 @@ class Crypto(ModuleBase):
|
|
|
457
457
|
message=f"Cipher init received\nHashcode: {hashcode}\nOpmode: {opmode}\nKeytype: {key_class}",
|
|
458
458
|
script_location=script_location
|
|
459
459
|
)
|
|
460
|
+
|
|
461
|
+
elif module == "cipher.getInstance":
|
|
462
|
+
hashcode = received_data.get('hashcode', None)
|
|
463
|
+
algorithm = received_data.get('algorithm', None)
|
|
464
|
+
|
|
465
|
+
self._crypto_db.insert_crypto(
|
|
466
|
+
package=self._package,
|
|
467
|
+
hashcode=hashcode,
|
|
468
|
+
algorithm=algorithm,
|
|
469
|
+
init_key=None
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
if not self._suppress_messages:
|
|
473
|
+
Logger.print_message(
|
|
474
|
+
level="D",
|
|
475
|
+
message=f"Cipher getInstance received\n{stack_trace}",
|
|
476
|
+
script_location=script_location
|
|
477
|
+
)
|
|
460
478
|
|
|
461
479
|
elif module == "cipher.doFinal":
|
|
462
480
|
hashcode = received_data.get('hashcode', None)
|
|
@@ -516,6 +534,18 @@ class Crypto(ModuleBase):
|
|
|
516
534
|
script_location=script_location
|
|
517
535
|
)
|
|
518
536
|
|
|
537
|
+
elif module == "KeyFactory.generatePrivate":
|
|
538
|
+
#print(received_data)
|
|
539
|
+
pass
|
|
540
|
+
|
|
541
|
+
elif module == "KeyFactory.generatePublic":
|
|
542
|
+
#print(received_data)
|
|
543
|
+
pass
|
|
544
|
+
|
|
545
|
+
elif module == "org.bouncycastle.asn1!init":
|
|
546
|
+
#print(received_data)
|
|
547
|
+
pass
|
|
548
|
+
|
|
519
549
|
return True
|
|
520
550
|
|
|
521
551
|
def data_event(self,
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// Script Frida: Method.invoke -> build object + enviar via fusion_sendKeyValueData
|
|
2
|
+
Java.perform(function () {
|
|
3
|
+
|
|
4
|
+
var jlrmethod = Java.use("java.lang.reflect.Method");
|
|
5
|
+
var origInvoke = jlrmethod.invoke; // referência original
|
|
6
|
+
|
|
7
|
+
function safeGetClassName(obj) {
|
|
8
|
+
try {
|
|
9
|
+
if (obj === null || obj === undefined) return null;
|
|
10
|
+
var cls = obj.getClass();
|
|
11
|
+
return cls.getName();
|
|
12
|
+
} catch (e) {
|
|
13
|
+
try { if (obj && obj.$className) return obj.$className; } catch(e2) {}
|
|
14
|
+
try { return Object.prototype.toString.call(obj); } catch(_) { return typeof obj; }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function buildParamInfo(param, depth) {
|
|
19
|
+
depth = depth || 0;
|
|
20
|
+
var maxDepth = 3;
|
|
21
|
+
var info = { type: null, class_name: null, value: null };
|
|
22
|
+
|
|
23
|
+
if (param === null || param === undefined) {
|
|
24
|
+
info.type = 'null';
|
|
25
|
+
return info;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var className = safeGetClassName(param);
|
|
29
|
+
info.class_name = className;
|
|
30
|
+
info.type = className;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
if (className && className.charAt(0) === '[') {
|
|
34
|
+
info.type = className;
|
|
35
|
+
if (className === '[B') {
|
|
36
|
+
try {
|
|
37
|
+
var buffer = Java.array('byte', param);
|
|
38
|
+
try { info.value = toBase64(buffer); } catch (e) {
|
|
39
|
+
var sb = []; for (var i=0;i<Math.min(buffer.length,256);i++) sb.push((buffer[i]&0xff).toString(16));
|
|
40
|
+
info.value = sb.join('');
|
|
41
|
+
}
|
|
42
|
+
} catch (errByte) { info.value = '[unreadable byte array]'; }
|
|
43
|
+
return info;
|
|
44
|
+
}
|
|
45
|
+
if (className.indexOf('Ljava.lang.String') !== -1) {
|
|
46
|
+
try {
|
|
47
|
+
var arrLen = param.length;
|
|
48
|
+
var vals = [];
|
|
49
|
+
for (var i=0;i<Math.min(arrLen,50);i++) { vals.push(String(param[i])); }
|
|
50
|
+
info.value = { length: arrLen, preview: vals };
|
|
51
|
+
} catch(e) { info.value = '[unreadable string array]'; }
|
|
52
|
+
return info;
|
|
53
|
+
}
|
|
54
|
+
if (depth >= maxDepth) { info.value = '[array - depth limit]'; return info; }
|
|
55
|
+
try {
|
|
56
|
+
var n = param.length;
|
|
57
|
+
var items = [];
|
|
58
|
+
for (var j=0; j<Math.min(n,50); j++) items.push(buildParamInfo(param[j], depth+1));
|
|
59
|
+
info.value = { length: n, preview: items };
|
|
60
|
+
} catch(e) { info.value = '[unreadable array]'; }
|
|
61
|
+
return info;
|
|
62
|
+
}
|
|
63
|
+
} catch(eArrDetect) {}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
if (className === 'java.lang.String' || (typeof param === 'string')) { info.value = String(param); return info; }
|
|
67
|
+
if (className && (
|
|
68
|
+
className.indexOf('java.lang.Number') !== -1 ||
|
|
69
|
+
className.indexOf('java.lang.Integer') !== -1 ||
|
|
70
|
+
className.indexOf('java.lang.Long') !== -1 ||
|
|
71
|
+
className.indexOf('java.lang.Boolean') !== -1 ||
|
|
72
|
+
className.indexOf('java.lang.Double') !== -1 ||
|
|
73
|
+
className.indexOf('java.lang.Float') !== -1 ||
|
|
74
|
+
className.indexOf('java.lang.Short') !== -1 ||
|
|
75
|
+
className.indexOf('java.lang.Character') !== -1)) {
|
|
76
|
+
try { info.value = param.toString(); } catch(e){ info.value = String(param); }
|
|
77
|
+
return info;
|
|
78
|
+
}
|
|
79
|
+
try { info.value = param.toString(); } catch (e) { info.value = '[toString failed]'; }
|
|
80
|
+
return info;
|
|
81
|
+
} catch(errGeneric) {
|
|
82
|
+
info.value = '[error building param]';
|
|
83
|
+
return info;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function buildInvokeObject(methodRef, targetObject, parameters) {
|
|
88
|
+
var obj = {
|
|
89
|
+
timestamp: (new Date()).toISOString(),
|
|
90
|
+
method: {
|
|
91
|
+
toString: String(methodRef),
|
|
92
|
+
name: null,
|
|
93
|
+
declaring_class: null,
|
|
94
|
+
b64: null
|
|
95
|
+
},
|
|
96
|
+
target: {
|
|
97
|
+
class_name: null,
|
|
98
|
+
toString: null
|
|
99
|
+
},
|
|
100
|
+
parameters: [],
|
|
101
|
+
return: {
|
|
102
|
+
class_name: null,
|
|
103
|
+
value: null
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
if (methodRef && methodRef.getName) obj.method.name = methodRef.getName(); else obj.method.name = String(methodRef);
|
|
109
|
+
try {
|
|
110
|
+
var decl = methodRef.getDeclaringClass();
|
|
111
|
+
obj.method.declaring_class = decl ? decl.getName() : null;
|
|
112
|
+
} catch(e) { obj.method.declaring_class = null; }
|
|
113
|
+
} catch(_) {}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
var tName = "" + methodRef;
|
|
117
|
+
try { obj.method.b64 = fusion_stringToBase64(tName); } catch(e){ obj.method.b64 = null; }
|
|
118
|
+
} catch(_) { obj.method.b64 = null; }
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
if (targetObject === null || targetObject === undefined) {
|
|
122
|
+
obj.target.class_name = null; obj.target.toString = null;
|
|
123
|
+
} else {
|
|
124
|
+
obj.target.class_name = safeGetClassName(targetObject);
|
|
125
|
+
try { obj.target.toString = targetObject.toString(); } catch(e){ obj.target.toString = '[toString failed]'; }
|
|
126
|
+
}
|
|
127
|
+
} catch(e) {}
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
var type = Object.prototype.toString.call(parameters);
|
|
131
|
+
if (parameters === null || parameters === undefined) {
|
|
132
|
+
obj.parameters = [];
|
|
133
|
+
} else if (type === '[object Array]') {
|
|
134
|
+
var arrLen = parameters.length;
|
|
135
|
+
for (var i=0; i<Math.min(arrLen,200); i++) {
|
|
136
|
+
obj.parameters.push({ index: i, info: buildParamInfo(parameters[i], 0) });
|
|
137
|
+
}
|
|
138
|
+
if (arrLen > 200) obj.parameters_truncated = true;
|
|
139
|
+
} else {
|
|
140
|
+
try {
|
|
141
|
+
var maybeLen = parameters.length;
|
|
142
|
+
if (typeof maybeLen === 'number') {
|
|
143
|
+
for (var k=0; k<Math.min(maybeLen,100); k++) obj.parameters.push({ index: k, info: buildParamInfo(parameters[k],0) });
|
|
144
|
+
} else {
|
|
145
|
+
obj.parameters.push({ index: 0, info: buildParamInfo(parameters,0) });
|
|
146
|
+
}
|
|
147
|
+
} catch(e2) {
|
|
148
|
+
obj.parameters.push({ index: 0, info: buildParamInfo(parameters,0) });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} catch(errParams) {
|
|
152
|
+
obj.parameters = [{ index:0, info: { type: '[error enumerating parameters]' } }];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return obj;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// coleta backtrace da Thread Java atual e retorna [pretty, raw]
|
|
159
|
+
function collectBacktrace() {
|
|
160
|
+
var pretty = "[unavailable]";
|
|
161
|
+
var raw = [];
|
|
162
|
+
try {
|
|
163
|
+
var Thread = Java.use('java.lang.Thread');
|
|
164
|
+
var trace = Thread.currentThread().getStackTrace(); // array of StackTraceElement
|
|
165
|
+
var prettyLines = [];
|
|
166
|
+
for (var i=0; i<trace.length; i++) {
|
|
167
|
+
try {
|
|
168
|
+
var ste = trace[i];
|
|
169
|
+
var line = ste.toString(); // ex: com.foo.Bar.method(File.java:123)
|
|
170
|
+
prettyLines.push(line);
|
|
171
|
+
raw.push(String(ste));
|
|
172
|
+
} catch(eSte) { /* ignore */ }
|
|
173
|
+
}
|
|
174
|
+
pretty = prettyLines.join('\n');
|
|
175
|
+
} catch(e) {
|
|
176
|
+
try {
|
|
177
|
+
// fallback: usar Java.perform stack (menos informativo)
|
|
178
|
+
pretty = (new Error()).stack;
|
|
179
|
+
raw = [pretty];
|
|
180
|
+
} catch(e2) {}
|
|
181
|
+
}
|
|
182
|
+
return { pretty: pretty, raw: raw };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// sobrescreve invoke para montar objeto e enviar via fusion_sendKeyValueData
|
|
186
|
+
jlrmethod.invoke.implementation = function(object, parameters) {
|
|
187
|
+
// monta objeto
|
|
188
|
+
var infoObj = buildInvokeObject(this, object, parameters);
|
|
189
|
+
|
|
190
|
+
// coletar backtrace
|
|
191
|
+
var bt = collectBacktrace();
|
|
192
|
+
|
|
193
|
+
// chamar o método original e capturar retorno
|
|
194
|
+
var retvalue;
|
|
195
|
+
try {
|
|
196
|
+
retvalue = origInvoke.call(this, object, parameters);
|
|
197
|
+
} catch (invokeErr) {
|
|
198
|
+
// enviar também quando lançar (pode ser útil)
|
|
199
|
+
try {
|
|
200
|
+
fusion_sendKeyValueData("java.lang.reflect.Method!invoke!throw", [
|
|
201
|
+
{ key: "method", value: String(infoObj.method.toString) },
|
|
202
|
+
{ key: "declaring_class", value: String(infoObj.method.declaring_class) },
|
|
203
|
+
{ key: "method_b64", value: String(infoObj.method.b64) },
|
|
204
|
+
{ key: "target_class", value: String(infoObj.target.class_name) },
|
|
205
|
+
{ key: "params_count", value: String(infoObj.parameters.length) },
|
|
206
|
+
{ key: "params_preview", value: JSON.stringify(infoObj.parameters) },
|
|
207
|
+
{ key: "backtrace", value: bt.pretty },
|
|
208
|
+
{ key: "backtrace_raw", value: JSON.stringify(bt.raw) },
|
|
209
|
+
{ key: "error", value: String(invokeErr) }
|
|
210
|
+
]);
|
|
211
|
+
} catch(eSendErr) { fusion_sendMessage("W", "fusion_sendKeyValueData error: " + eSendErr); }
|
|
212
|
+
|
|
213
|
+
throw invokeErr; // rethrow para manter comportamento
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// preencher retorno infoObj.return
|
|
217
|
+
try {
|
|
218
|
+
if (retvalue === null || retvalue === undefined) {
|
|
219
|
+
infoObj.return.class_name = null; infoObj.return.value = null;
|
|
220
|
+
} else {
|
|
221
|
+
infoObj.return.class_name = safeGetClassName(retvalue);
|
|
222
|
+
if (infoObj.return.class_name && infoObj.return.class_name.charAt(0) === '[') {
|
|
223
|
+
if (infoObj.return.class_name === '[B') {
|
|
224
|
+
try { infoObj.return.value = toBase64(Java.array('byte', retvalue)); } catch(e) { infoObj.return.value = '[unreadable byte array]'; }
|
|
225
|
+
} else {
|
|
226
|
+
try {
|
|
227
|
+
var lenR = retvalue.length, itemsR = [];
|
|
228
|
+
for (var ri=0; ri<Math.min(lenR,50); ri++) { itemsR.push(String(retvalue[ri])); }
|
|
229
|
+
infoObj.return.value = { length: lenR, preview: itemsR };
|
|
230
|
+
} catch(eArrR) { infoObj.return.value = '[unreadable array]'; }
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
try { infoObj.return.value = retvalue.toString(); } catch(e) { infoObj.return.value = '[toString failed]'; }
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} catch(eRetFill) { infoObj.return.value = '[error reading return]'; }
|
|
237
|
+
|
|
238
|
+
// preparar payload para fusion_sendKeyValueData (array de {key, value})
|
|
239
|
+
var payload = [];
|
|
240
|
+
try {
|
|
241
|
+
payload.push({ key: "method", value: String(infoObj.method.toString) });
|
|
242
|
+
payload.push({ key: "method_name", value: String(infoObj.method.name) });
|
|
243
|
+
payload.push({ key: "declaring_class", value: String(infoObj.method.declaring_class) });
|
|
244
|
+
payload.push({ key: "method_b64", value: String(infoObj.method.b64) });
|
|
245
|
+
payload.push({ key: "target_class", value: String(infoObj.target.class_name) });
|
|
246
|
+
payload.push({ key: "target_tostring", value: String(infoObj.target.toString) });
|
|
247
|
+
payload.push({ key: "params_count", value: String(infoObj.parameters.length) });
|
|
248
|
+
|
|
249
|
+
// params_preview como JSON string (pode truncar se muito grande)
|
|
250
|
+
try {
|
|
251
|
+
var paramsJson = JSON.stringify(infoObj.parameters);
|
|
252
|
+
if (paramsJson.length > 20000) paramsJson = paramsJson.slice(0,20000) + "...(truncated)";
|
|
253
|
+
payload.push({ key: "params_preview", value: paramsJson });
|
|
254
|
+
} catch(ePJ) {
|
|
255
|
+
payload.push({ key: "params_preview", value: "[error serializing params]" });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// retorno
|
|
259
|
+
try {
|
|
260
|
+
var retSummary = (infoObj.return.class_name === null && infoObj.return.value === null) ? "null" :
|
|
261
|
+
("(" + String(infoObj.return.class_name) + ") " + String(infoObj.return.value));
|
|
262
|
+
if (retSummary.length > 4000) retSummary = retSummary.slice(0,4000) + "...(truncated)";
|
|
263
|
+
payload.push({ key: "return_summary", value: retSummary });
|
|
264
|
+
payload.push({ key: "return_class", value: String(infoObj.return.class_name) });
|
|
265
|
+
} catch(eRetPush) {
|
|
266
|
+
payload.push({ key: "return_summary", value: "[error]" });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// backtrace
|
|
270
|
+
payload.push({ key: "backtrace", value: bt.pretty });
|
|
271
|
+
payload.push({ key: "backtrace_raw", value: JSON.stringify(bt.raw) });
|
|
272
|
+
|
|
273
|
+
// timestamp
|
|
274
|
+
payload.push({ key: "timestamp", value: infoObj.timestamp });
|
|
275
|
+
|
|
276
|
+
} catch(ePayload) {
|
|
277
|
+
fusion_sendMessage("W", "Erro ao preparar payload: " + ePayload);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// enviar pelo canal customizado
|
|
281
|
+
try {
|
|
282
|
+
fusion_sendKeyValueData("java.lang.reflect.Method!invoke!call", payload);
|
|
283
|
+
} catch(eSend) {
|
|
284
|
+
// se fusion_sendKeyValueData não existir, fallback para console + fusion_sendMessage (se existir)
|
|
285
|
+
try { fusion_sendMessage("E", "fusion_sendKeyValueData missing or failed"); } catch(_) {}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return retvalue;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
try { fusion_sendMessage("I", "Reflection module with fusion_sendKeyValueData loaded!"); } catch(e) { fusion_sendMessage("W", "Reflection module loaded!"); }
|
|
292
|
+
|
|
293
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import errno
|
|
2
|
+
import sys
|
|
3
|
+
import json
|
|
4
|
+
import os.path
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from argparse import _ArgumentGroup, Namespace
|
|
7
|
+
from frida_fusion.libs.logger import Logger
|
|
8
|
+
from frida_fusion.libs.database import Database
|
|
9
|
+
from frida_fusion.module import ModuleBase
|
|
10
|
+
from frida_fusion.libs.scriptlocation import ScriptLocation
|
|
11
|
+
from frida_fusion.exceptions import SilentKillError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Reflection(ModuleBase):
|
|
15
|
+
|
|
16
|
+
_EXCLUSION_LIST = [
|
|
17
|
+
"android.view.View.onDraw",
|
|
18
|
+
"android.graphics.Picture",
|
|
19
|
+
"com.facebook.fbreact.specs.NativeAnimatedModuleSpec",
|
|
20
|
+
"com.facebook.react.uimanager.BaseViewManager.setTransform",
|
|
21
|
+
"com.facebook.react.uimanager.ReanimatedUIManager.updateView",
|
|
22
|
+
"com.facebook.fbreact.specs.NativeStatusBarManagerAndroidSpec"
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
class StalkerDB(Database):
|
|
26
|
+
dbName = ""
|
|
27
|
+
|
|
28
|
+
def __init__(self, db_name: str):
|
|
29
|
+
super().__init__(
|
|
30
|
+
auto_create=True,
|
|
31
|
+
db_name=db_name
|
|
32
|
+
)
|
|
33
|
+
self.create_db()
|
|
34
|
+
|
|
35
|
+
def create_db(self):
|
|
36
|
+
super().create_db()
|
|
37
|
+
conn = self.connect_to_db(check=False)
|
|
38
|
+
|
|
39
|
+
# definindo um cursor
|
|
40
|
+
cursor = conn.cursor()
|
|
41
|
+
|
|
42
|
+
# criando a tabela (schema)
|
|
43
|
+
cursor.execute("""
|
|
44
|
+
CREATE TABLE IF NOT EXISTS [reflection_stalker] (
|
|
45
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46
|
+
method TEXT NOT NULL,
|
|
47
|
+
method_b64 TEXT NULL,
|
|
48
|
+
method_name TEXT NULL,
|
|
49
|
+
target_class TEXT NULL,
|
|
50
|
+
target_tostring TEXT NULL,
|
|
51
|
+
params_count TEXT NULL,
|
|
52
|
+
params TEXT NULL,
|
|
53
|
+
error TEXT NULL,
|
|
54
|
+
return_summary TEXT NULL,
|
|
55
|
+
return_class TEXT NULL,
|
|
56
|
+
stack_trace TEXT NULL,
|
|
57
|
+
created_date datetime not null DEFAULT (datetime('now','localtime'))
|
|
58
|
+
);
|
|
59
|
+
""")
|
|
60
|
+
|
|
61
|
+
conn.commit()
|
|
62
|
+
|
|
63
|
+
# Must get the constraints
|
|
64
|
+
self.get_constraints(conn)
|
|
65
|
+
|
|
66
|
+
def __init__(self):
|
|
67
|
+
super().__init__('Reflection Stalker', 'Monitor reflection calls/invoke by Helvio Junior (M4v3r1ck)')
|
|
68
|
+
self.mod_path = str(Path(__file__).resolve().parent)
|
|
69
|
+
self._stalker_db = None
|
|
70
|
+
self._suppress_messages = False
|
|
71
|
+
self._ignore_list = []
|
|
72
|
+
self.js_file = os.path.join(self.mod_path, "reflection-stalker.js")
|
|
73
|
+
|
|
74
|
+
def start_module(self, **kwargs) -> bool:
|
|
75
|
+
if 'db_path' not in kwargs:
|
|
76
|
+
raise Exception("parameter db_path not found")
|
|
77
|
+
|
|
78
|
+
self._stalker_db = Reflection.StalkerDB(db_name=kwargs['db_path'])
|
|
79
|
+
|
|
80
|
+
def js_files(self) -> list:
|
|
81
|
+
return [
|
|
82
|
+
self.js_file
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
def suppress_messages(self):
|
|
86
|
+
self._suppress_messages = True
|
|
87
|
+
|
|
88
|
+
def dynamic_script(self) -> str:
|
|
89
|
+
return f""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def add_params(self, flags: _ArgumentGroup):
|
|
93
|
+
flags.add_argument('--ignore-stalker-text',
|
|
94
|
+
dest='reflection_stalker_ignore',
|
|
95
|
+
metavar='text',
|
|
96
|
+
action='append',
|
|
97
|
+
help='Text to ignore at screen output. You can specify multiple values repeating the flag.')
|
|
98
|
+
|
|
99
|
+
def load_from_arguments(self, args: Namespace) -> bool:
|
|
100
|
+
self._ignore_list = []
|
|
101
|
+
|
|
102
|
+
if args.reflection_stalker_ignore is None or len(args.reflection_stalker_ignore) == 0:
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
self._ignore_list = list(set([
|
|
106
|
+
txt.lower()
|
|
107
|
+
for t1 in args.reflection_stalker_ignore
|
|
108
|
+
if (txt := t1.strip()) != ''
|
|
109
|
+
]))
|
|
110
|
+
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
def key_value_event(self,
|
|
114
|
+
script_location: ScriptLocation = None,
|
|
115
|
+
stack_trace: str = None,
|
|
116
|
+
module: str = None,
|
|
117
|
+
received_data: dict = None
|
|
118
|
+
) -> bool:
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if module in ["java.lang.reflect.Method!invoke!throw",
|
|
122
|
+
"java.lang.reflect.Method!invoke!call"
|
|
123
|
+
]:
|
|
124
|
+
|
|
125
|
+
# Exclusion list
|
|
126
|
+
method = received_data.get('method', '')
|
|
127
|
+
for em in Reflection._EXCLUSION_LIST:
|
|
128
|
+
if em in method:
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
params_preview = received_data.get('params_preview', '[]')
|
|
132
|
+
try:
|
|
133
|
+
params = json.dumps(json.loads(params_preview), default=Logger.json_serial)
|
|
134
|
+
except Exception:
|
|
135
|
+
params = params_preview
|
|
136
|
+
|
|
137
|
+
return_summary = received_data.get('return_summary', None)
|
|
138
|
+
try:
|
|
139
|
+
return_summary = json.dumps(json.loads(return_summary), default=Logger.json_serial)
|
|
140
|
+
except Exception:
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
backtrace = received_data.get('backtrace_raw', None)
|
|
144
|
+
try:
|
|
145
|
+
if backtrace is None:
|
|
146
|
+
raise Exception()
|
|
147
|
+
|
|
148
|
+
if isinstance(backtrace, str):
|
|
149
|
+
backtrace = json.loads(backtrace)
|
|
150
|
+
|
|
151
|
+
bt = "Stack trace:\n at "
|
|
152
|
+
bt += '\n at '.join([
|
|
153
|
+
ln
|
|
154
|
+
for ln in backtrace
|
|
155
|
+
if 'dalvik.system.VMStack.getThreadStackTrace' not in ln
|
|
156
|
+
and 'java.lang.Thread.getStackTrace' not in ln
|
|
157
|
+
])
|
|
158
|
+
|
|
159
|
+
backtrace = bt
|
|
160
|
+
except Exception:
|
|
161
|
+
backtrace = received_data.get('backtrace', stack_trace)
|
|
162
|
+
|
|
163
|
+
self._stalker_db.insert_one(
|
|
164
|
+
table_name='reflection_stalker',
|
|
165
|
+
stack_trace=backtrace,
|
|
166
|
+
error=received_data.get('error', ''),
|
|
167
|
+
method=received_data.get('method', ''),
|
|
168
|
+
method_b64=received_data.get('method_b64', ''),
|
|
169
|
+
method_name=received_data.get('method_name', ''),
|
|
170
|
+
target_class=received_data.get('target_class', ''),
|
|
171
|
+
target_tostring=received_data.get('target_tostring', ''),
|
|
172
|
+
params_count=received_data.get('params_count', ''),
|
|
173
|
+
params=params,
|
|
174
|
+
return_summary=return_summary,
|
|
175
|
+
return_class=received_data.get('return_class', '')
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if not self._suppress_messages:
|
|
179
|
+
txt = json.dumps(received_data, indent=4)
|
|
180
|
+
for em in self._ignore_list:
|
|
181
|
+
if em in txt.lower():
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
Logger.print_message(
|
|
185
|
+
level="I",
|
|
186
|
+
message=f"Reflection: {received_data.get('method', '')}",
|
|
187
|
+
script_location=script_location
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
Logger.print_message(
|
|
192
|
+
level="D",
|
|
193
|
+
message=f"Reflection: {module}\n{txt}",
|
|
194
|
+
script_location=script_location
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return True
|
|
198
|
+
|
|
199
|
+
def data_event(self,
|
|
200
|
+
script_location: ScriptLocation = None,
|
|
201
|
+
stack_trace: str = None,
|
|
202
|
+
received_data: str = None
|
|
203
|
+
) -> bool:
|
|
204
|
+
return True
|
|
205
|
+
|
|
206
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frida-fusion
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.16
|
|
4
4
|
Summary: Hook your mobile tests with Frida
|
|
5
5
|
Author-email: "Helvio Junior (M4v3r1ck)" <helvio_junior@hotmail.com>
|
|
6
6
|
Maintainer-email: "Helvio Junior (M4v3r1ck)" <helvio_junior@hotmail.com>
|
|
@@ -101,11 +101,14 @@ void fusion_printStackTrace();
|
|
|
101
101
|
# Print all methods of class 'name'
|
|
102
102
|
void fusion_printMethods(String name);
|
|
103
103
|
|
|
104
|
+
# Get value of a field inside an class instance
|
|
105
|
+
Object fusion_getFieldValue(Object obj, String fieldName);
|
|
106
|
+
|
|
104
107
|
# Wait until the class 'name' exists in memory to execute the callback function
|
|
105
108
|
void fusion_waitForClass(String name, CallbackFunction onReady)
|
|
106
109
|
|
|
107
110
|
# Conversions
|
|
108
|
-
|
|
111
|
+
String fusion_stringToBase64(String message);
|
|
109
112
|
String fusion_bytesToBase64(byte[] byteArray);
|
|
110
113
|
String fusion_encodeHex(byte[] byteArray);
|
|
111
114
|
```
|
|
@@ -29,5 +29,7 @@ frida_fusion/modules/android_setings/settings.py
|
|
|
29
29
|
frida_fusion/modules/crypto/__init__.py
|
|
30
30
|
frida_fusion/modules/crypto/crypto.js
|
|
31
31
|
frida_fusion/modules/crypto/crypto.py
|
|
32
|
+
frida_fusion/modules/reflection/reflection-stalker.js
|
|
33
|
+
frida_fusion/modules/reflection/reflection-stalker.py
|
|
32
34
|
frida_fusion/modules/tls_unpinning/__init__.py
|
|
33
35
|
frida_fusion/modules/tls_unpinning/frida_multiple_unpinning.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/android_setings/__init__.py
RENAMED
|
File without changes
|
{frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/android_setings/settings.js
RENAMED
|
File without changes
|
{frida_fusion-0.1.14 → frida_fusion-0.1.16}/frida_fusion/modules/android_setings/settings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|