frida-fusion 0.1.4__tar.gz → 0.1.5__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 (30) hide show
  1. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/PKG-INFO +58 -1
  2. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/README.md +57 -0
  3. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/__meta__.py +1 -1
  4. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/config.py +21 -9
  5. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/fusion.py +11 -8
  6. frida-fusion-0.1.5/frida_fusion/module.py +272 -0
  7. frida-fusion-0.1.5/frida_fusion/modules/crypto/__init__.py +0 -0
  8. {frida-fusion-0.1.4/frida_fusion/modules → frida-fusion-0.1.5/frida_fusion/modules/crypto}/crypto.py +9 -6
  9. frida-fusion-0.1.5/frida_fusion/modules/tls_unpinning/__init__.py +0 -0
  10. frida-fusion-0.1.5/frida_fusion/modules/tls_unpinning/frida_multiple_unpinning.py +66 -0
  11. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion.egg-info/PKG-INFO +58 -1
  12. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion.egg-info/SOURCES.txt +5 -2
  13. frida-fusion-0.1.4/frida_fusion/module.py +0 -116
  14. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/__init__.py +0 -0
  15. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/__main__.py +0 -0
  16. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/args.py +0 -0
  17. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/libs/__init__.py +0 -0
  18. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/libs/color.py +0 -0
  19. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/libs/database.py +0 -0
  20. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/libs/helpers.js +0 -0
  21. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/libs/logger.py +0 -0
  22. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion/modules/__init__.py +0 -0
  23. {frida-fusion-0.1.4/frida_fusion/modules → frida-fusion-0.1.5/frida_fusion/modules/crypto}/crypto.js +0 -0
  24. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion.egg-info/dependency_links.txt +0 -0
  25. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion.egg-info/entry_points.txt +0 -0
  26. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion.egg-info/requires.txt +0 -0
  27. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/frida_fusion.egg-info/top_level.txt +0 -0
  28. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/pyproject.toml +0 -0
  29. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/setup.cfg +0 -0
  30. {frida-fusion-0.1.4 → frida-fusion-0.1.5}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: frida-fusion
3
- Version: 0.1.4
3
+ Version: 0.1.5
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>
@@ -68,4 +68,61 @@ Modules:
68
68
  pip3 install frida-fusion
69
69
  ```
70
70
 
71
+ ## Module engine
71
72
 
73
+ You can check available modules with `frida-fusion --list-modules` command.
74
+
75
+ ```bash
76
+ frida-fusion --list-modules
77
+
78
+ [ FRIDA ]—o—( FUSION )—o—[ MOBILE TESTS ] // v0.1.4
79
+ > hook your mobile tests with Frida
80
+
81
+
82
+ Available modules
83
+ Module Name : Description
84
+ Crypto : Hook cryptography/hashing functions
85
+ ```
86
+
87
+ ### External modules
88
+
89
+ You can develop or download community modules and load into frida-fusion.
90
+
91
+ To pass to the Frida Fusion the external module path you can use the environment variable `FUSION_MODULES` with the full path of modules
92
+
93
+ At linux:
94
+
95
+ ```bash
96
+ export FUSION_MODULES=/tmp/modules
97
+
98
+ # List all modules
99
+ frida-fusion --list-modules
100
+
101
+ # Using available module
102
+ frida-fusion -f [app_id] -U --script-path . -m [module_name]
103
+ ```
104
+
105
+ At windows:
106
+
107
+ ```bash
108
+ $env:FUSION_MODULES = "C:\extra_mods"
109
+
110
+ # List all modules
111
+ frida-fusion --list-modules
112
+
113
+ # Using available module
114
+ frida-fusion -f [app_id] -U --script-path . -m [module_name]
115
+ ```
116
+
117
+ ### Community modules
118
+
119
+ You can also use one of community developed modules
120
+
121
+ ```bash
122
+ cd /tmp/
123
+ git clone https://github.com/helviojunior/frida-fusion-community-modules
124
+ export FUSION_MODULES=/tmp/frida-fusion-community-modules
125
+
126
+ # List all modules
127
+ frida-fusion --list-modules
128
+ ```
@@ -38,4 +38,61 @@ Modules:
38
38
  pip3 install frida-fusion
39
39
  ```
