mirror of
https://github.com/frida/frida-python
synced 2026-06-08 14:16:17 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77d9c51109 | |||
| 040fa5536b | |||
| c9226b976a | |||
| d851e4eb6a | |||
| ccea05e00b | |||
| 6416edc773 | |||
| ce718c1dc9 | |||
| 0b854499cd | |||
| 243cce22e6 | |||
| c4d0fe55a5 | |||
| f3d767ecb6 | |||
| 8838d885ce | |||
| 213e373056 | |||
| 8b888558c7 | |||
| de55d964a0 | |||
| e920bad7f5 | |||
| 2b947e6d75 | |||
| 9992e8ad7a | |||
| 3c7eb89653 | |||
| 16f1be2a88 | |||
| 280c6d73f3 | |||
| 140dc6d13a | |||
| eac203e327 | |||
| bc3429a80d | |||
| e929477ecd | |||
| ebeb7a86b1 |
@@ -0,0 +1,9 @@
|
||||
import pprint
|
||||
|
||||
import frida
|
||||
|
||||
device = frida.get_usb_device()
|
||||
|
||||
deviceinfo = device.open_service("dtx:com.apple.instruments.server.services.deviceinfo")
|
||||
response = deviceinfo.request({"method": "runningProcesses"})
|
||||
pprint.pp(response)
|
||||
@@ -0,0 +1,27 @@
|
||||
import sys
|
||||
|
||||
import frida
|
||||
|
||||
|
||||
def on_message(message):
|
||||
print("on_message:", message)
|
||||
|
||||
|
||||
device = frida.get_usb_device()
|
||||
|
||||
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],
|
||||
}
|
||||
)
|
||||
|
||||
sys.stdin.read()
|
||||
@@ -0,0 +1,31 @@
|
||||
import sys
|
||||
|
||||
import frida
|
||||
|
||||
|
||||
def on_message(message):
|
||||
print("on_message:", message)
|
||||
|
||||
|
||||
device = frida.get_usb_device()
|
||||
|
||||
control = device.open_service("dtx:com.apple.instruments.server.services.processcontrol")
|
||||
control.on("message", on_message)
|
||||
pid = control.request(
|
||||
{
|
||||
"method": "launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:",
|
||||
"args": [
|
||||
"",
|
||||
"no.oleavr.HelloIOS",
|
||||
{},
|
||||
[],
|
||||
{
|
||||
"StartSuspendedKey": False,
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
control.request({"method": "startObservingPid:", "args": [pid]})
|
||||
|
||||
print(f"App spawned, PID: {pid}. Kill it to see an example message being emitted.")
|
||||
sys.stdin.read()
|
||||
@@ -0,0 +1,15 @@
|
||||
import sys
|
||||
|
||||
import frida
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: {sys.argv[0]} outfile.png", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
outfile = sys.argv[1]
|
||||
|
||||
device = frida.get_usb_device()
|
||||
|
||||
screenshot = device.open_service("dtx:com.apple.instruments.server.services.screenshot")
|
||||
png = screenshot.request({"method": "takeScreenshot"})
|
||||
with open(outfile, "wb") as f:
|
||||
f.write(png)
|
||||
@@ -0,0 +1,29 @@
|
||||
import time
|
||||
|
||||
import frida
|
||||
|
||||
|
||||
def on_message(message):
|
||||
print("on_message:", message)
|
||||
|
||||
|
||||
device = frida.get_usb_device()
|
||||
|
||||
sysmon = device.open_service("dtx:com.apple.instruments.server.services.sysmontap")
|
||||
sysmon.on("message", on_message)
|
||||
sysmon.request(
|
||||
{
|
||||
"method": "setConfig:",
|
||||
"args": [
|
||||
{
|
||||
"ur": 1000,
|
||||
"cpuUsage": True,
|
||||
"sampleInterval": 1000000000,
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
sysmon.request({"method": "start"})
|
||||
time.sleep(5)
|
||||
sysmon.request({"method": "stop"})
|
||||
time.sleep(1)
|
||||
@@ -0,0 +1,7 @@
|
||||
import frida
|
||||
|
||||
device = frida.get_usb_device()
|
||||
|
||||
diag = device.open_service("plist:com.apple.mobile.diagnostics_relay")
|
||||
diag.request({"type": "query", "payload": {"Request": "Sleep", "WaitForDisconnect": True}})
|
||||
diag.request({"type": "query", "payload": {"Request": "Goodbye"}})
|
||||
@@ -0,0 +1,15 @@
|
||||
import pprint
|
||||
|
||||
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": {},
|
||||
}
|
||||
)
|
||||
pprint.pp(response)
|
||||
@@ -342,6 +342,12 @@ class Device(Object):
|
||||
"""
|
||||
...
|
||||
|
||||
def open_service(self, address: str) -> Service:
|
||||
"""
|
||||
Open a device-specific service.
|
||||
"""
|
||||
...
|
||||
|
||||
def unpair(self) -> None:
|
||||
"""
|
||||
Unpair device.
|
||||
@@ -629,6 +635,25 @@ class Script(Object):
|
||||
"""
|
||||
...
|
||||
|
||||
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:
|
||||
|
||||
+325
-89
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2023 Ole André Vadla Ravnås <oleavr@nowsecure.com>
|
||||
* Copyright (C) 2024 Håvard Sørbø <havard@hsorbo.no>
|
||||
*
|
||||
* Licence: wxWindows Library Licence, Version 3.1
|
||||
*/
|
||||
@@ -148,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;
|
||||
@@ -255,6 +257,11 @@ struct _PyBus
|
||||
PyGObject parent;
|
||||
};
|
||||
|
||||
struct _PyService
|
||||
{
|
||||
PyGObject parent;
|
||||
};
|
||||
|
||||
struct _PySession
|
||||
{
|
||||
PyGObject parent;
|
||||
@@ -349,7 +356,12 @@ static gboolean PyGObject_unmarshal_enum (const gchar * str, GType type, gpointe
|
||||
static PyObject * PyGObject_marshal_bytes (GBytes * bytes);
|
||||
static PyObject * PyGObject_marshal_bytes_non_nullable (GBytes * bytes);
|
||||
static PyObject * PyGObject_marshal_variant (GVariant * variant);
|
||||
static PyObject * PyGObject_marshal_variant_byte_array (GVariant * variant);
|
||||
static PyObject * PyGObject_marshal_variant_dict (GVariant * variant);
|
||||
static PyObject * PyGObject_marshal_variant_array (GVariant * variant);
|
||||
static gboolean PyGObject_unmarshal_variant (PyObject * value, GVariant ** variant);
|
||||
static gboolean PyGObject_unmarshal_variant_from_mapping (PyObject * mapping, GVariant ** variant);
|
||||
static gboolean PyGObject_unmarshal_variant_from_sequence (PyObject * sequence, GVariant ** variant);
|
||||
static PyObject * PyGObject_marshal_parameters_dict (GHashTable * dict);
|
||||
static PyObject * PyGObject_marshal_socket_address (GSocketAddress * address);
|
||||
static gboolean PyGObject_unmarshal_certificate (const gchar * str, GTlsCertificate ** certificate);
|
||||
@@ -391,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);
|
||||
@@ -428,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);
|
||||
@@ -568,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 }
|
||||
};
|
||||
@@ -635,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." },
|
||||
@@ -812,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 },
|
||||
@@ -1594,73 +1626,99 @@ PyGObject_marshal_bytes_non_nullable (GBytes * bytes)
|
||||
static PyObject *
|
||||
PyGObject_marshal_variant (GVariant * variant)
|
||||
{
|
||||
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING))
|
||||
return PyGObject_marshal_string (g_variant_get_string (variant, NULL));
|
||||
|
||||
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT64))
|
||||
return PyLong_FromLongLong (g_variant_get_int64 (variant));
|
||||
|
||||
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN))
|
||||
return PyBool_FromLong (g_variant_get_boolean (variant));
|
||||
|
||||
if (g_variant_is_of_type (variant, G_VARIANT_TYPE ("ay")))
|
||||
switch (g_variant_classify (variant))
|
||||
{
|
||||
gconstpointer elements;
|
||||
gsize n_elements;
|
||||
case G_VARIANT_CLASS_STRING:
|
||||
return PyGObject_marshal_string (g_variant_get_string (variant, NULL));
|
||||
case G_VARIANT_CLASS_INT64:
|
||||
return PyLong_FromLongLong (g_variant_get_int64 (variant));
|
||||
case G_VARIANT_CLASS_UINT64:
|
||||
return PyLong_FromLongLong (g_variant_get_uint64 (variant));
|
||||
case G_VARIANT_CLASS_DOUBLE:
|
||||
return PyFloat_FromDouble (g_variant_get_double (variant));
|
||||
case G_VARIANT_CLASS_BOOLEAN:
|
||||
return PyBool_FromLong (g_variant_get_boolean (variant));
|
||||
case G_VARIANT_CLASS_ARRAY:
|
||||
if (g_variant_is_of_type (variant, G_VARIANT_TYPE ("ay")))
|
||||
return PyGObject_marshal_variant_byte_array (variant);
|
||||
|
||||
elements = g_variant_get_fixed_array (variant, &n_elements, sizeof (guint8));
|
||||
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT))
|
||||
return PyGObject_marshal_variant_dict (variant);
|
||||
|
||||
return PyBytes_FromStringAndSize (elements, n_elements);
|
||||
}
|
||||
|
||||
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT))
|
||||
{
|
||||
PyObject * dict;
|
||||
GVariantIter iter;
|
||||
gchar * key;
|
||||
GVariant * raw_value;
|
||||
|
||||
dict = PyDict_New ();
|
||||
|
||||
g_variant_iter_init (&iter, variant);
|
||||
|
||||
while (g_variant_iter_next (&iter, "{sv}", &key, &raw_value))
|
||||
{
|
||||
PyObject * value = PyGObject_marshal_variant (raw_value);
|
||||
|
||||
PyDict_SetItemString (dict, key, value);
|
||||
|
||||
Py_DECREF (value);
|
||||
g_variant_unref (raw_value);
|
||||
g_free (key);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_ARRAY))
|
||||
{
|
||||
GVariantIter iter;
|
||||
PyObject * list;
|
||||
guint i;
|
||||
GVariant * child;
|
||||
|
||||
g_variant_iter_init (&iter, variant);
|
||||
|
||||
list = PyList_New (g_variant_iter_n_children (&iter));
|
||||
|
||||
for (i = 0; (child = g_variant_iter_next_value (&iter)) != NULL; i++)
|
||||
{
|
||||
PyList_SetItem (list, i, PyGObject_marshal_variant (child));
|
||||
g_variant_unref (child);
|
||||
}
|
||||
|
||||
return list;
|
||||
return PyGObject_marshal_variant_array (variant);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PyGObject_marshal_variant_byte_array (GVariant * variant)
|
||||
{
|
||||
gconstpointer elements;
|
||||
gsize n_elements;
|
||||
|
||||
elements = g_variant_get_fixed_array (variant, &n_elements, sizeof (guint8));
|
||||
|
||||
return PyBytes_FromStringAndSize (elements, n_elements);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PyGObject_marshal_variant_dict (GVariant * variant)
|
||||
{
|
||||
PyObject * dict;
|
||||
GVariantIter iter;
|
||||
gchar * key;
|
||||
GVariant * raw_value;
|
||||
|
||||
dict = PyDict_New ();
|
||||
|
||||
g_variant_iter_init (&iter, variant);
|
||||
|
||||
while (g_variant_iter_next (&iter, "{sv}", &key, &raw_value))
|
||||
{
|
||||
PyObject * value = PyGObject_marshal_variant (raw_value);
|
||||
|
||||
PyDict_SetItemString (dict, key, value);
|
||||
|
||||
Py_DECREF (value);
|
||||
g_variant_unref (raw_value);
|
||||
g_free (key);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PyGObject_marshal_variant_array (GVariant * variant)
|
||||
{
|
||||
GVariantIter iter;
|
||||
PyObject * list;
|
||||
guint i;
|
||||
GVariant * child;
|
||||
|
||||
g_variant_iter_init (&iter, variant);
|
||||
|
||||
list = PyList_New (g_variant_iter_n_children (&iter));
|
||||
|
||||
for (i = 0; (child = g_variant_iter_next_value (&iter)) != NULL; i++)
|
||||
{
|
||||
if (g_variant_is_of_type (child, G_VARIANT_TYPE_VARIANT))
|
||||
{
|
||||
GVariant * inner = g_variant_get_variant (child);
|
||||
g_variant_unref (child);
|
||||
child = inner;
|
||||
}
|
||||
|
||||
PyList_SetItem (list, i, PyGObject_marshal_variant (child));
|
||||
|
||||
g_variant_unref (child);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
PyGObject_unmarshal_variant (PyObject * value, GVariant ** variant)
|
||||
{
|
||||
@@ -1671,53 +1729,148 @@ PyGObject_unmarshal_variant (PyObject * value, GVariant ** variant)
|
||||
PyGObject_unmarshal_string (value, &str);
|
||||
|
||||
*variant = g_variant_new_take_string (str);
|
||||
}
|
||||
else if (PyBool_Check (value))
|
||||
{
|
||||
*variant = g_variant_new_boolean (value == Py_True);
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
else if (PyUnicode_Check (value))
|
||||
{
|
||||
PyObject * value_utf8;
|
||||
|
||||
value_utf8 = PyUnicode_AsUTF8String (value);
|
||||
if (value_utf8 == NULL)
|
||||
goto propagate_error;
|
||||
|
||||
*variant = g_variant_new_string (PyBytes_AsString (value_utf8));
|
||||
|
||||
Py_DECREF (value_utf8);
|
||||
return TRUE;
|
||||
}
|
||||
else if (PyInt_Check (value))
|
||||
{
|
||||
*variant = g_variant_new_int64 (PyInt_AS_LONG (value));
|
||||
}
|
||||
#endif
|
||||
else if (PyLong_Check (value))
|
||||
|
||||
if (PyLong_Check (value))
|
||||
{
|
||||
PY_LONG_LONG l;
|
||||
|
||||
l = PyLong_AsLongLong (value);
|
||||
if (l == -1 && PyErr_Occurred ())
|
||||
goto propagate_error;
|
||||
return FALSE;
|
||||
|
||||
*variant = g_variant_new_int64 (l);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
|
||||
if (PyFloat_Check (value))
|
||||
{
|
||||
goto unsupported_type;
|
||||
*variant = g_variant_new_double (PyFloat_AsDouble (value));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (PyBool_Check (value))
|
||||
{
|
||||
*variant = g_variant_new_boolean (value == Py_True);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (PyBytes_Check (value))
|
||||
{
|
||||
char * buffer;
|
||||
Py_ssize_t length;
|
||||
gpointer copy;
|
||||
|
||||
PyBytes_AsStringAndSize (value, &buffer, &length);
|
||||
|
||||
copy = g_memdup2 (buffer, length);
|
||||
*variant = g_variant_new_from_data (G_VARIANT_TYPE_BYTESTRING, copy, length, TRUE, g_free, copy);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (PySequence_Check (value))
|
||||
return PyGObject_unmarshal_variant_from_sequence (value, variant);
|
||||
|
||||
if (PyMapping_Check (value))
|
||||
return PyGObject_unmarshal_variant_from_mapping (value, variant);
|
||||
|
||||
PyErr_SetString (PyExc_TypeError, "unsupported type");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
PyGObject_unmarshal_variant_from_mapping (PyObject * mapping, GVariant ** variant)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
PyObject * items = NULL;
|
||||
Py_ssize_t n, i;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
|
||||
|
||||
items = PyMapping_Items (mapping);
|
||||
if (items == NULL)
|
||||
goto propagate_error;
|
||||
|
||||
n = PyList_Size (items);
|
||||
|
||||
for (i = 0; i != n; i++)
|
||||
{
|
||||
PyObject * pair, * key, * val, * key_bytes;
|
||||
GVariant * raw_value;
|
||||
|
||||
pair = PyList_GetItem (items, i);
|
||||
key = PyTuple_GetItem (pair, 0);
|
||||
val = PyTuple_GetItem (pair, 1);
|
||||
|
||||
if (!PyGObject_unmarshal_variant (val, &raw_value))
|
||||
goto propagate_error;
|
||||
|
||||
key_bytes = PyUnicode_AsUTF8String (key);
|
||||
|
||||
g_variant_builder_add (&builder, "{sv}", PyBytes_AsString (key_bytes), raw_value);
|
||||
|
||||
Py_DECREF (key_bytes);
|
||||
}
|
||||
|
||||
Py_DecRef (items);
|
||||
|
||||
*variant = g_variant_builder_end (&builder);
|
||||
|
||||
return TRUE;
|
||||
|
||||
unsupported_type:
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError, "unsupported type");
|
||||
goto propagate_error;
|
||||
}
|
||||
propagate_error:
|
||||
{
|
||||
Py_XDECREF (items);
|
||||
g_variant_builder_clear (&builder);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
PyGObject_unmarshal_variant_from_sequence (PyObject * sequence, GVariant ** variant)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
Py_ssize_t n, i;
|
||||
PyObject * val = NULL;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
|
||||
|
||||
n = PySequence_Length (sequence);
|
||||
if (n == -1)
|
||||
goto propagate_error;
|
||||
|
||||
for (i = 0; i != n; i++)
|
||||
{
|
||||
GVariant * raw_value;
|
||||
|
||||
val = PySequence_GetItem (sequence, i);
|
||||
if (val == NULL)
|
||||
goto propagate_error;
|
||||
|
||||
if (!PyGObject_unmarshal_variant (val, &raw_value))
|
||||
goto propagate_error;
|
||||
|
||||
g_variant_builder_add (&builder, "v", raw_value);
|
||||
|
||||
Py_DECREF (val);
|
||||
}
|
||||
|
||||
*variant = g_variant_builder_end (&builder);
|
||||
|
||||
return TRUE;
|
||||
|
||||
propagate_error:
|
||||
{
|
||||
Py_XDECREF (val);
|
||||
g_variant_builder_clear (&builder);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -2839,6 +2992,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)
|
||||
{
|
||||
@@ -3338,6 +3510,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)
|
||||
{
|
||||
@@ -5207,6 +5442,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);
|
||||
|
||||
+98
-13
@@ -176,7 +176,8 @@ class ScriptExportsSync:
|
||||
js_name = _to_camel_case(name)
|
||||
|
||||
def method(*args: Any, **kwargs: Any) -> Any:
|
||||
return script._rpc_request("call", js_name, args, **kwargs)
|
||||
request, data = make_rpc_call_request(js_name, args)
|
||||
return script._rpc_request(request, data, **kwargs)
|
||||
|
||||
return method
|
||||
|
||||
@@ -202,7 +203,8 @@ class ScriptExportsAsync:
|
||||
js_name = _to_camel_case(name)
|
||||
|
||||
async def method(*args: Any, **kwargs: Any) -> Any:
|
||||
return await script._rpc_request_async("call", js_name, args, **kwargs)
|
||||
request, data = make_rpc_call_request(js_name, args)
|
||||
return await script._rpc_request_async(request, data, **kwargs)
|
||||
|
||||
return method
|
||||
|
||||
@@ -210,6 +212,16 @@ class ScriptExportsAsync:
|
||||
return self._script.list_exports_sync()
|
||||
|
||||
|
||||
def make_rpc_call_request(js_name: str, args: Sequence[Any]) -> Tuple[List[Any], Optional[bytes]]:
|
||||
if args and isinstance(args[-1], bytes):
|
||||
raw_args = args[:-1]
|
||||
data = args[-1]
|
||||
else:
|
||||
raw_args = args
|
||||
data = None
|
||||
return (["call", js_name, raw_args], data)
|
||||
|
||||
|
||||
class ScriptErrorMessage(TypedDict):
|
||||
type: Literal["error"]
|
||||
description: str
|
||||
@@ -404,7 +416,7 @@ class Script:
|
||||
Asynchronously list all the exported attributes from the script's rpc
|
||||
"""
|
||||
|
||||
result = await self._rpc_request_async("list")
|
||||
result = await self._rpc_request_async(["list"])
|
||||
assert isinstance(result, list)
|
||||
return result
|
||||
|
||||
@@ -413,7 +425,7 @@ class Script:
|
||||
List all the exported attributes from the script's rpc
|
||||
"""
|
||||
|
||||
result = self._rpc_request("list")
|
||||
result = self._rpc_request(["list"])
|
||||
assert isinstance(result, list)
|
||||
return result
|
||||
|
||||
@@ -429,7 +441,7 @@ class Script:
|
||||
)
|
||||
return self.list_exports_sync()
|
||||
|
||||
def _rpc_request_async(self, *args: Any) -> asyncio.Future[Any]:
|
||||
def _rpc_request_async(self, args: Any, data: Optional[bytes] = None) -> asyncio.Future[Any]:
|
||||
loop = asyncio.get_event_loop()
|
||||
future: asyncio.Future[Any] = asyncio.Future()
|
||||
|
||||
@@ -442,14 +454,14 @@ class Script:
|
||||
request_id = self._append_pending(on_complete)
|
||||
|
||||
if not self.is_destroyed:
|
||||
self._send_rpc_call(request_id, *args)
|
||||
self._send_rpc_call(request_id, args, data)
|
||||
else:
|
||||
self._on_destroyed()
|
||||
|
||||
return future
|
||||
|
||||
@cancellable
|
||||
def _rpc_request(self, *args: Any) -> Any:
|
||||
def _rpc_request(self, args: Any, data: Optional[bytes] = None) -> Any:
|
||||
result = RPCResult()
|
||||
|
||||
def on_complete(value: Any, error: Optional[Union[RPCException, _frida.InvalidOperationError]]) -> None:
|
||||
@@ -466,7 +478,7 @@ class Script:
|
||||
request_id = self._append_pending(on_complete)
|
||||
|
||||
if not self.is_destroyed:
|
||||
self._send_rpc_call(request_id, *args)
|
||||
self._send_rpc_call(request_id, args, data)
|
||||
|
||||
cancellable = Cancellable.get_current()
|
||||
cancel_handler = cancellable.connect(on_cancelled)
|
||||
@@ -495,10 +507,8 @@ class Script:
|
||||
self._pending[request_id] = callback
|
||||
return request_id
|
||||
|
||||
def _send_rpc_call(self, request_id: int, *args: Any) -> None:
|
||||
message = ["frida:rpc", request_id]
|
||||
message.extend(args)
|
||||
self.post(message)
|
||||
def _send_rpc_call(self, request_id: int, args: Any, data: Optional[bytes]) -> None:
|
||||
self.post(["frida:rpc", request_id, *args], data)
|
||||
|
||||
def _on_rpc_message(self, request_id: int, operation: str, params: List[Any], data: Optional[Any]) -> None:
|
||||
if operation in ("ok", "error"):
|
||||
@@ -509,7 +519,10 @@ class Script:
|
||||
value = None
|
||||
error = None
|
||||
if operation == "ok":
|
||||
value = params[0] if data is None else data
|
||||
if data is not None:
|
||||
value = (params[1], data) if len(params) > 1 else data
|
||||
else:
|
||||
value = params[0]
|
||||
else:
|
||||
error = RPCException(*params[0:3])
|
||||
|
||||
@@ -792,6 +805,70 @@ class Bus:
|
||||
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 +1096,14 @@ class Device:
|
||||
|
||||
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:
|
||||
"""
|
||||
|
||||
+1
-1
Submodule releng updated: 6bad5eec71...b0e46f7b69
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-core.git
|
||||
revision = 16.2.4
|
||||
revision = 16.3.2
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
+21
-8
@@ -31,19 +31,26 @@ class TestRpc(unittest.TestCase):
|
||||
name="test-rpc",
|
||||
source="""\
|
||||
rpc.exports = {
|
||||
add: function (a, b) {
|
||||
var result = a + b;
|
||||
add(a, b) {
|
||||
const result = a + b;
|
||||
if (result < 0)
|
||||
throw new Error("No");
|
||||
throw new Error("No");
|
||||
return result;
|
||||
},
|
||||
sub: function (a, b) {
|
||||
sub(a, b) {
|
||||
return a - b;
|
||||
},
|
||||
speak: function () {
|
||||
var buf = Memory.allocUtf8String("Yo");
|
||||
speak() {
|
||||
const buf = Memory.allocUtf8String("Yo");
|
||||
return Memory.readByteArray(buf, 2);
|
||||
}
|
||||
},
|
||||
speakWithMetadata() {
|
||||
const buf = Memory.allocUtf8String("Yo");
|
||||
return ['soft', Memory.readByteArray(buf, 2)];
|
||||
},
|
||||
processData(val, data) {
|
||||
return { val, dump: hexdump(data, { header: false }) };
|
||||
},
|
||||
};
|
||||
""",
|
||||
)
|
||||
@@ -52,7 +59,13 @@ rpc.exports = {
|
||||
self.assertEqual(agent.add(2, 3), 5)
|
||||
self.assertEqual(agent.sub(5, 3), 2)
|
||||
self.assertRaises(Exception, lambda: agent.add(1, -2))
|
||||
self.assertListEqual([x for x in iter(agent.speak())], [0x59, 0x6F])
|
||||
self.assertEqual(agent.speak(), b"\x59\x6f")
|
||||
meta, data = agent.speak_with_metadata()
|
||||
self.assertEqual(meta, "soft")
|
||||
self.assertEqual(data, b"\x59\x6f")
|
||||
result = agent.process_data(1337, b"\x13\x37")
|
||||
self.assertEqual(result["val"], 1337)
|
||||
self.assertEqual(result["dump"], "00000000 13 37 .7")
|
||||
|
||||
def test_post_failure(self):
|
||||
script = self.session.create_script(
|
||||
|
||||
Reference in New Issue
Block a user