frida-fusion 0.1.16__tar.gz → 0.1.17__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.

Files changed (42) hide show
  1. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/PKG-INFO +2 -1
  2. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/README.md +1 -0
  3. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/__meta__.py +2 -2
  4. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/fusion.py +78 -37
  5. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/libs/helpers.js +105 -8
  6. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/crypto/crypto.py +8 -2
  7. frida_fusion-0.1.17/frida_fusion/modules/okhttp-logging/okhttp-logging.js +1584 -0
  8. frida_fusion-0.1.17/frida_fusion/modules/okhttp-logging/okhttp-logging.py +80 -0
  9. frida_fusion-0.1.17/frida_fusion/modules/shared_preferences/shared_preferences.js +448 -0
  10. frida_fusion-0.1.17/frida_fusion/modules/shared_preferences/shared_preferences.py +184 -0
  11. frida_fusion-0.1.17/frida_fusion/modules/tls_unpinning/__init__.py +0 -0
  12. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion.egg-info/PKG-INFO +2 -1
  13. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion.egg-info/SOURCES.txt +5 -0
  14. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/LICENSE +0 -0
  15. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/__init__.py +0 -0
  16. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/__main__.py +0 -0
  17. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/args.py +0 -0
  18. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/config.py +0 -0
  19. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/exceptions.py +0 -0
  20. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/libs/__init__.py +0 -0
  21. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/libs/color.py +0 -0
  22. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/libs/database.py +0 -0
  23. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/libs/logger.py +0 -0
  24. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/libs/scriptlocation.py +0 -0
  25. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/module.py +0 -0
  26. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/__init__.py +0 -0
  27. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/android_setings/__init__.py +0 -0
  28. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/android_setings/settings.js +0 -0
  29. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/android_setings/settings.py +0 -0
  30. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/crypto/__init__.py +0 -0
  31. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/crypto/crypto.js +0 -0
  32. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/reflection/reflection-stalker.js +0 -0
  33. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/reflection/reflection-stalker.py +0 -0
  34. {frida_fusion-0.1.16/frida_fusion/modules/tls_unpinning → frida_fusion-0.1.17/frida_fusion/modules/shared_preferences}/__init__.py +0 -0
  35. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion/modules/tls_unpinning/frida_multiple_unpinning.py +0 -0
  36. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion.egg-info/dependency_links.txt +0 -0
  37. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion.egg-info/entry_points.txt +0 -0
  38. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion.egg-info/requires.txt +0 -0
  39. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/frida_fusion.egg-info/top_level.txt +0 -0
  40. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/pyproject.toml +0 -0
  41. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/setup.cfg +0 -0
  42. {frida_fusion-0.1.16 → frida_fusion-0.1.17}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frida-fusion
3
- Version: 0.1.16
3
+ Version: 0.1.17
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>
@@ -92,6 +92,7 @@ The Frida Fusion define/expose several functions to be used at frida scripts. Fo
92
92
  ```java
93
93
  # Send message/data to Frida-Fusion
94
94
  void fusion_sendMessage(String level, String message);
95
+ void fusion_sendError(Error error);
95
96
  void fusion_sendMessageWithTrace(String level, String message);
96
97
  void fusion_sendKeyValueData(String module, Object items);
97
98
 
@@ -57,6 +57,7 @@ The Frida Fusion define/expose several functions to be used at frida scripts. Fo
57
57
  ```java
58
58
  # Send message/data to Frida-Fusion
59
59
  void fusion_sendMessage(String level, String message);
60
+ void fusion_sendError(Error error);
60
61
  void fusion_sendMessageWithTrace(String level, String message);
61
62
  void fusion_sendKeyValueData(String module, Object items);
62
63
 
@@ -1,8 +1,8 @@
1
- __version__ = '0.1.16'
1
+ __version__ = '0.1.17'
2
2
  __title__ = "Frida Fusion"
3
3
  __description__ = "📱 frida-fusion - runtime mobile exploration"
4
4
  __url__ = "https://github.com/helviojunior/frida-fusion"
5
- __build__ = 0xfe136e5
5
+ __build__ = 0xe289b59
6
6
  __author__ = "Helvio Junior (M4v3r1ck)"
