Python pyc文件格式解析(下)

admin 2023年11月20日14:29:03评论16 views字数 13711阅读45分42秒阅读模式

Python pyc文件格式解析(下)


Python pyc文件格式解析(下)


在上篇文章中分析到PyMarshal_ReadLastObjectFromFile内部的一个执行逻辑,主要是通过一个r_object递归解析pyc中的各个对象结构,随后构造成一个PyObject对象并将对象返回。随后一直返回到run_pyc_file中方法。

(https://github.com/python/cpython/blob/08b640e1575b75aeb605e72cb30c337480055b75/Python/pythonrun.c#L1271)


在这篇文章中将分析PyMarshal_ReadLastObjectFromFile之后的代码逻辑,分析python是如何执行PyObject的。

pyc运行环境搭建(下)

1、我们先写一段样例代码把它转成pyc来通过010editor观察:

Test.py

def check_command(cmd):    for char in cmd:        if char in ['"', ''', 'n']:            return False    return True
import oscmd = input('ping> ')if check_command(cmd):    os.system("ping "" + cmd + "" -n 1")else:    print('No No No')

2、随后通过Python3.9将Test.py编译成pyc。          

     python -m py_compile Test.py

3、随后在__pycache__中找到Test.cpython-39.pyc,将其导入010editor。

来到python源码:

在PyMarshal_ReadLastObjectFromFile紧跟其后的是PyCode_Check的一个检查,随后直接传入run_eval_code_obj方法中。

Python pyc文件格式解析(下)

4、通过定义得知作用是判断参数是不是PyCode类型,也就是说pyc的根对象必须是TYPE_CODE类型。

Python pyc文件格式解析(下)

而我们的pyc中根对象确实是TYPE_CODE对象。

Python pyc文件格式解析(下)

5、一同传入run_eval_code_obj的还有globals和locals,通过向上追溯在pyrun_simple_file函数中。

(https://github.com/python/cpython/blob/08b640e1575b75aeb605e72cb30c337480055b75/Python/pythonrun.c#L441)

发现它们都来自于同一个对象d,并在这之前有一些执行环境的初始化,包括:__file__、__cached__和set_main_loader。这些变量可以通过globals方法查看:

Python pyc文件格式解析(下)

并且此时它们是同一个对象:

Python pyc文件格式解析(下)

6、继续跟进run_eval_code_obj,开头先是设置了globals[‘__builtins__’],之后直接调用PyEval_EvalCode。

Python pyc文件格式解析(下)

PyEval_EvalCode中初始化PyFrameConstructor和参数后调用_PyEval_Vector

Python pyc文件格式解析(下)

在_PyEvalFramePushAndInit中将这些参数打包进frame,并传入_PyEval_EvalFrame

Python pyc文件格式解析(下)

在_PyEval_EvalFrame中通过ThreadState中寻找frame执行函数,如果没有则执行默认的执行函数。我们走默认的frame执行函数。

Python pyc文件格式解析(下)

https://github.com/python/cpython/blob/08b640e1575b75aeb605e72cb30c337480055b75/Python/ceval.c#L920

7、在方法开头有很多宏定义,对应着一些方法中将用到的功能:

Python pyc文件格式解析(下)
Python pyc文件格式解析(下)
Python pyc文件格式解析(下)
Python pyc文件格式解析(下)
Python pyc文件格式解析(下)
Python pyc文件格式解析(下)
Python pyc文件格式解析(下)

8、从此开始是方法代码的开头

https://github.com/python/cpython/blob/08b640e1575b75aeb605e72cb30c337480055b75/Python/ceval.c#L1254C1-L1254C20

一个基础的变量复制:

Python pyc文件格式解析(下)
Python pyc文件格式解析(下)

9、我们的目标是得到python中opcode的结构,所以我们跟进PyBytes_AS_STRING。

Python pyc文件格式解析(下)

不过这个函数并没有解析opcode,而是将对象中的数据提取出来,然后转换成了uint16_t指针。

Python pyc文件格式解析(下)

typedef uint16_t _Py_CODEUNIT;

10、此时first_instr将指向第一条指令,而next_instr自然指向下一条指令。

跳过一系列初始化来到main_loop标签,可以看到是一个大循环。开头是一个全局解释器锁(GIL)用来判断是否需要因一些原因挂起。这部分详情参照Python GIL。

Python pyc文件格式解析(下)

11、跳过一系列无关指令解析无关操作后来到,注释 /* Extract opcode and argument */处。首先通过NEXTOPARG获取将要执行的指令与参数:

Python pyc文件格式解析(下)

12、涉及两个宏_Py_OPCODE与_Py_OPARG。格式很简单,按照小端低字节是opcode高字节是参数。

https://github.com/python/cpython/blob/3.9/Include/cpython/code.h

Python pyc文件格式解析(下)

13、跳过一些宏定义来到switch (opcode),此处是opcode处理的地方了,这里每个case都是一个指令:

Python pyc文件格式解析(下)

定义在opcode.h中

https://github.com/python/cpython/blob/3.9/Include/opcode.h

Python pyc文件格式解析(下)

14、回到switch我们先看NOP指令,这个指令表示什么都不做。直接调用FAST_DISPATCH:

Python pyc文件格式解析(下)

FAST_DISPATCH 是一个goto:

#define FAST_DISPATCH() goto fast_next_opcode

这样就又回到了之前的NEXTOPARG语句上。

我们会发现所有的指令都会在末尾加 FAST_DISPATCH或者DISPATCH。DISPATCH就是直接continue,相当于回到main_loop开头。多做了一个GIL锁的检测。

#define DISPATCH() continue

15、此时我们来看看我们的pyc文件中对应的code都是什么指令:

Python pyc文件格式解析(下)

指令1

第一条指令是0x64也就是100,通过opcode.h可以找到这条指令是LOAD_CONST:

#define LOAD_CONST              100

回到switch(opcode)找到LOAD_CONST:

Python pyc文件格式解析(下)

开头有个PREDICTED,通过观察发现实际上是通过宏创建了个标签,格式是PRED_xxx。之后可以通过PREDICT来跳转到指令的位置执行。

Python pyc文件格式解析(下)

之后是一个GETITEM,这个宏是从arg1的元组中获取一个成员,成员的下标来自于oparg。64 00 中的00就是这个下标。

Python pyc文件格式解析(下)

通过010editor模板找到了这个consts对象,它也是一个CODE对象:

Python pyc文件格式解析(下)

回到LOAD_CONST,此时取出的value就是这个TYPE_CODE对象,之后Py_INCREF对这个对象添加了一个引用防止被提前释放,随后PUSH压栈:

Python pyc文件格式解析(下)
#define PUSH(v)                BASIC_PUSH(v)#define BASIC_PUSH(v)     (*stack_pointer++ = (v))

至此这个指令就算执行完了,就是将consts[0]的值压栈。

指令2

下一条指令依旧是LOAD_CONST,表示的是PUST(consts[1])。

Python pyc文件格式解析(下)

但consts[1]是一个引用,这个ref_index对应着上篇文章中的ref_flag。上篇文章中没有详细讲解是如何寻找ref的。实际原理是每加载一个带有FLAG_REF的对象时就会将这个对象追加记录在某个数组中。随后ref_index就是这个数组的下标。由于对象内有子对象,子对象也可以携带FLAG_REF,因此必须深度优先遍历整个对象树才能确定引用的是哪个对象,人工搜索太耗时。

Python pyc文件格式解析(下)

为了找到这些引用的具体对象,我完善了模板代码,现在可以找到这个对象。发现是一段字符串:check_command

Python pyc文件格式解析(下)

那么这段代码的含义是PUSH(“check_command”)。

指令3

随后是0x84指令,对应的指令是MAKE_FUNCTION:

Python pyc文件格式解析(下)
#define MAKE_FUNCTION           132

通过代码可以看出POP了check_command和consts[0]中的TYPE_CODE对象。分别赋值到了qualname 和codeobj ,最后传入PyFunction_NewWithQualName并返回了一个PyFunctionObject。

Python pyc文件格式解析(下)

这一套流程很容易看出是创建了一个function 名字叫做check_command,并把它压栈了。

刚好对应的是我们Test.py的第一个函数。

Python pyc文件格式解析(下)

指令4

然后是5A指令:

Python pyc文件格式解析(下)
#define STORE_NAME               90

将 5A 00中的00作为下标names获得一个名称,names[0]引用的仍然是check_command。随后POP出了刚才创的函数。随后获得执行环境中的locals,将check_command放入了locals。

Python pyc文件格式解析(下)
Python pyc文件格式解析(下)
Python pyc文件格式解析(下)

指令5&6

随后是紧跟这的两个LOAD_CONST分别加载2和3。

Python pyc文件格式解析(下)

分别是0和None。

Python pyc文件格式解析(下)

指令7

随后是6C指令:

Python pyc文件格式解析(下)
#define IMPORT_NAME             108

同样的按照指令参数01下标寻找names,找到的是”os”。

然后POP出了刚才一个0和一个None,一并传入import_name。

最后通过SET_TOP将import_name的返回值设置到了栈顶。

#define SET_TOP(v)        (stack_pointer[-1] = (v))
Python pyc文件格式解析(下)
Python pyc文件格式解析(下)

这条几条指令相当于Test.py中的import os。

Python pyc文件格式解析(下)

总         结

由于篇幅问题,之后的指令不再继续手动分析。

由于时间问题没有做010editor中pyc3.9模板的指令分析部分,但添加了ref的解析。

这就是从解析pyc到执行opcode的基本流程,包括pyc的文件解析、常见的opcode和python虚拟机的原理与代码实现,关于python的其他特性(如await)需要更加深入的分析。此pyc的文件解析教程到此结束。

向上滑动查看源码

//------------------------------------------------

//--- 010 Editor v3.0.4 Binary Template

//

//      File: PYC.bt

//   Authors: Kuang-che Wu

//   Version: 1.1

//   Purpose: Parse python bytecode .pyc and .pyo files, 

//            support python 2.4 to 2.7, 3.9. 

//  Category: Programming

// File Mask: *.pyc,*.pyo

//  ID Bytes: 

//   History: 

//   1.2   2023-10-17 Srinater:Support 3.9 file format, TODO:3.9 Instructions

//   1.1   2016-02-02 SweetScape: Updated header for repository submission.

//   1.0   2009-04-02 K Wu: Initial release.

//------------------------------------------------

local int ref_objs[100000];

local int ref_index= 0;

local int ref_lock = false;


enumMagicValue {

PY_24a0 = 62041,

PY_24a3 = 62051,

PY_24b1 = 62061,

PY_25a0_1 = 62071,

PY_25a0_2 = 62081,

PY_25a0_3 = 62091,

PY_25a0_4 = 62092,

PY_25b3_1 = 62101,

PY_25b3_2 = 62111,

PY_25c1 = 62121,

PY_25c2 = 62131,

PY_26a0 = 62151,

PY_26a1 = 62161,

PY_27a0_1 = 62171,

PY_27a0_2 = 62181,

PY_39     = 3425,

};

enumObjTypeOrigin {

TYPE_NULL= '0',

TYPE_NONE= 'N',

TYPE_FALSE= 'F',

TYPE_TRUE= 'T',

TYPE_STOPITER= 'S',

TYPE_ELLIPSIS= '.',

TYPE_INT= 'i',

TYPE_INT64= 'I',

TYPE_FLOAT= 'f',

TYPE_BINARY_FLOAT= 'g',

TYPE_COMPLEX= 'x',

TYPE_BINARY_COMPLEX= 'y',

TYPE_LONG= 'l',

TYPE_STRING= 's',

TYPE_INTERNED= 't',

TYPE_STRINGREF= 'R',

TYPE_TUPLE= '(',

TYPE_LIST= '[',

TYPE_DICT= '{',

TYPE_CODE= 'c',

TYPE_UNICODE= 'u',

TYPE_UNKNOWN= '?',

TYPE_SET= '<',

TYPE_FROZENSET= '>',

    TYPE_ASCII            = 'a',

    TYPE_ASCII_INTERNED   = 'A',

    TYPE_SMALL_TUPLE      = ')',

    TYPE_SHORT_ASCII      = 'z',

    TYPE_SHORT_ASCII_INTERNED='Z',

    TYPE_REF = 'r',

};

// marshal obj type of version 2

// version 2 is backward compatible to version 1 (for read)

struct ObjType{

    ObjTypeOrigin origin_type : 7;

    unsigned char ref_flag : 1;

    if (ref_flag == 1 && ref_lock == false)

    {

        ref_objs[ref_index] = FTell() - 1;

        ref_index += 1;

    }

};


// Python/import.c

struct Magic {

MagicValue magic1;

char magic2[2];

if (magic2 != "x0dx0a") {

Warning("bad magic");

return 0;

}

if (EnumToString(magic1) == "") {

Warning("Unknown magic version");

return 0;

}

};


// opcode.h

// this is opname of python 2.4

// please add new opcode in ReadInstruction()

enumOpCode {

STOP_CODE= 0,

POP_TOP= 1,

ROT_TWO= 2,

ROT_THREE= 3,

DUP_TOP= 4,

ROT_FOUR= 5,

UNARY_POSITIVE= 10,

UNARY_NEGATIVE= 11,

UNARY_NOT= 12,

UNARY_CONVERT= 13,

UNARY_INVERT= 15,

LIST_APPEND= 18,

BINARY_POWER= 19,

BINARY_MULTIPLY= 20,

BINARY_DIVIDE= 21,

BINARY_MODULO= 22,

BINARY_ADD= 23,

BINARY_SUBTRACT= 24,

BINARY_SUBSCR= 25,

BINARY_FLOOR_DIVIDE = 26,

BINARY_TRUE_DIVIDE = 27,

INPLACE_FLOOR_DIVIDE = 28,

INPLACE_TRUE_DIVIDE = 29,

SLICE= 30,

/* Also uses 31-33 */

SLICE_a= 31,

SLICE_b= 32,

SLICE_c= 33,


STORE_SLICE= 40,

/* Also uses 41-43 */

STORE_SLICE_a= 41,

STORE_SLICE_b= 42,

STORE_SLICE_c= 43,


DELETE_SLICE= 50,

/* Also uses 51-53 */

DELETE_SLICE_a= 51,

DELETE_SLICE_b= 52,

DELETE_SLICE_c= 53,


INPLACE_ADD= 55,

INPLACE_SUBTRACT= 56,

INPLACE_MULTIPLY= 57,

INPLACE_DIVIDE= 58,

INPLACE_MODULO= 59,

STORE_SUBSCR= 60,

DELETE_SUBSCR= 61,

BINARY_LSHIFT= 62,

BINARY_RSHIFT= 63,

BINARY_AND= 64,

BINARY_XOR= 65,

BINARY_OR= 66,

INPLACE_POWER= 67,

GET_ITER= 68,

PRINT_EXPR= 70,

PRINT_ITEM= 71,

PRINT_NEWLINE= 72,

PRINT_ITEM_TO   = 73,

PRINT_NEWLINE_TO = 74,

INPLACE_LSHIFT= 75,

INPLACE_RSHIFT= 76,

INPLACE_AND= 77,

INPLACE_XOR= 78,

INPLACE_OR= 79,

BREAK_LOOP= 80,

WITH_CLEANUP    = 81,

LOAD_LOCALS= 82,

RETURN_VALUE= 83,

IMPORT_STAR= 84,

EXEC_STMT= 85,

YIELD_VALUE= 86,

POP_BLOCK= 87,

END_FINALLY= 88,

BUILD_CLASS= 89,


STORE_NAME= 90,/* Index in name list */

DELETE_NAME= 91,/* "" */

UNPACK_SEQUENCE= 92,/* Number of sequence items */

FOR_ITER= 93,


STORE_ATTR= 95,/* Index in name list */

DELETE_ATTR= 96,/* "" */

STORE_GLOBAL= 97,/* "" */

DELETE_GLOBAL= 98,/* "" */

DUP_TOPX= 99,/* number of items to duplicate */

LOAD_CONST= 100,/* Index in const list */

LOAD_NAME= 101,/* Index in name list */

BUILD_TUPLE= 102,/* Number of tuple items */

BUILD_LIST= 103,/* Number of list items */

BUILD_MAP= 104,/* Always zero for now */

LOAD_ATTR= 105,/* Index in name list */

COMPARE_OP= 106,/* Comparison operator */

IMPORT_NAME= 107,/* Index in name list */

IMPORT_FROM= 108,/* Index in name list */


JUMP_FORWARD= 110,/* Number of bytes to skip */

JUMP_IF_FALSE= 111,/* "" */

JUMP_IF_TRUE= 112,/* "" */

JUMP_ABSOLUTE= 113,/* Target byte offset from beginning of code */


LOAD_GLOBAL= 116,/* Index in name list */


CONTINUE_LOOP= 119,/* Start of loop (absolute) */

SETUP_LOOP= 120,/* Target address (relative) */

SETUP_EXCEPT= 121,/* "" */

SETUP_FINALLY= 122,/* "" */


LOAD_FAST= 124,/* Local variable number */

STORE_FAST= 125,/* Local variable number */

DELETE_FAST= 126,/* Local variable number */


RAISE_VARARGS= 130,/* Number of raise arguments (1, 2 or 3) */

/* CALL_FUNCTION_XXX opcodes defined below depend on this definition */

CALL_FUNCTION= 131,/* #args + (#kwargs<<8) */

MAKE_FUNCTION= 132,/* #defaults */

BUILD_SLICE = 133,/* Number of items */

MAKE_CLOSURE    = 134,     /* #free vars */

LOAD_CLOSURE    = 135,     /* Load free variable from closure */

LOAD_DEREF      = 136,     /* Load and dereference from closure cell */ 

STORE_DEREF     = 137,     /* Store into cell */ 


/* The next 3 opcodes must be contiguous and satisfy

   (CALL_FUNCTION_VAR - CALL_FUNCTION) & 3 == 1  */

CALL_FUNCTION_VAR        = 140,/* #args + (#kwargs<<8) */

CALL_FUNCTION_KW         = 141,/* #args + (#kwargs<<8) */

CALL_FUNCTION_VAR_KW     = 142,/* #args + (#kwargs<<8) */


/* Support for opargs more than 16 bits long */

EXTENDED_ARG = 143,

};


// ceval.c

const int HAVE_ARGUMENT = 90;

const int EXTENDED_ARG = 143;

struct Instruction {

if (ReadUByte(FTell()) == EXTENDED_ARG) {

ubyte opcode_extended_arg;

uint16 oparg_hi;

ubyte opcode;

if (opcode >= HAVE_ARGUMENT)

uint16 oparg;

} else {

ubyte opcode;

if (opcode >= HAVE_ARGUMENT)

uint16 oparg;

}

};


typedef int32 r_long;

typedef int64 r_long64;

typedef int16 r_short;

typedef ubyte r_byte;


struct Code {

ObjType type;

if (type.origin_type != TYPE_STRING) {

Warning("code not in string type");

Exit(1);

}

r_long n;

local int remain = n;

local int end = FTell() + n;


/* trick to optimize parse speed */

while (remain >= 6) {

Instruction inst[remain/6];

remain = end - FTell();

}

remain = end - FTell();


while (remain > 0) {

Instruction inst;

remain -= sizeof(inst);

}

};


string Opcode2Opname(OpCode opcode)

{

uint16 magic = file.magic.magic1;

local string opname = EnumToString(opcode);

if (magic >= 0) { // history between python 2.0 and 2.4

// r27197

if (opcode == 114) opname = "";

// r28249

if (opcode == 81) opname = "RETURN_NONE";

// r28494

if (opcode == 81) opname = "";

// r32346

if (opcode == 9) opname = "NOP";

// r32389

if (opcode == 9) opcode = "";

// r35378

if (opcode == 18) opname = "LIST_APPEND";

// r36216

if (opcode == 9) opname = "NOP";

}

// magic 62041 r36242 marshal version 1

// magic 62051 r37112

// magic 62061 r37403

// magic 62071 r38931 marshal version 2

// magic 62081 r39773

if (magic >= 62091) {  // r42624

// r42624

if (opcode == 81) opname = "WITH_CLEANUP";

}

// magic 62092 r42952

// magic 62101 r50600

// magic 62111 r50968

// magic 62121 r51082

// magic 62131 r51729

if (magic >= 62151) { // r59548

// r59548

if (opcode == 54) opname = "STORE_MAP";

}

// magic 62161 r61290

if (magic >= 62171) { // r67818

// r67818

if (opcode == 18) opname = "";

if (opcode == 94) opname = "LIST_APPEND";

}

if (magic >= 62181) { // r70071

// r70071

if (opcode == 111) opname = "JUMP_IF_FALSE_OR_POP";

if (opcode == 112) opname = "JUMP_IF_TRUE_OR_POP";

if (opcode == 114) opname = "POP_JUMP_IF_FALSE";

if (opcode == 115) opname = "POP_JUMP_IF_TRUE";

}


return opname;

}


string ReadInstruction(Instruction &ins)

{

string s;

uint16 magic = file.magic.magic1;

OpCode opcode = (OpCode)ins.opcode;

string opname = Opcode2Opname(opcode);

if (exists(ins.oparg)) {

uint32 oparg = ins.oparg;

if (exists(ins.oparg_hi))

oparg += (uint32)ins.oparg_hi << 16;

// Note, COMPARE_OP oparg change name in r24970

if (opname == "COMPARE_OP") {

string cmp_op;

switch (oparg) {

case 0: cmp_op = "<"; break;

case 1: cmp_op = "<="; break;

case 2: cmp_op = "=="; break;

case 3: cmp_op = "!="; break;

case 4: cmp_op = ">"; break;

case 5: cmp_op = ">="; break;

case 6: cmp_op = "in"; break;

case 7: cmp_op = "not in"; break;

case 8: cmp_op = "is"; break;

case 9: cmp_op = "is not"; break;

case 10: cmp_op = "exception match"; break;

case 11: cmp_op = "BAD"; break;

}

SPrintf(s, "%s (%s)", opname, cmp_op);


} else {

SPrintf(s, "%s %d", opname, oparg);

}

} else {

s = opname;

}

return s;

}


struct LnoTab {

ObjType type;

if (type.origin_type != TYPE_STRING) {

Warning("lnotab not in string type");

Exit(1);

}

r_long n;

    if (n)

    {

    struct {

    uchar bytecode_offset_diff;

    uchar line_diff;

    } pair[n/2];

    }

};


// Python/marshal.c

typedef struct r_object { 

ObjType type;

switch (type.origin_type) {

case TYPE_NULL:

case TYPE_NONE:

case TYPE_STOPITER:

case TYPE_ELLIPSIS:

case TYPE_FALSE:

case TYPE_TRUE:

break;

case TYPE_INT:

r_long value;

break;

case TYPE_INT64:

r_long64 value;

break;

case TYPE_LONG:

r_long n;

local int size = n<0?-n:n;

r_short digit[size];

break;

case TYPE_FLOAT:

r_byte n;

char value[n];

break;

case TYPE_BINARY_FLOAT:

double value;

break;


case TYPE_COMPLEX:

r_byte nr;

char real[nr];

r_byte ni;

char imag[ni];

break;


case TYPE_BINARY_COMPLEX:

double real;

double imag;

break;

case TYPE_INTERNED:

case TYPE_STRING:

r_long n;

if (n)

char str[n];

break;

case TYPE_STRINGREF:

r_long n;

break;

case TYPE_TUPLE:

r_long n;

if (n)

struct r_object elements[n];

break;

case TYPE_LIST:

r_long n;

if (n)

struct r_object elements[n];

break;

case TYPE_DICT:

while (1) {

struct r_object key;

if (key.type == TYPE_NULL)

break;

struct r_object val;

}

break;

case TYPE_SET:

case TYPE_FROZENSET:

r_long n;

if (n)

struct r_object elements[n];

break;

case TYPE_CODE:

        r_long argcount;

            if (magic.magic1 == PY_39)

            {

                r_long posonlyargcount;

                r_long kwonlyargcount;

            }

        r_long nlocals;

        r_long stacksize;

        r_long flags;

        //struct r_object code;

        Code code;

        struct r_object consts;

        struct r_object names;

        struct r_object varnames;

        struct r_object freevars;

        struct r_object cellvars;

        struct r_object filename;

        struct r_object name;

        r_long firstlineno;

        //struct r_object lnotab;

            struct LnoTab lnotab;

break;

        case TYPE_ASCII_INTERNED:

        case TYPE_ASCII:

            r_long n;

if (n)

char str[n];

            break;

        case TYPE_SMALL_TUPLE:

unsigned char n;

if (n)

struct r_object elements[n];

break;

        case TYPE_SHORT_ASCII_INTERNED:

        case TYPE_SHORT_ASCII:

            unsigned char n;

if (n)

char str[n];

            break;

        case TYPE_REF:

            r_long ref_index;

            break;

        case TYPE_UNICODE:

            r_long n;

            if (n)

                char unicode[n];

            break;

default:

Warning("unknown type code");

Exit(1);

};


struct RefShow

{

    ref_lock = true;

    local int i;

    for (i = 0; i < ref_index; ++i)

    {

        FSeek(ref_objs[i]);

        r_object obj;

    }

    Exit(0);

};


struct {

Magic magic;

    if (magic.magic1 == PY_39)

    long skip[3];

    else

    char mtime[4];

r_object data;

    if (magic.magic1 == PY_39)

        RefShow refs;

} file;



END



原文始发于微信公众号(锋刃科技):Python pyc文件格式解析(下)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月20日14:29:03
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Python pyc文件格式解析(下)http://cn-sec.com/archives/2222325.html

发表评论

匿名网友 填写信息