From e65477d9d1723367d5295ee8b779014d9bf59147 Mon Sep 17 00:00:00 2001 From: Andreas Moser Date: Thu, 28 Sep 2017 18:11:48 +0200 Subject: [PATCH 1/3] Allowing the full range of PIDs to be passed to match (including 0). --- yara-python.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yara-python.c b/yara-python.c index bc7d2c7..ee4a858 100644 --- a/yara-python.c +++ b/yara-python.c @@ -1337,7 +1337,7 @@ static PyObject* Rules_match( char* filepath = NULL; char* data = NULL; - int pid = 0; + unsigned int pid = UINT_MAX; int timeout = 0; int length; int error = ERROR_SUCCESS; @@ -1358,7 +1358,7 @@ static PyObject* Rules_match( if (PyArg_ParseTupleAndKeywords( args, keywords, - "|sis#OOOiOO", + "|sIs#OOOiOO", kwlist, &filepath, &pid, @@ -1371,7 +1371,7 @@ static PyObject* Rules_match( &callback_data.modules_data, &callback_data.modules_callback)) { - if (filepath == NULL && data == NULL && pid == 0) + if (filepath == NULL && data == NULL && pid == UINT_MAX) { return PyErr_Format( PyExc_TypeError, @@ -1465,7 +1465,7 @@ static PyObject* Rules_match( Py_END_ALLOW_THREADS } - else if (pid != 0) + else if (pid != UINT_MAX) { callback_data.matches = PyList_New(0); From 93a32502ccada207c5b4a3914b30d601bd74715b Mon Sep 17 00:00:00 2001 From: Andreas Moser Date: Tue, 3 Oct 2017 14:02:06 +0200 Subject: [PATCH 2/3] Exposing the process memory iterator interface to Python. This also updates the yara submodule to 41591cb25f9bd425bd7e58ca6f52a2523b4ed293. --- yara | 2 +- yara-python.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 1 deletion(-) diff --git a/yara b/yara index 9e7390c..41591cb 160000 --- a/yara +++ b/yara @@ -1 +1 @@ -Subproject commit 9e7390c5756f9af91c1df9f007a24317eddd2b80 +Subproject commit 41591cb25f9bd425bd7e58ca6f52a2523b4ed293 diff --git a/yara-python.c b/yara-python.c index ee4a858..e187086 100644 --- a/yara-python.c +++ b/yara-python.c @@ -28,6 +28,7 @@ limitations under the License. #include #include +#include #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) typedef int Py_ssize_t; @@ -2082,6 +2083,187 @@ static PyObject* yara_load( return (PyObject*) rules; } +typedef struct +{ + PyObject_HEAD + PyObject* externals; + YR_MEMORY_BLOCK_ITERATOR* block_iterator; + YR_MEMORY_BLOCK* block; +} ProcessMemoryIterator; + +static PyObject* ProcessMemoryIterator_getattro( + PyObject* self, + PyObject* name) +{ + return PyObject_GenericGetAttr(self, name); +} + +static void ProcessMemoryIterator_dealloc(PyObject* self); + +static PyObject* ProcessMemoryIterator_next(PyObject* self); + +static PyTypeObject ProcessMemoryIterator_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "yara.ProcessMemoryIterator", /*tp_name*/ + sizeof(ProcessMemoryIterator), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor) ProcessMemoryIterator_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + ProcessMemoryIterator_getattro, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "ProcessMemoryIterator", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc) ProcessMemoryIterator_next, /* tp_iternext */ + 0, /* tp_methods */ // TODO???? + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static ProcessMemoryIterator* ProcessMemoryIterator_NEW(void) +{ + ProcessMemoryIterator* it = PyObject_NEW(ProcessMemoryIterator, &ProcessMemoryIterator_Type); + if (it == NULL) + return NULL; + + it->block_iterator = NULL; + it->block = NULL; + + return it; +} + +static void ProcessMemoryIterator_dealloc( + PyObject* self) +{ + ProcessMemoryIterator* it = (ProcessMemoryIterator*) self; + + if (it->block_iterator != NULL) + { + yr_process_close_iterator(it->block_iterator); + PyMem_Free(it->block_iterator); + it->block_iterator = NULL; + } + PyObject_Del(self); +} + +static PyObject* ProcessMemoryIterator_next( + PyObject* self) +{ + ProcessMemoryIterator* it = (ProcessMemoryIterator*) self; + int err; + + // This indicates that the iterator has been used. + if (it->block_iterator == NULL) + { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + // During the first invocation, we need to use get_first_memory_block. + if (it->block == NULL) + it->block = yr_process_get_first_memory_block(it->block_iterator); + else + it->block = yr_process_get_next_memory_block(it->block_iterator); + + if (it->block == NULL) + { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + uint8_t* data_ptr = yr_process_fetch_memory_block_data(it->block); + if (data_ptr == NULL) + { + // This is how we are notified that the process is done. + it->block = NULL; + err = yr_process_close_iterator(it->block_iterator); + PyMem_Free(it->block_iterator); + it->block_iterator = NULL; + if (err != 0) + { + return handle_error(err, NULL); + } + + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + return PyBytes_FromStringAndSize( + (const char*) data_ptr, + it->block->size); +} + +static PyObject* yara_process_memory_iterator( + PyObject* self, + PyObject* args, + PyObject* keywords) +{ + static char *kwlist[] = { + "pid", NULL}; + + unsigned int pid = UINT_MAX; + int err; + + ProcessMemoryIterator *result; + + if (!PyArg_ParseTupleAndKeywords( + args, + keywords, + "|I", + kwlist, + &pid)) + { + return PyErr_Format( + PyExc_TypeError, + "Error parsing arguments."); + } + + result = ProcessMemoryIterator_NEW(); + + result->block_iterator = PyMem_Malloc(sizeof(YR_MEMORY_BLOCK_ITERATOR)); + if (result->block_iterator == NULL) + return PyErr_NoMemory(); + + // Fail early if we can't access the process with the given pid. + err = yr_process_open_iterator(pid, result->block_iterator); + if (err != 0) + { + PyMem_Free(result->block_iterator); + return handle_error(err, NULL); + } + + result->block = yr_process_get_first_memory_block(result->block_iterator); + if (result->block == NULL) + { + PyMem_Free(result->block_iterator); + result->block_iterator = NULL; + return PyErr_NoMemory(); + } + return (PyObject *) result; +} void finalize(void) { @@ -2102,6 +2284,13 @@ static PyMethodDef yara_methods[] = { METH_VARARGS | METH_KEYWORDS, "Loads a previously saved YARA rules file and returns an instance of class Rules" }, + { + "process_memory_iterator", + (PyCFunction) yara_process_memory_iterator, + METH_VARARGS | METH_KEYWORDS, + "Returns an iterator over blocks of memory of a process.\n" + "Signature: process_memory_iterator(pid=None)" + }, { NULL, NULL } }; From 162f2fbcfeb26d34d899d52a917b151f588e59d5 Mon Sep 17 00:00:00 2001 From: Andreas Moser Date: Tue, 3 Oct 2017 14:33:10 +0200 Subject: [PATCH 3/3] Improving a comment. --- yara-python.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yara-python.c b/yara-python.c index e187086..6d1fcf6 100644 --- a/yara-python.c +++ b/yara-python.c @@ -2175,7 +2175,7 @@ static PyObject* ProcessMemoryIterator_next( ProcessMemoryIterator* it = (ProcessMemoryIterator*) self; int err; - // This indicates that the iterator has been used. + // This indicates that the iterator has been used up. if (it->block_iterator == NULL) { PyErr_SetNone(PyExc_StopIteration);