7
7
  __author_email__ = "helvio_junior@hotmail.com"
8
8
  __license__ = "GPL-3.0"
@@ -35,6 +35,7 @@ class Fusion(object):
35
35
  print_timestamp = False
36
36
  max_filename = 28
37
37
 
38
+ _bundle_pattern = re.compile(r'(fusion_bundle\.js):(\d+)')
38
39
  _script_name = Path(__file__).name
39
40
  _db_jobs = queue.Queue()
40
41
 
@@ -108,25 +109,6 @@ class Fusion(object):
108
109
  if v[0] <= loc.get_int_line() <= v[1]
109
110
  ]), loc)
110
111
 
111
- @classmethod
112
- def print_message(cls, level: str = "*", message: str = "",
113
- script_location: ScriptLocation = None):
114
-
115
- if Fusion.running is False and Logger.debug_level >= 2:
116
- return
117
-
118
- if script_location is None:
119
- script_location = ScriptLocation(
120
- file_name=Fusion._script_name
121
- )
122
-
123
- Logger.print_message(
124
- level=level,
125
- message=message,
126
- script_location=script_location,
127
- filename_col_len=Fusion.max_filename
128
- )
129
-
130
112
  def load_all_scripts(self):
131
113
  self.script_trace = {}
132
114
  offset = 1
@@ -334,10 +316,11 @@ class Fusion(object):
334
316
  msg = base64.b64decode(msg).decode("UTF-8")
335
317
  except:
336
318
  pass
337
- self.print_message(mLevel, msg, script_location=script_location)
319
+
320
+ self.print_message_inst(mLevel, msg, script_location=script_location)
338
321
 
339
322
  elif mType == "key_value_data":
340
- self.print_message("V", "RAW JSON:\n %s" % (
323
+ self.print_message_inst("V", "RAW JSON:\n %s" % (
341
324
  json.dumps(jData, indent=4).replace("\n", "\n ")
342
325
  ), script_location=script_location)
343
326
 
@@ -367,7 +350,7 @@ class Fusion(object):
367
350
 
368
351
  # Legacy
369
352
  elif mType == "data":
370
- self.print_message("V", "RAW JSON:\n %s" % (
353
+ self.print_message_inst("V", "RAW JSON:\n %s" % (
371
354
  json.dumps(jData, indent=4).replace("\n", "\n ")
372
355
  ), script_location=script_location)
373
356
 
@@ -391,15 +374,15 @@ class Fusion(object):
391
374
 
392
375
  elif mType == "java-uncaught":
393
376
  self.insert_history('frida', json.dumps(jData))
394
- self.print_message("E", jData.get('stack', ''), script_location=script_location)
377
+ self.print_message_inst("E", jData.get('stack', ''), script_location=script_location)
395
378
 
396
379
  else:
397
- self.print_message(mLevel, message, script_location=script_location)
380
+ self.print_message_inst(mLevel, message, script_location=script_location)
398
381
 
399
382
  except SilentKillError as sk:
400
383
  skm = str(sk)
401
384
 
402
- self.print_message("D", "Silent kill requested",
385
+ self.print_message_inst("D", "Silent kill requested",
403
386
  script_location=Logger.get_caller_info(stack_index=1))
404
387
  Fusion.running = False
405
388
  time.sleep(0.2)
@@ -410,8 +393,8 @@ class Fusion(object):
410
393
 
411
394
  except Exception as err:
412
395
  script_location = ScriptLocation(file_name=Fusion._script_name)
413
- self.print_message("E", message, script_location=script_location)
414
- self.print_message("E", payload, script_location=script_location)
396
+ self.print_message_inst("E", message, script_location=script_location)
397
+ self.print_message_inst("E", payload, script_location=script_location)
415
398
  self.print_exception(err)
416
399
 
417
400
  else:
@@ -425,7 +408,6 @@ class Fusion(object):
425
408
  stack = "Stack trace:\n"
426
409
  stack += message.get('stack', '')
427
410
 
428
- pattern = re.compile(r'(fusion_bundle\.js):(\d+)')
429
411
  matches = [
430
412
  (
431
413
  m.group(0),
@@ -434,7 +416,7 @@ class Fusion(object):
434
416
  line=m.group(2),
435
417
  ))
436
418
  )
437
- for m in pattern.finditer(stack)
419
+ for m in Fusion._bundle_pattern.finditer(stack)
438
420
  ]
439
421
  for m in matches:
440
422
  stack = stack.replace(m[0], f"{m[1].file_name}:{m[1].line}")
@@ -453,18 +435,19 @@ class Fusion(object):
453
435
  "stack": stack
454
436
  }))
