From b203e4bbcac4b43dd8c815804fa92e3b2400c473 Mon Sep 17 00:00:00 2001 From: Yotam Nachum Date: Sat, 29 Oct 2022 19:43:00 +0300 Subject: [PATCH 1/2] core: Add specific signals type hinting --- frida/core.py | 344 ++++++++++++++++++++++++++++++++++++++++++++++++-- setup.py | 2 +- 2 files changed, 336 insertions(+), 10 deletions(-) diff --git a/frida/core.py b/frida/core.py index 658cdc7..779308c 100644 --- a/frida/core.py +++ b/frida/core.py @@ -24,9 +24,14 @@ ) if sys.version_info >= (3, 8): - from typing import Literal + from typing import Literal, TypedDict else: - from typing_extensions import Literal + from typing_extensions import Literal, TypedDict + +if sys.version_info >= (3, 11): + from typing import NotRequired +else: + from typing_extensions import NotRequired import _frida @@ -174,13 +179,32 @@ def __dir__(self) -> List[str]: return self._script.list_exports() +class ScriptErrorMessage(TypedDict): + type: Literal["error"] + description: str + stack: NotRequired[str] + fileName: NotRequired[str] + lineNumber: NotRequired[int] + columnNumber: NotRequired[int] + + +class ScriptPayloadMessage(TypedDict): + type: Literal["send"] + payload: NotRequired[Any] + + +ScriptMessage = Union[ScriptPayloadMessage, ScriptErrorMessage] +ScriptMessageCallback = Callable[[ScriptMessage, Optional[bytes]], None] +ScriptDestroyedCallback = Callable[[], None] + + class Script: def __init__(self, impl: _frida.Script) -> None: self.exports = ScriptExports(self) self._impl = impl - self._on_message_callbacks: List[Callable[..., Any]] = [] + self._on_message_callbacks: List[ScriptMessageCallback] = [] self._log_handler: Callable[[str, str], None] = self.default_log_handler self._pending: Dict[int, Callable[..., Any]] = {} @@ -254,7 +278,11 @@ def disable_debugger(self) -> None: self._impl.disable_debugger() @overload - def on(self, signal: Literal["message"], callback: Callable[[Mapping[Any, Any], Any], Any]) -> None: + def on(self, signal: Literal["destroyed"], callback: ScriptDestroyedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["message"], callback: ScriptMessageCallback) -> None: ... @overload @@ -272,7 +300,11 @@ def on(self, signal: str, callback: Callable[..., Any]) -> None: self._impl.on(signal, callback) @overload - def off(self, signal: Literal["message"], callback: Callable[[Mapping[Any, Any], Any], Any]) -> None: + def off(self, signal: Literal["destroyed"], callback: ScriptDestroyedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["message"], callback: ScriptMessageCallback) -> None: ... @overload @@ -398,7 +430,7 @@ def _on_destroyed(self) -> None: next_pending(None, _frida.InvalidOperationError("script has been destroyed")) - def _on_message(self, raw_message: str, data: Any) -> None: + def _on_message(self, raw_message: str, data: Optional[bytes]) -> None: message = json.loads(raw_message) mtype = message["type"] @@ -420,6 +452,17 @@ def _on_message(self, raw_message: str, data: Any) -> None: traceback.print_exc() +SessionDetachedCallback = Callable[ + [ + Literal[ + "application-requested", "process-replaced", "process-terminated", "connection-terminated", "device-lost" + ], + Optional[_frida.Crash], + ], + None, +] + + class Session: def __init__(self, impl: _frida.Session) -> None: self._impl = impl @@ -538,6 +581,18 @@ def join_portal( _filter_missing_kwargs(kwargs) return PortalMembership(self._impl.join_portal(address, **kwargs)) + @overload + def on( + self, + signal: Literal["detached"], + callback: SessionDetachedCallback, + ) -> None: + ... + + @overload + def on(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def on(self, signal: str, callback: Callable[..., Any]) -> None: """ Add a signal handler @@ -545,6 +600,18 @@ def on(self, signal: str, callback: Callable[..., Any]) -> None: self._impl.on(signal, callback) + @overload + def off( + self, + signal: Literal["detached"], + callback: SessionDetachedCallback, + ) -> None: + ... + + @overload + def off(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def off(self, signal: str, callback: Callable[..., Any]) -> None: """ Remove a signal handler @@ -553,6 +620,10 @@ def off(self, signal: str, callback: Callable[..., Any]) -> None: self._impl.off(signal, callback) +BusDetachedCallback = Callable[[], None] +BusMessageCallback = Callable[[Mapping[Any, Any], Optional[bytes]], None] + + class Bus: def __init__(self, impl: _frida.Bus) -> None: self._impl = impl @@ -578,6 +649,18 @@ def post(self, message: Any, data: Optional[Union[str, bytes]] = None) -> None: _filter_missing_kwargs(kwargs) self._impl.post(raw_message, **kwargs) + @overload + def on(self, signal: Literal["detached"], callback: BusDetachedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["message"], callback: BusMessageCallback) -> None: + ... + + @overload + def on(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def on(self, signal: str, callback: Callable[..., Any]) -> None: """ Add a signal handler @@ -588,6 +671,18 @@ def on(self, signal: str, callback: Callable[..., Any]) -> None: else: self._impl.on(signal, callback) + @overload + def off(self, signal: Literal["detached"], callback: BusDetachedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["message"], callback: BusMessageCallback) -> None: + ... + + @overload + def off(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def off(self, signal: str, callback: Callable[..., Any]) -> None: """ Remove a signal handler @@ -608,6 +703,16 @@ def _on_message(self, raw_message: str, data: Any) -> None: traceback.print_exc() +DeviceSpawnAddedCallback = Callable[[_frida.Spawn], None] +DeviceSpawnRemovedCallback = Callable[[_frida.Spawn], None] +DeviceChildAddedCallback = Callable[[_frida.Child], None] +DeviceChildRemovedCallback = Callable[[_frida.Child], None] +DeviceProcessCrashedCallback = Callable[[_frida.Crash], None] +DeviceOutputCallback = Callable[[int, int, bytes], None] +DeviceUninjectedCallback = Callable[[int], None] +DeviceLostCallback = Callable[[], None] + + class Device: """ Represents a device that Frida connects to @@ -833,6 +938,42 @@ def get_bus(self) -> Bus: return self.bus + @overload + def on(self, signal: Literal["spawn-added"], callback: DeviceSpawnAddedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["spawn-removed"], callback: DeviceSpawnRemovedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["child-added"], callback: DeviceChildAddedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["child-removed"], callback: DeviceChildRemovedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["process-crashed"], callback: DeviceProcessCrashedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["output"], callback: DeviceOutputCallback) -> None: + ... + + @overload + def on(self, signal: Literal["uninjected"], callback: DeviceUninjectedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["lost"], callback: DeviceLostCallback) -> None: + ... + + @overload + def on(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def on(self, signal: str, callback: Callable[..., Any]) -> None: """ Add a signal handler @@ -840,6 +981,42 @@ def on(self, signal: str, callback: Callable[..., Any]) -> None: self._impl.on(signal, callback) + @overload + def off(self, signal: Literal["spawn-added"], callback: DeviceSpawnAddedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["spawn-removed"], callback: DeviceSpawnRemovedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["child-added"], callback: DeviceChildAddedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["child-removed"], callback: DeviceChildRemovedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["process-crashed"], callback: DeviceProcessCrashedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["output"], callback: DeviceOutputCallback) -> None: + ... + + @overload + def off(self, signal: Literal["uninjected"], callback: DeviceUninjectedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["lost"], callback: DeviceLostCallback) -> None: + ... + + @overload + def off(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def off(self, signal: str, callback: Callable[..., Any]) -> None: """ Remove a signal handler @@ -854,6 +1031,11 @@ def _pid_of(self, target: ProcessTarget) -> int: return target +DeviceManagerAddedCallback = Callable[[_frida.Device], None] +DeviceManagerRemovedCallback = Callable[[_frida.Device], None] +DeviceManagerChangedCallback = Callable[[], None] + + class DeviceManager: def __init__(self, impl: _frida.DeviceManager) -> None: self._impl = impl @@ -943,6 +1125,22 @@ def remove_remote_device(self, address: str) -> None: self._impl.remove_remote_device(address=address) + @overload + def on(self, signal: Literal["added"], callback: DeviceManagerAddedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["removed"], callback: DeviceManagerRemovedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["changed"], callback: DeviceManagerChangedCallback) -> None: + ... + + @overload + def on(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def on(self, signal: str, callback: Callable[..., Any]) -> None: """ Add a signal handler @@ -950,6 +1148,22 @@ def on(self, signal: str, callback: Callable[..., Any]) -> None: self._impl.on(signal, callback) + @overload + def off(self, signal: Literal["added"], callback: DeviceManagerAddedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["removed"], callback: DeviceManagerRemovedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["changed"], callback: DeviceManagerChangedCallback) -> None: + ... + + @overload + def off(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def off(self, signal: str, callback: Callable[..., Any]) -> None: """ Remove a signal handler @@ -998,6 +1212,17 @@ def __init__( self._impl = _frida.EndpointParameters(**kwargs) +PortalServiceNodeJoinedCallback = Callable[[int, _frida.Application], None] +PortalServiceNodeLeftCallback = Callable[[int, _frida.Application], None] +PortalServiceNodeConnectedCallback = Callable[[int, Tuple[str, int]], None] +PortalServiceNodeDisconnectedCallback = Callable[[int, Tuple[str, int]], None] +PortalServiceControllerConnectedCallback = Callable[[int, Tuple[str, int]], None] +PortalServiceControllerDisconnectedCallback = Callable[[int, Tuple[str, int]], None] +PortalServiceAuthenticatedCallback = Callable[[int, Mapping[Any, Any]], None] +PortalServiceSubscribeCallback = Callable[[int], None] +PortalServiceMessageCallback = Callable[[int, Mapping[Any, Any], Optional[bytes]], None] + + class PortalService: def __init__( self, @@ -1011,8 +1236,8 @@ def __init__( self.device = impl.device self._impl = impl - self._on_authenticated_callbacks: List[Callable[[int, Dict[str, Any]], Any]] = [] - self._on_message_callbacks: List[Callable[[int, Dict[str, Any], Any], Any]] = [] + self._on_authenticated_callbacks: List[PortalServiceAuthenticatedCallback] = [] + self._on_message_callbacks: List[PortalServiceMessageCallback] = [] impl.on("authenticated", self._on_authenticated) impl.on("message", self._on_message) @@ -1087,6 +1312,48 @@ def untag(self, connection_id: int, tag: str) -> None: self._impl.untag(connection_id, tag) + @overload + def on(self, signal: Literal["node-joined"], callback: PortalServiceNodeJoinedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["node-left"], callback: PortalServiceNodeLeftCallback) -> None: + ... + + @overload + def on(self, signal: Literal["controller-connected"], callback: PortalServiceControllerConnectedCallback) -> None: + ... + + @overload + def on( + self, signal: Literal["controller-disconnected"], callback: PortalServiceControllerDisconnectedCallback + ) -> None: + ... + + @overload + def on(self, signal: Literal["node-connected"], callback: PortalServiceNodeConnectedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["node-disconnected"], callback: PortalServiceNodeDisconnectedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["authenticated"], callback: PortalServiceAuthenticatedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["subscribe"], callback: PortalServiceSubscribeCallback) -> None: + ... + + @overload + def on(self, signal: Literal["message"], callback: PortalServiceMessageCallback) -> None: + ... + + @overload + def on(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def on(self, signal: str, callback: Callable[..., Any]) -> None: """ Add a signal handler @@ -1120,7 +1387,7 @@ def _on_authenticated(self, connection_id: int, raw_session_info: str) -> None: except: traceback.print_exc() - def _on_message(self, connection_id: int, raw_message: str, data: Any) -> None: + def _on_message(self, connection_id: int, raw_message: str, data: Optional[bytes]) -> None: message = json.loads(raw_message) for callback in self._on_message_callbacks[:]: @@ -1130,6 +1397,25 @@ def _on_message(self, connection_id: int, raw_message: str, data: Any) -> None: traceback.print_exc() +class CompilerDiagnosticFile(TypedDict): + path: str + line: int + character: int + + +class CompilerDiagnostic(TypedDict): + category: str + code: int + file: NotRequired[CompilerDiagnosticFile] + text: str + + +CompilerStartingCallback = Callable[[], None] +CompilerFinishedCallback = Callable[[], None] +CompilerOutputCallback = Callable[[str], None] +CompilerDiagnosticsCallback = Callable[[List[CompilerDiagnostic]], None] + + class Compiler: def __init__(self) -> None: self._impl = _frida.Compiler(get_device_manager()._impl) @@ -1161,9 +1447,49 @@ def watch( _filter_missing_kwargs(kwargs) return self._impl.watch(entrypoint, **kwargs) + @overload + def on(self, signal: Literal["starting"], callback: CompilerStartingCallback) -> None: + ... + + @overload + def on(self, signal: Literal["finished"], callback: CompilerFinishedCallback) -> None: + ... + + @overload + def on(self, signal: Literal["output"], callback: CompilerOutputCallback) -> None: + ... + + @overload + def on(self, signal: Literal["diagnostics"], callback: CompilerDiagnosticsCallback) -> None: + ... + + @overload + def on(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def on(self, signal: str, callback: Callable[..., Any]) -> None: self._impl.on(signal, callback) + @overload + def off(self, signal: Literal["starting"], callback: CompilerStartingCallback) -> None: + ... + + @overload + def off(self, signal: Literal["finished"], callback: CompilerFinishedCallback) -> None: + ... + + @overload + def off(self, signal: Literal["output"], callback: CompilerOutputCallback) -> None: + ... + + @overload + def off(self, signal: Literal["diagnostics"], callback: CompilerDiagnosticsCallback) -> None: + ... + + @overload + def off(self, signal: str, callback: Callable[..., Any]) -> None: + ... + def off(self, signal: str, callback: Callable[..., Any]) -> None: self._impl.off(signal, callback) diff --git a/setup.py b/setup.py index 5030d73..2a0fff7 100755 --- a/setup.py +++ b/setup.py @@ -108,7 +108,7 @@ def build_extension(self, ext): author="Frida Developers", author_email="oleavr@frida.re", url="https://frida.re", - install_requires=["setuptools", "typing_extensions; python_version<'3.8'"], + install_requires=["setuptools", "typing_extensions; python_version<'3.11'"], python_requires=">=3.7", license="wxWindows Library Licence, Version 3.1", keywords="frida debugger dynamic instrumentation inject javascript windows macos linux ios iphone ipad android qnx", From ddcfbcf25d89ee976dd8ad8129668da278bf4baf Mon Sep 17 00:00:00 2001 From: Yotam Nachum Date: Sat, 14 Jan 2023 15:15:59 +0200 Subject: [PATCH 2/2] setup: Move setuptools requirment to pyproject.toml PEP 517 specify a new format for build systems and PEP 518 specify how we should require packages for our build system. Setuptools support PEP 517 so we should use it that way. --- pyproject.toml | 4 ++++ setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 96fa357..85966e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,7 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + [tool.black] line-length = 120 diff --git a/setup.py b/setup.py index 2a0fff7..2699ab3 100755 --- a/setup.py +++ b/setup.py @@ -108,7 +108,7 @@ def build_extension(self, ext): author="Frida Developers", author_email="oleavr@frida.re", url="https://frida.re", - install_requires=["setuptools", "typing_extensions; python_version<'3.11'"], + install_requires=["typing_extensions; python_version<'3.11'"], python_requires=">=3.7", license="wxWindows Library Licence, Version 3.1", keywords="frida debugger dynamic instrumentation inject javascript windows macos linux ios iphone ipad android qnx",