40
40
 
41
+ ## Module engine
41
42
 
43
+ You can check available modules with `frida-fusion --list-modules` command.
44
+
45
+ ```bash
46
+ frida-fusion --list-modules
47
+
48
+ [ FRIDA ]—o—( FUSION )—o—[ MOBILE TESTS ] // v0.1.4
49
+ > hook your mobile tests with Frida
50
+
51
+
52
+ Available modules
53
+ Module Name : Description
54
+ Crypto : Hook cryptography/hashing functions
55
+ ```
56
+
57
+ ### External modules
58
+
59
+ You can develop or download community modules and load into frida-fusion.
60
+
61
+ To pass to the Frida Fusion the external module path you can use the environment variable `FUSION_MODULES` with the full path of modules
62
+
63
+ At linux:
64
+
65
+ ```bash
66
+ export FUSION_MODULES=/tmp/modules
67
+
68
+ # List all modules
69
+ frida-fusion --list-modules
70
+
71
+ # Using available module
72
+ frida-fusion -f [app_id] -U --script-path . -m [module_name]
73
+ ```
74
+
75
+ At windows:
76
+
77
+ ```bash
78
+ $env:FUSION_MODULES = "C:\extra_mods"
79
+
80
+ # List all modules
81
+ frida-fusion --list-modules
82
+
83
+ # Using available module
84
+ frida-fusion -f [app_id] -U --script-path . -m [module_name]
85
+ ```
86
+
87
+ ### Community modules
88
+
89
+ You can also use one of community developed modules
90
+
91
+ ```bash
92
+ cd /tmp/
93
+ git clone https://github.com/helviojunior/frida-fusion-community-modules
94
+ export FUSION_MODULES=/tmp/frida-fusion-community-modules
95
+
96
+ # List all modules
97
+ frida-fusion --list-modules
98
+ ```
@@ -1,4 +1,4 @@
1
- __version__ = '0.1.4'
1
+ __version__ = '0.1.5'
2
2
  __title__ = "Frida Fusion"
3
3
  __description__ = "📱 frida-fusion - runtime mobile exploration"
4
4
  __url__ = "https://github.com/helviojunior/frida-fusion"
@@ -6,7 +6,7 @@ import sys
6
6
  import signal
7
7
  from pathlib import Path
8
8
 
9
- from .module import Module
9
+ from .module import Module, ModuleManager, InternalModule, ExternalModule
10
10
  from .libs.color import Color
11
11
  from .libs.logger import Logger
12
12
  from .__meta__ import __version__
@@ -71,15 +71,27 @@ class Configuration(object):
71
71
  #show_help = any(['-h' == word for word in sys.argv])
72
72
 
73
73
  if list_modules:
74
- mods = Module.list_modules()
74
+ mods = ModuleManager.list_modules()
75
+
76
+ if len(mods) == 0:
77
+ Color.pl('{!} {R}error: no modules found{R}{W}\r\n')
78
+ sys.exit(1)
79
+
75
80
  max_name = max(iter([
76
81
  len(m.name) + 3
77
82
  for _, m in mods.items()
78
83
  ] + [15]))
79
- Color.pl(f"Available modules")
80
- Color.pl(f" {'Module Name'.ljust(max_name)} : Description")
81
- for _, m in mods.items():
82
- Color.pl(f" {m.name.ljust(max_name)} : {m.description}")
84
+ Color.pl(f"Available internal modules")
85
+ for m in [m for _, m in mods.items() if isinstance(m, InternalModule)]:
86
+ Color.pl(f" {m.safe_name().ljust(max_name)} : {m.description}")
87
+
88
+ Color.pl(f"\nAvailable external modules")
89
+ ext_mods = [m for _, m in mods.items() if isinstance(m, ExternalModule)]
90
+ if len(ext_mods) == 0:
91
+ Color.pl((" No external modules available. You can set {G}FUSION_MODULES{W} environment variable "
92
+ "to set an external modules Path"))
93
+ for m in ext_mods:
94
+ Color.pl(f" {m.safe_name().ljust(max_name)} : {m.description}")
83
95
 
