From 7bcd1077afec668621a346e3c572b1cc71f8c5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Tue, 28 May 2024 14:53:05 +0200 Subject: [PATCH] device: Add open_service() and Service API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Håvard Sørbø --- examples/open_service.py | 79 ++++++++++++++++++++++++++++ frida/_frida/extension.c | 109 +++++++++++++++++++++++++++++++++++++++ frida/core.py | 72 ++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 examples/open_service.py diff --git a/examples/open_service.py b/examples/open_service.py new file mode 100644 index 0000000..1550b2b --- /dev/null +++ b/examples/open_service.py @@ -0,0 +1,79 @@ +import frida + +device = frida.get_usb_device() + +#appservice = device.open_service("xpc:com.apple.coredevice.appservice") +#response = appservice.request({ +# "CoreDevice.featureIdentifier": "com.apple.coredevice.feature.listprocesses", +# "CoreDevice.action": {}, +# "CoreDevice.input": {}, +#}) +#print("Got response:", response) + +#screenshot = device.open_service("dtx:com.apple.instruments.server.services.screenshot") +#response = screenshot.request({ "method": "takeScreenshot" }) +#print("Got response:", response) + +#deviceinfo = device.open_service("dtx:com.apple.instruments.server.services.deviceinfo") +#response = deviceinfo.request({ "method": "runningProcesses" }) +#print("Got response:", response) + +#processcontrol = device.open_service("dtx:com.apple.instruments.server.services.processcontrol") +#response = processcontrol.request({ +# "method": "launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:", +# "args": [ +# "", +# "no.oleavr.HelloIOS", +# {}, +# [], +# { +# "StartSuspendedKey": False, +# "KillExisting": True, +# } +# ] +#}) +#response = processcontrol.request({ "method": "killPid:", "args": [ 2729 ] }) +#print("Got response:", response) + +def on_message(message): + print("on_message:", message) + +#sysmontap = device.open_service("dtx:com.apple.instruments.server.services.sysmontap") +#sysmontap.on("message", on_message) +#sysmontap.request({ +# "method": "setConfig:", +# "args": [ +# { +# "ur": 1000, +# "bm": 0, +# "cpuUsage": True, +# "sampleInterval": 1000000000, +# "procAttrs": [ +# "cpuUsage", +# "ctxSwitch", +# "intWakeups", +# "memAnon", +# "memResidentSize", +# "memVirtualSize", +# "physFootprint", +# "pid", +# ], +# }, +# ], +#}) +#response = sysmontap.request({ "method": "start" }) +#print("Got start() response:", response) + +opengl = device.open_service("dtx:com.apple.instruments.server.services.graphics.opengl") +opengl.on("message", on_message) +opengl.request({ + "method": "setSamplingRate:", + "args": [ 5.0 ], +}) +opengl.request({ + "method": "startSamplingAtTimeInterval:", + "args": [ 0.0 ], +}) + +import sys +sys.stdin.read() diff --git a/frida/_frida/extension.c b/frida/_frida/extension.c index 2ed5705..7b9686e 100644 --- a/frida/_frida/extension.c +++ b/frida/_frida/extension.c @@ -149,6 +149,7 @@ typedef struct _PySpawn PySpawn; typedef struct _PyChild PyChild; typedef struct _PyCrash PyCrash; typedef struct _PyBus PyBus; +typedef struct _PyService PyService; typedef struct _PySession PySession; typedef struct _PyScript PyScript; typedef struct _PyRelay PyRelay; @@ -256,6 +257,11 @@ struct _PyBus PyGObject parent; }; +struct _PyService +{ + PyGObject parent; +}; + struct _PySession { PyGObject parent; @@ -397,6 +403,7 @@ static FridaSessionOptions * PyDevice_parse_session_options (const gchar * realm static PyObject * PyDevice_inject_library_file (PyDevice * self, PyObject * args); static PyObject * PyDevice_inject_library_blob (PyDevice * self, PyObject * args); static PyObject * PyDevice_open_channel (PyDevice * self, PyObject * args); +static PyObject * PyDevice_open_service (PyDevice * self, PyObject * args); static PyObject * PyDevice_unpair (PyDevice * self); static PyObject * PyApplication_new_take_handle (FridaApplication * handle); @@ -434,6 +441,11 @@ static PyObject * PyBus_new_take_handle (FridaBus * handle); static PyObject * PyBus_attach (PySession * self); static PyObject * PyBus_post (PyScript * self, PyObject * args, PyObject * kw); +static PyObject * PyService_new_take_handle (FridaService * handle); +static PyObject * PyService_activate (PyService * self); +static PyObject * PyService_cancel (PyService * self); +static PyObject * PyService_request (PyService * self, PyObject * args); + static PyObject * PySession_new_take_handle (FridaSession * handle); static int PySession_init (PySession * self, PyObject * args, PyObject * kw); static void PySession_init_from_handle (PySession * self, FridaSession * handle); @@ -574,6 +586,7 @@ static PyMethodDef PyDevice_methods[] = { "inject_library_file", (PyCFunction) PyDevice_inject_library_file, METH_VARARGS, "Inject a library file to a PID." }, { "inject_library_blob", (PyCFunction) PyDevice_inject_library_blob, METH_VARARGS, "Inject a library blob to a PID." }, { "open_channel", (PyCFunction) PyDevice_open_channel, METH_VARARGS, "Open a device-specific communication channel." }, + { "open_service", (PyCFunction) PyDevice_open_service, METH_VARARGS, "Open a device-specific service." }, { "unpair", (PyCFunction) PyDevice_unpair, METH_NOARGS, "Unpair device." }, { NULL } }; @@ -641,6 +654,14 @@ static PyMethodDef PyBus_methods[] = { NULL } }; +static PyMethodDef PyService_methods[] = +{ + { "activate", (PyCFunction) PyService_activate, METH_NOARGS, "Activate the service." }, + { "cancel", (PyCFunction) PyService_cancel, METH_NOARGS, "Cancel the service." }, + { "request", (PyCFunction) PyService_request, METH_VARARGS, "Perform a request." }, + { NULL } +}; + static PyMethodDef PySession_methods[] = { { "is_detached", (PyCFunction) PySession_is_detached, METH_NOARGS, "Query whether the session is detached." }, @@ -818,6 +839,11 @@ PYFRIDA_DEFINE_TYPE ("_frida.Bus", Bus, GObject, NULL, g_object_unref, { Py_tp_methods, PyBus_methods }, ); +PYFRIDA_DEFINE_TYPE ("_frida.Service", Service, GObject, NULL, g_object_unref, + { Py_tp_doc, "Frida Service" }, + { Py_tp_methods, PyService_methods }, +); + PYFRIDA_DEFINE_TYPE ("_frida.Session", Session, GObject, PySession_init_from_handle, frida_unref, { Py_tp_doc, "Frida Session" }, { Py_tp_init, PySession_init }, @@ -2964,6 +2990,25 @@ PyDevice_open_channel (PyDevice * self, PyObject * args) return PyIOStream_new_take_handle (stream); } +static PyObject * +PyDevice_open_service (PyDevice * self, PyObject * args) +{ + const char * address; + GError * error = NULL; + FridaService * service; + + if (!PyArg_ParseTuple (args, "s", &address)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + service = frida_device_open_service_sync (PY_GOBJECT_HANDLE (self), address, g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + if (error != NULL) + return PyFrida_raise (error); + + return PyService_new_take_handle (service); +} + static PyObject * PyDevice_unpair (PyDevice * self) { @@ -3463,6 +3508,69 @@ PyBus_post (PyScript * self, PyObject * args, PyObject * kw) } +static PyObject * +PyService_new_take_handle (FridaService * handle) +{ + return PyGObject_new_take_handle (handle, PYFRIDA_TYPE (Service)); +} + +static PyObject * +PyService_activate (PyService * self) +{ + GError * error = NULL; + + Py_BEGIN_ALLOW_THREADS + frida_service_activate_sync (PY_GOBJECT_HANDLE (self), g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + if (error != NULL) + return PyFrida_raise (error); + + Py_RETURN_NONE; +} + +static PyObject * +PyService_cancel (PyService * self) +{ + GError * error = NULL; + + Py_BEGIN_ALLOW_THREADS + frida_service_cancel_sync (PY_GOBJECT_HANDLE (self), g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + if (error != NULL) + return PyFrida_raise (error); + + Py_RETURN_NONE; +} + +static PyObject * +PyService_request (PyService * self, PyObject * args) +{ + PyObject * result, * params; + GVariant * raw_params, * raw_result; + GError * error = NULL; + + if (!PyArg_ParseTuple (args, "O", ¶ms)) + return NULL; + + if (!PyGObject_unmarshal_variant (params, &raw_params)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + raw_result = frida_service_request_sync (PY_GOBJECT_HANDLE (self), raw_params, g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + + g_variant_unref (raw_params); + + if (error != NULL) + return PyFrida_raise (error); + + result = PyGObject_marshal_variant (raw_result); + g_variant_unref (raw_result); + + return result; +} + + static PyObject * PySession_new_take_handle (FridaSession * handle) { @@ -5332,6 +5440,7 @@ MOD_INIT (_frida) PYFRIDA_REGISTER_TYPE (Child, FRIDA_TYPE_CHILD); PYFRIDA_REGISTER_TYPE (Crash, FRIDA_TYPE_CRASH); PYFRIDA_REGISTER_TYPE (Bus, FRIDA_TYPE_BUS); + PYFRIDA_REGISTER_TYPE (Service, FRIDA_TYPE_SERVICE); PYFRIDA_REGISTER_TYPE (Session, FRIDA_TYPE_SESSION); PYFRIDA_REGISTER_TYPE (Script, FRIDA_TYPE_SCRIPT); PYFRIDA_REGISTER_TYPE (Relay, FRIDA_TYPE_RELAY); diff --git a/frida/core.py b/frida/core.py index 90b5d23..7736d21 100644 --- a/frida/core.py +++ b/frida/core.py @@ -792,6 +792,70 @@ def _on_message(self, raw_message: str, data: Any) -> None: traceback.print_exc() +ServiceCloseCallback = Callable[[], None] +ServiceMessageCallback = Callable[[Any], None] + + +class Service: + def __init__(self, impl: _frida.Service) -> None: + self._impl = impl + + @cancellable + def activate(self) -> None: + """ + Activate the service + """ + + self._impl.activate() + + @cancellable + def cancel(self) -> None: + """ + Cancel the service + """ + + self._impl.cancel() + + def request(self, parameters: Any) -> Any: + """ + Perform a request + """ + + return self._impl.request(parameters) + + @overload + def on(self, signal: Literal["close"], callback: ServiceCloseCallback) -> None: ... + + @overload + def on(self, signal: Literal["message"], callback: ServiceMessageCallback) -> None: ... + + @overload + def on(self, signal: str, callback: Callable[..., Any]) -> None: ... + + def on(self, signal: str, callback: Callable[..., Any]) -> None: + """ + Add a signal handler + """ + + self._impl.on(signal, callback) + + @overload + def off(self, signal: Literal["close"], callback: ServiceCloseCallback) -> None: ... + + @overload + def off(self, signal: Literal["message"], callback: ServiceMessageCallback) -> None: ... + + @overload + def off(self, signal: str, callback: Callable[..., Any]) -> None: ... + + def off(self, signal: str, callback: Callable[..., Any]) -> None: + """ + Remove a signal handler + """ + + self._impl.off(signal, callback) + + DeviceSpawnAddedCallback = Callable[[_frida.Spawn], None] DeviceSpawnRemovedCallback = Callable[[_frida.Spawn], None] DeviceChildAddedCallback = Callable[[_frida.Child], None] @@ -1019,6 +1083,14 @@ def open_channel(self, address: str) -> IOStream: return IOStream(self._impl.open_channel(address)) + @cancellable + def open_service(self, address: str) -> Service: + """ + Open a device-specific service + """ + + return Service(self._impl.open_service(address)) + @cancellable def unpair(self) -> None: """