Files

434 lines
10 KiB
C

/* radare2 - LGPL - Copyright 2009-2025 - pancake */
/* python extension for radare2's r_lang */
#include <r_core.h>
#include "python/common.h"
#include "python/core.h"
#include "python/io.h"
#if R2_VERSION_NUMBER < 50809
#include "python/anal.h"
#include "python/asm.h"
#include "python/bin.h"
#else
#include "python/arch.h"
#endif
#define PLUGIN_NAME r_lang_plugin_python
typedef struct {
const char *type;
PyObject* (*handler)(Radare*, PyObject*);
} R2Plugins;
static const char *const py_nullstr = "";
static const R2Plugins plugins[] = {
{ "core", &Radare_plugin_core },
#if R2_VERSION_NUMBER < 50800
{ "asm", &Radare_plugin_asm },
#endif
#if R2_VERSION_NUMBER < 50809
{ "anal", &Radare_plugin_anal },
{ "bin", &Radare_plugin_bin },
#else
{ "arch", &Radare_plugin_arch },
#endif
{ "io", &Radare_plugin_io },
{ NULL }
};
static bool run(RLangSession *s, const char *code, int len) {
RLang *lang = s->lang;
Gcore = (RCore *)lang->user;
PyRun_SimpleString (code);
return true;
}
static int slurp_python(const char *file) {
FILE *fd = r_sandbox_fopen (file, "r");
if (fd) {
PyRun_SimpleFile (fd, file);
fclose (fd);
return true;
}
return false;
}
static bool run_file(RLangSession *session, const char *file) {
return slurp_python (file);
}
static void Radare_dealloc(Radare* self) {
if (self) {
if (self->first) {
Py_XDECREF (self->first);
}
if (self->last) {
Py_XDECREF (self->last);
}
}
Py_TYPE (self)->tp_free ((PyObject *)self);
}
static PyObject * Radare_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
Radare *self = (Radare *)type->tp_alloc (type, 0);
if (self) {
self->first = PyUnicode_FromString ("");
if (!self->first) {
Py_DECREF (self);
return NULL;
}
self->last = PyUnicode_FromString ("");
if (!self->last) {
Py_DECREF (self);
return NULL;
}
self->number = 0;
}
return (PyObject *)self;
}
static PyObject *Radare_plugin(Radare* self, PyObject *args) {
char *type = NULL;
void *cb = NULL;
int i;
if (!PyArg_ParseTuple (args, "sO", &type, &cb)) {
return NULL;
}
if (!PyCallable_Check (cb)) {
PyErr_SetString (PyExc_TypeError, "second parameter must be callable");
return NULL;
}
for (i = 0; plugins[i].type; i++) {
if (!strcmp (type, plugins[i].type)) {
return plugins[i].handler (self, cb);
}
}
R_LOG_INFO ("r2lang.plugin does not support '%s' plugins yet", type);
Py_RETURN_FALSE;
}
static PyObject *Radare_print(Radare* self, PyObject *args) {
char *cmd = NULL;
if (!PyArg_ParseTuple (args, "s", &cmd)) {
return NULL;
}
r_cons_printf (NULL, "%s\n", cmd);
Py_RETURN_TRUE;
}
static PyObject *Radare_cmd(Radare* self, PyObject *args) {
char *cmd = NULL;
if (!PyArg_ParseTuple (args, "s", &cmd)) {
return NULL;
}
char *str = r_core_cmd_str (Gcore, cmd);
PyObject *res = PyUnicode_FromString (str? str: py_nullstr);
free (str);
return res;
}
static int Radare_init(Radare *self, PyObject *args, PyObject *kwds) {
static char *kwlist[] = { "first", "last", "number", NULL };
PyObject *first = NULL, *last = NULL, *tmp;
if (!PyArg_ParseTupleAndKeywords (args, kwds, "|OOi",
(char **)kwlist, &first, &last, &self->number)) {
return -1;
}
if (first) {
tmp = self->first;
Py_INCREF (first);
self->first = first;
Py_XDECREF (tmp);
}
if (last) {
tmp = self->last;
Py_INCREF (last);
self->last = last;
Py_XDECREF (tmp);
}
return 0;
}
static PyMemberDef Radare_members[] = {
{"first", T_OBJECT_EX, offsetof(Radare, first), 0, "first name"},
{"last", T_OBJECT_EX, offsetof(Radare, last), 0, "last name"},
{"number", T_INT, offsetof(Radare, number), 0, "noddy number"},
{NULL} /* Sentinel */
};
static PyMethodDef Radare_methods[] = {
{"print", (PyCFunction)Radare_print, METH_VARARGS,
"Print text using RCons api, so the output can be captured by radare2" },
{"cmd", (PyCFunction)Radare_cmd, METH_VARARGS,
"Executes a radare command and returns a string" },
{"plugin", (PyCFunction)Radare_plugin, METH_VARARGS,
"Register plugins in radare2" },
{NULL} /* Sentinel */
};
static PyTypeObject RadareType = {
PyVarObject_HEAD_INIT (NULL, 0)
"radare.RadareInternal", /*tp_name*/
sizeof (Radare), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)Radare_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*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"Radare objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Radare_methods, /* tp_methods */
Radare_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Radare_init, /* tp_init */
0, /* tp_alloc */
Radare_new, /* tp_new */
};
/*
SEE
static PyMethodDef EmbMethods[] = {
{"numargs", emb_numargs, METH_VARARGS,
"Return the number of arguments received by the process."},
{NULL, NULL, 0, NULL}
};
*/
static PyModuleDef EmbModule = {
PyModuleDef_HEAD_INIT,
"r2lang",
NULL, -1, Radare_methods,
NULL, NULL, NULL, NULL
};
static PyObject *init_radare_module(void) {
// TODO import r2-swig api
//eprintf ("TODO: python>3.x instantiate 'r' object\n");
/* Gcore = lang->user; */
/* eprintf("\x1b[31;1minit_radare_module\x1b[0m\n"); */
if (PyType_Ready (&RadareType) < 0) {
return NULL;
}
RadareType.tp_dict = PyDict_New ();
#if R2_VERSION_NUMBER < 50809
py_export_anal_enum (RadareType.tp_dict);
#endif
#if R2_VERSION_NUMBER < 50800
py_export_asm_enum (RadareType.tp_dict);
#endif
#if R2_VERSION_NUMBER > 50808
py_export_arch_enum (RadareType.tp_dict);
#endif
PyObject *m = PyModule_Create (&EmbModule);
if (!m) {
R_LOG_ERROR ("Cannot create python3 r2 module");
return NULL;
}
Py_INCREF (&RadareType);
if (PyModule_AddObject (m, "R", (PyObject *)&RadareType) < 0) {
Py_DECREF (&RadareType);
Py_DECREF (m);
return NULL;
}
return m;
}
/* -init- */
static bool setup(RLangSession *user);
static bool prompt(RLangSession *s) {
return !PyRun_SimpleString (
"r2 = None\n"
"have_ipy = False\n"
"try:\n"
" import IPython\n"
" have_ipy = True\n"
"except:\n"
" pass\n"
"import r2lang\n"
"try:\n"
" import r2pipe\n"
" r2 = r2pipe.open()\n"
"except:\n"
" pass\n"
"if have_ipy:\n"
" IPython.embed()\n"
"else:\n"
" import code\n"
" code.InteractiveConsole(locals=globals()).interact()\n"
);
}
static bool setup(RLangSession *s) {
RLang *lang = s->lang;
RListIter *iter;
RLangDef *def;
// Segfault if already initialized ?
PyRun_SimpleString (
"try:\n"
" from r2.r_core import RCore\n"
"except:\n"
" pass\n");
PyRun_SimpleString (
"try:\n"
" import r2pipe\n"
"except:\n"
" pass\n");
Gcore = lang->user;
r_list_foreach (lang->defs, iter, def) {
if (!def->type || !def->name) {
continue;
}
char *cmd = NULL;
if (!strcmp (def->type, "int")) {
cmd = r_str_newf ("%s=%d", def->name, (int)(size_t)def->value);
} else if (!strcmp (def->type, "string")) {
cmd = r_str_newf ("%s=\"%s\"", def->name, (char *)def->value);
} else {
cmd = r_str_newf ("try:\n"
" %s=%s.ncast(%p)\n"
"except:\n"
" pass", def->name, def->type, def->value);
}
PyRun_SimpleString (cmd);
free (cmd);
}
return true;
}
#if R2_VERSION_NUMBER > 50808
static bool init(RLangSession *session) {
if (session == NULL) {
return true;
}
RLang *lang = session->lang;
#elif R2_VERSION_NUMBER < 50800
static bool init(RLang *lang) {
#else
static void *init(RLangSession *session) {
RLang *lang = session->lang;
#endif
if (lang) {
Gcore = lang->user;
}
// DO NOT INITIALIZE MODULE IF ALREADY INITIALIZED
if (Py_IsInitialized ()) {
#if R2_VERSION_NUMBER > 50808
return true;
#elif R2_VERSION_NUMBER < 50800
return lang != NULL;
#else
return NULL;
#endif
}
PyImport_AppendInittab ("r2lang", init_radare_module);
#if R2_VERSION_NUMBER < 50809
PyImport_AppendInittab ("binfile", init_pybinfile_module);
#endif
Py_Initialize ();
// Add a current directory to the PYTHONPATH
PyObject *sys = PyImport_ImportModule ("sys");
if (!sys) {
#if R2_VERSION_NUMBER > 50808
return false;
#elif R2_VERSION_NUMBER < 50800
return NULL;
#else
return NULL;
#endif
}
PyObject *path = PyObject_GetAttrString (sys, "path");
if (path) {
PyObject *cwd = PyUnicode_FromString (".");
if (cwd) {
PyList_Append (path, cwd);
Py_DECREF (cwd);
}
}
#if R2_VERSION_NUMBER < 50809
Py_XDECREF (path);
#endif
#if R2_VERSION_NUMBER < 50809
return sys;
#else
Py_XDECREF (path);
session->plugin_data = sys;
return true;
#endif
}
static bool fini(RLangSession *user) {
#if 1
// better leak than segfault here. future check
return true;
#else
#if (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION >= 6)
return Py_FinalizeEx () ? false : true;
#else
Py_Finalize ();
return true;
#endif
#endif
}
static const char *help =
//" r = new RadareInternal()\n"
" print r2.cmd(\"p8 10\");\n";
RLangPlugin PLUGIN_NAME = {
#if R2_VERSION_NUMBER > 50808
.meta = {
.name = "python",
.desc = "Python language extension",
.license = "LGPL-3.0-only",
},
#else
.name = "python",
.desc = "Python language extension",
.license = "LGPL-3.0-only",
#endif
.alias = "python",
.ext = "py",
.init = &init,
.setup = &setup,
.fini = (void *)&fini,
.help = &help,
.prompt = (void *)&prompt,
.run = &run,
.run_file = &run_file,
};
#if !CORELIB
RLibStruct radare_plugin = {
.type = R_LIB_TYPE_LANG,
.data = &PLUGIN_NAME,
.version = R2_VERSION
};
#endif