84
96
  print("")
85
97
  sys.exit(0)
@@ -167,19 +179,19 @@ class Configuration(object):
167
179
  Logger.pl(' {C}min debug level:{O} %s{W}' % str(args.debug_level).upper())
168
180
 
169
181
  if args.enabled_modules is not None and isinstance(args.enabled_modules, list):
170
- mods = Module.list_modules()
182
+ mods = ModuleManager.list_modules()
171
183
  for mod in args.enabled_modules:
172
184
  fm = next(iter([
173
185
  m
174
186
  for _, m in mods.items()
175
- if m.name.lower() == mod.lower()
187
+ if m.safe_name() == mod.lower()
176
188
  ]), None)
177
189
  if fm is None:
178
190
  Color.pl(
179
191
  '{!} {R}error: module {O}%s{R} not found{W}\r\n' % mod)
180
192
  sys.exit(1)
181
193
 
182
- name = fm.name.lower()
194
+ name = fm.safe_name()
183
195
  if name not in Configuration.enabled_modules.keys():
184
196
  Configuration.enabled_modules[name] = fm
185
197
 
@@ -216,7 +216,7 @@ class Fusion(object):
216
216
  self.session = self.device.attach(self.pid)
217
217
  self.session.on("detached", self.on_detached)
218
218
 
219
- Logger.pl("{+} Iniciando scripts frida...")
219
+ Logger.pl("{+} Starting frida scripts")
220
220
  self.load_all_scripts()
221
221
  self.device.resume(self.pid)
222
222
 
@@ -227,7 +227,7 @@ class Fusion(object):
227
227
  self.session = self.device.attach(self.pid)
228
228
  self.session.on("detached", self.on_detached)
229
229
 
230
- Logger.pl("{+} Iniciando scripts frida...")
230
+ Logger.pl("{+} Starting frida scripts")
231
231
  self.load_all_scripts()
232
232
  self.device.resume(self.pid)
233
233
 
@@ -242,7 +242,7 @@ class Fusion(object):
242
242
  self.session = self.device.attach(self.pid)
243
243
  self.session.on("detached", self.on_detached)
244
244
 
245
- Logger.pl("{+} Iniciando scripts frida...")
245
+ Logger.pl("{+} Starting frida scripts")
246
246
  self.load_all_scripts()
247
247
 
248
248
  def make_handler(self, script_name):
@@ -631,16 +631,19 @@ class Fusion(object):
631
631
  Configuration.initialize()
632
632
 
633
633
  try:
634
+ print(f" 🛠️ Starting Frida Fusion instrumentation")
635
+ timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
636
+ Logger.pl('{+} {C}Start time {O}%s{W}' % timestamp)
637
+
634
638
  self._modules = [
635
639
  m.create_instance()
636
640
  for _, m in Configuration.enabled_modules.items()
637
641
  ]
638
- for m in self._modules:
639
- m.start_db(db_path=Configuration.db_path)
640
642
 
641
- print(f" 🛠️ Iniciando instrumentação Frida")
642
- timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
643
- Logger.pl('{+} {C}Start time {O}%s{W}' % timestamp)
643
+ if len(self._modules) > 0:
644
+ Logger.pl("{+} Starting selected modules")
645
+ for m in self._modules:
646
+ m.start_module(db_path=Configuration.db_path)
644
647
 
645
648
  self.get_device()
646
649
  if self.device is not None:
