frida-fusion 0.1.12__tar.gz → 0.1.14__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 (35) hide show
  1. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/PKG-INFO +1 -1
  2. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/__meta__.py +2 -2
  3. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/args.py +31 -0
  4. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/config.py +23 -20
  5. frida_fusion-0.1.14/frida_fusion/exceptions.py +19 -0
  6. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/fusion.py +56 -7
  7. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/libs/helpers.js +54 -4
  8. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/module.py +23 -5
  9. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion.egg-info/PKG-INFO +1 -1
  10. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion.egg-info/SOURCES.txt +1 -0
  11. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/LICENSE +0 -0
  12. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/README.md +0 -0
  13. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/__init__.py +0 -0
  14. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/__main__.py +0 -0
  15. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/libs/__init__.py +0 -0
  16. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/libs/color.py +0 -0
  17. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/libs/database.py +0 -0
  18. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/libs/logger.py +0 -0
  19. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/libs/scriptlocation.py +0 -0
  20. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/modules/__init__.py +0 -0
  21. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/modules/android_setings/__init__.py +0 -0
  22. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/modules/android_setings/settings.js +0 -0
  23. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/modules/android_setings/settings.py +0 -0
  24. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/modules/crypto/__init__.py +0 -0
  25. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/modules/crypto/crypto.js +0 -0
  26. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/modules/crypto/crypto.py +0 -0
  27. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/modules/tls_unpinning/__init__.py +0 -0
  28. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion/modules/tls_unpinning/frida_multiple_unpinning.py +0 -0
  29. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion.egg-info/dependency_links.txt +0 -0
  30. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion.egg-info/entry_points.txt +0 -0
  31. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion.egg-info/requires.txt +0 -0
  32. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/frida_fusion.egg-info/top_level.txt +0 -0
  33. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/pyproject.toml +0 -0
  34. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/setup.cfg +0 -0
  35. {frida_fusion-0.1.12 → frida_fusion-0.1.14}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frida-fusion
3
- Version: 0.1.12
3
+ Version: 0.1.14
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>
@@ -1,8 +1,8 @@
1
- __version__ = '0.1.12'
1
+ __version__ = '0.1.14'
2
2
  __title__ = "Frida Fusion"
3
3
  __description__ = "📱 frida-fusion - runtime mobile exploration"
4
4
  __url__ = "https://github.com/helviojunior/frida-fusion"
5
- __build__ = 0x6c949ce
5
+ __build__ = 0x8a5c3b4
6
6
  __author__ = "Helvio Junior (M4v3r1ck)"
7
7
  __author_email__ = "helvio_junior@hotmail.com"
8
8
  __license__ = "GPL-3.0"
@@ -6,6 +6,8 @@ import sys
6
6
 
7
7
  from .libs.color import Color
8
8
  from .__meta__ import __description__
9
+ from .module import ModuleManager
10
+
9
11
 
10
12
  class Arguments(object):
11
13
  ''' Holds arguments used by the Frida Fusion '''
@@ -43,8 +45,37 @@ class Arguments(object):
43
45
  modules_group = parser.add_argument_group('Modules')
44
46
  self._add_modules_args(modules_group)
45
47
 
48
+ # Add module args
49
+ for mod in self._get_requested_module_list():
50
+ flags = parser.add_argument_group(f'{mod.name} Flags')
51
+ mod.create_instance().add_params(flags)
52
+
46
53
  return parser.parse_args()
47
54
 
55
+ def _get_requested_module_list(self):
56
+ mods = ModuleManager.list_modules()
57
+ parser = argparse.ArgumentParser()
58
+ parser.add_argument('-m',
59
+ '--module',
60
+ action='append',
61
+ dest='enabled_modules')
62
+
63
+ t_args, _ = parser.parse_known_args([
64
+ word
65
+ for word in sys.argv
66
+ if word != "-h" and word != "--list-modules"
67
+ ])
68
+ if t_args is None or t_args.enabled_modules is None:
69
+ return []
70
+ return [
71
+ m
72
+ for md in t_args.enabled_modules
73
+ for mn in md.split(",")
74
+ if mn.strip() != ""
75
+ for _, m in mods.items()
76
+ if m.safe_name() == mn.lower()
77
+ ]
78
+
48
79
  def _add_app_args(self, app):
