mirror of
https://github.com/frida/frida-python
synced 2026-06-08 14:16:17 +00:00
Compare commits
289 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee92ba7dea | |||
| 9f0ce44424 | |||
| bbea00ce9f | |||
| 8527e5f830 | |||
| 985410b6ff | |||
| 21c7b9ee6a | |||
| 7fba1cdc67 | |||
| a709856e2a | |||
| c3afe6089d | |||
| 6435a78d13 | |||
| 09175036ea | |||
| e0c90c5203 | |||
| 9f463fb602 | |||
| 9fdee2270f | |||
| 6e536a56b1 | |||
| 74925d082e | |||
| bf1245ddb0 | |||
| 0f247e9439 | |||
| 17aae5fe5c | |||
| 477a90534e | |||
| c87cdca293 | |||
| 30968b3805 | |||
| 5e3f27d0cb | |||
| 904461eb01 | |||
| dc652faa90 | |||
| f5ba22ad36 | |||
| dccb6810ca | |||
| 82441c6d61 | |||
| 323dca48c2 | |||
| ef71f0fa92 | |||
| e92e829f6d | |||
| 5793fb5528 | |||
| cac6993d87 | |||
| 714b7f8626 | |||
| ab0b9e98ae | |||
| 19a7fa4f45 | |||
| 38552a368a | |||
| 38724cab58 | |||
| b60d085ad9 | |||
| 1a4afbcc62 | |||
| 9e15cab647 | |||
| 9ef1c9c1f7 | |||
| 3da6af4759 | |||
| a9075822b1 | |||
| 2e61509c04 | |||
| aa9396c6b0 | |||
| 6530b2a7d4 | |||
| 2d833af062 | |||
| 9f6ab2d384 | |||
| a005f1bf4c | |||
| 59a24aa48f | |||
| 782d626419 | |||
| 3aa122f4fd | |||
| ef315fc0ad | |||
| daf93eea54 | |||
| 5d02a36906 | |||
| ba8daa423d | |||
| afb27bfa7c | |||
| 93a201acf2 | |||
| ca1cdad96a | |||
| fbc83cc9bf | |||
| b838e1df9a | |||
| e2409be987 | |||
| b061ea43d2 | |||
| a50a62999a | |||
| 3a47d33ee8 | |||
| ea93d827bb | |||
| 8d4c05f664 | |||
| 2f78c96622 | |||
| 4b5aa28119 | |||
| 4f87afa0f1 | |||
| 82272945ac | |||
| bf496a81d9 | |||
| c2dcae3ff4 | |||
| 0cb42c47dc | |||
| 0e3a9fb551 | |||
| 4e16e5c667 | |||
| 09d6c7b044 | |||
| e11efce5c2 | |||
| e174be9998 | |||
| eae3d1dc65 | |||
| 2d26b94c7d | |||
| f396958dfe | |||
| 93fb33206f | |||
| aed3c1b7bf | |||
| 690da0652d | |||
| bec6221c02 | |||
| 53479e1814 | |||
| 68bc65ecd4 | |||
| 6492081c92 | |||
| 262b984e23 | |||
| 730ec03652 | |||
| 25f96f92d3 | |||
| dec23f9ef5 | |||
| 906d27bcb7 | |||
| b0018c9812 | |||
| 5e011e6d64 | |||
| b68dda4eb5 | |||
| 6fbe238c1a | |||
| 4d750dfe58 | |||
| fe1c61ce5d | |||
| ea91df0d77 | |||
| b78ae7555f | |||
| b456683f2d | |||
| ddfe2b1a3b | |||
| 329795dbfc | |||
| e9c62fd581 | |||
| 40e945107d | |||
| 6c27bf0da7 | |||
| d37451f3bc | |||
| 6d4e4e27ee | |||
| 33a9c51387 | |||
| 4344eb9220 | |||
| 3a94e642d7 | |||
| 7a5948a95a | |||
| bbe926c606 | |||
| b653958fa4 | |||
| bca5146b74 | |||
| 2bd254702c | |||
| 82ee16d299 | |||
| 51501ceacb | |||
| ef45347385 | |||
| 0024f6719a | |||
| 87de091250 | |||
| cf4870e13d | |||
| 2ac00cb1e2 | |||
| dd94c73d4f | |||
| 40eb2dae38 | |||
| 73004f9598 | |||
| a0e9e64346 | |||
| 2706e19615 | |||
| 6edaf679ac | |||
| c2c45ce231 | |||
| 0180450178 | |||
| e07370158d | |||
| f73fdff4ee | |||
| b565fa19dc | |||
| 58d5ea9e2f | |||
| 91826487e1 | |||
| b1abfd53fa | |||
| 1afde6cb34 | |||
| 6bee5f45f3 | |||
| efc1009ece | |||
| 3def865cf8 | |||
| 3c41862ac5 | |||
| 4bd11602d7 | |||
| d07c8a16cd | |||
| 4e6d28cba7 | |||
| e0562ed0bc | |||
| 9dc17772cd | |||
| 0f9f4a5da8 | |||
| 4a9fb75adf | |||
| c37ef15695 | |||
| f6ad8a504d | |||
| 808e4ab6b5 | |||
| 50ee91f63f | |||
| 56dc49b5fb | |||
| 4cbb5881bd | |||
| 6ff0e34ca3 | |||
| 4df903dca5 | |||
| 6ed81cbfb6 | |||
| 98cc4c44bc | |||
| 6190b3c57b | |||
| 212973bbc1 | |||
| 79f7ae1deb | |||
| 522366a55e | |||
| 8017d10e3e | |||
| e78a5fd3b3 | |||
| 5cbef2fb1f | |||
| 8694918de7 | |||
| ab16fe04c5 | |||
| a1d1740541 | |||
| 09d2a1aa54 | |||
| 215669c151 | |||
| f7b08f8375 | |||
| 133687aafc | |||
| 06130770e1 | |||
| 2d72d0be57 | |||
| d463d7f2f2 | |||
| 11613adee6 | |||
| 8dc6509929 | |||
| 3f8b122ce7 | |||
| 29abf88e31 | |||
| 5370147e85 | |||
| 507917744d | |||
| 8f2858fd8d | |||
| c2cf7fa951 | |||
| 9ff0651481 | |||
| 2674d6ef7d | |||
| 5d65919693 | |||
| d453cdd5ba | |||
| e8fd88f3d4 | |||
| 414cf0e029 | |||
| 76825537e8 | |||
| 2ab18cf80f | |||
| aaa703436b | |||
| 387962ab9d | |||
| 97f4c236a6 | |||
| 88cb2c97b8 | |||
| ec5035fd96 | |||
| bd883147ed | |||
| f7c32b2772 | |||
| 68009a8df9 | |||
| 85d56a0e45 | |||
| 228610a79c | |||
| d4b1b6aa8b | |||
| 29661c6b3b | |||
| d5b2a6fd18 | |||
| b862e9ae82 | |||
| 6e6f84969d | |||
| d6933eccf3 | |||
| 3bee61ffdb | |||
| 647e45f8bf | |||
| 5b6cfeba8b | |||
| c2b8043d8d | |||
| 69a4fa2ad2 | |||
| 546f65f25b | |||
| 168fcd5fe6 | |||
| 2ae7df42d0 | |||
| e8eff270ce | |||
| 62b6839d12 | |||
| d83456efe7 | |||
| fa62593fed | |||
| 305d2c1360 | |||
| 67b563a72c | |||
| d988aa2920 | |||
| d346babcf9 | |||
| 96a3777eb0 | |||
| 6c4ad9bf98 | |||
| 0490814f79 | |||
| 3c21f23327 | |||
| fe24ef56d1 | |||
| 2d8dca8d53 | |||
| 77f7d0f3c3 | |||
| c8f3b40aad | |||
| 7416839ae5 | |||
| 5c782dcc1c | |||
| de4f0e906d | |||
| 623cb160f8 | |||
| 3c265a483d | |||
| f43a1ed12c | |||
| 9201c4c007 | |||
| 135dac88ec | |||
| 28e8b1f32e | |||
| 0b7bde2fd4 | |||
| 6a18d4c4cc | |||
| 9a3ec81843 | |||
| ec55dcb3bc | |||
| e5404ae4eb | |||
| 64396bce72 | |||
| ccdd63dccc | |||
| e8f69618da | |||
| d809147cd0 | |||
| 24cc1fbcc9 | |||
| 7a2762ecc7 | |||
| d92b8e684c | |||
| 7c17bd5330 | |||
| 5991067e4e | |||
| 0780e5f813 | |||
| ce326094be | |||
| 092f6c38a1 | |||
| 9e83d29bd9 | |||
| 90533db5d4 | |||
| 30e009997e | |||
| 32e3c1922b | |||
| 69c5696aee | |||
| 0394ff3585 | |||
| c15b92fd55 | |||
| c53b453059 | |||
| d467d91e4f | |||
| ed3fdb8ce0 | |||
| 4d4632dbbe | |||
| aab9c4e76f | |||
| 2be19bcdad | |||
| 915c0678bd | |||
| 573a5a7d24 | |||
| 148ca1461d | |||
| 8eb4987563 | |||
| 84c7375044 | |||
| 267d5d555d | |||
| e219e5edbc | |||
| 70aaa04070 | |||
| 463fd65f03 | |||
| 41c3092dfe | |||
| 1b11eeab12 | |||
| 10aad4273c | |||
| d921e98e0c | |||
| 8935cf3651 | |||
| 2e3b9474bf |
@@ -0,0 +1 @@
|
||||
github: frida
|
||||
@@ -1,172 +0,0 @@
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
try:
|
||||
from . import _frida
|
||||
except Exception as ex:
|
||||
print("")
|
||||
print("***")
|
||||
if str(ex).startswith("No module named "):
|
||||
print("Frida native extension not found")
|
||||
print("Please check your PYTHONPATH.")
|
||||
else:
|
||||
print(f"Failed to load the Frida native extension: {ex}")
|
||||
print("Please ensure that the extension was compiled correctly")
|
||||
print("***")
|
||||
print("")
|
||||
raise ex
|
||||
from . import core
|
||||
|
||||
__version__: str = _frida.__version__
|
||||
|
||||
get_device_manager = core.get_device_manager
|
||||
Relay = _frida.Relay
|
||||
PortalService = core.PortalService
|
||||
EndpointParameters = core.EndpointParameters
|
||||
Compiler = core.Compiler
|
||||
FileMonitor = _frida.FileMonitor
|
||||
Cancellable = core.Cancellable
|
||||
|
||||
ServerNotRunningError = _frida.ServerNotRunningError
|
||||
ExecutableNotFoundError = _frida.ExecutableNotFoundError
|
||||
ExecutableNotSupportedError = _frida.ExecutableNotSupportedError
|
||||
ProcessNotFoundError = _frida.ProcessNotFoundError
|
||||
ProcessNotRespondingError = _frida.ProcessNotRespondingError
|
||||
InvalidArgumentError = _frida.InvalidArgumentError
|
||||
InvalidOperationError = _frida.InvalidOperationError
|
||||
PermissionDeniedError = _frida.PermissionDeniedError
|
||||
AddressInUseError = _frida.AddressInUseError
|
||||
TimedOutError = _frida.TimedOutError
|
||||
NotSupportedError = _frida.NotSupportedError
|
||||
ProtocolError = _frida.ProtocolError
|
||||
TransportError = _frida.TransportError
|
||||
OperationCancelledError = _frida.OperationCancelledError
|
||||
|
||||
|
||||
def query_system_parameters() -> Dict[str, Any]:
|
||||
"""
|
||||
Returns a dictionary of information about the host system
|
||||
"""
|
||||
|
||||
return get_local_device().query_system_parameters()
|
||||
|
||||
|
||||
def spawn(
|
||||
program: Union[str, List[Union[str, bytes]], Tuple[Union[str, bytes]]],
|
||||
argv: Union[None, List[Union[str, bytes]], Tuple[Union[str, bytes]]] = None,
|
||||
envp: Optional[Dict[str, str]] = None,
|
||||
env: Optional[Dict[str, str]] = None,
|
||||
cwd: Optional[str] = None,
|
||||
stdio: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
) -> int:
|
||||
"""
|
||||
Spawn a process into an attachable state
|
||||
"""
|
||||
|
||||
return get_local_device().spawn(program=program, argv=argv, envp=envp, env=env, cwd=cwd, stdio=stdio, **kwargs)
|
||||
|
||||
|
||||
def resume(target: core.ProcessTarget) -> None:
|
||||
"""
|
||||
Resume a process from the attachable state
|
||||
:param target: the PID or name of the process
|
||||
"""
|
||||
|
||||
get_local_device().resume(target)
|
||||
|
||||
|
||||
def kill(target: core.ProcessTarget) -> None:
|
||||
"""
|
||||
Kill a process
|
||||
:param target: the PID or name of the process
|
||||
"""
|
||||
|
||||
get_local_device().kill(target)
|
||||
|
||||
|
||||
def attach(
|
||||
target: core.ProcessTarget, realm: Optional[str] = None, persist_timeout: Optional[int] = None
|
||||
) -> core.Session:
|
||||
"""
|
||||
Attach to a process
|
||||
:param target: the PID or name of the process
|
||||
"""
|
||||
|
||||
return get_local_device().attach(target, realm=realm, persist_timeout=persist_timeout)
|
||||
|
||||
|
||||
def inject_library_file(target: core.ProcessTarget, path: str, entrypoint: str, data: str) -> int:
|
||||
"""
|
||||
Inject a library file to a process.
|
||||
:param target: the PID or name of the process
|
||||
"""
|
||||
|
||||
return get_local_device().inject_library_file(target, path, entrypoint, data)
|
||||
|
||||
|
||||
def inject_library_blob(target: core.ProcessTarget, blob: bytes, entrypoint: str, data: str) -> int:
|
||||
"""
|
||||
Inject a library blob to a process
|
||||
:param target: the PID or name of the process
|
||||
"""
|
||||
|
||||
return get_local_device().inject_library_blob(target, blob, entrypoint, data)
|
||||
|
||||
|
||||
def get_local_device() -> core.Device:
|
||||
"""
|
||||
Get the local device
|
||||
"""
|
||||
|
||||
return get_device_manager().get_local_device()
|
||||
|
||||
|
||||
def get_remote_device() -> core.Device:
|
||||
"""
|
||||
Get the first remote device in the devices list
|
||||
"""
|
||||
|
||||
return get_device_manager().get_remote_device()
|
||||
|
||||
|
||||
def get_usb_device(timeout: int = 0) -> core.Device:
|
||||
"""
|
||||
Get the first device connected over USB in the devices list
|
||||
"""
|
||||
|
||||
return get_device_manager().get_usb_device(timeout)
|
||||
|
||||
|
||||
def get_device(id: Optional[str], timeout: int = 0) -> core.Device:
|
||||
"""
|
||||
Get a device by its id
|
||||
"""
|
||||
|
||||
return get_device_manager().get_device(id, timeout)
|
||||
|
||||
|
||||
def get_device_matching(predicate: Callable[[core.Device], bool], timeout: int = 0) -> core.Device:
|
||||
"""
|
||||
Get device matching predicate.
|
||||
:param predicate: a function to filter the devices
|
||||
:param timeout: operation timeout in seconds
|
||||
"""
|
||||
|
||||
return get_device_manager().get_device_matching(predicate, timeout)
|
||||
|
||||
|
||||
def enumerate_devices() -> List[core.Device]:
|
||||
"""
|
||||
Enumerate all the devices from the device manager
|
||||
"""
|
||||
|
||||
return get_device_manager().enumerate_devices()
|
||||
|
||||
|
||||
@core.cancellable
|
||||
def shutdown() -> None:
|
||||
"""
|
||||
Shutdown the main device manager
|
||||
"""
|
||||
|
||||
get_device_manager()._impl.close()
|
||||
@@ -1,777 +0,0 @@
|
||||
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
|
||||
|
||||
# Exceptions
|
||||
class AddressInUseError(Exception): ...
|
||||
class ExecutableNotFoundError(Exception): ...
|
||||
class ExecutableNotSupportedError(Exception): ...
|
||||
class ServerNotRunningError(Exception): ...
|
||||
class TimedOutError(Exception): ...
|
||||
class TransportError(Exception): ...
|
||||
class ProcessNotFoundError(Exception): ...
|
||||
class ProcessNotRespondingError(Exception): ...
|
||||
class ProtocolError(Exception): ...
|
||||
class InvalidArgumentError(Exception): ...
|
||||
class InvalidOperationError(Exception): ...
|
||||
class NotSupportedError(Exception): ...
|
||||
class OperationCancelledError(Exception): ...
|
||||
class PermissionDeniedError(Exception): ...
|
||||
|
||||
class Object:
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
|
||||
def on(self, signal: str, callback: Callable[..., Any]) -> None:
|
||||
"""
|
||||
Add a signal handler.
|
||||
"""
|
||||
...
|
||||
|
||||
def off(self, signal: str, callback: Callable[..., Any]) -> None:
|
||||
"""
|
||||
Remove a signal handler.
|
||||
"""
|
||||
...
|
||||
|
||||
class Application(Object):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
"""
|
||||
Application identifier.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""
|
||||
Human-readable application name.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def parameters(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Parameters.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def pid(self) -> int:
|
||||
"""
|
||||
Process ID, or 0 if not running.
|
||||
"""
|
||||
...
|
||||
|
||||
class Bus(Object):
|
||||
def attach(self) -> None:
|
||||
"""
|
||||
Attach to the bus.
|
||||
"""
|
||||
...
|
||||
|
||||
def post(self, message: str, data: Optional[Union[bytes, str]]) -> None:
|
||||
"""
|
||||
Post a JSON-encoded message to the bus.
|
||||
"""
|
||||
...
|
||||
|
||||
class Cancellable(Object):
|
||||
def cancel(self) -> None:
|
||||
"""
|
||||
Set cancellable to cancelled.
|
||||
"""
|
||||
...
|
||||
|
||||
def connect(self, callback: Callable[..., Any]) -> int:
|
||||
"""
|
||||
Register notification callback.
|
||||
"""
|
||||
...
|
||||
|
||||
def disconnect(self, handler_id: int) -> None:
|
||||
"""
|
||||
Unregister notification callback.
|
||||
"""
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def get_current(cls) -> "Cancellable":
|
||||
"""
|
||||
Get the top cancellable from the stack.
|
||||
"""
|
||||
...
|
||||
|
||||
def get_fd(self) -> int:
|
||||
"""
|
||||
Get file descriptor for integrating with an event loop.
|
||||
"""
|
||||
...
|
||||
|
||||
def is_cancelled(self) -> bool:
|
||||
"""
|
||||
Query whether cancellable has been cancelled.
|
||||
"""
|
||||
...
|
||||
|
||||
def pop_current(self) -> None:
|
||||
"""
|
||||
Pop cancellable off the cancellable stack.
|
||||
"""
|
||||
...
|
||||
|
||||
def push_current(self) -> None:
|
||||
"""
|
||||
Push cancellable onto the cancellable stack.
|
||||
"""
|
||||
...
|
||||
|
||||
def raise_if_cancelled(self) -> None:
|
||||
"""
|
||||
Raise an exception if cancelled.
|
||||
"""
|
||||
...
|
||||
|
||||
def release_fd(self) -> None:
|
||||
"""
|
||||
Release a resource previously allocated by get_fd().
|
||||
"""
|
||||
...
|
||||
|
||||
class Child(Object):
|
||||
@property
|
||||
def argv(self) -> List[str]:
|
||||
"""
|
||||
Argument vector.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def envp(self) -> Dict[str, str]:
|
||||
"""
|
||||
Environment vector.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
"""
|
||||
Application identifier.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def origin(self) -> str:
|
||||
"""
|
||||
Origin.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def parent_pid(self) -> int:
|
||||
"""
|
||||
Parent Process ID.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def path(self) -> str:
|
||||
"""
|
||||
Path of executable.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def pid(self) -> int:
|
||||
"""
|
||||
Process ID.
|
||||
"""
|
||||
...
|
||||
|
||||
class Crash(Object):
|
||||
@property
|
||||
def parameters(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Parameters.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def pid(self) -> int:
|
||||
"""
|
||||
Process ID.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def process_name(self) -> str:
|
||||
"""
|
||||
Process name.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def report(self) -> str:
|
||||
"""
|
||||
Human-readable crash report.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def summary(self) -> str:
|
||||
"""
|
||||
Human-readable crash summary.
|
||||
"""
|
||||
...
|
||||
|
||||
class Device(Object):
|
||||
@property
|
||||
def id(self) -> Optional[str]:
|
||||
"""
|
||||
Device ID.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def name(self) -> Optional[str]:
|
||||
"""
|
||||
Human-readable device name.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def icon(self) -> Optional[Any]:
|
||||
"""
|
||||
Icon.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def type(self) -> Optional[str]:
|
||||
"""
|
||||
Device type. One of: local, remote, usb.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def bus(self) -> Optional[Bus]:
|
||||
"""
|
||||
Message bus.
|
||||
"""
|
||||
...
|
||||
|
||||
def attach(self, pid: int, realm: Optional[str] = None, persist_timeout: Optional[int] = None) -> "Session":
|
||||
"""
|
||||
Attach to a PID.
|
||||
"""
|
||||
...
|
||||
|
||||
def disable_spawn_gating(self) -> None:
|
||||
"""
|
||||
Disable spawn gating.
|
||||
"""
|
||||
...
|
||||
|
||||
def enable_spawn_gating(self) -> None:
|
||||
"""
|
||||
Enable spawn gating.
|
||||
"""
|
||||
...
|
||||
|
||||
def enumerate_applications(
|
||||
self, identifiers: Optional[Sequence[str]] = None, scope: Optional[str] = None
|
||||
) -> List[Application]:
|
||||
"""
|
||||
Enumerate applications.
|
||||
"""
|
||||
...
|
||||
|
||||
def enumerate_pending_children(self) -> List[Child]:
|
||||
"""
|
||||
Enumerate pending children.
|
||||
"""
|
||||
...
|
||||
|
||||
def enumerate_pending_spawn(self) -> List["Spawn"]:
|
||||
"""
|
||||
Enumerate pending spawn.
|
||||
"""
|
||||
...
|
||||
|
||||
def enumerate_processes(self, pids: Optional[Sequence[int]] = None, scope: Optional[str] = None) -> List[Process]:
|
||||
"""
|
||||
Enumerate processes.
|
||||
"""
|
||||
...
|
||||
|
||||
def get_frontmost_application(self, scope: Optional[str] = None) -> Optional[Application]:
|
||||
"""
|
||||
Get details about the frontmost application.
|
||||
"""
|
||||
...
|
||||
|
||||
def inject_library_blob(self, pid: int, blob_buffer: bytes, entrypoint: str, data: str) -> int:
|
||||
"""
|
||||
Inject a library blob to a PID.
|
||||
"""
|
||||
...
|
||||
|
||||
def inject_library_file(self, pid: int, path: str, entrypoint: str, data: str) -> int:
|
||||
"""
|
||||
Inject a library file to a PID.
|
||||
"""
|
||||
...
|
||||
|
||||
def input(self, pid: int, data: bytes) -> None:
|
||||
"""
|
||||
Input data on stdin of a spawned process.
|
||||
"""
|
||||
...
|
||||
|
||||
def is_lost(self) -> bool:
|
||||
"""
|
||||
Query whether the device has been lost.
|
||||
"""
|
||||
...
|
||||
|
||||
def kill(self, pid: int) -> None:
|
||||
"""
|
||||
Kill a PID.
|
||||
"""
|
||||
...
|
||||
|
||||
def open_channel(self, address: str) -> "IOStream":
|
||||
"""
|
||||
Open a device-specific communication channel.
|
||||
"""
|
||||
...
|
||||
|
||||
def open_service(self, address: str) -> Service:
|
||||
"""
|
||||
Open a device-specific service.
|
||||
"""
|
||||
...
|
||||
|
||||
def unpair(self) -> None:
|
||||
"""
|
||||
Unpair device.
|
||||
"""
|
||||
...
|
||||
|
||||
def query_system_parameters(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Returns a dictionary of information about the host system.
|
||||
"""
|
||||
...
|
||||
|
||||
def resume(self, pid: int) -> None:
|
||||
"""
|
||||
Resume a process from the attachable state.
|
||||
"""
|
||||
...
|
||||
|
||||
def spawn(
|
||||
self,
|
||||
program: str,
|
||||
argv: Union[None, List[Union[str, bytes]], Tuple[Union[str, bytes]]] = None,
|
||||
envp: Optional[Dict[str, str]] = None,
|
||||
env: Optional[Dict[str, str]] = None,
|
||||
cwd: Optional[str] = None,
|
||||
stdio: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
) -> int:
|
||||
"""
|
||||
Spawn a process into an attachable state.
|
||||
"""
|
||||
...
|
||||
|
||||
class DeviceManager(Object):
|
||||
def add_remote_device(
|
||||
self,
|
||||
address: str,
|
||||
certificate: Optional[str] = None,
|
||||
origin: Optional[str] = None,
|
||||
token: Optional[str] = None,
|
||||
keepalive_interval: Optional[int] = None,
|
||||
) -> Device:
|
||||
"""
|
||||
Add a remote device.
|
||||
"""
|
||||
...
|
||||
|
||||
def close(self) -> None:
|
||||
"""
|
||||
Close the device manager.
|
||||
"""
|
||||
...
|
||||
|
||||
def enumerate_devices(self) -> List[Device]:
|
||||
"""
|
||||
Enumerate devices.
|
||||
"""
|
||||
...
|
||||
|
||||
def get_device_matching(self, predicate: Callable[[Device], bool], timeout: int) -> Device:
|
||||
"""
|
||||
Get device matching predicate.
|
||||
"""
|
||||
...
|
||||
|
||||
def remove_remote_device(self, address: str) -> None:
|
||||
"""
|
||||
Remove a remote device.
|
||||
"""
|
||||
...
|
||||
|
||||
class EndpointParameters(Object): ...
|
||||
|
||||
class FileMonitor(Object):
|
||||
def disable(self) -> None:
|
||||
"""
|
||||
Disable the file monitor.
|
||||
"""
|
||||
...
|
||||
|
||||
def enable(self) -> None:
|
||||
"""
|
||||
Enable the file monitor.
|
||||
"""
|
||||
...
|
||||
|
||||
class IOStream(Object):
|
||||
def close(self) -> None:
|
||||
"""
|
||||
Close the stream.
|
||||
"""
|
||||
...
|
||||
|
||||
def is_closed(self) -> bool:
|
||||
"""
|
||||
Query whether the stream is closed.
|
||||
"""
|
||||
...
|
||||
|
||||
def read(self, size: int) -> bytes:
|
||||
"""
|
||||
Read up to the specified number of bytes from the stream.
|
||||
"""
|
||||
...
|
||||
|
||||
def read_all(self, size: int) -> bytes:
|
||||
"""
|
||||
Read exactly the specified number of bytes from the stream.
|
||||
"""
|
||||
...
|
||||
|
||||
def write(self, data: bytes) -> int:
|
||||
"""
|
||||
Write as much as possible of the provided data to the stream.
|
||||
"""
|
||||
...
|
||||
|
||||
def write_all(self, data: bytes) -> None:
|
||||
"""
|
||||
Write all of the provided data to the stream.
|
||||
"""
|
||||
...
|
||||
|
||||
class PortalMembership(Object):
|
||||
def terminate(self) -> None:
|
||||
"""
|
||||
Terminate the membership.
|
||||
"""
|
||||
...
|
||||
|
||||
class PortalService(Object):
|
||||
@property
|
||||
def device(self) -> Device:
|
||||
"""
|
||||
Device for in-process control.
|
||||
"""
|
||||
...
|
||||
|
||||
def broadcast(self, message: str, data: Optional[Union[str, bytes]] = None) -> None:
|
||||
"""
|
||||
Broadcast a message to all control channels.
|
||||
"""
|
||||
...
|
||||
|
||||
def enumerate_tags(self, connection_id: int) -> List[str]:
|
||||
"""
|
||||
Enumerate tags of a specific connection.
|
||||
"""
|
||||
...
|
||||
|
||||
def kick(self, connection_id: int) -> None:
|
||||
"""
|
||||
Kick out a specific connection.
|
||||
"""
|
||||
...
|
||||
|
||||
def narrowcast(self, tag: str, message: str, data: Optional[Union[str, bytes]] = None) -> None:
|
||||
"""
|
||||
Post a message to control channels with a specific tag.
|
||||
"""
|
||||
...
|
||||
|
||||
def post(self, connection_id: int, message: str, data: Optional[Union[str, bytes]] = None) -> None:
|
||||
"""
|
||||
Post a message to a specific control channel.
|
||||
"""
|
||||
...
|
||||
|
||||
def start(self) -> None:
|
||||
"""
|
||||
Start listening for incoming connections.
|
||||
"""
|
||||
...
|
||||
|
||||
def stop(self) -> None:
|
||||
"""
|
||||
Stop listening for incoming connections, and kick any connected clients.
|
||||
"""
|
||||
...
|
||||
|
||||
def tag(self, connection_id: int, tag: str) -> None:
|
||||
"""
|
||||
Tag a specific control channel.
|
||||
"""
|
||||
...
|
||||
|
||||
def untag(self, connection_id: int, tag: str) -> None:
|
||||
"""
|
||||
Untag a specific control channel.
|
||||
"""
|
||||
...
|
||||
|
||||
class Process(Object):
|
||||
@property
|
||||
def pid(self) -> int:
|
||||
"""
|
||||
Process ID.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""
|
||||
Human-readable process name.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def parameters(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Parameters.
|
||||
"""
|
||||
...
|
||||
|
||||
class Relay(Object):
|
||||
def __init__(self, address: str, username: str, password: str, kind: str) -> None: ...
|
||||
@property
|
||||
def address(self) -> str:
|
||||
"""
|
||||
Network address or address:port of the TURN server.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def kind(self) -> str:
|
||||
"""
|
||||
Relay kind. One of: turn-udp, turn-tcp, turn-tls.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def password(self) -> str:
|
||||
"""
|
||||
The TURN password to use for the allocate request.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def username(self) -> str:
|
||||
"""
|
||||
The TURN username to use for the allocate request.
|
||||
"""
|
||||
...
|
||||
|
||||
class Script(Object):
|
||||
def eternalize(self) -> None:
|
||||
"""
|
||||
Eternalize the script.
|
||||
"""
|
||||
...
|
||||
|
||||
def is_destroyed(self) -> bool:
|
||||
"""
|
||||
Query whether the script has been destroyed.
|
||||
"""
|
||||
...
|
||||
|
||||
def load(self) -> None:
|
||||
"""
|
||||
Load the script.
|
||||
"""
|
||||
...
|
||||
|
||||
def post(self, message: str, data: Optional[Union[str, bytes]] = None) -> None:
|
||||
"""
|
||||
Post a JSON-encoded message to the script.
|
||||
"""
|
||||
...
|
||||
|
||||
def unload(self) -> None:
|
||||
"""
|
||||
Unload the script.
|
||||
"""
|
||||
...
|
||||
|
||||
def enable_debugger(self, port: Optional[int]) -> None:
|
||||
"""
|
||||
Enable the Node.js compatible script debugger
|
||||
"""
|
||||
...
|
||||
|
||||
def disable_debugger(self) -> None:
|
||||
"""
|
||||
Disable the Node.js compatible script debugger
|
||||
"""
|
||||
...
|
||||
|
||||
class Service(Object):
|
||||
def activate(self) -> None:
|
||||
"""
|
||||
Activate the service.
|
||||
"""
|
||||
...
|
||||
|
||||
def cancel(self) -> None:
|
||||
"""
|
||||
Cancel the service.
|
||||
"""
|
||||
...
|
||||
|
||||
def request(self, parameters: Any) -> Any:
|
||||
"""
|
||||
Perform a request.
|
||||
"""
|
||||
...
|
||||
|
||||
class Session(Object):
|
||||
@property
|
||||
def pid(self) -> int:
|
||||
"""
|
||||
Process ID.
|
||||
"""
|
||||
...
|
||||
|
||||
def compile_script(self, source: str, name: Optional[str] = None, runtime: Optional[str] = None) -> bytes:
|
||||
"""
|
||||
Compile script source code to bytecode.
|
||||
"""
|
||||
...
|
||||
|
||||
def create_script(self, source: str, name: Optional[str] = None, runtime: Optional[str] = None) -> Script:
|
||||
"""
|
||||
Create a new script.
|
||||
"""
|
||||
...
|
||||
|
||||
def create_script_from_bytes(
|
||||
self, data: bytes, name: Optional[str] = None, runtime: Optional[str] = None
|
||||
) -> Script:
|
||||
"""
|
||||
Create a new script from bytecode.
|
||||
"""
|
||||
...
|
||||
|
||||
def snapshot_script(self, embed_script: str, warmup_script: Optional[str], runtime: Optional[str] = None) -> bytes:
|
||||
"""
|
||||
Evaluate script and snapshot the resulting VM state
|
||||
"""
|
||||
...
|
||||
|
||||
def detach(self) -> None:
|
||||
"""
|
||||
Detach session from the process.
|
||||
"""
|
||||
...
|
||||
|
||||
def disable_child_gating(self) -> None:
|
||||
"""
|
||||
Disable child gating.
|
||||
"""
|
||||
...
|
||||
|
||||
def enable_child_gating(self) -> None:
|
||||
"""
|
||||
Enable child gating.
|
||||
"""
|
||||
...
|
||||
|
||||
def is_detached(self) -> bool:
|
||||
"""
|
||||
Query whether the session is detached.
|
||||
"""
|
||||
...
|
||||
|
||||
def join_portal(
|
||||
self, address: str, certificate: Optional[str] = None, token: Optional[str] = None, acl: Optional[Any] = None
|
||||
) -> PortalMembership:
|
||||
"""
|
||||
Join a portal.
|
||||
"""
|
||||
...
|
||||
|
||||
def resume(self) -> None:
|
||||
"""
|
||||
Resume session after network error.
|
||||
"""
|
||||
...
|
||||
|
||||
def setup_peer_connection(
|
||||
self, stun_server: Optional[str] = None, relays: Optional[Sequence[Relay]] = None
|
||||
) -> None:
|
||||
"""
|
||||
Set up a peer connection with the target process.
|
||||
"""
|
||||
...
|
||||
|
||||
class Spawn(Object):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
"""
|
||||
Application identifier.
|
||||
"""
|
||||
...
|
||||
|
||||
@property
|
||||
def pid(self) -> int:
|
||||
"""
|
||||
Process ID.
|
||||
"""
|
||||
...
|
||||
|
||||
class Compiler(Object):
|
||||
def build(
|
||||
self,
|
||||
entrypoint: str,
|
||||
project_root: Optional[str] = None,
|
||||
source_maps: Optional[str] = None,
|
||||
compression: Optional[str] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Build an agent.
|
||||
"""
|
||||
...
|
||||
|
||||
def watch(
|
||||
self,
|
||||
entrypoint: str,
|
||||
project_root: Optional[str] = None,
|
||||
source_maps: Optional[str] = None,
|
||||
compression: Optional[str] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Continuously build an agent.
|
||||
"""
|
||||
...
|
||||
|
||||
__version__: str
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
||||
py_sources = [
|
||||
'__init__.pyi',
|
||||
'py.typed',
|
||||
]
|
||||
python.install_sources(py_sources, subdir: 'frida' / '_frida', pure: false)
|
||||
|
||||
extra_link_args = []
|
||||
if host_os_family == 'darwin'
|
||||
extra_link_args += '-Wl,-exported_symbol,_PyInit__frida'
|
||||
elif host_os_family != 'windows'
|
||||
extra_link_args += '-Wl,--version-script,' + meson.current_source_dir() / 'extension.version'
|
||||
endif
|
||||
|
||||
extension = python.extension_module('_frida', 'extension.c',
|
||||
limited_api: '3.7',
|
||||
c_args: frida_component_cflags,
|
||||
link_args: extra_link_args,
|
||||
dependencies: [python_dep, frida_core_dep, os_deps],
|
||||
install: true,
|
||||
subdir: 'frida',
|
||||
)
|
||||
-1747
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
from .cli import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
static GHashTable * pygobject_type_spec_by_type;
|
||||
@@ -0,0 +1,77 @@
|
||||
static void
|
||||
PyGObject_class_init (void)
|
||||
{
|
||||
pygobject_type_spec_by_type = g_hash_table_new_full (NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
PyGObject_init (PyGObject * self)
|
||||
{
|
||||
self->handle = NULL;
|
||||
self->type = PYFRIDA_TYPE (GObject);
|
||||
|
||||
self->signal_closures = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
PyGObject_dealloc (PyGObject * self)
|
||||
{
|
||||
gpointer handle;
|
||||
|
||||
handle = PyGObject_steal_handle (self);
|
||||
if (handle != NULL)
|
||||
{
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
self->type->destroy (handle);
|
||||
Py_END_ALLOW_THREADS
|
||||
}
|
||||
|
||||
((freefunc) PyType_GetSlot (Py_TYPE (self), Py_tp_free)) (self);
|
||||
}
|
||||
|
||||
static void
|
||||
PyGObject_take_handle (PyGObject * self,
|
||||
gpointer handle,
|
||||
const PyGObjectType * type)
|
||||
{
|
||||
self->handle = handle;
|
||||
self->type = type;
|
||||
|
||||
if (handle != NULL)
|
||||
g_object_set_data (G_OBJECT (handle), "pyobject", self);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
PyGObject_steal_handle (PyGObject * self)
|
||||
{
|
||||
gpointer handle = self->handle;
|
||||
GSList * entry;
|
||||
|
||||
if (handle == NULL)
|
||||
return NULL;
|
||||
|
||||
for (entry = self->signal_closures; entry != NULL; entry = entry->next)
|
||||
{
|
||||
PyGObjectSignalClosure * closure = entry->data;
|
||||
G_GNUC_UNUSED guint num_matches;
|
||||
|
||||
num_matches = g_signal_handlers_disconnect_matched (handle, G_SIGNAL_MATCH_CLOSURE, closure->signal_id, 0, &closure->parent, NULL, NULL);
|
||||
g_assert (num_matches == 1);
|
||||
}
|
||||
g_clear_pointer (&self->signal_closures, g_slist_free);
|
||||
|
||||
g_object_set_data (G_OBJECT (handle), "pyobject", NULL);
|
||||
|
||||
self->handle = NULL;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void
|
||||
PyGObject_register_type (GType instance_type,
|
||||
PyGObjectType * python_type)
|
||||
{
|
||||
g_hash_table_insert (pygobject_type_spec_by_type, GSIZE_TO_POINTER (instance_type), python_type);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
static void PyGObject_class_init (void);
|
||||
static int PyGObject_init (PyGObject * self);
|
||||
static void PyGObject_dealloc (PyGObject * self);
|
||||
static gpointer PyGObject_steal_handle (PyGObject * self);
|
||||
static void PyGObject_register_type (GType instance_type, PyGObjectType * python_type);
|
||||
@@ -0,0 +1,100 @@
|
||||
type SignalTransformer<
|
||||
Source extends SignalHandler,
|
||||
Target extends SignalHandler
|
||||
> = (...args: Parameters<Source>) => Parameters<Target>;
|
||||
|
||||
type SignalInterceptor<H extends SignalHandler> = (...args: Parameters<H>) => boolean;
|
||||
|
||||
interface SignalWrapperOptionsNoTransform<H extends SignalHandler> {
|
||||
transform?: undefined;
|
||||
intercept?: SignalInterceptor<H>;
|
||||
}
|
||||
|
||||
interface SignalWrapperOptionsTransform<
|
||||
Source extends SignalHandler,
|
||||
Target extends SignalHandler
|
||||
> {
|
||||
transform: SignalTransformer<Source, Target>;
|
||||
intercept?: SignalInterceptor<Target>;
|
||||
}
|
||||
|
||||
type SignalWrapperOptions<
|
||||
Source extends SignalHandler,
|
||||
Target extends SignalHandler
|
||||
> =
|
||||
| SignalWrapperOptionsNoTransform<Source & Target>
|
||||
| SignalWrapperOptionsTransform<Source, Target>;
|
||||
|
||||
class SignalWrapper<
|
||||
SourceHandler extends SignalHandler,
|
||||
TargetHandler extends SignalHandler
|
||||
> {
|
||||
#source: Signal<SourceHandler>;
|
||||
#transform?: SignalTransformer<SourceHandler, TargetHandler>;
|
||||
#intercept?: SignalInterceptor<any>;
|
||||
|
||||
#handlers = new Set<TargetHandler>();
|
||||
|
||||
constructor(
|
||||
source: Signal<SourceHandler>,
|
||||
options?: SignalWrapperOptions<SourceHandler, TargetHandler>
|
||||
) {
|
||||
this.#source = source;
|
||||
|
||||
if (options === undefined || options.transform === undefined) {
|
||||
this.#intercept = options?.intercept;
|
||||
} else {
|
||||
this.#transform = options.transform;
|
||||
this.#intercept = options.intercept;
|
||||
}
|
||||
}
|
||||
|
||||
connect(handler: TargetHandler): void {
|
||||
this.#handlers.add(handler);
|
||||
if (this.#handlers.size === 1) {
|
||||
this.#source.connect(this.#wrappedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
disconnect(handler: TargetHandler): void {
|
||||
this.#handlers.delete(handler);
|
||||
if (this.#handlers.size === 0) {
|
||||
this.#source.disconnect(this.#wrappedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
#wrappedHandler = ((...sourceArgs: Parameters<SourceHandler>) => {
|
||||
let targetArgs: Parameters<TargetHandler>;
|
||||
const transform = this.#transform;
|
||||
if (transform === undefined) {
|
||||
targetArgs = sourceArgs as unknown as Parameters<TargetHandler>;
|
||||
} else {
|
||||
targetArgs = transform(...sourceArgs);
|
||||
}
|
||||
|
||||
const intercept = this.#intercept;
|
||||
if (intercept !== undefined) {
|
||||
if (!intercept(...targetArgs)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (const handler of this.#handlers) {
|
||||
handler(...targetArgs);
|
||||
}
|
||||
}) as SourceHandler;
|
||||
}
|
||||
|
||||
function inspectWrapper(object: any, name: string, properties: string[], depth: number, options: util.InspectOptionsStylized): string {
|
||||
if (depth < 0) {
|
||||
return options.stylize(`[${name}]`, "special");
|
||||
}
|
||||
|
||||
const summary = Object.fromEntries(properties.map(name => [name, object[name]]));
|
||||
|
||||
const nextOptions = Object.assign({}, options, {
|
||||
depth: (options.depth === null) ? null : depth - 1
|
||||
});
|
||||
|
||||
return name + " " + inspect(summary, nextOptions);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#define PYFRIDA_TYPE(name) \
|
||||
(&_PYFRIDA_TYPE_VAR (name, type))
|
||||
#define PYFRIDA_TYPE_OBJECT(name) \
|
||||
PYFRIDA_TYPE (name)->object
|
||||
#define _PYFRIDA_TYPE_VAR(name, var) \
|
||||
G_PASTE (G_PASTE (G_PASTE (Py, name), _), var)
|
||||
#define PYFRIDA_DEFINE_BASETYPE(pyname, cname, ...) \
|
||||
_PYFRIDA_DEFINE_TYPE_SLOTS (cname, __VA_ARGS__); \
|
||||
_PYFRIDA_DEFINE_TYPE_SPEC (cname, pyname, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE); \
|
||||
static PyGObjectType _PYFRIDA_TYPE_VAR (cname, type) = \
|
||||
{ \
|
||||
.parent = NULL, \
|
||||
.object = NULL, \
|
||||
}
|
||||
#define PYFRIDA_DEFINE_TYPE(pyname, cname, parent_cname, ...) \
|
||||
_PYFRIDA_DEFINE_TYPE_SLOTS (cname, __VA_ARGS__); \
|
||||
_PYFRIDA_DEFINE_TYPE_SPEC (cname, pyname, Py_TPFLAGS_DEFAULT); \
|
||||
static PyGObjectType _PYFRIDA_TYPE_VAR (cname, type) = \
|
||||
{ \
|
||||
.parent = PYFRIDA_TYPE (parent_cname), \
|
||||
.object = NULL, \
|
||||
}
|
||||
#define PYFRIDA_REGISTER_TYPE(cname, gtype) \
|
||||
G_BEGIN_DECLS \
|
||||
{ \
|
||||
PyGObjectType * t = PYFRIDA_TYPE (cname); \
|
||||
t->object = PyType_FromSpecWithBases (&_PYFRIDA_TYPE_VAR (cname, spec), \
|
||||
(t->parent != NULL) ? PyTuple_Pack (1, t->parent->object) : NULL); \
|
||||
PyGObject_register_type (gtype, t); \
|
||||
Py_IncRef (t->object); \
|
||||
PyModule_AddObject (module, G_STRINGIFY (cname), t->object); \
|
||||
} \
|
||||
G_END_DECLS
|
||||
#define _PYFRIDA_DEFINE_TYPE_SPEC(cname, pyname, type_flags) \
|
||||
static PyType_Spec _PYFRIDA_TYPE_VAR (cname, spec) = \
|
||||
{ \
|
||||
.name = pyname, \
|
||||
.basicsize = sizeof (G_PASTE (Py, cname)), \
|
||||
.itemsize = 0, \
|
||||
.flags = type_flags, \
|
||||
.slots = _PYFRIDA_TYPE_VAR (cname, slots), \
|
||||
}
|
||||
#define _PYFRIDA_DEFINE_TYPE_SLOTS(cname, ...) \
|
||||
static PyType_Slot _PYFRIDA_TYPE_VAR (cname, slots)[] = \
|
||||
{ \
|
||||
__VA_ARGS__ \
|
||||
{ 0 }, \
|
||||
}
|
||||
|
||||
#define PY_GOBJECT(o) ((PyGObject *) (o))
|
||||
#define PY_GOBJECT_HANDLE(o) (PY_GOBJECT (o)->handle)
|
||||
#define PY_GOBJECT_SIGNAL_CLOSURE(o) ((PyGObjectSignalClosure *) (o))
|
||||
|
||||
#define PyFrida_RETURN_NONE \
|
||||
G_STMT_START \
|
||||
{ \
|
||||
Py_IncRef (Py_None); \
|
||||
return Py_None; \
|
||||
} \
|
||||
G_STMT_END
|
||||
@@ -0,0 +1,78 @@
|
||||
G_GNUC_UNUSED static gboolean fdn_is_null (napi_env env, napi_value value);
|
||||
static gboolean fdn_is_undefined_or_null (napi_env env, napi_value value);
|
||||
static gboolean fdn_is_function (napi_env env, napi_value value);
|
||||
|
||||
static gboolean fdn_boolean_from_value (napi_env env, napi_value value, gboolean * b);
|
||||
static napi_value fdn_boolean_to_value (napi_env env, gboolean b);
|
||||
static gboolean fdn_size_from_value (napi_env env, napi_value value, gsize * s);
|
||||
static napi_value fdn_size_to_value (napi_env env, gsize s);
|
||||
static napi_value fdn_ssize_to_value (napi_env env, gssize s);
|
||||
static gboolean fdn_int_from_value (napi_env env, napi_value value, gint * i);
|
||||
static napi_value fdn_int_to_value (napi_env env, gint i);
|
||||
static gboolean fdn_uint_from_value (napi_env env, napi_value value, guint * u);
|
||||
static napi_value fdn_uint_to_value (napi_env env, guint u);
|
||||
static gboolean fdn_uint16_from_value (napi_env env, napi_value value, guint16 * u);
|
||||
static napi_value fdn_uint16_to_value (napi_env env, guint16 u);
|
||||
static napi_value fdn_uint32_to_value (napi_env env, guint32 u);
|
||||
static gboolean fdn_int64_from_value (napi_env env, napi_value value, gint64 * i);
|
||||
static napi_value fdn_int64_to_value (napi_env env, gint64 i);
|
||||
static napi_value fdn_uint64_to_value (napi_env env, guint64 u);
|
||||
static gboolean fdn_ulong_from_value (napi_env env, napi_value value, gulong * u);
|
||||
static gboolean fdn_double_from_value (napi_env env, napi_value value, gdouble * d);
|
||||
static napi_value fdn_double_to_value (napi_env env, gdouble d);
|
||||
static gboolean fdn_enum_from_value (napi_env env, GType enum_type, napi_value value, gint * e);
|
||||
static napi_value fdn_enum_to_value (napi_env env, GType enum_type, gint e);
|
||||
static gboolean fdn_utf8_from_value (napi_env env, napi_value value, gchar ** str);
|
||||
static napi_value fdn_utf8_to_value (napi_env env, const gchar * str);
|
||||
static gboolean fdn_strv_from_value (napi_env env, napi_value value, gchar *** strv);
|
||||
static napi_value fdn_strv_to_value (napi_env env, gchar ** strv);
|
||||
static napi_value fdn_buffer_to_value (napi_env env, const guint8 * data, gsize size);
|
||||
static gboolean fdn_bytes_from_value (napi_env env, napi_value value, GBytes ** bytes);
|
||||
static napi_value fdn_bytes_to_value (napi_env env, GBytes * bytes);
|
||||
static gboolean fdn_vardict_from_value (napi_env env, napi_value value, GHashTable ** vardict);
|
||||
static napi_value fdn_vardict_to_value (napi_env env, GHashTable * vardict);
|
||||
static gboolean fdn_variant_from_value (napi_env env, napi_value value, GVariant ** variant);
|
||||
static napi_value fdn_variant_to_value (napi_env env, GVariant * variant);
|
||||
static gboolean fdn_gvalue_from_value (napi_env env, GType type, napi_value js_value, GValue * value);
|
||||
static napi_value fdn_gvalue_to_value (napi_env env, GValue * value);
|
||||
static gboolean fdn_error_from_value (napi_env env, napi_value value, GError ** error);
|
||||
static napi_value fdn_error_to_value (napi_env env, GError * error);
|
||||
static gboolean fdn_file_from_value (napi_env env, napi_value value, GFile ** file);
|
||||
static napi_value fdn_file_to_value (napi_env env, GFile * file);
|
||||
static gboolean fdn_tls_certificate_from_value (napi_env env, napi_value value, GTlsCertificate ** certificate);
|
||||
static napi_value fdn_tls_certificate_to_value (napi_env env, GTlsCertificate * certificate);
|
||||
static gboolean fdn_options_from_value (napi_env env, GType object_type, napi_value value, gpointer * options);
|
||||
|
||||
static napi_value fdn_object_subclass_to_value (napi_env env, GObject * object);
|
||||
static napi_value fdn_object_new (napi_env env, GObject * handle, napi_ref constructor);
|
||||
static gboolean fdn_object_wrap (napi_env env, napi_value wrapper, GObject * handle, napi_finalize finalizer);
|
||||
static gboolean fdn_object_unwrap (napi_env env, napi_value wrapper, GType expected_type, GObject ** handle);
|
||||
static void fdn_object_finalize (napi_env env, void * finalize_data, void * finalize_hint);
|
||||
static napi_value fdn_object_get_signal (napi_env env, napi_callback_info info, const gchar * name, const gchar * js_storage_name, FdnSignalBehavior behavior);
|
||||
|
||||
static napi_value fdn_signal_new (napi_env env, GObject * handle, const gchar * name, FdnSignalBehavior behavior);
|
||||
static void fdn_signal_register (napi_env env, napi_value exports);
|
||||
static napi_value fdn_signal_construct (napi_env env, napi_callback_info info);
|
||||
static void fdn_signal_finalize (napi_env env, void * finalize_data, void * finalize_hint);
|
||||
static napi_value fdn_signal_connect (napi_env env, napi_callback_info info);
|
||||
static napi_value fdn_signal_disconnect (napi_env env, napi_callback_info info);
|
||||
static void fdn_signal_disconnect_closure (FdnSignal * self, FdnSignalClosure * closure);
|
||||
static gboolean fdn_signal_parse_arguments (napi_env env, napi_callback_info info, FdnSignal ** self, napi_value * js_self, napi_value * handler);
|
||||
static FdnSignalClosure * fdn_signal_closure_new (napi_env env, FdnSignal * sig, napi_value js_sig, napi_value handler);
|
||||
static void fdn_signal_closure_finalize (gpointer data, GClosure * closure);
|
||||
static void fdn_signal_closure_marshal (GClosure * closure, GValue * return_gvalue, guint n_param_values, const GValue * param_values, gpointer invocation_hint, gpointer marshal_data);
|
||||
static void fdn_signal_closure_deliver (napi_env env, napi_value js_cb, void * context, void * data);
|
||||
|
||||
static void fdn_keep_alive_until (napi_env env, napi_value js_object, GObject * handle, FdnIsDestroyedFunc is_destroyed, const gchar * destroy_signal_name);
|
||||
static void fdn_keep_alive_on_finalize (napi_env env, void * finalize_data, void * finalize_hint);
|
||||
static void fdn_keep_alive_on_destroy_signal (GObject * handle, gpointer user_data);
|
||||
static void fdn_keep_alive_on_destroy_signal_handler_detached (gpointer data, GClosure * closure);
|
||||
static void fdn_keep_alive_schedule_cleanup (FdnKeepAliveContext * context);
|
||||
static void fdn_keep_alive_on_tsfn_invoke (napi_env env, napi_value js_cb, void * context, void * data);
|
||||
|
||||
static void fdn_inherit_val_val (napi_env env, napi_value sub_ctor, napi_value super_ctor, napi_value object_ctor, napi_value set_proto);
|
||||
G_GNUC_UNUSED static void fdn_inherit_val_ref (napi_env env, napi_value sub_ctor, napi_ref super_ctor, napi_value object_ctor, napi_value set_proto);
|
||||
static void fdn_inherit_ref_val (napi_env env, napi_ref sub_ctor, napi_value super_ctor, napi_value object_ctor, napi_value set_proto);
|
||||
static void fdn_inherit_ref_ref (napi_env env, napi_ref sub_ctor, napi_ref super_ctor, napi_value object_ctor, napi_value set_proto);
|
||||
|
||||
static gchar * fdn_camel_case_to_kebab_case (const gchar * name);
|
||||
@@ -0,0 +1,24 @@
|
||||
struct _PyGObject
|
||||
{
|
||||
PyObject_HEAD
|
||||
|
||||
gpointer handle;
|
||||
const PyGObjectType * type;
|
||||
|
||||
GSList * signal_closures;
|
||||
};
|
||||
|
||||
struct _PyGObjectType
|
||||
{
|
||||
PyGObjectType * parent;
|
||||
PyObject * object;
|
||||
PyGObjectInitFromHandleFunc init_from_handle;
|
||||
GDestroyNotify destroy;
|
||||
};
|
||||
|
||||
struct _PyGObjectSignalClosure
|
||||
{
|
||||
GClosure parent;
|
||||
guint signal_id;
|
||||
guint max_arg_count;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
typedef struct _PyGObject PyGObject;
|
||||
typedef void (* PyGObjectInitFromHandleFunc) (PyObject * self, gpointer handle);
|
||||
typedef struct _PyGObjectType PyGObjectType;
|
||||
typedef struct _PyGObjectSignalClosure PyGObjectSignalClosure;
|
||||
@@ -0,0 +1,13 @@
|
||||
querySystemParameters
|
||||
spawn
|
||||
resume
|
||||
kill
|
||||
attach
|
||||
injectLibraryFile
|
||||
injectLibraryBlob
|
||||
enumerateDevices
|
||||
getDeviceManager
|
||||
getLocalDevice
|
||||
getRemoteDevice
|
||||
getUsbDevice
|
||||
getDevice
|
||||
@@ -0,0 +1,157 @@
|
||||
let sharedDeviceManager: DeviceManager | null = null;
|
||||
|
||||
export async function querySystemParameters(cancellable?: Cancellable | null): Promise<SystemParameters> {
|
||||
const device = await getLocalDevice(cancellable);
|
||||
return await device.querySystemParameters(cancellable);
|
||||
}
|
||||
|
||||
export async function spawn(program: string | string[], options?: SpawnOptions, cancellable?: Cancellable | null): Promise<number> {
|
||||
const device = await getLocalDevice(cancellable);
|
||||
return await device.spawn(program, options, cancellable);
|
||||
}
|
||||
|
||||
export async function resume(target: TargetProcess, cancellable?: Cancellable | null): Promise<void> {
|
||||
const device = await getLocalDevice(cancellable);
|
||||
await device.resume(target, cancellable);
|
||||
}
|
||||
|
||||
export async function kill(target: TargetProcess, cancellable?: Cancellable | null): Promise<void> {
|
||||
const device = await getLocalDevice(cancellable);
|
||||
await device.kill(target, cancellable);
|
||||
}
|
||||
|
||||
export async function attach(target: TargetProcess, options?: SessionOptions, cancellable?: Cancellable | null): Promise<Session> {
|
||||
const device = await getLocalDevice(cancellable);
|
||||
return await device.attach(target, options, cancellable);
|
||||
}
|
||||
|
||||
export async function injectLibraryFile(target: TargetProcess, path: string, entrypoint: string, data: string,
|
||||
cancellable?: Cancellable | null): Promise<number> {
|
||||
const device = await getLocalDevice(cancellable);
|
||||
return await device.injectLibraryFile(target, path, entrypoint, data, cancellable);
|
||||
}
|
||||
|
||||
export async function injectLibraryBlob(target: TargetProcess, blob: Buffer, entrypoint: string, data: string,
|
||||
cancellable?: Cancellable | null): Promise<number> {
|
||||
const device = await getLocalDevice(cancellable);
|
||||
return await device.injectLibraryBlob(target, blob, entrypoint, data, cancellable);
|
||||
}
|
||||
|
||||
export async function enumerateDevices(cancellable?: Cancellable | null): Promise<Device[]> {
|
||||
const deviceManager = getDeviceManager();
|
||||
return await deviceManager.enumerateDevices(cancellable);
|
||||
};
|
||||
|
||||
export function getDeviceManager(): DeviceManager {
|
||||
if (sharedDeviceManager === null) {
|
||||
sharedDeviceManager = new DeviceManager();
|
||||
}
|
||||
return sharedDeviceManager;
|
||||
}
|
||||
|
||||
export function getLocalDevice(cancellable?: Cancellable | null): Promise<Device> {
|
||||
return getMatchingDevice(device => device.type === DeviceType.Local, {}, cancellable);
|
||||
}
|
||||
|
||||
export function getRemoteDevice(cancellable?: Cancellable | null): Promise<Device> {
|
||||
return getMatchingDevice(device => device.type === DeviceType.Remote, {}, cancellable);
|
||||
}
|
||||
|
||||
export function getUsbDevice(options?: GetDeviceOptions, cancellable?: Cancellable | null): Promise<Device> {
|
||||
return getMatchingDevice(device => device.type === DeviceType.Usb, options, cancellable);
|
||||
}
|
||||
|
||||
export function getDevice(id: string, options?: GetDeviceOptions, cancellable?: Cancellable | null): Promise<Device> {
|
||||
return getMatchingDevice(device => device.id === id, options, cancellable);
|
||||
}
|
||||
|
||||
export interface GetDeviceOptions {
|
||||
timeout?: number | null;
|
||||
}
|
||||
|
||||
async function getMatchingDevice(predicate: DevicePredicate, options: GetDeviceOptions = {}, cancellable: Cancellable | null = null): Promise<Device> {
|
||||
const device = await findMatchingDevice(predicate, cancellable);
|
||||
if (device !== null) {
|
||||
return device;
|
||||
}
|
||||
|
||||
const { timeout = 0 } = options;
|
||||
if (timeout === 0) {
|
||||
throw new Error("Device not found");
|
||||
}
|
||||
|
||||
const getDeviceEventually = new Promise((resolve: (device: Device) => void, reject: (error: Error) => void) => {
|
||||
const deviceManager = getDeviceManager();
|
||||
|
||||
deviceManager.added.connect(onDeviceAdded);
|
||||
|
||||
const timer = (timeout !== null) ? setTimeout(onTimeout, timeout) : null;
|
||||
|
||||
if (cancellable !== null) {
|
||||
cancellable.cancelled.connect(onCancel);
|
||||
if (cancellable.isCancelled) {
|
||||
onCancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
findMatchingDevice(predicate, cancellable)
|
||||
.then(device => {
|
||||
if (device !== null) {
|
||||
onSuccess(device);
|
||||
}
|
||||
})
|
||||
.catch(onError);
|
||||
|
||||
function onDeviceAdded(device: Device): void {
|
||||
if (predicate(device)) {
|
||||
onSuccess(device);
|
||||
}
|
||||
}
|
||||
|
||||
function onSuccess(device: Device): void {
|
||||
stopMonitoring();
|
||||
resolve(device);
|
||||
}
|
||||
|
||||
function onError(error: Error): void {
|
||||
stopMonitoring();
|
||||
reject(error);
|
||||
}
|
||||
|
||||
function onTimeout(): void {
|
||||
onError(new Error("Timed out while waiting for device to appear"));
|
||||
}
|
||||
|
||||
function onCancel(): void {
|
||||
onError(new Error("Operation was cancelled"));
|
||||
}
|
||||
|
||||
function stopMonitoring(): void {
|
||||
cancellable?.cancelled.disconnect(onCancel);
|
||||
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
|
||||
deviceManager.added.disconnect(onDeviceAdded);
|
||||
}
|
||||
});
|
||||
|
||||
return await getDeviceEventually;
|
||||
}
|
||||
|
||||
async function findMatchingDevice(predicate: DevicePredicate, cancellable?: Cancellable | null): Promise<Device | null> {
|
||||
const deviceManager = getDeviceManager();
|
||||
|
||||
const devices = await deviceManager.enumerateDevices(cancellable);
|
||||
|
||||
const matching = devices.filter(predicate);
|
||||
if (matching.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return matching[0];
|
||||
}
|
||||
|
||||
type DevicePredicate = (device: Device) => boolean;
|
||||
@@ -0,0 +1,2 @@
|
||||
import { Minimatch } from "minimatch";
|
||||
import { Duplex } from "stream";
|
||||
@@ -0,0 +1,396 @@
|
||||
const STANDARD_SPAWN_OPTION_NAMES = new Set<keyof SpawnOptions>([
|
||||
"argv",
|
||||
"envp",
|
||||
"env",
|
||||
"cwd",
|
||||
"stdio",
|
||||
]);
|
||||
|
||||
interface LogMessage {
|
||||
type: "log";
|
||||
level: LogLevel;
|
||||
payload: string;
|
||||
}
|
||||
|
||||
class ScriptServices implements RpcController {
|
||||
exportsProxy = new ScriptExportsProxy(this);
|
||||
|
||||
#script: Script;
|
||||
#pendingRequests = new Map<number, (error: Error | null, result?: any) => void>();
|
||||
#nextRequestId = 1;
|
||||
|
||||
constructor(script: Script) {
|
||||
this.#script = script;
|
||||
process.nextTick(() => {
|
||||
script.message.connect(() => {});
|
||||
});
|
||||
}
|
||||
|
||||
handleMessageIntercept = (message: Message, data: Buffer | null): boolean => {
|
||||
if (message.type === MessageType.Send && isRpcSendMessage(message)) {
|
||||
const [ , id, operation, ...params ] = message.payload;
|
||||
this.#onRpcMessage(id, operation, params, data);
|
||||
return false;
|
||||
} else if (isLogMessage(message)) {
|
||||
const opaqueMessage: any = message;
|
||||
const logMessage: LogMessage = opaqueMessage;
|
||||
this.#script.logHandler(logMessage.level, logMessage.payload);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
request(operation: string, params: any[], data: Buffer | null, cancellable: Cancellable | null = null): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = this.#nextRequestId++;
|
||||
|
||||
const complete = (error: Error | null, result?: any): void => {
|
||||
if (cancellable !== null) {
|
||||
cancellable.cancelled.disconnect(onOperationCancelled);
|
||||
}
|
||||
this.#script.destroyed.disconnect(onScriptDestroyed);
|
||||
|
||||
this.#pendingRequests.delete(id);
|
||||
|
||||
if (error === null) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
function onScriptDestroyed(): void {
|
||||
complete(new Error("Script is destroyed"));
|
||||
}
|
||||
|
||||
function onOperationCancelled(): void {
|
||||
complete(new Error("Operation was cancelled"));
|
||||
}
|
||||
|
||||
this.#pendingRequests.set(id, complete);
|
||||
|
||||
this.#script.post(["frida:rpc", id, operation, ...params], data);
|
||||
this.#script.destroyed.connect(onScriptDestroyed);
|
||||
if (cancellable !== null) {
|
||||
cancellable.cancelled.connect(onOperationCancelled);
|
||||
if (cancellable.isCancelled) {
|
||||
onOperationCancelled();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.#script.isDestroyed) {
|
||||
onScriptDestroyed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#onRpcMessage(id: number, operation: RpcOperation, params: any[], data: Buffer | null) {
|
||||
if (operation === RpcOperation.Ok || operation === RpcOperation.Error) {
|
||||
const callback = this.#pendingRequests.get(id);
|
||||
if (callback === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let value = null;
|
||||
let error = null;
|
||||
if (operation === RpcOperation.Ok) {
|
||||
if (data !== null) {
|
||||
value = (params.length > 1) ? [params[1], data] : data;
|
||||
} else {
|
||||
value = params[0];
|
||||
}
|
||||
} else {
|
||||
const [message, name, stack, rawErr] = params;
|
||||
error = new Error(message);
|
||||
error.name = name;
|
||||
error.stack = stack;
|
||||
Object.assign(error, rawErr);
|
||||
}
|
||||
|
||||
callback(error, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScriptExportsProxy implements ScriptExports {
|
||||
[name: string]: (...args: any[]) => Promise<any>;
|
||||
|
||||
constructor(rpcController: RpcController) {
|
||||
return new Proxy(this, {
|
||||
has(target, property) {
|
||||
return !isReservedMethodName(property);
|
||||
},
|
||||
get(target, property, receiver) {
|
||||
if (typeof property === "symbol") {
|
||||
if (property === inspect.custom) {
|
||||
return inspectProxy;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (property in target) {
|
||||
return target[property];
|
||||
}
|
||||
|
||||
if (isReservedMethodName(property)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (...args: any[]): Promise<any> => {
|
||||
let cancellable: Cancellable | null = null;
|
||||
if (args[args.length - 1] instanceof Cancellable) {
|
||||
cancellable = args.pop();
|
||||
}
|
||||
|
||||
let data: Buffer | null = null;
|
||||
if (Buffer.isBuffer(args[args.length - 1])) {
|
||||
data = args.pop();
|
||||
}
|
||||
|
||||
return rpcController.request("call", [property, args], data, cancellable);
|
||||
};
|
||||
},
|
||||
set(target, property, value, receiver) {
|
||||
if (typeof property === "symbol") {
|
||||
return false;
|
||||
}
|
||||
target[property] = value;
|
||||
return true;
|
||||
},
|
||||
ownKeys(target) {
|
||||
return Object.getOwnPropertyNames(target);
|
||||
},
|
||||
getOwnPropertyDescriptor(target, property) {
|
||||
if (property in target) {
|
||||
return Object.getOwnPropertyDescriptor(target, property);
|
||||
}
|
||||
|
||||
if (isReservedMethodName(property)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function inspectProxy() {
|
||||
return "ScriptExportsProxy {}";
|
||||
}
|
||||
|
||||
interface RpcController {
|
||||
request(operation: string, params: any[], data: ArrayBuffer | null, cancellable: Cancellable | null): Promise<any>;
|
||||
}
|
||||
|
||||
enum RpcOperation {
|
||||
Ok = "ok",
|
||||
Error = "error"
|
||||
}
|
||||
|
||||
function isInternalMessage(message: Message): boolean {
|
||||
return isRpcMessage(message) || isLogMessage(message);
|
||||
}
|
||||
|
||||
function isRpcMessage(message: Message): boolean {
|
||||
return message.type === MessageType.Send && isRpcSendMessage(message);
|
||||
}
|
||||
|
||||
function isRpcSendMessage(message: SendMessage): boolean {
|
||||
const payload = message.payload;
|
||||
if (!Array.isArray(payload)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return payload[0] === "frida:rpc";
|
||||
}
|
||||
|
||||
function isLogMessage(message: Message): boolean {
|
||||
return message.type as string === "log";
|
||||
}
|
||||
|
||||
function log(level: LogLevel, text: string): void {
|
||||
switch (level) {
|
||||
case LogLevel.Info:
|
||||
console.log(text);
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
console.warn(text);
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
console.error(text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const reservedMethodNames = new Set<string>([
|
||||
"then",
|
||||
"catch",
|
||||
"finally",
|
||||
]);
|
||||
|
||||
function isReservedMethodName(name: string | number | symbol): boolean {
|
||||
return reservedMethodNames.has(name.toString());
|
||||
}
|
||||
|
||||
const IO_PRIORITY_DEFAULT = 0;
|
||||
|
||||
class IOStreamAdapter extends Duplex {
|
||||
#impl: IOStream;
|
||||
#input: InputStream;
|
||||
#output: OutputStream;
|
||||
#pending = new Set<Promise<void>>();
|
||||
|
||||
#cancellable = new Cancellable();
|
||||
|
||||
constructor(impl: IOStream) {
|
||||
super({});
|
||||
|
||||
this.#impl = impl;
|
||||
this.#input = impl.inputStream;
|
||||
this.#output = impl.outputStream;
|
||||
}
|
||||
|
||||
async _destroy(error: Error | null, callback: (error: Error | null) => void): Promise<void> {
|
||||
this.#cancellable.cancel();
|
||||
|
||||
for (const operation of this.#pending) {
|
||||
try {
|
||||
await operation;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.#impl.close(IO_PRIORITY_DEFAULT);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
callback(error);
|
||||
}
|
||||
|
||||
_read(size: number): void {
|
||||
const operation = this.#input.read(size, IO_PRIORITY_DEFAULT, this.#cancellable)
|
||||
.then((data: Buffer): void => {
|
||||
const isEof = data.length === 0;
|
||||
if (isEof) {
|
||||
this.push(null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.push(data);
|
||||
})
|
||||
.catch((error: Error): void => {
|
||||
if (this.#impl.closed) {
|
||||
this.push(null);
|
||||
}
|
||||
this.emit("error", error);
|
||||
});
|
||||
this.#track(operation);
|
||||
}
|
||||
|
||||
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
|
||||
let data: Buffer;
|
||||
if (Buffer.isBuffer(chunk)) {
|
||||
data = chunk;
|
||||
} else {
|
||||
data = Buffer.from(chunk, encoding);
|
||||
}
|
||||
const operation = this.#writeAll(data)
|
||||
.then((): void => {
|
||||
callback(null);
|
||||
})
|
||||
.catch((error: Error): void => {
|
||||
callback(error);
|
||||
});
|
||||
this.#track(operation);
|
||||
}
|
||||
|
||||
async #writeAll(data: Buffer): Promise<void> {
|
||||
let offset = 0;
|
||||
do {
|
||||
const n = await this.#output.write(data.slice(offset), IO_PRIORITY_DEFAULT, this.#cancellable);
|
||||
offset += n;
|
||||
} while (offset !== data.length);
|
||||
}
|
||||
|
||||
#track(operation: Promise<void>): void {
|
||||
this.#pending.add(operation);
|
||||
operation
|
||||
.catch(_ => {})
|
||||
.finally(() => {
|
||||
this.#pending.delete(operation);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class CallbackAuthenticationService extends binding.AbstractAuthenticationService {
|
||||
#callback: AuthenticationCallback;
|
||||
|
||||
constructor(callback: AuthenticationCallback) {
|
||||
super();
|
||||
this.#callback = callback;
|
||||
}
|
||||
|
||||
async authenticate(token: string, cancellable: Cancellable | null): Promise<string> {
|
||||
const info = await this.#callback(token);
|
||||
return JSON.stringify(info);
|
||||
}
|
||||
}
|
||||
|
||||
function parseSocketAddress(address: BaseSocketAddress): SocketAddress {
|
||||
const family = address.family;
|
||||
switch (family) {
|
||||
case SocketFamily.Unix: {
|
||||
const addr = address as UnixSocketAddress;
|
||||
switch (addr.addressType) {
|
||||
case UnixSocketAddressType.Anonymous:
|
||||
return {
|
||||
family: "unix:anonymous",
|
||||
};
|
||||
case UnixSocketAddressType.Path:
|
||||
return {
|
||||
family: "unix:path",
|
||||
path: addr.path.toString(),
|
||||
};
|
||||
case UnixSocketAddressType.Abstract:
|
||||
case UnixSocketAddressType.AbstractPadded:
|
||||
return {
|
||||
family: "unix:abstract",
|
||||
path: addr.path,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SocketFamily.Ipv4: {
|
||||
const addr = address as InetSocketAddress;
|
||||
return {
|
||||
family: "ipv4",
|
||||
address: addr.address.toString(),
|
||||
port: addr.port,
|
||||
};
|
||||
}
|
||||
case SocketFamily.Ipv6: {
|
||||
const addr = address as InetSocketAddress;
|
||||
return {
|
||||
family: "ipv6",
|
||||
address: addr.address.toString(),
|
||||
port: addr.port,
|
||||
flowlabel: addr.flowinfo,
|
||||
scopeid: addr.scopeId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("invalid BaseSocketAddress");
|
||||
}
|
||||
|
||||
function objectToStrv(object: { [name: string]: string }): string[] {
|
||||
return Object.entries(object).map(([k, v]) => `${k}=${v}`);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
|
||||
from . import codegen
|
||||
from .customization import load_customizations
|
||||
from .loader import compute_model
|
||||
|
||||
|
||||
def main():
|
||||
run(build_arguments())
|
||||
|
||||
|
||||
def build_arguments() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(
|
||||
description="Generate TypeScript and Node-API bindings for Frida."
|
||||
)
|
||||
p.add_argument(
|
||||
"--frida-gir",
|
||||
required=True,
|
||||
type=Path,
|
||||
help="Path to the Frida .gir file.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--glib-gir",
|
||||
required=True,
|
||||
type=Path,
|
||||
help="Path to the GLib .gir file.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--gobject-gir",
|
||||
required=True,
|
||||
type=Path,
|
||||
help="Path to the GObject .gir file.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--gio-gir",
|
||||
required=True,
|
||||
type=Path,
|
||||
help="Path to the GIO .gir file.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--output-py",
|
||||
required=True,
|
||||
type=Path,
|
||||
help="Path to the output .py file.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--output-pyi",
|
||||
required=True,
|
||||
type=Path,
|
||||
help="Path to the output .pyi file.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--output-c",
|
||||
required=True,
|
||||
type=Path,
|
||||
help="Path to the output C file for the Python extension.",
|
||||
)
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def run(args: argparse.Namespace) -> None:
|
||||
customizations = load_customizations()
|
||||
model = compute_model(
|
||||
args.frida_gir, args.glib_gir, args.gobject_gir, args.gio_gir, customizations
|
||||
)
|
||||
|
||||
artefacts = codegen.generate_all(model)
|
||||
|
||||
with OutputFile(args.output_py) as f:
|
||||
f.write(artefacts["py"])
|
||||
with OutputFile(args.output_pyi) as f:
|
||||
f.write(artefacts["pyi"])
|
||||
with OutputFile(args.output_c) as f:
|
||||
f.write(artefacts["c"])
|
||||
|
||||
|
||||
class OutputFile:
|
||||
def __init__(self, output_path):
|
||||
self._output_path = output_path
|
||||
self._io = StringIO()
|
||||
|
||||
def __enter__(self):
|
||||
return self._io
|
||||
|
||||
def __exit__(self, *exc):
|
||||
result = self._io.getvalue()
|
||||
if self._output_path.exists():
|
||||
existing_contents = self._output_path.read_text(encoding="utf-8")
|
||||
if existing_contents == result:
|
||||
return False
|
||||
self._output_path.write_text(result, encoding="utf-8")
|
||||
return False
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,924 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Mapping
|
||||
|
||||
from .model import (ConstructorCustomizations, CustomCode, CustomDeclaration,
|
||||
Customizations, CustomMethod, CustomType, CustomTypeKind,
|
||||
EnumerationCustomizations, EnumerationMemberCustomizations,
|
||||
KeepAliveCustomization, MethodCustomizations,
|
||||
ObjectTypeCustomizations, PropertyCustomizations,
|
||||
SignalCustomizations, TypeCustomizations)
|
||||
|
||||
ASSETS_DIR = Path(__file__).resolve().parent / "assets"
|
||||
CUSTOMIZATION_FACADE_EXPORTS = (
|
||||
(ASSETS_DIR / "customization_facade.exports")
|
||||
.read_text(encoding="utf-8")
|
||||
.strip()
|
||||
.split("\n")
|
||||
)
|
||||
CUSTOMIZATION_FACADE_TS = (ASSETS_DIR / "customization_facade.ts").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
CUSTOMIZATION_HELPERS_IMPORTS = (
|
||||
(ASSETS_DIR / "customization_helpers.imports")
|
||||
.read_text(encoding="utf-8")
|
||||
.strip()
|
||||
.split("\n")
|
||||
)
|
||||
CUSTOMIZATION_HELPERS_TS = (ASSETS_DIR / "customization_helpers.ts").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
|
||||
def load_customizations() -> Customizations:
|
||||
custom_types: List[CustomType] = {
|
||||
"TargetProcess": CustomType(CustomTypeKind.TYPE, "ProcessID | ProcessName"),
|
||||
"ProcessID": CustomType(CustomTypeKind.TYPE, "number"),
|
||||
"InjecteeID": CustomType(CustomTypeKind.TYPE, "number"),
|
||||
"FileDescriptor": CustomType(CustomTypeKind.TYPE, "number"),
|
||||
"ProcessName": CustomType(CustomTypeKind.TYPE, "string"),
|
||||
"SystemParameters": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
/**
|
||||
* Operating System details.
|
||||
*/
|
||||
os: {
|
||||
/**
|
||||
* ID, e.g.: windows, macos, linux, ios, android, qnx, fedora, ubuntu, etc.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Human-readable name, e.g. `"macOS"`.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Human-readable version string, e.g. `"11.2.2"`.
|
||||
*/
|
||||
version?: string;
|
||||
|
||||
/**
|
||||
* Build version, e.g. `"21B91"`.
|
||||
*/
|
||||
build?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform, same as `Process.platform` in GumJS.
|
||||
*/
|
||||
platform: "windows" | "darwin" | "linux" | "freebsd" | "qnx";
|
||||
|
||||
/**
|
||||
* Architecture, same as `Process.arch` in GumJS.
|
||||
*/
|
||||
arch: "ia32" | "x64" | "arm" | "arm64" | "mips";
|
||||
|
||||
/**
|
||||
* Hardware details.
|
||||
*/
|
||||
hardware?: {
|
||||
/**
|
||||
* Product type, e.g. `"iPad6,3"`.
|
||||
*/
|
||||
product?: string;
|
||||
|
||||
/**
|
||||
* Hardware platform, e.g. `"t8010"`.
|
||||
*/
|
||||
platform?: string;
|
||||
|
||||
/**
|
||||
* Hardware model, e.g. `"J71bAP"`.
|
||||
*/
|
||||
model?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Level of access.
|
||||
*/
|
||||
access: "full" | "jailed";
|
||||
|
||||
/**
|
||||
* System name, e.g. `"Ole André’s iPhone"`.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* iOS UDID (Unique Device ID).
|
||||
*/
|
||||
udid?: string;
|
||||
|
||||
/**
|
||||
* Details about cellular and networking interfaces.
|
||||
*/
|
||||
interfaces?: SystemInterface[];
|
||||
|
||||
/**
|
||||
* Android API level, e.g.: `30`.
|
||||
*/
|
||||
apiLevel?: number;
|
||||
|
||||
[name: string]: any;
|
||||
""",
|
||||
),
|
||||
"SystemInterface": CustomType(
|
||||
CustomTypeKind.TYPE, "NetworkInterface | CellularInterface"
|
||||
),
|
||||
"NetworkInterface": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
type: "ethernet" | "wifi" | "bluetooth";
|
||||
|
||||
/**
|
||||
* MAC address, e.g.: `"aa:bb:cc:dd:ee:ff"`.
|
||||
*/
|
||||
address: string;
|
||||
""",
|
||||
),
|
||||
"CellularInterface": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
type: "cellular";
|
||||
|
||||
/**
|
||||
* Phone number, e.g. `"+47 123 45 678"`.
|
||||
*/
|
||||
phoneNumber: string;
|
||||
""",
|
||||
),
|
||||
"SpawnOptions": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
argv?: string[];
|
||||
envp?: { [name: string]: string };
|
||||
env?: { [name: string]: string };
|
||||
cwd?: string;
|
||||
stdio?: Stdio;
|
||||
|
||||
[name: string]: any;
|
||||
""",
|
||||
),
|
||||
"RelayProperties": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
address: string;
|
||||
username: string;
|
||||
password: string;
|
||||
kind: RelayKind;
|
||||
""",
|
||||
),
|
||||
"Message": CustomType(CustomTypeKind.TYPE, "SendMessage | ErrorMessage"),
|
||||
"MessageType": CustomType(
|
||||
CustomTypeKind.ENUM,
|
||||
"""
|
||||
Send = "send",
|
||||
Error = "error"
|
||||
""",
|
||||
),
|
||||
"SendMessage": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
type: MessageType.Send;
|
||||
payload: any;
|
||||
""",
|
||||
),
|
||||
"ErrorMessage": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
type: MessageType.Error;
|
||||
description: string;
|
||||
stack?: string;
|
||||
fileName?: string;
|
||||
lineNumber?: number;
|
||||
columnNumber?: number;
|
||||
""",
|
||||
),
|
||||
"ScriptLogHandler": CustomType(
|
||||
CustomTypeKind.TYPE, "(level: LogLevel, text: string) => void"
|
||||
),
|
||||
"ScriptExports": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
[name: string]: (...args: any[]) => Promise<any>;
|
||||
""",
|
||||
),
|
||||
"LogLevel": CustomType(
|
||||
CustomTypeKind.ENUM,
|
||||
"""
|
||||
Info = "info",
|
||||
Warning = "warning",
|
||||
Error = "error",
|
||||
""",
|
||||
),
|
||||
"EnableDebuggerOptions": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
port?: number;
|
||||
""",
|
||||
),
|
||||
"PortalServiceOptions": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
clusterParams?: EndpointParameters;
|
||||
controlParams?: EndpointParameters;
|
||||
""",
|
||||
),
|
||||
"PortalConnectionId": CustomType(CustomTypeKind.TYPE, "number"),
|
||||
"PortalConnectionTag": CustomType(CustomTypeKind.TYPE, "string"),
|
||||
"EndpointParametersSubset": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
address?: string;
|
||||
port?: number;
|
||||
certificate?: string;
|
||||
origin?: string;
|
||||
authentication?: AuthenticationScheme;
|
||||
assetRoot?: string;
|
||||
""",
|
||||
),
|
||||
"AuthenticationScheme": CustomType(
|
||||
CustomTypeKind.TYPE,
|
||||
"TokenAuthenticationScheme | CallbackAuthenticationScheme",
|
||||
),
|
||||
"TokenAuthenticationScheme": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
scheme: "token";
|
||||
token: string;
|
||||
""",
|
||||
),
|
||||
"CallbackAuthenticationScheme": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
scheme: "callback";
|
||||
callback: AuthenticationCallback;
|
||||
""",
|
||||
),
|
||||
"AuthenticationCallback": CustomType(
|
||||
CustomTypeKind.TYPE,
|
||||
"(token: string) => Promise<AuthenticatedSessionInfo>",
|
||||
),
|
||||
"AuthenticatedSessionInfo": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
[key: string]: any;
|
||||
""",
|
||||
),
|
||||
"SocketAddress": CustomType(
|
||||
CustomTypeKind.TYPE,
|
||||
"IPV4SocketAddress | IPV6SocketAddress | AnonymousUnixSocketAddress | PathUnixSocketAddress | AbstractUnixSocketAddress",
|
||||
),
|
||||
"IPV4SocketAddress": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
family: "ipv4";
|
||||
address: string;
|
||||
port: number;
|
||||
""",
|
||||
),
|
||||
"IPV6SocketAddress": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
family: "ipv6";
|
||||
address: string;
|
||||
port: number;
|
||||
flowlabel: number;
|
||||
scopeid: number;
|
||||
""",
|
||||
),
|
||||
"AnonymousUnixSocketAddress": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
family: "unix:anonymous";
|
||||
""",
|
||||
),
|
||||
"PathUnixSocketAddress": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
family: "unix:path";
|
||||
path: string;
|
||||
""",
|
||||
),
|
||||
"AbstractUnixSocketAddress": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
family: "unix:abstract";
|
||||
path: Buffer;
|
||||
""",
|
||||
),
|
||||
"Variant": CustomType(
|
||||
CustomTypeKind.TYPE, "VariantValue | [type: symbol, value: VariantValue]"
|
||||
),
|
||||
"VariantValue": CustomType(
|
||||
CustomTypeKind.TYPE,
|
||||
"""
|
||||
| boolean
|
||||
| number
|
||||
| string
|
||||
| Buffer
|
||||
| Variant[]
|
||||
| VariantDict
|
||||
""",
|
||||
),
|
||||
"VariantDict": CustomType(
|
||||
CustomTypeKind.INTERFACE,
|
||||
"""
|
||||
[key: string]: Variant;
|
||||
""",
|
||||
),
|
||||
}
|
||||
|
||||
type_customizations: Mapping[str, TypeCustomizations] = {
|
||||
"DeviceManager": ObjectTypeCustomizations(
|
||||
signals={
|
||||
"added": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
|
||||
"removed": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
|
||||
"changed": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
|
||||
},
|
||||
cleanup="close",
|
||||
),
|
||||
"Device": ObjectTypeCustomizations(
|
||||
methods={
|
||||
"query_system_parameters": MethodCustomizations(
|
||||
return_typing="Promise<SystemParameters>",
|
||||
return_wrapper="as SystemParameters",
|
||||
),
|
||||
"spawn": MethodCustomizations(
|
||||
param_typings=[
|
||||
"programOrArgv: string | string[]",
|
||||
"opts?: SpawnOptions",
|
||||
"cancellable?: Cancellable | null",
|
||||
],
|
||||
return_typing="Promise<ProcessID>",
|
||||
custom_logic="""
|
||||
const options: RawSpawnOptions = {};
|
||||
|
||||
let program: string;
|
||||
let argv;
|
||||
if (typeof programOrArgv === "string") {
|
||||
program = programOrArgv;
|
||||
argv = opts?.argv;
|
||||
} else {
|
||||
program = programOrArgv[0];
|
||||
argv = programOrArgv;
|
||||
if (argv.length === 1) {
|
||||
argv = undefined;
|
||||
}
|
||||
}
|
||||
if (argv !== undefined) {
|
||||
options.argv = argv;
|
||||
}
|
||||
|
||||
if (opts !== undefined) {
|
||||
const envp = opts.envp;
|
||||
if (envp !== undefined) {
|
||||
options.envp = objectToStrv(envp);
|
||||
}
|
||||
|
||||
const env = opts.env;
|
||||
if (env !== undefined) {
|
||||
options.env = objectToStrv(env);
|
||||
}
|
||||
|
||||
const cwd = opts.cwd;
|
||||
if (cwd !== undefined) {
|
||||
options.cwd = cwd;
|
||||
}
|
||||
|
||||
options.aux = Object.fromEntries(Object.entries(opts).filter(([k, v]) => !STANDARD_SPAWN_OPTION_NAMES.has(k)));
|
||||
}
|
||||
""",
|
||||
),
|
||||
"input": MethodCustomizations(
|
||||
param_typings=[
|
||||
"target: TargetProcess",
|
||||
"data: Buffer",
|
||||
"cancellable?: Cancellable | null",
|
||||
],
|
||||
custom_logic="const pid = await this.#getPid(target, cancellable);",
|
||||
),
|
||||
"resume": MethodCustomizations(
|
||||
param_typings=[
|
||||
"target: TargetProcess",
|
||||
"cancellable?: Cancellable | null",
|
||||
],
|
||||
custom_logic="const pid = await this.#getPid(target, cancellable);",
|
||||
),
|
||||
"kill": MethodCustomizations(
|
||||
param_typings=[
|
||||
"target: TargetProcess",
|
||||
"cancellable?: Cancellable | null",
|
||||
],
|
||||
custom_logic="const pid = await this.#getPid(target, cancellable);",
|
||||
),
|
||||
"attach": MethodCustomizations(
|
||||
param_typings=[
|
||||
"target: TargetProcess",
|
||||
"options?: SessionOptions",
|
||||
"cancellable?: Cancellable | null",
|
||||
],
|
||||
custom_logic="const pid = await this.#getPid(target, cancellable);",
|
||||
),
|
||||
"inject_library_file": MethodCustomizations(
|
||||
param_typings=[
|
||||
"target: TargetProcess",
|
||||
"path: string",
|
||||
"entrypoint: string",
|
||||
"data: string",
|
||||
"cancellable?: Cancellable | null",
|
||||
],
|
||||
return_typing="Promise<InjecteeID>",
|
||||
custom_logic="const pid = await this.#getPid(target, cancellable);",
|
||||
),
|
||||
"inject_library_blob": MethodCustomizations(
|
||||
param_typings=[
|
||||
"target: TargetProcess",
|
||||
"blob: Buffer",
|
||||
"entrypoint: string",
|
||||
"data: string",
|
||||
"cancellable?: Cancellable | null",
|
||||
],
|
||||
return_typing="Promise<InjecteeID>",
|
||||
custom_logic="const pid = await this.#getPid(target, cancellable);",
|
||||
),
|
||||
"open_channel": MethodCustomizations(
|
||||
return_typing='Promise<import("stream").Duplex>',
|
||||
return_wrapper="new IOStreamAdapter",
|
||||
),
|
||||
},
|
||||
properties={
|
||||
"dtype": PropertyCustomizations(
|
||||
js_name="type",
|
||||
),
|
||||
},
|
||||
signals={
|
||||
"spawn-added": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
|
||||
"spawn-removed": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
|
||||
"child-added": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
|
||||
"child-removed": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
|
||||
"process-crashed": SignalCustomizations(
|
||||
behavior="FDN_SIGNAL_KEEP_ALIVE"
|
||||
),
|
||||
"output": SignalCustomizations(
|
||||
behavior="FDN_SIGNAL_KEEP_ALIVE",
|
||||
transform={
|
||||
0: ("pid: ProcessID", None),
|
||||
1: ("fd: FileDescriptor", None),
|
||||
},
|
||||
),
|
||||
"uninjected": SignalCustomizations(
|
||||
behavior="FDN_SIGNAL_KEEP_ALIVE",
|
||||
transform={
|
||||
0: ("id: InjecteeID", None),
|
||||
},
|
||||
),
|
||||
},
|
||||
custom_code=CustomCode(
|
||||
methods=[
|
||||
CustomMethod(
|
||||
typing="getProcess(name: string, options?: ProcessMatchOptions, cancellable?: Cancellable | null): Promise<Process>",
|
||||
code="""
|
||||
async getProcess(name: string, options: ProcessMatchOptions = {}, cancellable?: Cancellable | null): Promise<Process> {
|
||||
const {
|
||||
scope = Scope.Minimal,
|
||||
} = options;
|
||||
const processes = await this.enumerateProcesses({ scope }, cancellable);
|
||||
const mm = new Minimatch(name.toLowerCase());
|
||||
const matching = processes.filter(process => mm.match(process.name.toLowerCase()));
|
||||
if (matching.length === 1) {
|
||||
return matching[0];
|
||||
} else if (matching.length > 1) {
|
||||
throw new Error("Ambiguous name; it matches: " + matching.map(process => `${process.name} (pid: ${process.pid})`).join(", "));
|
||||
} else {
|
||||
throw new Error("Process not found");
|
||||
}
|
||||
}
|
||||
""",
|
||||
),
|
||||
CustomMethod(
|
||||
typing=None,
|
||||
code="""
|
||||
async #getPid(target: TargetProcess, cancellable?: Cancellable | null): Promise<ProcessID> {
|
||||
if (typeof target === "number") {
|
||||
return target;
|
||||
}
|
||||
|
||||
const process = await this.getProcess(target, {}, cancellable);
|
||||
return process.pid;
|
||||
}
|
||||
""",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
"SpawnOptions": ObjectTypeCustomizations(js_name="RawSpawnOptions"),
|
||||
"Bus": ObjectTypeCustomizations(
|
||||
methods={
|
||||
"post": MethodCustomizations(
|
||||
param_typings=[
|
||||
"message: any",
|
||||
"data?: Buffer | null",
|
||||
],
|
||||
custom_logic="const json = JSON.stringify(message);",
|
||||
),
|
||||
},
|
||||
signals={
|
||||
"detached": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
|
||||
"message": SignalCustomizations(
|
||||
behavior="FDN_SIGNAL_KEEP_ALIVE",
|
||||
transform={
|
||||
0: ("message: any", "JSON.parse"),
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
"Service": ObjectTypeCustomizations(
|
||||
keep_alive=KeepAliveCustomization(
|
||||
is_destroyed_function="is_closed", destroy_signal_name="close"
|
||||
),
|
||||
),
|
||||
"Relay": ObjectTypeCustomizations(
|
||||
constructor=ConstructorCustomizations(
|
||||
param_typings=[
|
||||
"properties: RelayProperties",
|
||||
],
|
||||
custom_logic="const { address, username, password, kind } = properties;",
|
||||
),
|
||||
),
|
||||
"RelayKind": EnumerationCustomizations(
|
||||
members={
|
||||
"turn_udp": EnumerationMemberCustomizations(js_name="TurnUDP"),
|
||||
"turn_tcp": EnumerationMemberCustomizations(js_name="TurnTCP"),
|
||||
"turn_tls": EnumerationMemberCustomizations(js_name="TurnTLS"),
|
||||
},
|
||||
),
|
||||
"Script": ObjectTypeCustomizations(
|
||||
methods={
|
||||
"is_destroyed": MethodCustomizations(hide=True),
|
||||
"post": MethodCustomizations(
|
||||
param_typings=[
|
||||
"message: any",
|
||||
"data?: Buffer | null",
|
||||
],
|
||||
custom_logic="const json = JSON.stringify(message);",
|
||||
),
|
||||
"enable_debugger": MethodCustomizations(
|
||||
param_typings=[
|
||||
"options?: EnableDebuggerOptions",
|
||||
"cancellable?: Cancellable | null",
|
||||
],
|
||||
custom_logic="const port = options?.port ?? 0;",
|
||||
),
|
||||
},
|
||||
signals={
|
||||
"message": SignalCustomizations(
|
||||
transform={
|
||||
0: ("message: Message", "JSON.parse"),
|
||||
},
|
||||
intercept="this.#services.handleMessageIntercept",
|
||||
),
|
||||
},
|
||||
custom_code=CustomCode(
|
||||
declarations=[
|
||||
CustomDeclaration(
|
||||
typing=None, code="#services = new ScriptServices(this);"
|
||||
),
|
||||
CustomDeclaration(
|
||||
typing="logHandler: ScriptLogHandler",
|
||||
code="logHandler: ScriptLogHandler = log;",
|
||||
),
|
||||
],
|
||||
methods=[
|
||||
CustomMethod(
|
||||
typing="readonly isDestroyed: boolean",
|
||||
code="""
|
||||
get isDestroyed(): boolean {
|
||||
return this._isDestroyed();
|
||||
}
|
||||
""",
|
||||
),
|
||||
CustomMethod(
|
||||
typing="readonly exports: ScriptExports",
|
||||
code="""
|
||||
get exports(): ScriptExports {
|
||||
return this.#services.exportsProxy;
|
||||
}
|
||||
""",
|
||||
),
|
||||
CustomMethod(
|
||||
typing="readonly defaultLogHandler: ScriptLogHandler",
|
||||
code="""
|
||||
get defaultLogHandler(): ScriptLogHandler {
|
||||
return log;
|
||||
}
|
||||
""",
|
||||
),
|
||||
],
|
||||
),
|
||||
keep_alive=KeepAliveCustomization(
|
||||
is_destroyed_function="is_destroyed", destroy_signal_name="destroyed"
|
||||
),
|
||||
),
|
||||
"ScriptRuntime": EnumerationCustomizations(
|
||||
members={
|
||||
"qjs": EnumerationMemberCustomizations(js_name="QJS"),
|
||||
},
|
||||
),
|
||||
"ControlService": ObjectTypeCustomizations(
|
||||
methods={
|
||||
"start": MethodCustomizations(ref_keep_alive=True),
|
||||
"stop": MethodCustomizations(unref_keep_alive=True),
|
||||
"get_endpoint_params": MethodCustomizations(drop=True),
|
||||
},
|
||||
properties={
|
||||
"endpoint-params": PropertyCustomizations(drop=True),
|
||||
},
|
||||
),
|
||||
"PortalService": ObjectTypeCustomizations(
|
||||
constructor=ConstructorCustomizations(
|
||||
param_typings=[
|
||||
"options?: PortalServiceOptions",
|
||||
],
|
||||
custom_logic="""
|
||||
const clusterParams = options?.clusterParams ?? new EndpointParameters();
|
||||
const controlParams = options?.controlParams ?? null;
|
||||
""",
|
||||
),
|
||||
methods={
|
||||
"start": MethodCustomizations(ref_keep_alive=True),
|
||||
"stop": MethodCustomizations(unref_keep_alive=True),
|
||||
"kick": MethodCustomizations(
|
||||
param_typings=[
|
||||
"connectionId: PortalConnectionId",
|
||||
],
|
||||
),
|
||||
"post": MethodCustomizations(
|
||||
param_typings=[
|
||||
"connectionId: PortalConnectionId",
|
||||
"message: any",
|
||||
"data?: Buffer | null",
|
||||
],
|
||||
custom_logic="const json = JSON.stringify(message);",
|
||||
),
|
||||
"narrowcast": MethodCustomizations(
|
||||
param_typings=[
|
||||
"tag: string",
|
||||
"message: any",
|
||||
"data?: Buffer | null",
|
||||
],
|
||||
custom_logic="const json = JSON.stringify(message);",
|
||||
),
|
||||
"broadcast": MethodCustomizations(
|
||||
param_typings=[
|
||||
"message: any",
|
||||
"data?: Buffer | null",
|
||||
],
|
||||
custom_logic="const json = JSON.stringify(message);",
|
||||
),
|
||||
"enumerate_tags": MethodCustomizations(
|
||||
param_typings=[
|
||||
"connectionId: PortalConnectionId",
|
||||
],
|
||||
),
|
||||
"tag": MethodCustomizations(
|
||||
param_typings=[
|
||||
"connectionId: PortalConnectionId",
|
||||
"tag: string",
|
||||
],
|
||||
),
|
||||
"untag": MethodCustomizations(
|
||||
param_typings=[
|
||||
"connectionId: PortalConnectionId",
|
||||
"tag: string",
|
||||
],
|
||||
),
|
||||
},
|
||||
signals={
|
||||
"node-connected": SignalCustomizations(
|
||||
transform={
|
||||
0: ("connectionId: PortalConnectionId", None),
|
||||
1: ("remoteAddress: SocketAddress", "parseSocketAddress"),
|
||||
},
|
||||
),
|
||||
"node-joined": SignalCustomizations(
|
||||
transform={
|
||||
0: ("connectionId: PortalConnectionId", None),
|
||||
},
|
||||
),
|
||||
"node-left": SignalCustomizations(
|
||||
transform={
|
||||
0: ("connectionId: PortalConnectionId", None),
|
||||
},
|
||||
),
|
||||
"node-disconnected": SignalCustomizations(
|
||||
transform={
|
||||
0: ("connectionId: PortalConnectionId", None),
|
||||
1: ("remoteAddress: SocketAddress", "parseSocketAddress"),
|
||||
},
|
||||
),
|
||||
"controller-connected": SignalCustomizations(
|
||||
transform={
|
||||
0: ("connectionId: PortalConnectionId", None),
|
||||
1: ("remoteAddress: SocketAddress", "parseSocketAddress"),
|
||||
},
|
||||
),
|
||||
"controller-disconnected": SignalCustomizations(
|
||||
transform={
|
||||
0: ("connectionId: PortalConnectionId", None),
|
||||
1: ("remoteAddress: SocketAddress", "parseSocketAddress"),
|
||||
},
|
||||
),
|
||||
"authenticated": SignalCustomizations(
|
||||
transform={
|
||||
0: ("connectionId: PortalConnectionId", None),
|
||||
1: ("sessionInfo: AuthenticatedSessionInfo", "JSON.parse"),
|
||||
},
|
||||
),
|
||||
"subscribe": SignalCustomizations(
|
||||
transform={
|
||||
0: ("connectionId: PortalConnectionId", None),
|
||||
},
|
||||
),
|
||||
"message": SignalCustomizations(
|
||||
transform={
|
||||
0: ("connectionId: PortalConnectionId", None),
|
||||
1: ("message: any", "JSON.parse"),
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
"EndpointParameters": ObjectTypeCustomizations(
|
||||
constructor=ConstructorCustomizations(
|
||||
param_typings=[
|
||||
"params?: EndpointParametersSubset",
|
||||
],
|
||||
custom_logic="""
|
||||
const address = params?.address ?? null;
|
||||
const port = params?.port ?? 0;
|
||||
const certificate = params?.certificate ?? null;
|
||||
const origin = params?.origin ?? null;
|
||||
|
||||
let authService: AuthenticationService | null = null;
|
||||
const auth = params?.authentication;
|
||||
if (auth !== undefined) {
|
||||
if (auth.scheme === "token") {
|
||||
authService = new StaticAuthenticationService(auth.token);
|
||||
} else {
|
||||
authService = new CallbackAuthenticationService(auth.callback);
|
||||
}
|
||||
}
|
||||
|
||||
const assetRoot = params?.assetRoot ?? null;
|
||||
""",
|
||||
),
|
||||
),
|
||||
"Injector": ObjectTypeCustomizations(drop=True),
|
||||
"RpcClient": ObjectTypeCustomizations(drop=True),
|
||||
"RpcPeer": ObjectTypeCustomizations(drop=True),
|
||||
"Cancellable": ObjectTypeCustomizations(
|
||||
methods={
|
||||
"is_cancelled": MethodCustomizations(hide=True),
|
||||
"set_error_if_cancelled": MethodCustomizations(
|
||||
js_name="throwIfCancelled",
|
||||
return_typing="void",
|
||||
),
|
||||
"make_pollfd": MethodCustomizations(drop=True),
|
||||
"release_fd": MethodCustomizations(drop=True),
|
||||
"source_new": MethodCustomizations(drop=True),
|
||||
},
|
||||
custom_code=CustomCode(
|
||||
methods=[
|
||||
CustomMethod(
|
||||
typing="readonly isCancelled: boolean",
|
||||
code="""
|
||||
get isCancelled(): boolean {
|
||||
return this._isCancelled();
|
||||
}
|
||||
""",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
"IOStream": ObjectTypeCustomizations(
|
||||
methods={
|
||||
"close": MethodCustomizations(drop=True),
|
||||
"close_async": MethodCustomizations(js_name="close"),
|
||||
"splice_async": MethodCustomizations(drop=True),
|
||||
"has_pending": MethodCustomizations(drop=True),
|
||||
"set_pending": MethodCustomizations(drop=True),
|
||||
"clear_pending": MethodCustomizations(drop=True),
|
||||
},
|
||||
),
|
||||
"InputStream": ObjectTypeCustomizations(
|
||||
methods={
|
||||
"close": MethodCustomizations(drop=True),
|
||||
"close_async": MethodCustomizations(js_name="close"),
|
||||
"read": MethodCustomizations(drop=True),
|
||||
"read_async": MethodCustomizations(drop=True),
|
||||
"read_all": MethodCustomizations(drop=True),
|
||||
"read_all_async": MethodCustomizations(drop=True),
|
||||
"read_bytes": MethodCustomizations(drop=True),
|
||||
"read_bytes_async": MethodCustomizations(js_name="read"),
|
||||
"skip": MethodCustomizations(drop=True),
|
||||
"skip_async": MethodCustomizations(js_name="skip"),
|
||||
"is_closed": MethodCustomizations(drop=True),
|
||||
"has_pending": MethodCustomizations(drop=True),
|
||||
"set_pending": MethodCustomizations(drop=True),
|
||||
"clear_pending": MethodCustomizations(drop=True),
|
||||
},
|
||||
),
|
||||
"OutputStream": ObjectTypeCustomizations(
|
||||
methods={
|
||||
"close": MethodCustomizations(drop=True),
|
||||
"close_async": MethodCustomizations(js_name="close"),
|
||||
"flush": MethodCustomizations(drop=True),
|
||||
"flush_async": MethodCustomizations(js_name="flush"),
|
||||
"write": MethodCustomizations(drop=True),
|
||||
"write_async": MethodCustomizations(drop=True),
|
||||
"write_all": MethodCustomizations(drop=True),
|
||||
"write_all_async": MethodCustomizations(drop=True),
|
||||
"write_bytes": MethodCustomizations(drop=True),
|
||||
"write_bytes_async": MethodCustomizations(js_name="write"),
|
||||
"writev": MethodCustomizations(drop=True),
|
||||
"writev_async": MethodCustomizations(drop=True),
|
||||
"writev_all": MethodCustomizations(drop=True),
|
||||
"writev_all_async": MethodCustomizations(drop=True),
|
||||
"splice": MethodCustomizations(drop=True),
|
||||
"splice_async": MethodCustomizations(drop=True),
|
||||
"is_closing": MethodCustomizations(drop=True),
|
||||
"is_closed": MethodCustomizations(drop=True),
|
||||
"has_pending": MethodCustomizations(drop=True),
|
||||
"set_pending": MethodCustomizations(drop=True),
|
||||
"clear_pending": MethodCustomizations(drop=True),
|
||||
},
|
||||
),
|
||||
"UnixSocketAddress": ObjectTypeCustomizations(
|
||||
methods={
|
||||
"get_path": MethodCustomizations(
|
||||
return_typing="Buffer",
|
||||
return_cconversion="fdn_buffer_to_value (env, (const guint8 *) retval, g_unix_socket_address_get_path_len (handle))",
|
||||
),
|
||||
"get_path_len": MethodCustomizations(drop=True),
|
||||
"get_is_abstract": MethodCustomizations(drop=True),
|
||||
},
|
||||
properties={
|
||||
"path": PropertyCustomizations(typing="path: Buffer"),
|
||||
"abstract": PropertyCustomizations(drop=True),
|
||||
"path-as-array": PropertyCustomizations(drop=True),
|
||||
},
|
||||
),
|
||||
"SocketAddress": ObjectTypeCustomizations(
|
||||
js_name="BaseSocketAddress",
|
||||
constructor=ConstructorCustomizations(drop=True),
|
||||
methods={
|
||||
"to_native": MethodCustomizations(drop=True),
|
||||
},
|
||||
),
|
||||
"SocketConnectable": ObjectTypeCustomizations(drop_abstract_base=True),
|
||||
"InetAddress": ObjectTypeCustomizations(
|
||||
properties={
|
||||
"bytes": PropertyCustomizations(drop=True),
|
||||
},
|
||||
),
|
||||
"Object": ObjectTypeCustomizations(
|
||||
js_name="BaseObject",
|
||||
methods={
|
||||
"is_floating": MethodCustomizations(drop=True),
|
||||
"ref": MethodCustomizations(drop=True),
|
||||
"ref_sink": MethodCustomizations(drop=True),
|
||||
"unref": MethodCustomizations(drop=True),
|
||||
"getv": MethodCustomizations(drop=True),
|
||||
"get_property": MethodCustomizations(drop=True),
|
||||
"set_property": MethodCustomizations(drop=True),
|
||||
"notify_by_pspec": MethodCustomizations(drop=True),
|
||||
"freeze_notify": MethodCustomizations(drop=True),
|
||||
"thaw_notify": MethodCustomizations(drop=True),
|
||||
"bind_property": MethodCustomizations(drop=True),
|
||||
"bind_property_full": MethodCustomizations(drop=True),
|
||||
"bind_property_with_closures": MethodCustomizations(drop=True),
|
||||
"force_floating": MethodCustomizations(drop=True),
|
||||
"get_data": MethodCustomizations(drop=True),
|
||||
"get_qdata": MethodCustomizations(drop=True),
|
||||
"run_dispose": MethodCustomizations(drop=True),
|
||||
"set_data": MethodCustomizations(drop=True),
|
||||
"steal_data": MethodCustomizations(drop=True),
|
||||
"steal_qdata": MethodCustomizations(drop=True),
|
||||
"watch_closure": MethodCustomizations(drop=True),
|
||||
},
|
||||
signals={
|
||||
"notify": SignalCustomizations(drop=True),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
return Customizations(
|
||||
custom_types,
|
||||
type_customizations,
|
||||
CUSTOMIZATION_FACADE_EXPORTS,
|
||||
CUSTOMIZATION_FACADE_TS,
|
||||
CUSTOMIZATION_HELPERS_IMPORTS,
|
||||
CUSTOMIZATION_HELPERS_TS,
|
||||
)
|
||||
@@ -0,0 +1,61 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
|
||||
from .model import Customizations, Model, parse_gir
|
||||
|
||||
INCLUDED_GIO_OBJECT_TYPES = [
|
||||
"Cancellable",
|
||||
"IOStream",
|
||||
"InputStream",
|
||||
"OutputStream",
|
||||
"SocketAddress",
|
||||
"SocketAddressEnumerator",
|
||||
"SocketConnectable",
|
||||
"InetSocketAddress",
|
||||
"InetAddress",
|
||||
"UnixSocketAddress",
|
||||
]
|
||||
INCLUDED_GIO_ENUMERATIONS = [
|
||||
"FileMonitorEvent",
|
||||
"SocketFamily",
|
||||
"UnixSocketAddressType",
|
||||
]
|
||||
|
||||
|
||||
def compute_model(
|
||||
frida_gir: Path,
|
||||
glib_gir: Path,
|
||||
gobject_gir: Path,
|
||||
gio_gir: Path,
|
||||
customizations: Customizations,
|
||||
) -> Model:
|
||||
glib = parse_gir(glib_gir, [])
|
||||
gobject = parse_gir(gobject_gir, [glib])
|
||||
gio = parse_gir(gio_gir, [glib, gobject])
|
||||
frida = parse_gir(frida_gir, [glib, gobject, gio])
|
||||
|
||||
object_types = OrderedDict()
|
||||
object_types["Object"] = gobject.object_types["Object"]
|
||||
object_types.update(frida.object_types)
|
||||
for t in INCLUDED_GIO_OBJECT_TYPES:
|
||||
object_types[t] = gio.object_types[t]
|
||||
|
||||
enumerations = OrderedDict(frida.enumerations)
|
||||
for t in INCLUDED_GIO_ENUMERATIONS:
|
||||
enumerations[t] = gio.enumerations[t]
|
||||
|
||||
model = Model(
|
||||
frida.namespace,
|
||||
object_types,
|
||||
enumerations,
|
||||
customizations,
|
||||
)
|
||||
|
||||
for t in object_types.values():
|
||||
t.model = model
|
||||
for t in enumerations.values():
|
||||
t.model = model
|
||||
|
||||
return model
|
||||
File diff suppressed because it is too large
Load Diff
+90
-3
@@ -1,8 +1,95 @@
|
||||
subdir('_frida')
|
||||
if frida_core_dep.type_name() == 'internal'
|
||||
frida_core_subprj = subproject('frida-core')
|
||||
frida_gir = frida_core_subprj.get_variable('core_public_gir')
|
||||
glib_gir = frida_core_subprj.get_variable('glib_gir')
|
||||
gobject_gir = frida_core_subprj.get_variable('gobject_gir')
|
||||
gio_gir = frida_core_subprj.get_variable('gio_gir')
|
||||
else
|
||||
girdir = frida_core_dep.get_variable('frida_girdir')
|
||||
frida_gir = girdir / 'Frida-1.0.gir'
|
||||
glib_gir = girdir / 'GLib-2.0.gir'
|
||||
gobject_gir = girdir / 'GObject-2.0.gir'
|
||||
gio_gir = girdir / 'Gio-2.0.gir'
|
||||
endif
|
||||
|
||||
env = environment()
|
||||
env.set('PYTHONPATH', meson.current_source_dir())
|
||||
|
||||
code = custom_target('binding-code',
|
||||
output: [
|
||||
'__init__.py',
|
||||
'__init__.pyi',
|
||||
'extension.c',
|
||||
],
|
||||
input: [
|
||||
frida_gir,
|
||||
glib_gir,
|
||||
gobject_gir,
|
||||
gio_gir,
|
||||
files(
|
||||
'frida_bindgen' / '__init__.py',
|
||||
'frida_bindgen' / '__main__.py',
|
||||
'frida_bindgen' / 'cli.py',
|
||||
'frida_bindgen' / 'codegen.py',
|
||||
'frida_bindgen' / 'customization.py',
|
||||
'frida_bindgen' / 'loader.py',
|
||||
'frida_bindgen' / 'model.py',
|
||||
'frida_bindgen' / 'assets' / 'codegen_gobject_globals.c',
|
||||
'frida_bindgen' / 'assets' / 'codegen_gobject_methods.c',
|
||||
'frida_bindgen' / 'assets' / 'codegen_gobject_prototypes.h',
|
||||
'frida_bindgen' / 'assets' / 'codegen_helpers.c',
|
||||
'frida_bindgen' / 'assets' / 'codegen_helpers.ts',
|
||||
'frida_bindgen' / 'assets' / 'codegen_macros.h',
|
||||
'frida_bindgen' / 'assets' / 'codegen_prototypes.h',
|
||||
'frida_bindgen' / 'assets' / 'codegen_structs.h',
|
||||
'frida_bindgen' / 'assets' / 'codegen_typedefs.h',
|
||||
'frida_bindgen' / 'assets' / 'customization_facade.exports',
|
||||
'frida_bindgen' / 'assets' / 'customization_facade.ts',
|
||||
'frida_bindgen' / 'assets' / 'customization_helpers.imports',
|
||||
'frida_bindgen' / 'assets' / 'customization_helpers.ts',
|
||||
)
|
||||
],
|
||||
command: [
|
||||
python, '-m', 'frida_bindgen',
|
||||
'--frida-gir=@INPUT0@',
|
||||
'--glib-gir=@INPUT1@',
|
||||
'--gobject-gir=@INPUT2@',
|
||||
'--gio-gir=@INPUT3@',
|
||||
'--output-py=@OUTPUT0@',
|
||||
'--output-pyi=@OUTPUT1@',
|
||||
'--output-c=@OUTPUT2@',
|
||||
],
|
||||
env: env,
|
||||
install: true,
|
||||
install_dir: [
|
||||
python.get_install_dir() / 'frida',
|
||||
python.get_install_dir() / 'frida' / '_frida',
|
||||
false,
|
||||
]
|
||||
)
|
||||
|
||||
py_sources = [
|
||||
'__init__.py',
|
||||
'core.py',
|
||||
'py.typed',
|
||||
]
|
||||
python.install_sources(py_sources, subdir: 'frida', pure: false)
|
||||
|
||||
extension_py_sources = [
|
||||
'py.typed',
|
||||
]
|
||||
python.install_sources(extension_py_sources, subdir: 'frida' / '_frida', pure: false)
|
||||
|
||||
extra_link_args = []
|
||||
if host_os_family == 'darwin'
|
||||
extra_link_args += '-Wl,-exported_symbol,_PyInit__frida'
|
||||
elif host_os_family != 'windows'
|
||||
extra_link_args += '-Wl,--version-script,' + meson.current_source_dir() / 'extension.version'
|
||||
endif
|
||||
|
||||
extension = python.extension_module('_frida', code[2],
|
||||
limited_api: '3.7',
|
||||
c_args: frida_component_cflags,
|
||||
link_args: extra_link_args,
|
||||
dependencies: [python_dep, frida_core_dep, os_deps],
|
||||
install: true,
|
||||
subdir: 'frida',
|
||||
)
|
||||
|
||||
+6
-6
@@ -32,9 +32,9 @@ endif
|
||||
|
||||
subdir('frida')
|
||||
|
||||
test('frida-python', python,
|
||||
args: ['-m', 'unittest', 'discover'],
|
||||
workdir: meson.current_source_dir(),
|
||||
env: {'PYTHONPATH': meson.current_build_dir() / 'src'},
|
||||
timeout: 30,
|
||||
)
|
||||
#test('frida-python', python,
|
||||
# args: ['-m', 'unittest', 'discover'],
|
||||
# workdir: meson.current_source_dir(),
|
||||
# env: {'PYTHONPATH': meson.current_build_dir() / 'src'},
|
||||
# timeout: 30,
|
||||
#)
|
||||
|
||||
+1
-1
Submodule releng updated: 54cc29651c...71f828f7c8
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-core.git
|
||||
revision = 16.4.3
|
||||
revision = 17.6.0
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
Reference in New Issue
Block a user