@@ -0,0 +1,272 @@
1
+ import os
2
+ import sys
3
+ import re
4
+ import frida
5
+ import pkgutil
6
+ import importlib
7
+ import requests
8
+ import importlib.util
9
+ from pathlib import Path
10
+
11
+ from typing import TYPE_CHECKING
12
+
13
+ from .__meta__ import __version__
14
+ from .libs.logger import Logger
15
+
16
+ if TYPE_CHECKING:
17
+ from .fusion import Fusion # só no type checker
18
+
19
+
20
+ class ModuleLoaderError(Exception):
21
+ pass
22
+
23
+
24
+ class ModuleBase(object):
25
+
26
+ name = ''
27
+ description = ''
28
+ mod_path = ''
29
+
30
+ def __init__(self, name, description):
31
+ self.name = name
32
+ self.description = description
33
+ self.mod_path = str(Path(__file__).resolve().parent)
34
+ pass
35
+
36
+ def safe_name(self):
37
+ return ModuleBase.get_safe_name(self.name)
38
+
39
+ def start_module(self, **kwargs) -> bool:
40
+ raise Exception('Method "start_module" is not yet implemented.')
41
+
42
+ def js_files(self) -> list:
43
+ return []
44
+
45
+ def key_value_event(self,
46
+ script_location: "Fusion.ScriptLocation" = None,
47
+ stack_trace: str = None,
48
+ module: str = None,
49
+ received_data: dict = None
50
+ ) -> bool:
51
+ raise Exception('Method "key_value_event" is not yet implemented.')
52
+
53
+ def data_event(self,
54
+ script_location: "Fusion.ScriptLocation" = None,
55
+ stack_trace: str = None,
56
+ received_data: str = None
57
+ ) -> bool:
58
+ raise Exception('Method "data_event" is not yet implemented.')
59
+
60
+ @staticmethod
61
+ def get_safe_name(name):
62
+ name = name.replace(" ", "_").lower()
63
+ return re.sub(r'[^a-zA-Z0-9_.-]+', '', name)
64
+
65
+ @classmethod
66
+ def _get_codeshare(cls, uri: str) -> dict:
67
+
68
+ if uri is None or len(uri) <= 10:
69
+ raise Exception("Invalid codeshare uri. Uri must be only user/project_name.")
70
+
71
+ uri = uri.strip(" /@.")
72
+
73
+ headers = {
74
+ "Accept": "application/vnd.github+json",
75
+ "User-Agent": f"Frida-fusion v{__version__}, Frida v{frida.__version__}"
76
+ }
77
+
78
+ try:
79
+ resp = requests.get(f"https://codeshare.frida.re/api/project/{uri}", headers=headers, timeout=30)
80
+ resp.raise_for_status()
81
+ data = resp.json()
82
+ if data is None:
83
+ raise Exception("data is empty")
84
+
85
+ if data.get('source', None) is None or data.get('source', '').strip(" \r\n") == "":
86
+ raise Exception("source code is empty")
87
+
88
+ return data
89
+
90
+ except Exception as e:
91
+ raise ModuleLoaderError("Error getting codeshare data") from e
92
+
93
+
94
+ class Module(object):
95
+ modules = {}
96
+
97
+ def __init__(self, name, description, module, qualname, class_name):
98
+ self.name = name
99
+ self.description = description
100
+ self.module = module
101
+ self.qualname = qualname
102
+ self._class = class_name
103
+ pass
104
+
105
+ def safe_name(self):
106
+ return ModuleBase.get_safe_name(self.name)
107
+
108
+ def create_instance(self):
109
+ return self._class()
110
+
111
+ @classmethod
112
+ def get_base_module(cls) -> str:
113
+ file = Path(__file__).stem
114
+
115
+ parent_module = f'.{cls.__module__}.'.replace(f'.{file}.', '').strip(' .')
116
+
117
+ return '.'.join((parent_module, 'modules'))
118
+
119
+
120
+ class InternalModule(Module):
121
+ pass
122
+
123
+
124
+ class ExternalModule(Module):
125
+ pass
126
+
127
+
128
+ class ModuleManager:
129
+ @classmethod
130
+ def _safe_import_from_path(cls, path: Path, loaded_files: set):
131
+ """
132
+ Importa um .py arbitrário usando um nome único e registra o arquivo
133
+ para não ser importado duas vezes.
134
+ """
135
+ real = path.resolve()
136
+
137
+ try:
138
+ if real in loaded_files:
139
+ return
140
+
141
+ # nome único, estável, baseado no caminho
142
+ pseudo_name = (
143
+ "fusion_ext_"
144
+ + "_".join(real.parts).replace(":", "_").replace("\\", "_").replace("/", "_")
145
+ .replace(".", "_")
146
+ )
147
+ spec = importlib.util.spec_from_file_location(pseudo_name, real)
148
+ if spec and spec.loader:
149
+ mod = importlib.util.module_from_spec(spec)
150
+ sys.modules[pseudo_name] = mod
151
+ spec.loader.exec_module(mod)
152
+ loaded_files.add(real)
153
+ except Exception as ie:
154
+ Logger.pl('\n{!} {R}Error loading external module: {G}%s{R}\n {O} %s{W}' % (str(ie), str(real)))
155
+ pass
156
+
157
+ @classmethod
158
+ def _import_via_pkgutil(cls, roots: list[Path], loaded_files: set):
159
+ """
160
+ Varre roots com pkgutil.walk_packages. Isso encontra
161
+ - módulos .py no nível do root
162
+ - pacotes (pastas com __init__.py) e seus submódulos
163
+ NÃO entra em subpastas sem __init__.py (por isso depois complementamos).
164
+ """
165
+ str_roots = [str(p) for p in roots]
166
+ for loader, modname, is_pkg in pkgutil.walk_packages(str_roots):
167
+ try:
168
+ mod = importlib.import_module(modname)
169
+ mfile = getattr(mod, "__file__", None)
170
+ if mfile:
171
+ loaded_files.add(Path(mfile).resolve())
172
+ except Exception as ie:
173
+ Logger.pl(
174
+ '\n{!} {R}Error loading internal module: {G}%s{R}\n {O} %s{W}' % (
175
+ str(ie), str(loader.path)))
176
+ pass
177
+
178
+ @classmethod
179
+ def _load_any_py_recursively(cls, root: Path, loaded_files: set):
180
+ """
181
+ Carrega *todo* arquivo .py sob root (rglob), incluindo subpastas sem __init__.py,
182
+ sem duplicar o que já foi importado.
183
+ """
184
+ for py in root.rglob("*.py"):
185
+ # exclui caches e similares
186
+ if any(part in {"__pycache__"} for part in py.parts):
187
+ continue
188
+ cls._safe_import_from_path(py, loaded_files)
189
+
190
+ @classmethod
191
+ def list_modules(cls) -> dict:
192
+ try:
193
+ base_module = Module.get_base_module()
194
+ modules: dict[str, Module] = {}
195
+
196
+ # --- 1) Varredura padrão do seu pacote interno: <este_arquivo>/modules ---
197
+ base_path = Path(__file__).resolve().parent / "modules"
198
+ internal_mod_roots = [p for p in base_path.iterdir() if p.is_dir()]
199
+
200
+ internal_mods = []
201
+
202
+ # Vamos usar pkgutil para o pacote interno (mantém o comportamento)
203
+ loaded_files: set[Path] = set()
204
+ mods = [str(p) for p in internal_mod_roots]
205
+ for loader, modname, is_pkg in pkgutil.walk_packages(mods):
206
+ if not is_pkg:
207
+ # Reconstrói o caminho relativo para montar o import dentro do pacote base
208
+ mod_path = Path(getattr(loader, "path", ""))
209
+ try:
210
+ rel = mod_path.resolve().relative_to(base_path.resolve())
211
+ dotted = "." + ".".join(rel.parts) if rel.parts else ""
212
+ except Exception:
213
+ dotted = ""
214
+ importlib.import_module(f"{base_module}{dotted}.{modname}")
215
+ internal_mods.append(f"{base_module}{dotted}.{modname}")
216
+
217
+ # --- 2) Varredura de caminhos externos via FUSION_MODULES ---
218
+ env_value = os.environ.get("FUSION_MODULES", "").strip()
219
+ if env_value:
220
+ extra_roots = [Path(p).expanduser() for p in env_value.split(os.pathsep) if p.strip()]
221
+ existing_roots = [p for p in extra_roots if p.exists() and p.is_dir()]
222
+
223
+ # Para que pkgutil encontre módulos top-level nesses roots
224
+ # (sem precisar de nomes de pacote), colocamos cada root no sys.path
225
+ # durante a varredura. Usamos um conjunto para restaurar depois se preferir.
226
+ original_sys_path = list(sys.path)
227
+ try:
228
+ for root in existing_roots:
229
+ if str(root) not in sys.path:
230
+ sys.path.insert(0, str(root))
231
+
232
+ # 2a) Encontrar módulos top-level e pacotes (com __init__.py)
233
+ cls._import_via_pkgutil(existing_roots, loaded_files)
234
+
235
+ # 2b) Complementar: carregar QUALQUER .py (inclusive subpastas sem __init__.py)
236
+ for root in existing_roots:
237
+ cls._load_any_py_recursively(root, loaded_files)
238
+ finally:
239
+ # opcional: restaurar sys.path (seguro para evitar vazamentos)
240
+ sys.path[:] = original_sys_path
241
+
242
+ # --- 3) Instanciar subclasses de ModuleBase e montar o registry ---
243
+ for i_class in ModuleBase.__subclasses__():
244
+ t = i_class()
245
+ key = t.safe_name()
246
+ if key in modules:
247
+ raise ModuleLoaderError(
248
+ f"Duplicated Module name: {i_class.__module__}.{i_class.__qualname__}"
249
+ )
250
+
251
+ if str(i_class.__module__) in internal_mods:
252
+ modules[key] = InternalModule(
253
+ name=t.name,
254
+ description=t.description,
255
+ module=str(i_class.__module__),
256
+ qualname=str(i_class.__qualname__),
257
+ class_name=i_class,
258
+ )
259
+ else:
260
+ modules[key] = ExternalModule(
261
+ name=t.name,
262
+ description=t.description,
263
+ module=str(i_class.__module__),
264
+ qualname=str(i_class.__qualname__),
265
+ class_name=i_class,
266
+ )
267
+
268
+ return modules
269
+
270
+ except Exception as e:
271
+ # Envolve a exceção original para manter contexto
272
+ raise ModuleLoaderError("Error listing modules") from e
@@ -3,13 +3,13 @@ from pathlib import Path
3
3
  import base64
