Skip to content

Commit 3e7f947

Browse files
committed
Exposing the process memory iterator interface to Python.
1 parent 2538742 commit 3e7f947

File tree

1 file changed

+159
-1
lines changed

1 file changed

+159
-1
lines changed

yara-python.c

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ limitations under the License.
2828

2929
#include <time.h>
3030
#include <yara.h>
31+
#include <yara/proc.h>
3132

3233
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
3334
typedef int Py_ssize_t;
@@ -2082,6 +2083,138 @@ static PyObject* yara_load(
20822083
return (PyObject*) rules;
20832084
}
20842085

2086+
typedef struct
2087+
{
2088+
PyObject_HEAD
2089+
PyObject* externals;
2090+
YR_MEMORY_BLOCK_ITERATOR* block_iterator;
2091+
YR_MEMORY_BLOCK* block;
2092+
} ProcessMemoryIterator;
2093+
2094+
static PyObject* ProcessMemoryIterator_getattro(
2095+
PyObject* self,
2096+
PyObject* name)
2097+
{
2098+
return PyObject_GenericGetAttr(self, name);
2099+
}
2100+
2101+
static void ProcessMemoryIterator_dealloc(PyObject* self);
2102+
2103+
static PyObject* ProcessMemoryIterator_next(PyObject* self);
2104+
2105+
static PyTypeObject ProcessMemoryIterator_Type = {
2106+
PyVarObject_HEAD_INIT(NULL, 0)
2107+
"yara.ProcessMemoryIterator", /*tp_name*/
2108+
sizeof(ProcessMemoryIterator), /*tp_basicsize*/
2109+
0, /*tp_itemsize*/
2110+
(destructor) ProcessMemoryIterator_dealloc, /*tp_dealloc*/
2111+
0, /*tp_print*/
2112+
0, /*tp_getattr*/
2113+
0, /*tp_setattr*/
2114+
0, /*tp_compare*/
2115+
0, /*tp_repr*/
2116+
0, /*tp_as_number*/
2117+
0, /*tp_as_sequence*/
2118+
0, /*tp_as_mapping*/
2119+
0, /*tp_hash */
2120+
0, /*tp_call*/
2121+
0, /*tp_str*/
2122+
ProcessMemoryIterator_getattro, /*tp_getattro*/
2123+
0, /*tp_setattro*/
2124+
0, /*tp_as_buffer*/
2125+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
2126+
"ProcessMemoryIterator", /* tp_doc */
2127+
0, /* tp_traverse */
2128+
0, /* tp_clear */
2129+
0, /* tp_richcompare */
2130+
0, /* tp_weaklistoffset */
2131+
PyObject_SelfIter, /* tp_iter */
2132+
(iternextfunc) ProcessMemoryIterator_next, /* tp_iternext */
2133+
0, /* tp_methods */ // TODO????
2134+
0, /* tp_members */
2135+
0, /* tp_getset */
2136+
0, /* tp_base */
2137+
0, /* tp_dict */
2138+
0, /* tp_descr_get */
2139+
0, /* tp_descr_set */
2140+
0, /* tp_dictoffset */
2141+
0, /* tp_init */
2142+
0, /* tp_alloc */
2143+
0, /* tp_new */
2144+
};
2145+
2146+
static ProcessMemoryIterator* ProcessMemoryIterator_NEW(void)
2147+
{
2148+
ProcessMemoryIterator* it = PyObject_NEW(ProcessMemoryIterator, &ProcessMemoryIterator_Type);
2149+
if (it == NULL)
2150+
return NULL;
2151+
2152+
it->block_iterator = NULL;
2153+
it->block = NULL;
2154+
2155+
return it;
2156+
}
2157+
2158+
static void ProcessMemoryIterator_dealloc(
2159+
PyObject* self)
2160+
{
2161+
ProcessMemoryIterator* it = (ProcessMemoryIterator*) self;
2162+
2163+
if (it->block_iterator != NULL)
2164+
{
2165+
yr_process_close_iterator(it->block_iterator);
2166+
PyMem_Free(it->block_iterator);
2167+
it->block_iterator = NULL;
2168+
}
2169+
PyObject_Del(self);
2170+
}
2171+
2172+
static PyObject* ProcessMemoryIterator_next(
2173+
PyObject* self)
2174+
{
2175+
ProcessMemoryIterator* it = (ProcessMemoryIterator*) self;
2176+
int err;
2177+
2178+
// This indicates that the iterator has been used.
2179+
if (it->block_iterator == NULL)
2180+
{
2181+
PyErr_SetNone(PyExc_StopIteration);
2182+
return NULL;
2183+
}
2184+
2185+
// During the first invocation, we need to use get_first_memory_block.
2186+
if (it->block == NULL)
2187+
it->block = yr_process_get_first_memory_block(it->block_iterator);
2188+
else
2189+
it->block = yr_process_get_next_memory_block(it->block_iterator);
2190+
2191+
if (it->block == NULL)
2192+
{
2193+
PyErr_SetNone(PyExc_StopIteration);
2194+
return NULL;
2195+
}
2196+
2197+
uint8_t* data_ptr = yr_process_fetch_memory_block_data(it->block);
2198+
if (data_ptr == NULL)
2199+
{
2200+
// This is how we are notified that the process is done.
2201+
it->block = NULL;
2202+
err = yr_process_close_iterator(it->block_iterator);
2203+
PyMem_Free(it->block_iterator);
2204+
it->block_iterator = NULL;
2205+
if (err != 0)
2206+
{
2207+
return handle_error(err, NULL);
2208+
}
2209+
2210+
PyErr_SetNone(PyExc_StopIteration);
2211+
return NULL;
2212+
}
2213+
2214+
return PyBytes_FromStringAndSize(
2215+
(const char*) data_ptr,
2216+
it->block->size);
2217+
}
20852218

20862219
static PyObject* yara_process_memory_iterator(
20872220
PyObject* self,
@@ -2092,6 +2225,9 @@ static PyObject* yara_process_memory_iterator(
20922225
"pid", NULL};
20932226

20942227
unsigned int pid = UINT_MAX;
2228+
int err;
2229+
2230+
ProcessMemoryIterator *result;
20952231

20962232
if (!PyArg_ParseTupleAndKeywords(
20972233
args,
@@ -2104,7 +2240,29 @@ static PyObject* yara_process_memory_iterator(
21042240
PyExc_TypeError,
21052241
"Error parsing arguments.");
21062242
}
2107-
return Py_BuildValue("I", pid);
2243+
2244+
result = ProcessMemoryIterator_NEW();
2245+
2246+
result->block_iterator = PyMem_Malloc(sizeof(YR_MEMORY_BLOCK_ITERATOR));
2247+
if (result->block_iterator == NULL)
2248+
return PyErr_NoMemory();
2249+
2250+
// Fail early if we can't access the process with the given pid.
2251+
err = yr_process_open_iterator(pid, result->block_iterator);
2252+
if (err != 0)
2253+
{
2254+
PyMem_Free(result->block_iterator);
2255+
return handle_error(err, NULL);
2256+
}
2257+
2258+
result->block = yr_process_get_first_memory_block(result->block_iterator);
2259+
if (result->block == NULL)
2260+
{
2261+
PyMem_Free(result->block_iterator);
2262+
result->block_iterator = NULL;
2263+
return PyErr_NoMemory();
2264+
}
2265+
return (PyObject *) result;
21082266
}
21092267

21102268
void finalize(void)

0 commit comments

Comments
 (0)