frida-fusion 0.1.15__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.15 → frida_fusion-0.1.17}/PKG-INFO +5 -1
  2. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/README.md +4 -0
  3. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/__meta__.py +2 -2
  4. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/fusion.py +78 -37
  5. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/libs/helpers.js +145 -24
  6. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/crypto/crypto.js +136 -14
  7. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/crypto/crypto.py +37 -1
  8. frida_fusion-0.1.17/frida_fusion/modules/okhttp-logging/okhttp-logging.js +1584 -0
  9. frida_fusion-0.1.17/frida_fusion/modules/okhttp-logging/okhttp-logging.py +80 -0
  10. frida_fusion-0.1.17/frida_fusion/modules/shared_preferences/shared_preferences.js +448 -0
  11. frida_fusion-0.1.17/frida_fusion/modules/shared_preferences/shared_preferences.py +184 -0
  12. frida_fusion-0.1.17/frida_fusion/modules/tls_unpinning/__init__.py +0 -0
  13. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion.egg-info/PKG-INFO +5 -1
  14. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion.egg-info/SOURCES.txt +5 -0
  15. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/LICENSE +0 -0
  16. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/__init__.py +0 -0
  17. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/__main__.py +0 -0
  18. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/args.py +0 -0
  19. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/config.py +0 -0
  20. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/exceptions.py +0 -0
  21. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/libs/__init__.py +0 -0
  22. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/libs/color.py +0 -0
  23. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/libs/database.py +0 -0
  24. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/libs/logger.py +0 -0
  25. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/libs/scriptlocation.py +0 -0
  26. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/module.py +0 -0
  27. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/__init__.py +0 -0
  28. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/android_setings/__init__.py +0 -0
  29. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/android_setings/settings.js +0 -0
  30. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/android_setings/settings.py +0 -0
  31. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/crypto/__init__.py +0 -0
  32. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/reflection/reflection-stalker.js +0 -0
  33. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/reflection/reflection-stalker.py +0 -0
  34. {frida_fusion-0.1.15/frida_fusion/modules/tls_unpinning → frida_fusion-0.1.17/frida_fusion/modules/shared_preferences}/__init__.py +0 -0
  35. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion/modules/tls_unpinning/frida_multiple_unpinning.py +0 -0
  36. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion.egg-info/dependency_links.txt +0 -0
  37. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion.egg-info/entry_points.txt +0 -0
  38. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion.egg-info/requires.txt +0 -0
  39. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/frida_fusion.egg-info/top_level.txt +0 -0
  40. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/pyproject.toml +0 -0
  41. {frida_fusion-0.1.15 → frida_fusion-0.1.17}/setup.cfg +0 -0
  42. {frida_fusion-0.1.15 → 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.15
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
 
@@ -101,6 +102,9 @@ void fusion_printStackTrace();
101
102
  # Print all methods of class 'name'
102
103
  void fusion_printMethods(String name);
103
104
 
105
+ # Get value of a field inside an class instance
106
+ Object fusion_getFieldValue(Object obj, String fieldName);
107
+
104
108
  # Wait until the class 'name' exists in memory to execute the callback function
105
109
  void fusion_waitForClass(String name, CallbackFunction onReady)
106
110
 
@@ -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
 
@@ -66,6 +67,9 @@ void fusion_printStackTrace();
66
67
  # Print all methods of class 'name'
67
68
  void fusion_printMethods(String name);
68
69
 
70
+ # Get value of a field inside an class instance
71
+ Object fusion_getFieldValue(Object obj, String fieldName);
72
+
69
73
  # Wait until the class 'name' exists in memory to execute the callback function
70
74
  void fusion_waitForClass(String name, CallbackFunction onReady)
71
75
 
@@ -1,8 +1,8 @@
1
- __version__ = '0.1.15'
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__ = 0xef43fce
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');
@@ -128,22 +217,26 @@ function fusion_getCallerInfo() {
128
217
  }
129
218
 
130
219
  function fusion_sendKeyValueData(module, items) {
131
- var st = fusion_getB64StackTrace();
132
-
133
- var data = [];
220
+ try{
221
+ var st = fusion_getB64StackTrace();
134
222
 
135
- // Force as String
136
- for (let i = 0; i < items.length; i++) {
137
- data = data.concat([{key: `${items[i].key}`, value:`${items[i].value}`}]);
138
- }
223
+ var data = [];
139
224
 
140
- fusion_Send({
141
- type: "key_value_data",
142
- module: module,
143
- data: data,
144
- stack_trace: st
145
- }, null);
225
+ // Force as String
226
+ for (let i = 0; i < items.length; i++) {
227
+ data = data.concat([{key: `${items[i].key}`, value:`${items[i].value}`}]);
228
+ }
146
229
 
230
+ fusion_Send({
231
+ type: "key_value_data",
232
+ module: module,
233
+ data: data,
234
+ stack_trace: st
235
+ }, null);
236
+ } catch (err) {
237
+ fusion_sendMessage("W", `Error: ${err}`)
238
+ }
239
+ return null;
147
240
  }
148
241
 
149
242
  function fusion_sendMessage(level, message){
@@ -160,7 +253,7 @@ function fusion_sendMessage(level, message){
160
253
  message: b64Msg
161
254
  }, null)
162
255
  } catch (err) {
163
- fusion_sendMessage("W", err)
256
+ fusion_sendMessage("W", `Error: ${err}`)
164
257
  }