4
4
  import string
5
5
 
6
- from ..libs.color import Color
7
- from ..libs.database import Database
8
- from ..module import ModuleBase
6
+ from frida_fusion.libs.logger import Logger
7
+ from frida_fusion.libs.database import Database
8
+ from frida_fusion.module import ModuleBase
9
9
 
10
10
  from typing import TYPE_CHECKING
11
11
  if TYPE_CHECKING:
12
- from ..fusion import Fusion # só no type checker
12
+ from frida_fusion.fusion import Fusion # só no type checker
13
13
 
14
14
 
15
15
  class Crypto(ModuleBase):
@@ -274,8 +274,11 @@ class Crypto(ModuleBase):
274
274
  self._crypto_db = None
275
275
  self.mod_path = str(Path(__file__).resolve().parent)
276
276
 
277
- def start_db(self, db_path: str) -> bool:
278
- self._crypto_db = Crypto.CryptoDB(db_name=db_path)
277
+ def start_module(self, **kwargs) -> bool:
278
+ if 'db_path' not in kwargs:
279
+ raise Exception("parameter db_path not found")
280
+
281
+ self._crypto_db = Crypto.CryptoDB(db_name=kwargs['db_path'])
279
282
  return True
280
283
 
281
284
  def js_files(self) -> list:
