Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

関数などの実行結果をすりかえる

こんにちは、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を再実装することができます。
あとはわかるな?