diff --git a/examples/channels.py b/examples/channels.py new file mode 100644 index 0000000..08fb670 --- /dev/null +++ b/examples/channels.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals, print_function +import frida + + +device = frida.get_usb_device() + +channel = device.open_channel("tcp:21") +print("Got channel:", channel) + +welcome = channel.read(512) +print("Got welcome message:", welcome) + +channel.write_all(b"CWD foo") +reply = channel.read(512) +print("Got reply:", reply) + +channel.close() +print("Channel now:", channel) diff --git a/frida/core.py b/frida/core.py index 7ecb2bf..7e69552 100644 --- a/frida/core.py +++ b/frida/core.py @@ -163,6 +163,10 @@ class Device(object): def inject_library_blob(self, target, blob, entrypoint, data): return self._impl.inject_library_blob(self._pid_of(target), blob, entrypoint, data) + @cancellable + def open_channel(self, address): + return IOStream(self._impl.open_channel(address)) + def on(self, signal, callback): self._impl.on(signal, callback) @@ -398,6 +402,38 @@ class ScriptExports(object): return method +class IOStream(object): + def __init__(self, impl): + self._impl = impl + + def __repr__(self): + return repr(self._impl) + + @property + def is_closed(self): + return self._impl.is_closed() + + @cancellable + def close(self): + self._impl.close() + + @cancellable + def read(self, count): + return self._impl.read(count) + + @cancellable + def read_all(self, count): + return self._impl.read_all(count) + + @cancellable + def write(self, data): + return self._impl.write(data) + + @cancellable + def write_all(self, data): + self._impl.write_all(data) + + class Cancellable(object): def __init__(self): self._impl = _Cancellable() diff --git a/src/_frida.c b/src/_frida.c index acad397..b6dfbee 100644 --- a/src/_frida.c +++ b/src/_frida.c @@ -112,6 +112,7 @@ typedef struct _PyIcon PyIcon; typedef struct _PySession PySession; typedef struct _PyScript PyScript; typedef struct _PyFileMonitor PyFileMonitor; +typedef struct _PyIOStream PyIOStream; typedef struct _PyCancellable PyCancellable; typedef void (* PyGObjectInitFromHandleFunc) (PyObject * self, gpointer handle); @@ -223,6 +224,13 @@ struct _PyFileMonitor PyGObject parent; }; +struct _PyIOStream +{ + PyGObject parent; + GInputStream * input; + GOutputStream * output; +}; + struct _PyCancellable { PyGObject parent; @@ -286,6 +294,7 @@ static PyObject * PyDevice_kill (PyDevice * self, PyObject * args); static PyObject * PyDevice_attach (PyDevice * self, PyObject * args); 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 * PyApplication_new_take_handle (FridaApplication * handle); static int PyApplication_init (PyApplication * self, PyObject * args, PyObject * kw); @@ -351,6 +360,17 @@ static int PyFileMonitor_init (PyFileMonitor * self, PyObject * args, PyObject * static PyObject * PyFileMonitor_enable (PyFileMonitor * self); static PyObject * PyFileMonitor_disable (PyFileMonitor * self); +static PyObject * PyIOStream_new_take_handle (GIOStream * handle); +static int PyIOStream_init (PyIOStream * self, PyObject * args, PyObject * kw); +static void PyIOStream_init_from_handle (PyIOStream * self, GIOStream * handle); +static PyObject * PyIOStream_repr (PyIOStream * self); +static PyObject * PyIOStream_is_closed (PyIOStream * self); +static PyObject * PyIOStream_close (PyIOStream * self); +static PyObject * PyIOStream_read (PyIOStream * self, PyObject * args); +static PyObject * PyIOStream_read_all (PyIOStream * self, PyObject * args); +static PyObject * PyIOStream_write (PyIOStream * self, PyObject * args); +static PyObject * PyIOStream_write_all (PyIOStream * self, PyObject * args); + static int PyCancellable_init (PyCancellable * self, PyObject * args, PyObject * kw); static PyObject * PyCancellable_repr (PyCancellable * self); static PyObject * PyCancellable_is_cancelled (PyCancellable * self); @@ -402,6 +422,7 @@ static PyMethodDef PyDevice_methods[] = { "attach", (PyCFunction) PyDevice_attach, METH_VARARGS, "Attach to a PID." }, { "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." }, { NULL } }; @@ -517,6 +538,17 @@ static PyMethodDef PyFileMonitor_methods[] = { NULL } }; +static PyMethodDef PyIOStream_methods[] = +{ + { "is_closed", (PyCFunction) PyIOStream_is_closed, METH_NOARGS, "Query whether the stream is closed." }, + { "close", (PyCFunction) PyIOStream_close, METH_NOARGS, "Close the stream." }, + { "read", (PyCFunction) PyIOStream_read, METH_VARARGS, "Read up to the specified number of bytes from the stream." }, + { "read_all", (PyCFunction) PyIOStream_read_all, METH_VARARGS, "Read exactly the specified number of bytes from the stream." }, + { "write", (PyCFunction) PyIOStream_write, METH_VARARGS, "Write as much as possible of the provided data to the stream." }, + { "write_all", (PyCFunction) PyIOStream_write_all, METH_VARARGS, "Write all of the provided data to the stream." }, + { NULL } +}; + static PyMethodDef PyCancellable_methods[] = { { "is_cancelled", (PyCFunction) PyCancellable_is_cancelled, METH_NOARGS, "Query whether cancellable has been cancelled." }, @@ -1034,6 +1066,48 @@ static PyTypeObject PyFileMonitorType = PYFRIDA_DEFINE_TYPE (FileMonitor, NULL, frida_unref); +static PyTypeObject PyIOStreamType = +{ + PyVarObject_HEAD_INIT (NULL, 0) + "_frida.IOStream", /* tp_name */ + sizeof (PyIOStream), /* tp_basicsize */ + 0, /* tp_itemsize */ + NULL, /* tp_dealloc */ + NULL, /* tp_print */ + NULL, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + (reprfunc) PyIOStream_repr, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL, /* tp_hash */ + NULL, /* tp_call */ + NULL, /* tp_str */ + NULL, /* tp_getattro */ + NULL, /* tp_setattro */ + NULL, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Frida IOStream", /* tp_doc */ + NULL, /* tp_traverse */ + NULL, /* tp_clear */ + NULL, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + NULL, /* tp_iter */ + NULL, /* tp_iternext */ + PyIOStream_methods, /* tp_methods */ + NULL, /* tp_members */ + NULL, /* tp_getset */ + &PyGObjectType, /* tp_base */ + NULL, /* tp_dict */ + NULL, /* tp_descr_get */ + NULL, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc) PyIOStream_init, /* tp_init */ +}; + +PYFRIDA_DEFINE_TYPE (IOStream, PyIOStream_init_from_handle, g_object_unref); + static PyTypeObject PyCancellableType = { PyVarObject_HEAD_INIT (NULL, 0) @@ -2488,6 +2562,25 @@ PyDevice_inject_library_blob (PyDevice * self, PyObject * args) return PyLong_FromUnsignedLong (id); } +static PyObject * +PyDevice_open_channel (PyDevice * self, PyObject * args) +{ + const char * address; + GError * error = NULL; + GIOStream * stream; + + if (!PyArg_ParseTuple (args, "s", &address)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + stream = frida_device_open_channel_sync (PY_GOBJECT_HANDLE (self), address, g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + if (error != NULL) + return PyFrida_raise (error); + + return PyIOStream_new_take_handle (stream); +} + static PyObject * PyApplication_new_take_handle (FridaApplication * handle) @@ -3323,6 +3416,212 @@ PyFileMonitor_disable (PyFileMonitor * self) } +static PyObject * +PyIOStream_new_take_handle (GIOStream * handle) +{ + return PyGObject_new_take_handle (handle, &PYFRIDA_TYPE_SPEC (IOStream)); +} + +static int +PyIOStream_init (PyIOStream * self, PyObject * args, PyObject * kw) +{ + if (PyGObjectType.tp_init ((PyObject *) self, args, kw) < 0) + return -1; + + self->input = NULL; + self->output = NULL; + + return 0; +} + +static void +PyIOStream_init_from_handle (PyIOStream * self, GIOStream * handle) +{ + self->input = g_io_stream_get_input_stream (handle); + self->output = g_io_stream_get_output_stream (handle); +} + +static PyObject * +PyIOStream_repr (PyIOStream * self) +{ + GIOStream * handle = PY_GOBJECT_HANDLE (self); + + return PyRepr_FromFormat ("IOStream(handle=%p, is_closed=%s)", + handle, + g_io_stream_is_closed (handle) ? "TRUE" : "FALSE"); +} + +static PyObject * +PyIOStream_is_closed (PyIOStream * self) +{ + return PyBool_FromLong (g_io_stream_is_closed (PY_GOBJECT_HANDLE (self))); +} + +static PyObject * +PyIOStream_close (PyIOStream * self) +{ + GError * error = NULL; + + Py_BEGIN_ALLOW_THREADS + g_io_stream_close (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 * +PyIOStream_read (PyIOStream * self, PyObject * args) +{ + PyObject * result; + unsigned long count; + PyObject * buffer; + GError * error = NULL; + gssize bytes_read; + + if (!PyArg_ParseTuple (args, "k", &count)) + return NULL; + + buffer = PyBytes_FromStringAndSize (NULL, count); + if (buffer == NULL) + return NULL; + + Py_BEGIN_ALLOW_THREADS + bytes_read = g_input_stream_read (self->input, PyBytes_AS_STRING (buffer), count, g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + + if (error == NULL) + { + if (_PyBytes_Resize (&buffer, bytes_read) == 0) + result = buffer; + else + result = NULL; + } + else + { + result = PyFrida_raise (error); + + Py_DECREF (buffer); + } + + return result; +} + +static PyObject * +PyIOStream_read_all (PyIOStream * self, PyObject * args) +{ + PyObject * result; + unsigned long count; + PyObject * buffer; + gsize bytes_read; + GError * error = NULL; + + if (!PyArg_ParseTuple (args, "k", &count)) + return NULL; + + buffer = PyBytes_FromStringAndSize (NULL, count); + if (buffer == NULL) + return NULL; + + Py_BEGIN_ALLOW_THREADS + g_input_stream_read_all (self->input, PyBytes_AS_STRING (buffer), count, &bytes_read, g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + + if (error == NULL) + { + result = buffer; + } + else + { + result = PyFrida_raise (error); + + Py_DECREF (buffer); + } + + return result; +} + +static PyObject * +PyIOStream_write (PyIOStream * self, PyObject * args) +{ + Py_buffer data; + GError * error = NULL; + gssize bytes_written; + +#if PY_MAJOR_VERSION >= 3 + if (!PyArg_ParseTuple (args, "y*", &data)) + return NULL; + + if (!PyBuffer_IsContiguous (&data, 'C')) + { + PyErr_SetString (PyExc_TypeError, "expected a contiguous buffer"); + return NULL; + } +#else + PyObject * data_obj; + + if (!PyArg_ParseTuple (args, "O", &data_obj)) + return NULL; + + if (PyObject_GetBuffer (data_obj, &data, PyBUF_SIMPLE) != 0) + return NULL; +#endif + + Py_BEGIN_ALLOW_THREADS + bytes_written = g_output_stream_write (self->output, data.buf, data.len, g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + +#if PY_MAJOR_VERSION < 3 + PyBuffer_Release (&data); +#endif + + if (error != NULL) + return PyFrida_raise (error); + + return PyLong_FromSsize_t (bytes_written); +} + +static PyObject * +PyIOStream_write_all (PyIOStream * self, PyObject * args) +{ + Py_buffer data; + GError * error = NULL; + +#if PY_MAJOR_VERSION >= 3 + if (!PyArg_ParseTuple (args, "y*", &data)) + return NULL; + + if (!PyBuffer_IsContiguous (&data, 'C')) + { + PyErr_SetString (PyExc_TypeError, "expected a contiguous buffer"); + return NULL; + } +#else + PyObject * data_obj; + + if (!PyArg_ParseTuple (args, "O", &data_obj)) + return NULL; + + if (PyObject_GetBuffer (data_obj, &data, PyBUF_SIMPLE) != 0) + return NULL; +#endif + + Py_BEGIN_ALLOW_THREADS + g_output_stream_write_all (self->output, data.buf, data.len, NULL, g_cancellable_get_current (), &error); + Py_END_ALLOW_THREADS + +#if PY_MAJOR_VERSION < 3 + PyBuffer_Release (&data); +#endif + + if (error != NULL) + return PyFrida_raise (error); + + Py_RETURN_NONE; +} + + static PyObject * PyCancellable_new_take_handle (GCancellable * handle) { @@ -3661,6 +3960,7 @@ MOD_INIT (_frida) PYFRIDA_REGISTER_TYPE (Session, FRIDA_TYPE_SESSION); PYFRIDA_REGISTER_TYPE (Script, FRIDA_TYPE_SCRIPT); PYFRIDA_REGISTER_TYPE (FileMonitor, FRIDA_TYPE_FILE_MONITOR); + PYFRIDA_REGISTER_TYPE (IOStream, G_TYPE_IO_STREAM); PYFRIDA_REGISTER_TYPE (Cancellable, G_TYPE_CANCELLABLE); frida_exception_by_error_code = g_hash_table_new_full (NULL, NULL, NULL, PyFrida_object_decref);