2022年 11月 9日

python逆向入门

python逆向基础

一..python的运行机制

(1)、把原始代码编译成字节码
编译后的字节码是特定于Python的一种表现形式,它不是二进制的机器码,需要进一步编译才能被机器执行,这也是Python代码无法运行的像C/C++ 一样快的原因。如果Python进程在机器上拥有写入权限,那么它将把程序的字节码保存为一个以.pyc 为扩展名的文件,如果Python无法在机器上写入字节码,那么字节码将会在内存中生成并在程序结束时自动丢弃。在构建程序的时候最好给Python赋上在计算机上写的权限,这样只要源代码没有改变,生成的.pyc文件可以重复利用,提高执行效率。
(2)、把编译好的字节码转发到Python虚拟机(PVM)中进行执行
PVM是 Python Virtual Machine的简称,它是Python的运行引擎,是Python系统的一部分,它是迭代运行字节码指令的一个大循环、一个接一个地完成操作。

二.python源码的保护及对抗

保护 对抗
生成pyc文件 Uncompyle6支持通过字节码逆向出全版本python程序源码
pyc源码混淆 去混淆后Uncompyle6
打包成可执行的二进制文件 使用脚本解包
自定义opcode的python解释器 对抗较为困难,可以通过执行过程进行分析

python逆向所需的工具
uncompyle6是一个原生python的跨版本反编译器和fragment反编译器
安装库
在这里插入图片描述
安装完它会直接设置在环境变量里,可以直接使用

uncompyle6 -o pcat.py pcat.pyc
  • 1

python的pyc文件,python2与python3头有所不同,python有8字节的头,而python3有16字节的头,主要是魔术字(python版本)和时间属性的二进制信息
在这里插入图片描述
Python源码中的PyCodeObject

/* Bytecode object */
typedef struct {
    PyObject_HEAD
    int co_argcount;             /* 位置参数个数 */
    int co_kwonlyargcount;       /* keyword only arguments */
    int co_nlocals;              /* 局部变量个数 */
    int co_stacksize;            /* 栈大小 */
    int co_flags;                /* CO_..., see below */
    PyObject *co_code;           /* 字节码指令序列 */
    PyObject *co_consts;         /* 所有常量集合 */
    PyObject *co_names;          /* 所有符号名称集合 */
    PyObject *co_varnames;       /* 局部变量名称集合 */
    PyObject *co_freevars;       /* 闭包用的变量名集合 */
    PyObject *co_cellvars;       /* 内部嵌套函数引用的变量名集合 */
    /* The rest doesn't count for hash or comparisons */
    unsigned char *co_cell2arg;  /* Maps cell vars which are arguments. */
    PyObject *co_filename;       /* 代码所在文件名 */
    PyObject *co_name;           /* 模块|函数名|类名 */
    int co_firstlineno;          /* 代码块在文件中的起始行号 */
    PyObject *co_lnotab;         /* 字节码指令和行号对应关系 */
    void *co_zombieframe;        /* for optimization only (see frameobject.c) */
    PyObject *co_weakreflist;    /* to support weakrefs to code objects */
} PyCodeObject;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

获取python的‘汇编’

>>> import dis
>>> def myfun(i):
...     i = i+1
...     print(i)
...     return i
...
>>> dis.dis(myfun)
  2           0 LOAD_FAST                0 (i)
              2 LOAD_CONST               1 (1)
              4 BINARY_ADD
              6 STORE_FAST               0 (i)

  3           8 LOAD_GLOBAL              0 (print)
             10 LOAD_FAST                0 (i)
             12 CALL_FUNCTION            1
             14 POP_TOP

  4          16 LOAD_FAST                0 (i)
             18 RETURN_VALUE
>>>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
>>> import marshal,dis
>>> f = open(r'D:\__pycache__\1.cpython-37.pyc','rb').read()
>>> f
b'B\r\r\n\x00\x00\x00\x00\x0e\x11\x19`/\x00\x00\x00\xe3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00@\x00\x00\x00s\x0c\x00\x00\x00d\x00d\x01\x84\x00Z\x00d\x02S\x00)\x03c\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00C\x00\x00\x00s\x14\x00\x00\x00|\x00d\x01\x17\x00}\x00t\x00|\x00\x83\x01\x01\x00|\x00S\x00)\x02N\xe9\x01\x00\x00\x00)\x01\xda\x05print)\x01\xda\x01i\xa9\x00r\x04\x00\x00\x00\xfa\x07D:\\1.py\xda\x05myfun\x01\x00\x00\x00s\x06\x00\x00\x00\x00\x01\x08\x01\x08\x01r\x06\x00\x00\x00N)\x01r\x06\x00\x00\x00r\x04\x00\x00\x00r\x04\x00\x00\x00r\x04\x00\x00\x00r\x05\x00\x00\x00\xda\x08<module>\x01\x00\x00\x00s\x00\x00\x00\x00'
>>> code = marshal.loads(f[16:])
>>> dis.dis(code.co_code)
          0 LOAD_CONST               0 (0)
          2 LOAD_CONST               1 (1)
          4 MAKE_FUNCTION            0
          6 STORE_NAME               0 (0)
          8 LOAD_CONST               2 (2)
         10 RETURN_VALUE
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

用uncompyle6反编译源码

uncompyle6 D:\__pycache__\1.cpython-37.pyc
# uncompyle6 version 3.7.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)]
# Embedded file name: D:\1.py
# Compiled at: 2021-02-02 16:45:02
# Size of source mod 2**32: 47 bytes


def myfun():
    i = i + 1
    print(i)
    return i
# okay decompiling D:\__pycache__\1.cpython-37.pyc
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可以通过对这些字节码的理解手动去分析代码,也可以直接用uncompyle6进行分析,当然出题者肯定不会这么容易解出题目,保护的方法有很多,可以添加花指令等