mirror of
https://github.com/radareorg/radare2-rlang.git
synced 2026-11-08 09:09:34 -10:00
434 lines
10 KiB
C
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
|