165
258
  }
166
259
 
@@ -184,15 +277,15 @@ function fusion_sendMessageWithTrace(level, message){
184
277
  message: b64Msg
185
278
  }, null)
186
279
  } catch (err) {
187
- fusion_sendMessage("W", err)
280
+ fusion_sendMessage("W", `Error: ${err}`)
188
281
  }
189
282
  }
190
283
 
191
284
  function fusion_sendError(error) {
192
285
  try{
193
- fusion_sendMessage("E", error + '\n' + error.stack);
286
+ fusion_sendMessage("E", `${error}\n${error.stack}`);
194
287
  } catch (err) {
195
- fusion_sendMessage("W", err)
288
+ fusion_sendMessage("W", `Error: ${err}`);
196
289
  }
197
290
  }
198
291
 
@@ -218,7 +311,7 @@ function fusion_getB64StackTrace(){
218
311
  return b64Msg
219
312
 
220
313
  } catch (err) {
221
- fusion_sendMessage("W", err);
314
+ fusion_sendMessage("W", `Error: ${err}`)
222
315
  return '';
223
316
  }
224
317
  }
@@ -232,6 +325,8 @@ function fusion_printMethods(targetClass)
232
325
  });
233
326
  }
234
327
 
328
+
329
+ //java.lang.Class
235
330
  function fusion_getClassName(obj)
236
331
  {
237
332
  if (obj === null || obj === undefined) return "";
@@ -240,28 +335,54 @@ function fusion_getClassName(obj)
240
335
  // Caso seja um objeto Java real
241
336
  if (obj.$className !== undefined) {
242
337
  // Objetos instanciados via Java.use
243
- return obj.$className;
338
+ var name = obj.$className;
339
+ if (name == "java.lang.Class") return obj.getName();
340
+ return name;
244
341
  }
245
342
 
246
343
  // Caso seja uma instância Java (não necessariamente via Java.use)
247
- if (Java.isJavaObject(obj)) {
248
- 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;
249
348
  }
250
349
 
251
350
  // Caso seja uma classe Java carregada (Java.use)
252
- if (Java.isJavaClass(obj)) {
253
- 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;
254
355
  }
255
356
 
256
357
  // Se for algo não Java, apenas retorna tipo do JS
257
358
  return typeof obj;
258
359
  } catch (err) {
259
- fusion_sendMessage("W", err);
360
+ fusion_sendMessage("W", `Error: ${err}\n${err.stack}`)
260
361
  return '';
261
362
  }
262
363
 
263
364
  }
264
365
 
366
+ function fusion_getFieldValue(obj, fieldName) {
367
+ if (obj === null || obj === undefined) return "";
368
+ try {
369
+ var cls = obj.getClass();
370
+ while (cls != null) {
371
+ try {
372
+ var f = cls.getDeclaredField(fieldName);
373
+ f.setAccessible(true);
374
+ return f.get(obj);
375
+ } catch (e) {
376
+ cls = cls.getSuperclass();
377
+ }
378
+ }
379
+ } catch (err) {
380
+ fusion_sendMessage("W", `Error: ${err}`)
381
+ return '';
382
+ }
383
+ }
384
+
385
+
265
386
  function fusion_getReadableRange(p) {
266
387
  try { p = ptr(p); } catch (_) { return null; }
267
388
  const range = Process.findRangeByAddress(p); // não lança exceção