@@ -0,0 +1,66 @@
1
+ import errno
2
+ import os.path
3
+ import tempfile
4
+ from pathlib import Path
5
+ from frida_fusion.libs.logger import Logger
6
+ from frida_fusion.module import ModuleBase
7
+
8
+ from typing import TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING:
11
+ from frida_fusion.fusion import Fusion # só no type checker
12
+
13
+
14
+ class TlsUnpinning(ModuleBase):
15
+
16
+ def __init__(self):
17
+ super().__init__('Multiple unpinning', 'Use frida_multiple_unpinning by Maurizio Siddu (@akabe1)')
18
+ self.mod_path = str(Path(__file__).resolve().parent)
19
+ self.js_file = os.path.join(self.mod_path, "frida_multiple_unpinning.js")
20
+
21
+ def start_module(self, **kwargs) -> bool:
22
+ if not os.path.isfile(self.js_file):
23
+ Logger.pl("{+} Downloading CodeShare script from @akabe1/frida-multiple-unpinning")
24
+ data = self._get_codeshare("@akabe1/frida-multiple-unpinning/")
25
+ if data.get('source', None) is None or data.get('source', '').strip(" \r\n") == "":
26
+ raise Exception("source code is empty")
27
+
28
+ try:
29
+ with open(self.js_file, "w", encoding='utf-8') as f:
30
+ f.write(data.get('source', ''))
31
+ except IOError as x:
32
+ if x.errno == errno.EACCES:
33
+ Logger.pl('{!} {R}error: could not open output file to write {O}permission denied{R}{W}\r\n')
34
+ elif x.errno == errno.EISDIR:
35
+ Logger.pl('{!} {R}error: could not open output file to write {O}it is an directory{R}{W}\r\n')
36
+ else:
37
+ Logger.pl('{!} {R}error: could not open output file to write{W}\r\n')
38
+
39
+ # Try to save locally
40
+ self.js_file = str(Path("frida_multiple_unpinning.js").resolve().absolute())
41
+ with open(self.js_file, "w", encoding='utf-8') as f:
42
+ f.write(data.get('source', ''))
43
+
44
+ return True
45
+
46
+ def js_files(self) -> list:
47
+ return [
48
+ self.js_file
49
+ ]
50
+
51
+ def key_value_event(self,
52
+ script_location: "Fusion.ScriptLocation" = None,
53
+ stack_trace: str = None,
54
+ module: str = None,
55
+ received_data: dict = None
56
+ ) -> bool:
57
+ return True
58
+
59
+ def data_event(self,
60
+ script_location: "Fusion.ScriptLocation" = None,
61
+ stack_trace: str = None,
62
+ received_data: str = None
63
+ ) -> bool:
64
+ return True
65
+
66
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: frida-fusion
3
- Version: 0.1.4
3
+ Version: 0.1.5
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>
@@ -68,4 +68,61 @@ Modules:
68
68
  pip3 install frida-fusion
