struct _frame { PyObject_VAR_HEADstruct _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 */ /* Next free slot in f_valuestack. Frame creation sets to f_valuestack. Frame evaluation usually NULLs it, but a frame that yields sets it to the current stack top. */ PyObject **f_stacktop; PyObject *f_trace; /* Trace function */char f_trace_lines; /* Emit per-line trace events? */char f_trace_opcodes; /* Emit per-opcode trace events? */ /* Borrowed reference to a generator, or NULL */ PyObject *f_gen;int f_lasti; /* Last instruction if called */ /* Call PyFrame_GetLineNumber() instead of reading this field directly. As of 2.3 f_lineno is only valid when tracing is active (i.e. when f_trace is set). At other times we use PyCode_Addr2Line to calculate the line from the current bytecode index. */int f_lineno; /* Current line number */int f_iblock; /* index in f_blockstack */char f_executing; /* whether the frame is still executing */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */};
def a():if1==2:print("flag{233}")print("Opcode of a():",a.__code__.co_code.hex())print("CONST of a():",a.__code__.co_consts)#打印所有参数即对应值print("ALL of a():")for name in dir(a.__code__):print(name,getattr(a.__code__,name))#构造newcodedef b():if1!=2:print("flag{233}")print("Opcode of b():",b.__code__.co_code.hex())code=b.__code__.co_codenewcode =type(a.__code__)code =newcode(0,0,0,0,2,67, code,(None,1,2,'flag{0w0}'),('print',),(),"","a",1, b'\x00\x01\x08\x01')a.__code__ = codea()
//https://github.com/python/cpython/blob/master/Include/cpython/code.h/* Bytecode object */struct PyCodeObject { PyObject_HEADint co_argcount; /* #arguments, except *args */int co_posonlyargcount; /* #positional only arguments */int co_kwonlyargcount; /* #keyword only arguments */int co_nlocals; /* #local variables */int co_stacksize; /* #entries needed for evaluation stack */int co_flags; /* CO_..., see below */int co_firstlineno; /* first source line number */ 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 aren't used in either hash or comparisons, except for co_name, used in both. This is done to preserve the name and line number for tracebacks and debuggers; otherwise, constant de-duplication would collapse identical functions/lambdas defined on different lines. */Py_ssize_t*co_cell2arg; /* Maps cell vars which are arguments. */ PyObject *co_filename; /* unicode (where it was loaded from) */ PyObject *co_name; /* unicode (name, for reference) */ PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See Objects/lnotab_notes.txt for details. */void*co_zombieframe; /* for optimization only (see frameobject.c) */ PyObject *co_weakreflist; /* to support weakrefs to code objects */ /* Scratch space for extra data relating to the code object. Type is a void* to keep the format private in codeobject.c to force people to go through the proper APIs. */void*co_extra; /* Per opcodes just-in-time cache * * To reduce cache size, we use indirect mapping from opcode index to * cache object: * cache = co_opcache[co_opcache_map[next_instr - first_instr] - 1] */// co_opcache_map is indexed by (next_instr - first_instr).// * 0 means there is no cache for this opcode.// * n > 0 means there is cache in co_opcache[n-1].unsignedchar*co_opcache_map; _PyOpcache *co_opcache;int co_opcache_flag; // used to determine when create a cache.unsignedchar co_opcache_size; // length of co_opcache.};
switch (opcode){/* BEWARE! It is essential that any operation that fails must goto errorand that all operation that succeed call [FAST_]DISPATCH() ! */ case TARGET(NOP):{FAST_DISPATCH();} case TARGET(LOAD_FAST):{ PyObject *value = GETLOCAL(oparg);if (value == NULL) {format_exc_check_arg(tstate, PyExc_UnboundLocalError, UNBOUNDLOCAL_ERROR_MSG,PyTuple_GetItem(co->co_varnames, oparg)); goto error;}Py_INCREF(value);PUSH(value);FAST_DISPATCH();} ...}