0

I'm trying to extact a global variable from an executable. Basically, I'm tryin to reverse an executable that put some python bytecode in a global variable and I'd like to extract it. I found out that the data is in the .data of the PE File, but I can't find a way to get it in all the data segments. Any ideas?

1
  • @MaxenceHenneron: if it's a variable that can be found in the .data section that sounds to me like it's an initialized variable. What more than the part in the .data section do you need then?
    – 0xC0000022L
    Jun 30, 2014 at 9:16

1 Answer 1

3

Since you say you are trying to reverse an executable which stores some python bytecode in a variable, it means probably the executable embeds python. If the code is likely to be executed at some point of time, you can use a debugger.

Set a breakpoint on PyEval_EvalFrameEx .

It has a prototype of PyObject* PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)

The first parameter PyFrameObject has the following structure.

typedef struct _frame {
    PyObject_VAR_HEAD
    struct _frame *f_back;  /* previous frame, or NULL */
    PyCodeObject *f_code;   /* code segment */
    PyObject *f_builtins;   /* builtin symbol table (PyDictObject) */
    PyObject *f_globals;    /* global symbol table (PyDictObject) */
    PyObject *f_locals;     /* local symbol table (any mapping) */
    PyObject **f_valuestack;    /* points after the last local */
    PyObject **f_stacktop;
    PyObject *f_trace;      /* Trace function */
    PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
    PyThreadState *f_tstate;
    int f_lasti;        /* Last instruction if called */
    int f_lineno;       /* Current line number */
    int f_iblock;       /* index in f_blockstack */
    PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
    PyObject *f_localsplus[1];  /* locals+stack, dynamically sized */
} PyFrameObject;

The third member of PyFrameObject is PyCodeObject.

PyCodeObject has the following structure.

typedef struct {
    PyObject_HEAD
    int co_argcount;        /* #arguments, except *args */
    int co_nlocals;     /* #local variables */
    int co_stacksize;       /* #entries needed for evaluation stack */
    int co_flags;       /* CO_..., see below */
    PyObject *co_code;      /* instruction opcodes */
    PyObject *co_consts;    /* list (constants used) */
    PyObject *co_names;     /* list of strings (names used) */
    PyObject *co_varnames;  /* tuple of strings (local variable names) */
    PyObject *co_freevars;  /* tuple of strings (free variable names) */
    PyObject *co_cellvars;      /* tuple of strings (cell variable names) */
    /* The rest doesn't count for hash/cmp */
    PyObject *co_filename;  /* string (where it was loaded from) */
    PyObject *co_name;      /* string (name, for reference) */
    int co_firstlineno;     /* first source line number */
    PyObject *co_lnotab;    /* string (encoding addr<->lineno mapping) */
    void *co_zombieframe;     /* for optimization only (see frameobject.c) */
} PyCodeObject;

The sixth member of the above structure is co_code . It is basically a PyStringObject. It has the following structure.

typedef struct {
    PyObject_VAR_HEAD
    long ob_shash;
    int ob_sstate;
    char ob_sval[1];
} PyStringObject;

The ob_sval contains the bytecode you are after. So once you hit PyEval_EvalFrameEx follow the structures in memory to get the bytecode.

Another thing to note is you need to know the layout of PyObject_VAR_HEAD and PyObject_HEAD to get the actual offsets. Refer to the python source for more information.

Not the answer you're looking for? Browse other questions tagged or ask your own question.