69
69
  ```
70
70
 
71
+ ## Module engine
71
72
 
73
+ You can check available modules with `frida-fusion --list-modules` command.
74
+
75
+ ```bash
76
+ frida-fusion --list-modules
77
+
78
+ [ FRIDA ]—o—( FUSION )—o—[ MOBILE TESTS ] // v0.1.4
79
+ > hook your mobile tests with Frida
80
+
81
+
82
+ Available modules
83
+ Module Name : Description
84
+ Crypto : Hook cryptography/hashing functions
85
+ ```
86
+
87
+ ### External modules
88
+
89
+ You can develop or download community modules and load into frida-fusion.
90
+
91
+ To pass to the Frida Fusion the external module path you can use the environment variable `FUSION_MODULES` with the full path of modules
92
+
93
+ At linux:
94
+
95
+ ```bash
96
+ export FUSION_MODULES=/tmp/modules
97
+
98
+ # List all modules
99
+ frida-fusion --list-modules
100
+
101
+ # Using available module
102
+ frida-fusion -f [app_id] -U --script-path . -m [module_name]
103
+ ```
104
+
105
+ At windows:
106
+
107
+ ```bash
108
+ $env:FUSION_MODULES = "C:\extra_mods"
109
+
110
+ # List all modules
111
+ frida-fusion --list-modules
112
+
113
+ # Using available module
114
+ frida-fusion -f [app_id] -U --script-path . -m [module_name]
115
+ ```
116
+
117
+ ### Community modules
118
+
119
+ You can also use one of community developed modules
120
+
121
+ ```bash
122
+ cd /tmp/
123
+ git clone https://github.com/helviojunior/frida-fusion-community-modules
124
+ export FUSION_MODULES=/tmp/frida-fusion-community-modules
125
+
126
+ # List all modules
127
+ frida-fusion --list-modules
128
+ ```
@@ -20,5 +20,8 @@ frida_fusion/libs/database.py
20
20
  frida_fusion/libs/helpers.js
21
21
  frida_fusion/libs/logger.py
22
22
  frida_fusion/modules/__init__.py
23
- frida_fusion/modules/crypto.js
24
- frida_fusion/modules/crypto.py
23
+ frida_fusion/modules/crypto/__init__.py
24
+ frida_fusion/modules/crypto/crypto.js
25
+ frida_fusion/modules/crypto/crypto.py
26
+ frida_fusion/modules/tls_unpinning/__init__.py
27
+ frida_fusion/modules/tls_unpinning/frida_multiple_unpinning.py
@@ -1,116 +0,0 @@
1
- import os
2
- import pkgutil
3
- import importlib
4
- from pathlib import Path
5
-
6
- from .libs.database import Database
7
- from typing import TYPE_CHECKING
8
- if TYPE_CHECKING:
9
- from .fusion import Fusion # só no type checker
10
-
11
-
12
- class ModuleBase(object):
13
-
14
- name = ''
15
- description = ''
16
- mod_path = ''
17
-
18
- def __init__(self, name, description):
19
- self.name = name
20
- self.description = description
21
- self.mod_path = str(Path(__file__).resolve().parent)
22
- pass
23
-
24
- def start_db(self, db_path: str) -> bool:
25
- raise Exception('Method "start_db" is not yet implemented.')
26
-
27
- def js_files(self) -> list:
28
- return []
29
-
30
- def key_value_event(self,
31
- script_location: "Fusion.ScriptLocation" = None,
32
- stack_trace: str = None,
33
- module: str = None,
34
- received_data: dict = None
35
- ) -> bool:
36
- raise Exception('Method "key_value_event" is not yet implemented.')
37
-
38
- def data_event(self,
39
- script_location: "Fusion.ScriptLocation" = None,
40
- stack_trace: str = None,
41
- received_data: str = None
42
- ) -> bool:
43
- raise Exception('Method "data_event" is not yet implemented.')
44
-
45
-
46
- class Module(object):
47
- modules = {}
48
-
49
- def __init__(self, name, description, module, qualname, class_name):
50
- self.name = name
51
- self.description = description
52
- self.module = module
53
- self.qualname = qualname
54
- self._class = class_name
55
- pass
56
-
57
- def create_instance(self):
58
- return self._class()
59
-
60
- @classmethod
61
- def get_instance(cls, name: str):
62
- if len(Module.modules) == 0:
63
- Module.modules = Module.list_modules()
64
-
65
- selected_modules = [
66
- mod for mod in Module.modules
67
- if mod == name
68
- ]
69
-
70
- mod = None
71
- if len(selected_modules) == 1:
72
- mod = Module.modules[selected_modules[0]].create_instance()
73
-
74
- return mod
75
-
76
- @classmethod
77
- def get_base_module(cls) -> str:
78
- file = Path(__file__).stem
79
-
80
- parent_module = f'.{cls.__module__}.'.replace(f'.{file}.', '').strip(' .')
81
-
82
- return '.'.join((parent_module, 'modules'))
83
-
84
- @classmethod
85
- def list_modules(cls) -> dict:
86
- try:
87
-
88
- base_module = Module.get_base_module()
89
-
90
- modules = {}
91
-
92
- base_path = os.path.join(
93
- Path(__file__).resolve().parent, 'modules'
94
- )
95
-
96
- for loader, modname, ispkg in pkgutil.walk_packages([base_path]):
97
- if not ispkg:
98
- importlib.import_module(f'{base_module}.{modname}')
99
-
100
- for iclass in ModuleBase.__subclasses__():
101
- t = iclass()
102
- if t.name in modules:
103
- raise Exception(f'Duplicated Module name: {iclass.__module__}.{iclass.__qualname__}')
104
-
105
- modules[t.name.lower()] = Module(
106
- name=t.name,
107
- description=t.description,
108
- module=str(iclass.__module__),
109
- qualname=str(iclass.__qualname__),
110
- class_name=iclass
111
- )
112
-
113
- return modules
114
-
115
- except Exception as e:
116
- raise Exception('Error listing command modules', e)
File without changes
File without changes