関数などの実行結果をすりかえる
こんにちは、Python界のヘンリー塚本ことmopemopeです。
なんかpythonのpycファイルをいじってアタックするとかセキュリティの話が出てきてますね。
せっかくなので別の方法でこーいうこともできますよというのを紹介しておきます。
もちろん普通の人は書かないであろう、あらびきな方法です。
今回は関数などの実行結果をすりかえるという話です。
ではコード。
steal.h
#ifndef STEAL_H #define STEAL_H #include <Python.h> #ifdef DEVELOP #define DEBUG(...) \ do { \ /*printf("%-22s%4u: ", __FILE__, __LINE__);*/ \ printf("%-22s %-32s%4u: ", __FILE__, __func__, __LINE__); \ printf(__VA_ARGS__); \ printf("\n"); \ } while(0) #define RDEBUG(...) \ do { \ /*printf("%-22s%4u: ", __FILE__, __LINE__);*/ \ printf("\x1B[31m%-22s %-32s%4u: ", __FILE__, __func__, __LINE__); \ printf(__VA_ARGS__); \ printf("\x1B[0m\n"); \ } while(0) #else #define DEBUG(...) do{}while(0) #define RDEBUG(...) do{}while(0) #endif #define MODULE_NAME "evalsteal" #if __GNUC__ >= 3 # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0) #else # define likely(x) (x) # define unlikely(x) (x) #endif #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 1) || PY_MAJOR_VERSION > 3 #define PY3 #endif #if PY_MAJOR_VERSION < 3 #ifndef Py_REFCNT # define Py_REFCNT(ob) (((PyObject *) (ob))->ob_refcnt) #endif #ifndef Py_TYPE # define Py_TYPE(ob) (((PyObject *) (ob))->ob_type) #endif #endif #endif
これは僕がテンプレで使ってるヘッダなのであんまし意味ないです。
steal.c
#include "steal.h" #include "structmember.h" #include <frameobject.h> #include <opcode.h> static PyObject* retObj = NULL; static int run(PyFrameObject *f, PyObject *result, int entering) { PyObject** p; int r = 1; PyCodeObject* co = f->f_code; int new_i = PyString_GET_SIZE(co->co_code) - 1; while (PyString_AS_STRING(co->co_code)[new_i] != RETURN_VALUE){ --new_i; } new_i -= entering; f->f_lasti = new_i; f->f_iblock = 0; for (p=f->f_stacktop; --p >= f->f_valuestack; ) { Py_XDECREF(*p); *p = NULL; } p = f->f_valuestack; *p++ = result; f->f_stacktop = p; return r; } static int do_trace_or_profile(PyObject *v, PyFrameObject *f, int what, PyObject *arg) { int r = 0; if(what == PyTrace_CALL){ f->f_tstate->use_tracing = 1; f->f_tstate->tracing--; r = run(f, retObj, what == PyTrace_CALL); f->f_tstate->tracing++; } return 0; } static void set_profile(PyThreadState* tstate, Py_tracefunc func, PyObject* arg) { PyObject *temp = tstate->c_profileobj; Py_XINCREF(arg); tstate->c_profilefunc = NULL; tstate->c_profileobj = NULL; tstate->use_tracing = tstate->c_tracefunc != NULL; Py_XDECREF(temp); tstate->c_profilefunc = func; tstate->c_profileobj = arg; tstate->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL); } static PyObject* evalsteal_steal(PyObject *self, PyObject *arg) { retObj = arg; PyThreadState* tstate = PyThreadState_Get(); if (tstate->c_profilefunc == NULL) { set_profile(tstate, &do_trace_or_profile, NULL); } Py_RETURN_NONE; } static PyMethodDef EvalStealModMethods[] = { {"steal", evalsteal_steal, METH_O, 0}, {NULL, NULL, 0, NULL} /* Sentinel */ }; #ifdef PY3 #define INITERROR return NULL static struct PyModuleDef eavlsteal_module_def = { PyModuleDef_HEAD_INIT, MODULE_NAME, NULL, -1, EvalStealModMethods, }; PyObject * PyInit_evalsteal(void) #else #define INITERROR return PyMODINIT_FUNC initevalsteal(void) #endif { PyObject *m; #ifdef PY3 m = PyModule_Create(&evalsteal_module_def); #else m = Py_InitModule3(MODULE_NAME, EvalStealModMethods, ""); #endif if(m == NULL){ INITERROR; } #ifdef PY3 return m; #endif }
setup.pyは割愛します。
まあPyEval_SetProfileとかでもいいですけど、カレントスレッドだけで。
ceval.cで評価するフレーム内のスタックをバンバン捨てて任意の値を積みます。
「ceval.c涙目wwww」みたいなコードですね。
import evalsteal # return -1 evalsteal.steal(-1) def func(a, b): sum_val = a for i in xrange(b): sum_val += i return sum_val result = func(0, 10) print("result %d" % result)
本来ならば45が返りますが、実行結果は
result -1
となります。
悪用するとまあいろんな事ができますが、有用な使い方としてはceval.cを再実装することができます。
あとはわかるな?