455
437
 
456
- self.print_message("F", description + stack,
438
+ self.print_message_inst("F", description + stack,
457
439
  script_location=script_location)
458
440
  Fusion.running = False
459
441
  time.sleep(0.2)
460
442
  Logger.pl('\n{+} {O}Exiting...{O}{W}')
461
443
  self.done.set()
462
444
  else:
463
- self.print_message("I", message, script_location=script_location)
464
- self.print_message("I", payload, script_location=script_location)
465
- except:
466
- self.print_message("I", message, script_location=script_location)
467
- self.print_message("I", payload, script_location=script_location)
445
+ self.print_message_inst("I", message, script_location=script_location)
446
+ self.print_message_inst("I", payload, script_location=script_location)
447
+ except Exception as e:
448
+ self.print_message_inst("I", message, script_location=script_location)
449
+ self.print_message_inst("I", payload, script_location=script_location)
450
+ self.print_exception(e)
468
451
 
469
452
  return handler
470
453
 
@@ -482,6 +465,27 @@ class Fusion(object):
482
465
  Logger.pl("")
483
466
  self.done.set()
484
467
 
468
+
469
+ def _replace_location(self, message: str) -> str:
470
+ try:
471
+ matches = [
472
+ (
473
+ m.group(0),
474
+ self.translate_location(dict(
475
+ file_name=m.group(1),
476
+ line=m.group(2),
477
+ ))
478
+ )
479
+ for m in Fusion._bundle_pattern.finditer(message)
480
+ ]
481
+ for m in matches:
482
+ message = message.replace(m[0], f"{m[1].file_name}:{m[1].line}")
483
+ except Exception as e:
484
+ print(e)
485
+ pass
486
+
487
+ return message
488
+
485
489
  def _raise_key_value_event(self,
486
490
  script_location: ScriptLocation = None,
487
491
  stack_trace: str = None,
@@ -499,7 +503,7 @@ class Fusion(object):
499
503
  raise ske
500
504
  except Exception as e:
501
505
  if Configuration.debug_level >= 2:
502
- self.print_message("E", f"Error resizing event to module {m.name}: {str(e)}")
506
+ self.print_message_inst("E", f"Error resizing event to module {m.name}: {str(e)}")
503
507
  else:
504
508
  self.print_exception(e)
505
509
 
@@ -518,10 +522,47 @@ class Fusion(object):
518
522
  raise ske
519
523
  except Exception as e:
520
524
  if Configuration.debug_level >= 2:
521
- self.print_message("E", f"Error resizing event to module {m.name}: {str(e)}")
525
+ self.print_message_inst("E", f"Error resizing event to module {m.name}: {str(e)}")
522
526
  else:
523
527
  self.print_exception(e)
524
528
 
529
+ def print_message_inst(self, level: str = "*", message: str = "",
530
+ script_location: ScriptLocation = None):
531
+
532
+ return type(self)._print_message(
533
+ level=level,
534
+ message=self._replace_location(message),
535
+ script_location=script_location
536
+ )
537
+
538
+ @classmethod
539
+ def print_message(cls, level: str = "*", message: str = "",
540
+ script_location: ScriptLocation = None):
541
+ return cls._print_message(
542
+ level=level,
543
+ message=message,
544
+ script_location=script_location
545
+ )
546
+
547
+ @classmethod
548
+ def _print_message(cls, level: str = "*", message: str = "",
549
+ script_location: ScriptLocation = None):
550
+
551
+ if Fusion.running is False and Logger.debug_level >= 2:
552
+ return
553
+
554
+ if script_location is None:
555
+ script_location = ScriptLocation(
556
+ file_name=Fusion._script_name
557
+ )
558
+
559
+ Logger.print_message(
560
+ level=level,
561
+ message=message,
562
+ script_location=script_location,
563
+ filename_col_len=Fusion.max_filename
564
+ )
565
+
525
566
  @classmethod
526
567
  def insert_history(cls, source: str, data: str, stack_trace: str = ''):
527
568
  Fusion._db_jobs.put(dict(
@@ -2,6 +2,60 @@
2
2
  Author: Helvio Junior - M4v3r1ck
3
3
  */
4
4
 
5
+ // Lista de classes típicas do React Native (pode ampliar se quiser)
6
+ const FUSION_DEFAULT_REACT_CLASSES = [
7
+ "com.facebook.react.ReactApplication",
8
+ "com.facebook.react.ReactInstanceManager",
9
+ "com.facebook.react.bridge.ReactContext",
10
+ "com.facebook.react.bridge.JavaScriptModule",
11
+ "com.facebook.react.bridge.NativeModule",
12
+ "com.facebook.react.modules.core.DeviceEventManagerModule",
13
+ "com.facebook.react.bridge.ReactApplicationContext"
14
+ ];
15
+
16
+ /**
17
+ * Tenta determinar se a app tem indícios de ser React Native.
18
+ * Estratégia:
19
+ * - tenta Java.use em classes conhecidas (se lançar, ignora)
20
+ * - verifica classes já carregadas (enumerateLoadedClassesSync)
21
+ *
22
+ * Retorna true/false.
23
+ */
24
+ function fusion_isReactNativeApp(reactClasses) {
25
+ reactClasses = reactClasses || FUSION_DEFAULT_REACT_CLASSES;
26
+ if (!Java.available) return false;
27
+
28
+ try {
29
+ // 1) tentativa de usar as classes (pode carregar a classe se presente)
30
+ for (var i = 0; i < reactClasses.length; i++) {
31
+ try {
32
+ Java.use(reactClasses[i]);
33
+ return true; // encontrou uma classe RN na ClassLoader
34
+ } catch (e) {
35
+ // não encontrado/erro: continuar
36
+ }
37
+ }
38
+
39
+ // 2) se nada foi encontrado via Java.use, verificar classes já carregadas
40
+ try {
41
+ var loaded = Java.enumerateLoadedClassesSync();
42
+ for (var j = 0; j < loaded.length; j++) {
43
+ var name = loaded[j];
44
+ for (var k = 0; k < reactClasses.length; k++) {
45
+ if (name.indexOf(reactClasses[k]) !== -1 || name === reactClasses[k]) {
46
+ return true;
47
+ }
48
+ }
49
+ }
50
+ } catch (e) {
51
+ // enumerateLoadedClassesSync pode falhar em alguns contextos; ignorar
52
+ }
53
+ } catch (outer) {
54
+ // fallback
55
+ }
56
+ return false;
57
+ }
58
+
5
59
  function fusion_rawSend(payload1){
6
60
  send(payload1);
7
61
  }
@@ -22,6 +76,10 @@ function fusion_Send(payload1, payload2){
22
76
  send(message, payload2);
23
77
  }
24
78
 
79
+ function fusion_classExists(name) { try { Java.use(name); return true; } catch (_) { return false; } }
80
+
81
+ function fusion_useOrNull(name) { try { return Java.use(name); } catch (e) { return null; } }
82
+
25
83
  function fusion_waitForClass(name, onReady) {
26
84
  var intv = setInterval(function () {
27
85
  try {
@@ -38,6 +96,37 @@ function fusion_printStackTrace(){
38
96
  fusion_sendMessage("I", trace);
39
97
  }
40
98
 
99
+ function fusion_toLongPrimitive(v, fallback /* opcional */) {
100
+ const FB = (typeof fallback === 'number') ? fallback : -1;
101
+
102
+ try {
103
+ // Já é número JS
104
+ if (typeof v === 'number') {
105
+ // garante inteiro (contentLength é integral)
106
+ return Math.trunc(v);
107
+ }
108
+
109
+ if (v === null || v === undefined) return FB;
110
+
111
+ // java.lang.Long / Integer / Short (ou qualquer Number com longValue/intValue)
112
+ if (v.longValue) { try { return v.longValue(); } catch (_) {} }
113
+ if (v.intValue) { try { return v.intValue(); } catch (_) {} }
114
+ if (v.shortValue) { try { return v.shortValue(); } catch (_) {} }
115
+
116
+ // String numérica
117
+ if (typeof v === 'string' || (v.toString && typeof v.toString() === 'function')) {
118
+ const s = String(v);
119
+ if (/^-?\d+$/.test(s)) {
120
+ const JLong = Java.use('java.lang.Long');
121
+ // parseia com Java para respeitar faixa de long
122
+ return JLong.parseLong(s);
123
+ }
124
+ }
125
+ } catch (_) {}
126
+
127
+ return FB;
128
+ }
129
+
41
130
  function fusion_toBytes(message){
42
131
  try{
43
132
  const StringClass = Java.use('java.lang.String');
@@ -194,9 +283,9 @@ function fusion_sendMessageWithTrace(level, message){
194
283
 
195
284
  function fusion_sendError(error) {
196
285
  try{
197
- fusion_sendMessage("E", error + '\n' + error.stack);
286
+ fusion_sendMessage("E", `${error}\n${error.stack}`);
198
287
  } catch (err) {
199
- fusion_sendMessage("W", err)
288
+ fusion_sendMessage("W", `Error: ${err}`);
200
289
  }
201
290
  }
202
291
 
@@ -236,6 +325,8 @@ function fusion_printMethods(targetClass)
236
325
  });
237
326
  }
238
327
 
328
+
329
+ //java.lang.Class
239
330
  function fusion_getClassName(obj)
240
331
  {
241
332
  if (obj === null || obj === undefined) return "";
@@ -244,23 +335,29 @@ function fusion_getClassName(obj)
244
335
  // Caso seja um objeto Java real
245
336
  if (obj.$className !== undefined) {
246
337
  // Objetos instanciados via Java.use
247
- return obj.$className;
338
+ var name = obj.$className;
339
+ if (name == "java.lang.Class") return obj.getName();
340
+ return name;
248
341
  }
249
342
 
250
343
  // Caso seja uma instância Java (não necessariamente via Java.use)
251
- if (Java.isJavaObject(obj)) {
252
- return obj.getClass().getName();
344
+ if (typeof obj === 'object' && typeof obj.getClass === 'function') {
345
+ var name = obj.getClass().getName();
346
+ if (name == "java.lang.Class" && typeof obj.getClass === 'function') return obj.getName();
347
+ return name;
253
348
  }
254
349
 
255
350
  // Caso seja uma classe Java carregada (Java.use)
256
- if (Java.isJavaClass(obj)) {
257
- return obj.class.getName();
351
+ if (typeof obj === 'object' && obj.class !== undefined ) {
352
+ var name = obj.class.getName();
353
+ if (name == "java.lang.Class" && typeof obj.getClass === 'function') return obj.getName();
354
+ return name;
258
355
  }
259
356
 
260
357
  // Se for algo não Java, apenas retorna tipo do JS
261
358
  return typeof obj;
262
359
  } catch (err) {
263
- fusion_sendMessage("W", `Error: ${err}`)
360
+ fusion_sendMessage("W", `Error: ${err}\n${err.stack}`)
264
361
  return '';
265
362
  }
266
363
 
@@ -472,7 +472,7 @@ class Crypto(ModuleBase):
472
472
  if not self._suppress_messages:
473
473
  Logger.print_message(
474
474
  level="D",
475
- message=f"Cipher getInstance received\n{stack_trace}",
475
+ message=f"Cipher getInstance received\nHashcode: {hashcode}\nAlgorithm: {algorithm}\n{stack_trace}",
476
476
  script_location=script_location
477
477
  )
478
478
 
@@ -496,9 +496,15 @@ class Crypto(ModuleBase):
496
496
  )
497
497
 
498
498
  if not self._suppress_messages:
499
+ data=json.dumps(dict(
500
+ hashcode=hashcode,
501
+ before_final=received_data.get('input', ''),
502
+ after_final=received_data.get('output', ''),
503
+ ), default=Logger.json_serial, indent=4, sort_keys=False)
504
+
499
505
  Logger.print_message(
500
506
  level="D",
501
- message=f"Cipher doFinal received\n{stack_trace}",
507
+ message=f"Cipher doFinal received\n{data}\n{stack_trace}",
502
508
  script_location=script_location
503
509
  )
504
510