49
80
  app.add_argument('-f',
50
81
  '--package',
@@ -4,6 +4,7 @@ import os
4
4
  import errno
5
5
  import sys
6
6
  import signal
7
+ from argparse import Namespace
7
8
  from pathlib import Path
8
9
 
9
10
  from .module import Module, ModuleManager, InternalModule, ExternalModule
@@ -17,6 +18,7 @@ class Configuration(object):
17
18
  version = '0.0.0'
18
19
 
19
20
  initialized = False # Flag indicating config has been initialized
21
+ args: Namespace = {}
20
22
  debug_level = 0
21
23
  cmd_line = ''
22
24
  base_path = str(Path(__file__).resolve().parent)
@@ -98,6 +100,7 @@ class Configuration(object):
98
100
  sys.exit(0)
99
101
 
100
102
  args = Arguments().args
103
+ Configuration.args = args
101
104
 
102
105
  Color.pl('{+} {W}Startup parameters')
103
106
 
@@ -203,26 +206,26 @@ class Configuration(object):
203
206
  name = fm.safe_name()
204
207
  if name not in Configuration.enabled_modules.keys():
205
208
  Configuration.enabled_modules[name] = fm
206
-
207
- for mod in [
208
- m.strip()
209
- for md in args.ignore_messages_modules
210
- for m in md.split(",")
211
- if m.strip() != ""
212
- ]:
213
- fm = next(iter([
214
- m
215
- for _, m in mods.items()
216
- if m.safe_name() == mod.lower()
217
- ]), None)
218
- if fm is None:
219
- Color.pl(
220
- '{!} {R}error: module {O}%s{R} not found{W}\r\n' % mod)
221
- sys.exit(1)
222
-
223
- name = fm.safe_name()
224
- if name not in Configuration.ignore_messages_modules.keys():
225
- Configuration.ignore_messages_modules[name] = fm
209
+ if args.ignore_messages_modules is not None and isinstance(args.ignore_messages_modules, list):
210
+ for mod in [
211
+ m.strip()
212
+ for md in args.ignore_messages_modules
213
+ for m in md.split(",")
214
+ if m.strip() != ""
215
+ ]:
216
+ fm = next(iter([
217
+ m
218
+ for _, m in mods.items()
219
+ if m.safe_name() == mod.lower()
220
+ ]), None)
221
+ if fm is None:
222
+ Color.pl(
223
+ '{!} {R}error: module {O}%s{R} not found{W}\r\n' % mod)
224
+ sys.exit(1)
225
+
226
+ name = fm.safe_name()
227
+ if name not in Configuration.ignore_messages_modules.keys():
228
+ Configuration.ignore_messages_modules[name] = fm
226
229
 
227
230
  if len(Configuration.enabled_modules) > 0:
228
231
  Logger.pl(' {C}modules:{O} %s{W}' % ', '.join([
@@ -0,0 +1,19 @@
1
+
2
+ class SilentKillError(Exception):
3
+ def __init__(self, *args, **kwargs):
4
+ # args -> vão para a Exception (ex.: a mensagem)
5
+ # kwargs -> metadados opcionais seus
6
+ self.meta = kwargs
7
+ self._msg = kwargs.get("message") or ""
8
+ if args is not None and len(args) > 0 and self._msg == "":
9
+ self._msg = args[0]
10
+ super().__init__(*args)
11
+
12
+ def __str__(self):
13
+ if self._msg != "":
14
+ return self._msg
15
+ else:
16
+ return str(super(SilentKillError, self).__str__())
17
+
18
+ def __repr__(self):
19
+ return str(self)
@@ -2,6 +2,7 @@
2
2
  # -*- coding: UTF-8 -*-
3
3
  from __future__ import annotations
4
4
 
5
+ from .exceptions import SilentKillError
5
6
  from .libs.color import Color
6
7
  from .libs.scriptlocation import ScriptLocation
7
8
 
@@ -64,10 +65,12 @@ class Fusion(object):
64
65
  try:
65
66
  self.session.detach()
66
67
  except:
67
- try:
68
- self.device.kill(self.pid)
69
- except:
70
- pass
68
+ pass
69
+
70
+ try:
71
+ self.device.kill(self.pid)
72
+ except:
73
+ pass
71
74
 
72
75
  def get_device(self):
73
76
  try:
@@ -150,6 +153,13 @@ class Fusion(object):
150
153
 
151
154
  for m in self._modules:
152
155
  files_js += m.js_files()
156
+ dyn = m.dynamic_script()
157
+ if dyn is not None and dyn.strip(" \r\n") != "":
158
+ dyn += "\n\n"
159
+ line_cnt = len(dyn.split("\n")) - 1
160
+ self.script_trace[f"dyn_{m.safe_name()}.js"] = (offset, offset + line_cnt)
161
+ offset += line_cnt
162
+ src += dyn
153
163
 
154
164
  if os.path.isfile(Configuration.frida_scripts):
155
165
  files_js += [Configuration.frida_scripts]
@@ -220,9 +230,16 @@ class Fusion(object):
220
230
  sys.exit(1)
221
231
 
222
232
  def attach(self, pid: int):
223
- self.running = True
233
+ Fusion.running = True
224
234
  self.pid = pid
225
235
 
236
+ if self.session is not None:
237
+ try:
238
+ self.session.off("detached", self.on_detached)
239
+ except:
240
+ pass
241
+ self.session = None
242
+
226
243
  self.session = self.device.attach(self.pid)
227
244
  self.session.on("detached", self.on_detached)
228
245
 
@@ -231,7 +248,14 @@ class Fusion(object):
231
248
  self.device.resume(self.pid)
232
249
 
233
250
  def std_spawn(self):
234
- self.running = True
251
+ Fusion.running = True
252
+
253
+ if self.session is not None:
254
+ try:
255
+ self.session.off("detached", self.on_detached)
256
+ except:
257
+ pass
258
+ self.session = None
235
259
 
236
260
  self.pid = self.device.spawn([Configuration.package])
237
261
  self.session = self.device.attach(self.pid)
@@ -242,7 +266,14 @@ class Fusion(object):
242
266
  self.device.resume(self.pid)
243
267
 
244
268
  def wait_spawn(self):
245
- self.running = True
269
+ Fusion.running = True
270
+
271
+ if self.session is not None:
272
+ try:
273
+ self.session.off("detached", self.on_detached)
274
+ except:
275
+ pass
276
+ self.session = None
246
277
 
247
278
  self.pid = self.device.spawn([Configuration.package])
248
279
  self.device.resume(self.pid)
@@ -365,6 +396,18 @@ class Fusion(object):
365
396
  else:
366
397
  self.print_message(mLevel, message, script_location=script_location)
367
398
 
399
+ except SilentKillError as sk:
400
+ skm = str(sk)
401
+
402
+ self.print_message("D", "Silent kill requested",
403
+ script_location=Logger.get_caller_info(stack_index=1))
404
+ Fusion.running = False
405
+ time.sleep(0.2)
406
+ if skm != "":
407
+ Logger.pl(f'\n{skm}')
408
+ Logger.pl('\n{+} {O}Exiting...{O}{W}')
409
+ self.done.set()
410
+
368
411
  except Exception as err:
369
412
  script_location = ScriptLocation(file_name=Fusion._script_name)
370
413
  self.print_message("E", message, script_location=script_location)
@@ -436,6 +479,7 @@ class Fusion(object):
436
479
  elif crash is not None:
437
480
  Logger.pl("[CRASH] details: " + str(crash))
438
481
 
482
+ Logger.pl("")
439
483
  self.done.set()
440
484
 
441
485
  def _raise_key_value_event(self,
@@ -451,6 +495,8 @@ class Fusion(object):
451
495
  module=module,
452
496
  received_data=received_data
453
497
  )
498
+ except SilentKillError as ske:
499
+ raise ske
454
500
  except Exception as e:
455
501
  if Configuration.debug_level >= 2:
456
502
  self.print_message("E", f"Error resizing event to module {m.name}: {str(e)}")
@@ -468,6 +514,8 @@ class Fusion(object):
468
514
  stack_trace=stack_trace,
469
515
  received_data=received_data
470
516
  )
517
+ except SilentKillError as ske:
518
+ raise ske
471
519
  except Exception as e:
472
520
  if Configuration.debug_level >= 2:
473
521
  self.print_message("E", f"Error resizing event to module {m.name}: {str(e)}")
@@ -626,6 +674,7 @@ class Fusion(object):
626
674
  if len(self._modules) > 0:
627
675
  Logger.pl("{+} Starting selected modules")
628
676
  for m in self._modules:
677
+ m.load_from_arguments(Configuration.args)
629
678
  m.start_module(
630
679
  package=Configuration.package,
631
680
  db_path=Configuration.db_path
@@ -2,12 +2,24 @@
2
2
  Author: Helvio Junior - M4v3r1ck
3
3
  */
4
4
 
5
+ function fusion_rawSend(payload1){
6
+ send(payload1);
7
+ }
8
+
5
9
  function fusion_Send(payload1, payload2){
6
10
  const info = fusion_getCallerInfo();
7
- send({
8
- payload: payload1,
9
- location: info
10
- }, payload2);
11
+
12
+ const message = {
13
+ payload: payload1,
14
+ location: info
15
+ };
16
+
17
+ // if payload1 is objet and has "type"
18
+ if (payload1 && typeof payload1 === 'object' && 'type' in payload1) {
19
+ message.type = payload1.type;
20
+ }
21
+
22
+ send(message, payload2);
11
23
  }
12
24
 
13
25
  function fusion_waitForClass(name, onReady) {
@@ -80,6 +92,12 @@ function fusion_bytesToBase64(byteArray){
80
92
  }
81
93
  }
82
94
 
95
+ function fusion_normalizePtr(addr) {
96
+ let p = ptr(addr);
97
+ if (Process.arch === 'arm64') p = p.and('0x00FFFFFFFFFFFFFF'); // limpa TBI
98
+ return p;
99
+ }
100
+
83
101
  function fusion_getCallerInfo() {
84
102
  try{
85
103
  const stack = new Error().stack.split("\n");
@@ -244,6 +262,38 @@ function fusion_getClassName(obj)
244
262
 
245
263
  }
246
264
 
265
+ function fusion_getReadableRange(p) {
266
+ try { p = ptr(p); } catch (_) { return null; }
267
+ const range = Process.findRangeByAddress(p); // não lança exceção
268
+ if (!range) return null;
269
+ // range.protection exemplo: 'r-x', 'rw-'
270
+ return range.protection.indexOf('r') !== -1 ? range : null;
271
+ }
272
+
273
+ function fusion_isAddressReadable(p) {
274
+ const r = fusion_getReadableRange(p);
275
+ if (!r) return false;
276
+ // tenta ler 1 byte para confirmar acessibilidade
277
+ try { Memory.readU8(ptr(p)); return true; }
278
+ catch (_) { return false; }
279
+ }
280
+
281
+ function fusion_describeAddress(p) {
282
+ try { p = ptr(p); } catch (_) { return { ok:false, reason:'not a pointer' }; }
283
+ if (Process.arch === 'arm64') p = p.and('0x00FFFFFFFFFFFFFF'); // remove top byte
284
+ if (!fusion_isAddressReadable(p)) return { ok:false, reason:'invalid pointer' };
285
+ const range = Process.findRangeByAddress(p);
286
+ if (!range) return { ok:false, reason:'unmapped' };
287
+ return {
288
+ ok: true,
289
+ base: range.base,
290
+ size: range.size,
291
+ protection: range.protection,
292
+ file: range.file ? range.file.path : null
293
+ };
294
+ }
295
+
296
+
247
297
  Java.perform(function () {
248
298
  const Thread = Java.use('java.lang.Thread');
249
299
  const UEH = Java.registerClass({
@@ -1,6 +1,8 @@
1
1
  import os
2
2
  import sys
3
3
  import re
4
+ from argparse import _ArgumentGroup, Namespace
5
+
4
6
  import frida
5
7
  import pkgutil
6
8
  import importlib
@@ -35,12 +37,21 @@ class ModuleBase(object):
35
37
  def start_module(self, **kwargs) -> bool:
36
38
  raise Exception('Method "start_module" is not yet implemented.')
37
39
 
40
+ def load_from_arguments(self, args: Namespace) -> bool:
41
+ return True
42
+
38
43
  def js_files(self) -> list:
39
44
  return []
40
45
 
46
+ def dynamic_script(self) -> str:
47
+ return ""
48
+
41
49
  def suppress_messages(self):
42
50
  pass
43
51
 
52
+ def add_params(self, flags: _ArgumentGroup):
53
+ pass
54
+
44
55
  def key_value_event(self,
45
56
  script_location: ScriptLocation = None,
46
57
  stack_trace: str = None,
@@ -125,6 +136,9 @@ class ExternalModule(Module):
125
136
 
126
137
 
127
138
  class ModuleManager:
139
+ initialized = False # Flag indicating modules has been initialized
140
+ modules: dict[str, Module] = {}
141
+
128
142
  @classmethod
129
143
  def _safe_import_from_path(cls, path: Path, loaded_files: set):
130
144
  """
@@ -188,9 +202,12 @@ class ModuleManager:
188
202
 
189
203
  @classmethod
190
204
  def list_modules(cls) -> dict:
205
+ if ModuleManager.initialized:
206
+ return ModuleManager.modules
207
+
191
208
  try:
192
209
  base_module = Module.get_base_module()
193
- modules: dict[str, Module] = {}
210
+ ModuleManager.modules = {}
194
211
 
195
212
  # --- 1) Varredura padrão do seu pacote interno: <este_arquivo>/modules ---
196
213
  base_path = Path(__file__).resolve().parent / "modules"
@@ -242,13 +259,13 @@ class ModuleManager:
242
259
  for i_class in ModuleBase.__subclasses__():
243
260
  t = i_class()
244
261
  key = t.safe_name()
245
- if key in modules:
262
+ if key in ModuleManager.modules:
246
263
  raise ModuleLoaderError(
247
264
  f"Duplicated Module name: {i_class.__module__}.{i_class.__qualname__}"
248
265
  )
249
266
 
250
267
  if str(i_class.__module__) in internal_mods:
251
- modules[key] = InternalModule(
268
+ ModuleManager.modules[key] = InternalModule(
252
269
  name=t.name,
253
270
  description=t.description,
254
271
  module=str(i_class.__module__),
@@ -256,7 +273,7 @@ class ModuleManager:
256
273
  class_name=i_class,
257
274
  )
258
275
  else:
259
- modules[key] = ExternalModule(
276
+ ModuleManager.modules[key] = ExternalModule(
260
277
  name=t.name,
261
278
  description=t.description,
262
279
  module=str(i_class.__module__),
@@ -264,7 +281,8 @@ class ModuleManager:
264
281
  class_name=i_class,
265
282
  )
266
283
 
267
- return modules
284
+ ModuleManager.initialized = True
285
+ return ModuleManager.modules
268
286
 
269
287
  except Exception as e:
270
288
  # Envolve a exceção original para manter contexto
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frida-fusion
3
- Version: 0.1.12
3
+ Version: 0.1.14
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>
@@ -7,6 +7,7 @@ frida_fusion/__main__.py
7
7
  frida_fusion/__meta__.py
8
8
  frida_fusion/args.py
9
9
  frida_fusion/config.py
10
+ frida_fusion/exceptions.py
10
11
  frida_fusion/fusion.py
11
12
  frida_fusion/module.py
12
13
  frida_fusion.egg-info/PKG-INFO
File without changes
File without changes
File without changes
File without changes