AndroidFridaManager 1.9.2__tar.gz → 1.9.4__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.
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager/FridaManager.py +14 -1
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager/about.py +1 -1
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager/job.py +73 -3
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager/job_manager.py +305 -18
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/PKG-INFO +2 -2
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/PKG-INFO +2 -2
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/README.md +1 -1
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager/__init__.py +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/SOURCES.txt +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/dependency_links.txt +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/entry_points.txt +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/requires.txt +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/top_level.txt +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/LICENSE +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/MANIFEST.in +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/pyproject.toml +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/requirements.txt +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/setup.cfg +0 -0
- {androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/setup.py +0 -0
|
@@ -274,7 +274,9 @@ class FridaManager():
|
|
|
274
274
|
:return: Complete command list including device targeting
|
|
275
275
|
"""
|
|
276
276
|
cmd = ['adb']
|
|
277
|
-
if
|
|
277
|
+
# Always include device serial if set - important when user explicitly
|
|
278
|
+
# specifies a device or when multiple devices are connected
|
|
279
|
+
if self._device_serial:
|
|
278
280
|
cmd.extend(['-s', self._device_serial])
|
|
279
281
|
cmd.extend(args)
|
|
280
282
|
return cmd
|
|
@@ -441,13 +443,24 @@ class FridaManager():
|
|
|
441
443
|
:type dst_dir: string
|
|
442
444
|
:param version: The version. By default the latest version will be used.
|
|
443
445
|
:type version: string
|
|
446
|
+
:raises RuntimeError: If device is not rooted
|
|
444
447
|
|
|
445
448
|
"""
|
|
449
|
+
# Check root access BEFORE downloading - fail fast to save time
|
|
450
|
+
if not self.is_device_rooted():
|
|
451
|
+
self.logger.error(
|
|
452
|
+
"Device is not rooted. Frida server installation requires root access. "
|
|
453
|
+
"Please root the device or use an emulator."
|
|
454
|
+
)
|
|
455
|
+
raise RuntimeError("Cannot install frida-server: device not rooted")
|
|
456
|
+
|
|
446
457
|
if dst_dir is self.install_frida_server.__defaults__[0]:
|
|
447
458
|
frida_dir = self.frida_install_dst
|
|
448
459
|
else:
|
|
449
460
|
frida_dir = dst_dir
|
|
450
461
|
|
|
462
|
+
self.logger.info("Installing frida-server now...")
|
|
463
|
+
|
|
451
464
|
with tempfile.TemporaryDirectory() as dir:
|
|
452
465
|
if self.verbose:
|
|
453
466
|
self.logger.info(f"[*] downloading frida-server to {dir}")
|
|
@@ -1,18 +1,62 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Frida Job management for AndroidFridaManager.
|
|
3
4
|
|
|
5
|
+
This module provides the Job class for managing individual Frida instrumentation
|
|
6
|
+
jobs, including script loading, message handling, and lifecycle management.
|
|
7
|
+
"""
|
|
4
8
|
|
|
5
9
|
import threading
|
|
6
10
|
import frida
|
|
7
11
|
import uuid
|
|
8
12
|
import logging
|
|
13
|
+
import datetime
|
|
14
|
+
from typing import Optional, List, Callable, Any
|
|
15
|
+
|
|
9
16
|
|
|
10
17
|
# Define a custom exception for handling frida based exceptions
|
|
11
18
|
class FridaBasedException(Exception):
|
|
12
19
|
pass
|
|
13
20
|
|
|
21
|
+
|
|
14
22
|
class Job:
|
|
15
|
-
|
|
23
|
+
"""Represents a single Frida instrumentation job.
|
|
24
|
+
|
|
25
|
+
A job encapsulates a Frida script, its message handler, and lifecycle
|
|
26
|
+
management. Jobs run in separate threads and can be started/stopped
|
|
27
|
+
independently.
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
job_id: Unique identifier for this job.
|
|
31
|
+
job_type: Category of job (e.g., "fritap", "dexray", "trigdroid", "custom").
|
|
32
|
+
display_name: Human-readable name for UI display.
|
|
33
|
+
hooks_registry: List of methods/functions this job hooks (for conflict detection).
|
|
34
|
+
priority: Job priority (lower = higher priority, default 50).
|
|
35
|
+
state: Current state ("initialized", "running", "stopping").
|
|
36
|
+
started_at: Timestamp when job was started (None if not started).
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
frida_script_name: str,
|
|
42
|
+
custom_hooking_handler: Callable,
|
|
43
|
+
process: Any,
|
|
44
|
+
job_type: str = "custom",
|
|
45
|
+
display_name: Optional[str] = None,
|
|
46
|
+
hooks_registry: Optional[List[str]] = None,
|
|
47
|
+
priority: int = 50,
|
|
48
|
+
):
|
|
49
|
+
"""Initialize a new Job.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
frida_script_name: Path to the Frida script file.
|
|
53
|
+
custom_hooking_handler: Callback function for handling Frida messages.
|
|
54
|
+
process: Frida session/process to attach the script to.
|
|
55
|
+
job_type: Category of job for coordination (default: "custom").
|
|
56
|
+
display_name: Human-readable name (default: script filename).
|
|
57
|
+
hooks_registry: List of hooked methods for conflict detection.
|
|
58
|
+
priority: Job priority, lower = higher (default: 50).
|
|
59
|
+
"""
|
|
16
60
|
self.frida_script_name = frida_script_name
|
|
17
61
|
self.job_id = str(uuid.uuid4())
|
|
18
62
|
self.state = "initialized"
|
|
@@ -24,6 +68,13 @@ class Job:
|
|
|
24
68
|
self.is_script_created = False
|
|
25
69
|
self.logger = logging.getLogger(__name__)
|
|
26
70
|
|
|
71
|
+
# New metadata fields for job coordination
|
|
72
|
+
self.job_type = job_type
|
|
73
|
+
self.display_name = display_name or frida_script_name
|
|
74
|
+
self.hooks_registry = hooks_registry or []
|
|
75
|
+
self.priority = priority
|
|
76
|
+
self.started_at: Optional[datetime.datetime] = None
|
|
77
|
+
|
|
27
78
|
|
|
28
79
|
def create_job_script(self):
|
|
29
80
|
self.instrument(self.process_session)
|
|
@@ -31,7 +82,8 @@ class Job:
|
|
|
31
82
|
|
|
32
83
|
|
|
33
84
|
def run_job(self):
|
|
34
|
-
|
|
85
|
+
"""Start the job execution in a separate thread."""
|
|
86
|
+
self.started_at = datetime.datetime.now()
|
|
35
87
|
self.run_job_as_thread()
|
|
36
88
|
|
|
37
89
|
|
|
@@ -98,4 +150,22 @@ class Job:
|
|
|
98
150
|
|
|
99
151
|
|
|
100
152
|
def get_script_of_job(self):
|
|
101
|
-
return self.script
|
|
153
|
+
return self.script
|
|
154
|
+
|
|
155
|
+
def get_info(self) -> dict:
|
|
156
|
+
"""Get job information as a dictionary for UI display.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Dictionary containing job metadata and state information.
|
|
160
|
+
"""
|
|
161
|
+
return {
|
|
162
|
+
"job_id": self.job_id,
|
|
163
|
+
"job_type": self.job_type,
|
|
164
|
+
"display_name": self.display_name,
|
|
165
|
+
"state": self.state,
|
|
166
|
+
"priority": self.priority,
|
|
167
|
+
"hooks_count": len(self.hooks_registry),
|
|
168
|
+
"hooks_registry": self.hooks_registry.copy(),
|
|
169
|
+
"started_at": self.started_at.isoformat() if self.started_at else None,
|
|
170
|
+
"script_name": self.frida_script_name,
|
|
171
|
+
}
|
|
@@ -1,30 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Frida JobManager for coordinating multiple instrumentation jobs.
|
|
4
|
+
|
|
5
|
+
This module provides the JobManager class for managing Frida sessions and
|
|
6
|
+
coordinating multiple instrumentation jobs on Android devices.
|
|
7
|
+
"""
|
|
3
8
|
|
|
4
9
|
import atexit
|
|
5
10
|
import subprocess
|
|
6
11
|
import frida
|
|
7
|
-
from typing import Optional, Dict, Union, List
|
|
12
|
+
from typing import Optional, Dict, Union, List, Callable
|
|
8
13
|
from .job import Job, FridaBasedException
|
|
9
14
|
import time
|
|
10
15
|
import re
|
|
11
16
|
import logging
|
|
12
17
|
|
|
18
|
+
|
|
13
19
|
class JobManager(object):
|
|
14
|
-
"""
|
|
20
|
+
"""Job manager with multi-device support and hook coordination.
|
|
21
|
+
|
|
22
|
+
Manages Frida sessions and coordinates multiple instrumentation jobs,
|
|
23
|
+
providing hook conflict detection and session status information.
|
|
15
24
|
|
|
25
|
+
Attributes:
|
|
26
|
+
jobs: Dictionary of active jobs by job_id.
|
|
27
|
+
process_session: Current Frida session.
|
|
28
|
+
device: Current Frida device.
|
|
29
|
+
package_name: Name of the attached/spawned package.
|
|
30
|
+
pid: Process ID (-1 if attach mode).
|
|
31
|
+
"""
|
|
16
32
|
|
|
17
33
|
def __init__(self, host="", enable_spawn_gating=False, device_serial: Optional[str] = None) -> None:
|
|
18
|
-
"""
|
|
19
|
-
Init a new job manager with optional device targeting.
|
|
34
|
+
"""Init a new job manager with optional device targeting.
|
|
20
35
|
|
|
21
|
-
|
|
22
|
-
:
|
|
23
|
-
:
|
|
24
|
-
|
|
36
|
+
Args:
|
|
37
|
+
host: Remote host for Frida connection (ip:port format).
|
|
38
|
+
enable_spawn_gating: Enable spawn gating for child process tracking.
|
|
39
|
+
device_serial: Specific device serial to target (e.g., 'emulator-5554').
|
|
40
|
+
If None, uses default device selection.
|
|
25
41
|
"""
|
|
26
|
-
|
|
27
|
-
self.jobs = {}
|
|
42
|
+
self.jobs: Dict[str, Job] = {}
|
|
28
43
|
self.is_first_job = True
|
|
29
44
|
self.process_session = None
|
|
30
45
|
self.host = host
|
|
@@ -42,6 +57,11 @@ class JobManager(object):
|
|
|
42
57
|
self._device_serial = device_serial
|
|
43
58
|
self._multiple_devices = self._check_multiple_devices()
|
|
44
59
|
|
|
60
|
+
# Hook coordination (NEW)
|
|
61
|
+
self._hook_registry: Dict[str, str] = {} # hook_target -> job_id
|
|
62
|
+
self._mode: Optional[str] = None # "spawn" or "attach"
|
|
63
|
+
self._paused: bool = False # Track if spawned process is paused
|
|
64
|
+
|
|
45
65
|
atexit.register(self.cleanup)
|
|
46
66
|
|
|
47
67
|
def _ensure_logging_setup(self):
|
|
@@ -134,13 +154,80 @@ class JobManager(object):
|
|
|
134
154
|
'''
|
|
135
155
|
|
|
136
156
|
def spawn(self, target_process):
|
|
157
|
+
"""Spawn a new process and attach to it.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
target_process: Package name to spawn.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Process ID of the spawned process.
|
|
164
|
+
"""
|
|
165
|
+
self._mode = "spawn"
|
|
137
166
|
self.package_name = target_process
|
|
138
167
|
self.logger.info("[*] spawning app: "+ target_process)
|
|
139
168
|
pid = self.device.spawn(target_process)
|
|
140
169
|
self.process_session = self.device.attach(pid)
|
|
170
|
+
self.pid = pid
|
|
171
|
+
self._paused = True # Spawned processes start paused
|
|
141
172
|
self.logger.info(f"Spawned {target_process} with PID {pid}")
|
|
142
173
|
return pid
|
|
143
174
|
|
|
175
|
+
def spawn_paused(self, target_process: str) -> int:
|
|
176
|
+
"""Spawn a process but keep it paused for multi-tool loading.
|
|
177
|
+
|
|
178
|
+
Unlike the regular spawn flow where resume happens on first job,
|
|
179
|
+
this method keeps the process paused until explicitly resumed
|
|
180
|
+
via resume_app(). This allows loading multiple Jobs before
|
|
181
|
+
the app starts executing.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
target_process: Package name to spawn.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Process ID of the spawned (paused) process.
|
|
188
|
+
"""
|
|
189
|
+
self._mode = "spawn"
|
|
190
|
+
self.package_name = target_process
|
|
191
|
+
self.logger.info(f"[*] spawning app (paused): {target_process}")
|
|
192
|
+
pid = self.device.spawn(target_process)
|
|
193
|
+
self.process_session = self.device.attach(pid)
|
|
194
|
+
self.pid = pid
|
|
195
|
+
self._paused = True
|
|
196
|
+
# Mark that auto-resume should NOT happen
|
|
197
|
+
self.is_first_job = False # Prevent auto-resume in start_job()
|
|
198
|
+
self.logger.info(f"Spawned {target_process} with PID {pid} (PAUSED - awaiting manual resume)")
|
|
199
|
+
return pid
|
|
200
|
+
|
|
201
|
+
def resume_app(self) -> bool:
|
|
202
|
+
"""Resume a paused spawned process.
|
|
203
|
+
|
|
204
|
+
Call this after loading all Jobs to start the app with
|
|
205
|
+
all hooks already installed.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
True if resumed successfully, False if not paused or failed.
|
|
209
|
+
"""
|
|
210
|
+
if not self._paused or self.pid == -1:
|
|
211
|
+
self.logger.warning("No paused process to resume")
|
|
212
|
+
return False
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
self.device.resume(self.pid)
|
|
216
|
+
self._paused = False
|
|
217
|
+
time.sleep(1) # Required for Java.perform stability
|
|
218
|
+
self.logger.info(f"Resumed process {self.pid}")
|
|
219
|
+
return True
|
|
220
|
+
except Exception as e:
|
|
221
|
+
self.logger.error(f"Failed to resume process: {e}")
|
|
222
|
+
return False
|
|
223
|
+
|
|
224
|
+
def is_paused(self) -> bool:
|
|
225
|
+
"""Check if the spawned process is currently paused.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
True if process is spawned and waiting for resume.
|
|
229
|
+
"""
|
|
230
|
+
return self._paused
|
|
144
231
|
|
|
145
232
|
def start_android_app(self, package_name: str, main_activity: Optional[str] = None, extras: Optional[Dict[str, Union[str, bool]]] = None):
|
|
146
233
|
"""
|
|
@@ -190,6 +277,13 @@ class JobManager(object):
|
|
|
190
277
|
|
|
191
278
|
|
|
192
279
|
def attach_app(self, target_process, foreground=False):
|
|
280
|
+
"""Attach to a running process.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
target_process: Package name or PID to attach to.
|
|
284
|
+
foreground: If True, attach to the frontmost application.
|
|
285
|
+
"""
|
|
286
|
+
self._mode = "attach"
|
|
193
287
|
self.package_name = target_process
|
|
194
288
|
|
|
195
289
|
if foreground:
|
|
@@ -261,11 +355,44 @@ class JobManager(object):
|
|
|
261
355
|
raise FridaBasedException(f"Frida-Error: {fe}")
|
|
262
356
|
|
|
263
357
|
|
|
264
|
-
def start_job(
|
|
358
|
+
def start_job(
|
|
359
|
+
self,
|
|
360
|
+
frida_script_name: str,
|
|
361
|
+
custom_hooking_handler_name: Callable,
|
|
362
|
+
job_type: str = "custom",
|
|
363
|
+
display_name: Optional[str] = None,
|
|
364
|
+
hooks_registry: Optional[List[str]] = None,
|
|
365
|
+
priority: int = 50,
|
|
366
|
+
) -> Optional[Job]:
|
|
367
|
+
"""Start a new instrumentation job.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
frida_script_name: Path to the Frida script file.
|
|
371
|
+
custom_hooking_handler_name: Callback for handling Frida messages.
|
|
372
|
+
job_type: Category of job (e.g., "fritap", "dexray", "trigdroid").
|
|
373
|
+
display_name: Human-readable name for UI display.
|
|
374
|
+
hooks_registry: List of hooked methods for conflict detection.
|
|
375
|
+
priority: Job priority (lower = higher priority).
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
The created Job instance, or None if no session exists.
|
|
379
|
+
|
|
380
|
+
Raises:
|
|
381
|
+
FridaBasedException: If Frida encounters an error.
|
|
382
|
+
"""
|
|
383
|
+
job = None # Initialize before try block for safe exception handling
|
|
265
384
|
try:
|
|
266
385
|
if self.process_session:
|
|
267
|
-
job = Job(
|
|
268
|
-
|
|
386
|
+
job = Job(
|
|
387
|
+
frida_script_name,
|
|
388
|
+
custom_hooking_handler_name,
|
|
389
|
+
self.process_session,
|
|
390
|
+
job_type=job_type,
|
|
391
|
+
display_name=display_name,
|
|
392
|
+
hooks_registry=hooks_registry,
|
|
393
|
+
priority=priority,
|
|
394
|
+
)
|
|
395
|
+
self.logger.info(f"[*] created job: {job.job_id} ({job.display_name})")
|
|
269
396
|
self.jobs[job.job_id] = job
|
|
270
397
|
self.last_created_job = job
|
|
271
398
|
job.run_job()
|
|
@@ -274,13 +401,14 @@ class JobManager(object):
|
|
|
274
401
|
self.first_instrumenation_script = custom_hooking_handler_name
|
|
275
402
|
if self.pid != -1:
|
|
276
403
|
self.device.resume(self.pid)
|
|
277
|
-
|
|
404
|
+
self._paused = False # Mark as resumed
|
|
405
|
+
time.sleep(1) # without it Java.perform silently fails
|
|
278
406
|
|
|
279
407
|
return job
|
|
280
408
|
|
|
281
409
|
else:
|
|
282
410
|
self.logger.error("[-] no frida session. Aborting...")
|
|
283
|
-
|
|
411
|
+
return None
|
|
284
412
|
|
|
285
413
|
except frida.TransportError as fe:
|
|
286
414
|
raise FridaBasedException(f"Problems while attaching to frida-server: {fe}")
|
|
@@ -291,8 +419,9 @@ class JobManager(object):
|
|
|
291
419
|
except frida.ProcessNotFoundError as pe:
|
|
292
420
|
raise FridaBasedException(f"ProcessNotFoundError: {pe}")
|
|
293
421
|
except KeyboardInterrupt:
|
|
294
|
-
|
|
295
|
-
|
|
422
|
+
if job:
|
|
423
|
+
self.stop_app_with_last_job(job, self.package_name)
|
|
424
|
+
return None
|
|
296
425
|
|
|
297
426
|
|
|
298
427
|
def stop_jobs(self):
|
|
@@ -306,9 +435,16 @@ class JobManager(object):
|
|
|
306
435
|
'no longer be available.'.format(job_id))
|
|
307
436
|
|
|
308
437
|
|
|
309
|
-
def stop_job_with_id(self,job_id):
|
|
438
|
+
def stop_job_with_id(self, job_id: str) -> None:
|
|
439
|
+
"""Stop a specific job by ID.
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
job_id: UUID of the job to stop.
|
|
443
|
+
"""
|
|
310
444
|
if job_id in self.jobs:
|
|
311
445
|
job = self.jobs[job_id]
|
|
446
|
+
# Unregister hooks before closing
|
|
447
|
+
self.unregister_hooks(job_id)
|
|
312
448
|
job.close_job()
|
|
313
449
|
del self.jobs[job_id]
|
|
314
450
|
|
|
@@ -411,3 +547,154 @@ class JobManager(object):
|
|
|
411
547
|
raise FridaBasedException("Unable to find device")
|
|
412
548
|
except frida.ServerNotRunningError:
|
|
413
549
|
raise FridaBasedException("Frida server not running. Start frida-server and try it again.")
|
|
550
|
+
|
|
551
|
+
# ==================== Hook Coordination Methods ====================
|
|
552
|
+
|
|
553
|
+
def register_hooks(self, job_id: str, hooks: List[str]) -> List[str]:
|
|
554
|
+
"""Register hooks for a job and detect conflicts.
|
|
555
|
+
|
|
556
|
+
Args:
|
|
557
|
+
job_id: UUID of the job registering hooks.
|
|
558
|
+
hooks: List of hook targets (method names, function signatures).
|
|
559
|
+
|
|
560
|
+
Returns:
|
|
561
|
+
List of conflicting hooks that are already registered by other jobs.
|
|
562
|
+
"""
|
|
563
|
+
conflicts = []
|
|
564
|
+
for hook in hooks:
|
|
565
|
+
if hook in self._hook_registry:
|
|
566
|
+
existing_job_id = self._hook_registry[hook]
|
|
567
|
+
if existing_job_id != job_id:
|
|
568
|
+
conflicts.append(hook)
|
|
569
|
+
self.logger.warning(
|
|
570
|
+
f"Hook conflict: '{hook}' already registered by job {existing_job_id[:8]}"
|
|
571
|
+
)
|
|
572
|
+
else:
|
|
573
|
+
self._hook_registry[hook] = job_id
|
|
574
|
+
|
|
575
|
+
if conflicts:
|
|
576
|
+
self.logger.warning(
|
|
577
|
+
f"Job {job_id[:8]} has {len(conflicts)} hook conflict(s)"
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
return conflicts
|
|
581
|
+
|
|
582
|
+
def unregister_hooks(self, job_id: str) -> None:
|
|
583
|
+
"""Remove all hooks registered by a job.
|
|
584
|
+
|
|
585
|
+
Args:
|
|
586
|
+
job_id: UUID of the job whose hooks should be removed.
|
|
587
|
+
"""
|
|
588
|
+
hooks_to_remove = [
|
|
589
|
+
hook for hook, jid in self._hook_registry.items() if jid == job_id
|
|
590
|
+
]
|
|
591
|
+
for hook in hooks_to_remove:
|
|
592
|
+
del self._hook_registry[hook]
|
|
593
|
+
|
|
594
|
+
if hooks_to_remove:
|
|
595
|
+
self.logger.debug(
|
|
596
|
+
f"Unregistered {len(hooks_to_remove)} hooks for job {job_id[:8]}"
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
def check_hook_conflicts(self, hooks: List[str]) -> Dict[str, str]:
|
|
600
|
+
"""Check for potential conflicts before registering hooks.
|
|
601
|
+
|
|
602
|
+
Args:
|
|
603
|
+
hooks: List of hook targets to check.
|
|
604
|
+
|
|
605
|
+
Returns:
|
|
606
|
+
Dictionary mapping conflicting hooks to their owning job IDs.
|
|
607
|
+
"""
|
|
608
|
+
return {
|
|
609
|
+
hook: self._hook_registry[hook]
|
|
610
|
+
for hook in hooks
|
|
611
|
+
if hook in self._hook_registry
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
def get_hook_registry(self) -> Dict[str, str]:
|
|
615
|
+
"""Get a copy of the current hook registry.
|
|
616
|
+
|
|
617
|
+
Returns:
|
|
618
|
+
Dictionary mapping hook targets to job IDs.
|
|
619
|
+
"""
|
|
620
|
+
return self._hook_registry.copy()
|
|
621
|
+
|
|
622
|
+
# ==================== Session Info Methods ====================
|
|
623
|
+
|
|
624
|
+
def has_active_session(self) -> bool:
|
|
625
|
+
"""Check if there's an active Frida session.
|
|
626
|
+
|
|
627
|
+
Returns:
|
|
628
|
+
True if a session is active, False otherwise.
|
|
629
|
+
"""
|
|
630
|
+
return self.process_session is not None
|
|
631
|
+
|
|
632
|
+
def get_session_info(self) -> Dict[str, any]:
|
|
633
|
+
"""Get current session information for UI display.
|
|
634
|
+
|
|
635
|
+
Returns:
|
|
636
|
+
Dictionary containing session state information.
|
|
637
|
+
"""
|
|
638
|
+
return {
|
|
639
|
+
"package": self.package_name,
|
|
640
|
+
"pid": self.pid,
|
|
641
|
+
"mode": self._mode or ("spawn" if self.pid != -1 else "attach"),
|
|
642
|
+
"device_serial": self._device_serial,
|
|
643
|
+
"has_session": self.process_session is not None,
|
|
644
|
+
"job_count": len(self.jobs),
|
|
645
|
+
"running_job_count": len(self.running_jobs()),
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
def get_running_jobs_info(self) -> List[Dict[str, any]]:
|
|
649
|
+
"""Get information about running jobs for UI display.
|
|
650
|
+
|
|
651
|
+
Returns:
|
|
652
|
+
List of dictionaries containing job information.
|
|
653
|
+
"""
|
|
654
|
+
return [
|
|
655
|
+
job.get_info()
|
|
656
|
+
for job in self.jobs.values()
|
|
657
|
+
if job.state == "running"
|
|
658
|
+
]
|
|
659
|
+
|
|
660
|
+
def get_all_jobs_info(self) -> List[Dict[str, any]]:
|
|
661
|
+
"""Get information about all jobs (running and stopped).
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
List of dictionaries containing job information.
|
|
665
|
+
"""
|
|
666
|
+
return [job.get_info() for job in self.jobs.values()]
|
|
667
|
+
|
|
668
|
+
@property
|
|
669
|
+
def mode(self) -> Optional[str]:
|
|
670
|
+
"""Get the current session mode ('spawn' or 'attach')."""
|
|
671
|
+
return self._mode
|
|
672
|
+
|
|
673
|
+
def reset_session(self) -> None:
|
|
674
|
+
"""Reset the session state for a new connection.
|
|
675
|
+
|
|
676
|
+
Stops all jobs, clears hook registry, and resets session state.
|
|
677
|
+
"""
|
|
678
|
+
# Stop all running jobs
|
|
679
|
+
self.stop_jobs()
|
|
680
|
+
|
|
681
|
+
# Clear hook registry
|
|
682
|
+
self._hook_registry.clear()
|
|
683
|
+
|
|
684
|
+
# Detach from app
|
|
685
|
+
if self.process_session:
|
|
686
|
+
try:
|
|
687
|
+
self.process_session.detach()
|
|
688
|
+
except Exception:
|
|
689
|
+
pass
|
|
690
|
+
|
|
691
|
+
# Reset state
|
|
692
|
+
self.process_session = None
|
|
693
|
+
self.pid = -1
|
|
694
|
+
self.package_name = ""
|
|
695
|
+
self._mode = None
|
|
696
|
+
self.is_first_job = True
|
|
697
|
+
self.last_created_job = None
|
|
698
|
+
self.init_last_job = False
|
|
699
|
+
|
|
700
|
+
self.logger.info("[*] Session reset complete")
|
{androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AndroidFridaManager
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.4
|
|
4
4
|
Summary: A python API in order to install and run the frida-server on an Android device.
|
|
5
5
|
Home-page: https://github.com/fkie-cad/AndroidFridaManager
|
|
6
6
|
Author: Daniel Baier
|
|
@@ -33,7 +33,7 @@ Dynamic: requires-dist
|
|
|
33
33
|
Dynamic: requires-python
|
|
34
34
|
Dynamic: summary
|
|
35
35
|
|
|
36
|
-
 [](https://badge.fury.io/py/AndroidFridaManager) [](https://github.com/fkie-cad/AndroidFridaManager/actions/workflows/publish-to-pypi.yml)
|
|
37
37
|
|
|
38
38
|
# AndroidFridaManager
|
|
39
39
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AndroidFridaManager
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.4
|
|
4
4
|
Summary: A python API in order to install and run the frida-server on an Android device.
|
|
5
5
|
Home-page: https://github.com/fkie-cad/AndroidFridaManager
|
|
6
6
|
Author: Daniel Baier
|
|
@@ -33,7 +33,7 @@ Dynamic: requires-dist
|
|
|
33
33
|
Dynamic: requires-python
|
|
34
34
|
Dynamic: summary
|
|
35
35
|
|
|
36
|
-
 [](https://badge.fury.io/py/AndroidFridaManager) [](https://github.com/fkie-cad/AndroidFridaManager/actions/workflows/publish-to-pypi.yml)
|
|
37
37
|
|
|
38
38
|
# AndroidFridaManager
|
|
39
39
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
 [](https://badge.fury.io/py/AndroidFridaManager) [](https://github.com/fkie-cad/AndroidFridaManager/actions/workflows/publish-to-pypi.yml)
|
|
2
2
|
|
|
3
3
|
# AndroidFridaManager
|
|
4
4
|
|
|
File without changes
|
{androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/requires.txt
RENAMED
|
File without changes
|
{androidfridamanager-1.9.2 → androidfridamanager-1.9.4}/AndroidFridaManager.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|