精华内容
下载资源
问答
  • 易语言嵌入python解释器,在没安装python的电脑也能运行。 易语言调用python可把requests文件及需要的依赖包全部拷贝到当前易语言程序运行目录下再把python-3.6.6-embed-win32.zip全部解压到易语言程序运行目录下 ...
  • 目录 0x00 口水话 0x01 源码版本 ...由于是第一次阅读开源代码,没有相关经验,只能借助相关开发工具,以调试代码的形式对Python解释器源码调用关系、层次结构进行分析,分析过程可谓是“山穷水尽疑无...

    目录

    0x00 口水话

    0x01 源码版本

    0x02 分析工具

    0x03 分析方法

    0x04 目录结构

    1. Modules目录

    2. Python目录

    3. Parse目录

    4. Objects目录


    0x00 口水话

    由于是第一次阅读开源代码,没有相关经验,只能借助相关开发工具,以调试代码的形式对Python解释器源码调用关系、层次结构进行分析,分析过程可谓是“山穷水尽疑无路,柳暗花明又一村”。经过几番风雨,终于掌握了其大致流程和一些内部机制,同时也学到不少新知识。下面对本次的源码阅读进行简单总结,同时也起到笔记之目的。

    0x01 源码版本

    2.7.9。这里再啰嗦下,不是特意找的这个版本来分析,而是码机上刚好有此版本之源码,抱着省时(偷懒)的态度,于是乎就干脆分析此版本吧!

    0x02 分析工具

    Visual Studio 2013、Xmind 8。

    0x03 分析方法

    代码阅读、打开源码相关debug标志、编译并单步调试。

    0x04 目录结构

    这里只简单列举下比较重要的源码目录,主要有Modules、Python、Parse、Objects,后续将逐渐对相关目录下的模块进行分析探讨。

    1. Modules目录

    Modules目录主要包含主模块main.c以及其他Python内置模块,包括json、random、math、mmap、md5、sha、time、signal、time等内置模块源码。比较重要的主模块main.c是整个解释器的入口,如下图所示。

    main.c内部比较重要的函数有两个:Py_Main和RunModule:

    • Py_Main

    该函数是python进程的入口函数,主要包括命令行参数解析、初始化、运行三大部分。初始化的详细过程后续分析。运行主要分三类:模块级别、命令级别、其他级别,模块级别即以“python -m xxx.xx”形式运行某个模块级函数,命令级别即以“python -c ‘print 123’”形式运行某个语句,其他主要包括交互模式等。

    • RunModule

    模块级别运行调用了RunModule函数,其内部流程如上图所示。首先导入了runpy模块,然后利用runpy模块的_run_module_as_main函数根据模块名称实现模块的运行调用。runpy模块是一个python语言实现的模块调用库,Python解释器c源码对_run_module_as_main函数的调用,使用了c函数PyObject_Call来调用对象的可调用函数,该可调用函数也就是PyType_Object结构体内tp_call变量指向的函数。这里啰嗦一句,所有的Python内置对象都是PyType_Object结构体类型变量,该结构体的定义在Include/object.h头文件中。

    2. Python目录

    Python目录主要包含内存池、运行、编译、执行、内置对象以及导入等模块源码。

    3. Parse目录

    Parse目录包括读入读取(文件或者交互模式命令行等)、单词解析、词法分析、语法分析等模块源码。

    4. Objects目录

    Objects目录主要包含Python对象的相关源码,包括object对象、string对象、int对象、函数对象、类对象、类型对象等对象源码。在Python中一切皆对象,包括各种字典、数字、字符串值,以及数据类型、函数、类等都是对象。

    Python脚本的运行主要是对这几个目录中相关c文件中的相关函数调用实现的,这里只简单对一些重要目录进行简单描述,接下来将以交互模式下的执行print “Hello World”语句为例分析详细执行流程。

    更多文章请关注微信号

    展开全文
  • 易语言嵌入python解释器,在没安装python的电脑也能运行。易语言调用python可把requests文件及需要的依赖包全部拷贝到当前易语言程序运行目录下再把python-3.6.6-embed-win32.zip全部解压到易语言程序运行目录下即可...
  • 目录 0x01 准备工作 0x02 运行输出 0x03 主要流程分析 1 初始化 1.1 数据类型准备  ...本节以交互模式下执行print “Hello World”为例分析解释器的执行流程。 0x01 准备工作 打开Python-2.7....

    目录

    0x01 准备工作

    0x02 运行输出

    0x03 主要流程分析

    1 初始化

    1.1 数据类型准备 

    1.2 内置对象初始化

    2 运行

    2.1 申请内存池

    2.2 词法及语法解析

    2.3 解析树节点类型

    2.4 运行

    0x04 总结


    本节以交互模式下执行print “Hello World”为例分析解释器的执行流程。

    0x01 准备工作

    • 打开Python-2.7.9\PCbuild目录下的visual studio解决方案pcbuild
    • 设置python工程为启动工程
    • 打开词法语法分析调试变量,python\pythonrun.c源码中修改int Py_DebugFlag = 1
    • 编译python工程

    0x02 运行输出

        以Debug模式启动python工程,出现命令行提示窗口,输入print “Hello World”回车运行结果如下图所示。

    0x03 主要流程分析

       在解释器命令行窗口输入print “Hello World”回车后执行流程是怎样的呢? Python源码工程支持visual studio的编译和调试,加上visual studio非常强大的单步调试能力,因此对执行流程的分析基本没什么技术难度。这里简单总结下大致的运行流程,有兴趣的童鞋可以自己运行看看。

    1 初始化

       上一节中提到过Py_Main函数,它是python进程的入口函数,主要包括参数解析、初始化、运行等逻辑。初始化工作主要完成解释器的初始化,由Py_Main函数调用Py_Initialize函数完成,Py_Initialize函数位于Python\pythonrun.c源码文件内,初始化完成的主要流程如下图所示:

    1.1 数据类型准备 

        这里先看下数据类型准备_Py_ReadyTypes,类型的准备主要完成各种数据类型的准备工作。Python C源码中,所有内置的数据类型都是PyTypeObject结构体的一个实例,PyTypeObject结构体中以函数指针的形式,声明了各种处理函数,包括print、repr、hash、call、str、getattro、成员函数(tp_methods)、成员变量(tp_members)等。各个内置数据类型的定义在Objects目录下相应的C源码文件中,比如list数据类型实现的源码文件是Objects\listobject.c,在listobject.c源码文件中定义了结构体实例PyList_Type对象,如下图所示。

        _Py_ReadyTypes函数主要调用Objects\typeobject.c\PyType_Ready函数对内置的数据类型的结构体对象的个元素进行初始化。

        数据类型的初始化,主要完成对int、long、bytearray、float等数据类型的某些特定参数进行初始化。这里有一个东西没有搞明白,bytearray的初始化C函数,PyByteArray_Init函数什么事都没干直接返回1。

    1.2 内置对象初始化

      内置对象初始化,也就是__builtin__模块的初始化,通过调用Python\bltinmodule.c中的_PyBuiltin_Init函数完成内置常量、内置数据类型和函数的初始化。内置模块初始化包括两个动作,一是调用PyDict_SetItemString设置__builtin__模块的__dict__属性,二是调用Objects\object.c\_Py_AddToAllObjects函数将内置对象的结构体实例添加到双向循环链表refchain。代码如下图所示:

        内置常量主要包括None、Ellipsis、NotImplemented、False、True。None、Ellipsis、NotImplemented在内存中的对象是PyObject结构体(在Include\object.h中定义)实例,True和False是PyIntObject结构体(在Include\intobject.h中定义)实例。

        内置数据类型和函数,和内置常量的初始化动作一样。这里提一下,包括property、super、object、type、classmethod、staticmethod、str、file等内置对象都是PyTypeObject结构体(在Include\object.h中定义)实例。

    2 运行

     初始化完成后,进入交互模式。主要流程如下图。

    2.1 申请内存池

        Python采用内存池的方式管理内存的使用,实现源码为Pyhon\pyarena.c。其主要思想是先在内存中申请一块内存,后续在该预先申请的内存块上分配相应的空间给对象使用。arena内存池的管理由两个结构体实现,_block和_arena。内存池管理机制后续进行分析。

    2.2 词法及语法解析

        词法及语法解析,主要调用Parse\parsetok.c\parsetok函数。该函数主要流程如下图所示。

        在交互命令行输入的print “Hello World”的单词解析信息存储在结构体parse_state结构体(在Parse\parser.h中定义)

       

        该结构体中有一颗解析树,该解析树存储了词法语法解析结果,各节点是_node类型的结构体。parsetok函数最终返回该解析树的top节点。值得注意的是,新解析出来的单词会作为最新的top节点,并且如果设置了编码方式,解析树的top节点是编码方式。

       使用Parser\parser.c中的dumptree、showtree、printtree调试函数可以打印解析树。利用dumptree函数对parseok返回的解析树输出如下。

        词法语法解析完毕后,调用Parse\ast.c\PyAST_FromNode函数,将解析树各个节点由CST为AST(抽象语法树,Abstract Syntax Tree)。CST和AST都是语法分析的中间结果,不同的地方是CST直接对应语法分析的匹配过程,含有大量的冗余信息,AST省略了大量冗余信息,直接对应实际的语义,也就是最终的分析结果。

        Python为我们提供了parse和ast模块,将其内部的单词和语法解析、字节编译暴露了出来。利用parse和ast模块,我们可以访问python解析树。在python解释器命令行中导入模块parser,然后调用parser. suite(‘print “Hello World”’),可以看到解析结果和c函数dumptree的输出是一样的。同样使用ast模块的parse和dump函数可以看到ast解析结果。

    import parser
    cst = parser.suite('print "Hello World"')
    print cst.tolist()
    # [257, [267, [268, [269, [272, [1, 'print'], [304, [305, [306, [307, [308, [310, [311, [312, [313, [314, [315, [316, [317, [318, [3, '"Hello World"']]]]]]]]]]]]]]]]], [4, '']]], [4, ''], [0, '']]
    
    import ast
    astrst = ast.parse('print "Hello World"')
    print ast.dump(astrst)
    # Module(body=[Print(dest=None, values=[Str(s='Hello World')], nl=True)])

    2.3 解析树节点类型

             如2.2中描述,词法与语法分析的结果是一颗词法语法解析树,该树的各节点的结构体定义(在Include\node.h中)如下:

         那么节点类型有哪些呢?类型在Include\token.h及graminit.h两个头文件中给出了定义。例如257为文件输入、267为语句、268为单个语句、272为print语句、1为名称、310为表达式、312为AND表达式、3为字符串、4为新行、0为结束标志。

    2.4 运行

        运行主要调用Python\pythonrun.c\run_mod函数,其主要包括两个步骤,编译和执行。编译调用python\compile.c中的PyAST_Compile函数,对ast解析结果结构体mod_ty(在Include\Python-ast.h中定义)对象进行编译,返回代码结构体PyCodeObject(在Include\code.h中定义)对象,编译执行的具体实现细节后面再进行分析。另外,上述过程中所有中间数据包括词法语法解析树、AST解析结果、编译等结果都存储在2.1所申请的内存池中。

    0x04 总结

        本节简单分析了交互模式下print “Hello World”语句的执行流程,初步掌握了python解释器的工作流程。接下来会对内部一些核心的机制进行分析,包括arena内存池、单词解析、语法分析、编译、执行、全局锁等。

    更多文章敬请关注

    展开全文
  • 另一个简单的python解释器 安装 pip3 install -r requirements.txt 用法 从主目录执行python脚本: $ python3 simplepy.py example/$program_name 其中$ program_name是./example中文件的名称(例如while.spy)。
  • 0 前言 考虑前文所述的几个方案,均是从源码的加工入手,...由于发行商业 Python 程序到客户环境时通常会包含一个 Python 解释器,如果改造解释器能解决源码保护的问题,那么也是可选的一条路。 假定我们有一个...

     参考地址:https://www.cnblogs.com/dhcn/p/11077447.html

    0 前言

    考虑前文所述的几个方案,均是从源码的加工入手,或多或少都有些不足。假设我们从解释器的改造入手,会不会能够更好的保护代码呢?

    由于发行商业 Python 程序到客户环境时通常会包含一个 Python 解释器,如果改造解释器能解决源码保护的问题,那么也是可选的一条路。

    假定我们有一个算法,能够加密原始的 Python 代码,这些加密后代码随发行程序一起,可被任何人看到,却难以破解。另一方面,有一个定制好的 Python 解释器,它能够解密这些被加密的代码,然后解释执行。而由于 Python 解释器本身是二进制文件,人们也就无法从解释器中获取解密的关键数据。从而达到了保护源码的目的。

    要实现上述的设想,我们首先需要掌握基本的加解密算法,其次探究 Python 执行代码的方式从而了解在何处进行加解密,最后禁用字节码用以防止通过 .pyc 反编译。

    1 加解密算法

    1.1 对称密钥加密算法

    对称密钥加密(Symmetric-key algorithm)又称为对称加密、私钥加密、共享密钥加密,是密码学中的一类加密算法。这类算法在加密和解密时使用相同的密钥,或是使用两个可以简单地相互推算的密钥。

    对称加密算法的特点是算法公开、计算量小、加密速度快、加密效率高。

    常见的对称加密算法有:DES、3DES、AES、Blowfish、IDEA、RC5、RC6 等。

    对称密钥加解密过程如下:

    明文通过密钥加密成密文,密文也可通过相同的密钥解密为明文。

    通过 openssl 工具,我们能够方便选择对称加密算法进行加解密。下面我们以 AES 算法为例,介绍其用法。

    AES 加密

    # 指定密码进行对称加密
    $ openssl enc -aes-128-cbc -in test.py -out entest.py -pass pass:123456
    
    # 指定文件进行对称加密
    $ openssl enc -aes-128-cbc -in test.py -out entest.py -pass file:passwd.txt
    
    # 指定环境变量进行对称加密
    $ openssl enc -aes-128-cbc -in test.py -out entest.py -pass env:passwd

    AES 解密

    # 指定密码进行对称解密
    $ openssl enc -aes-128-cbc -d -in entest.py -out test.py -pass pass:123456
    
    # 指定文件进行对称解密
    $ openssl enc -aes-128-cbc -d -in entest.py -out test.py -pass file:passwd.txt
    
    # 指定环境变量进行对称解密
    $ openssl enc -aes-128-cbc -d -in entest.py -out test.py -pass env:passwd

    1.2 非对称密钥加密算法

    密钥加密(英语:public-key cryptography,又译为公开密钥加密),也称为非对称加密(asymmetric cryptography),一种密码学算法类型,在这种密码学方法中,需要一对密钥,一个是私钥,另一个则是公钥。这两个密钥是数学相关,用某用户公钥加密后所得的信息,只能用该用户的私钥才能解密。

    非对称加密算法的特点是算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。

    常见的对称加密算法有:RSA、Elgamal、背包算法、Rabin、D-H、ECC 等。

    非对称密钥加解密过程如下:

    明文通过公钥加密成密文,密文通过与公钥对应的私钥解密为明文。

    通过 openssl 工具,我们能够方便选择非对称加密算法进行加解密。下面我们以 RSA 算法为例,介绍其用法。

    生成私钥、公钥

    # 辅以 AES-128 算法,生成 2048 比特长度的私钥
    $ openssl genrsa -aes128 -out private.pem 2048
    
    # 根据私钥来生成公钥
    $ openssl rsa -in private.pem -outform PEM -pubout -out public.pem

    RSA 加密

    # 使用公钥进行加密
    openssl rsautl -encrypt -in passwd.txt -inkey public.pem -pubin -out enpasswd.txt

    RSA 解密

    # 使用私钥进行解密
    openssl rsautl -decrypt -in enpasswd.txt -inkey private.pem -out passwd.txt

    2 基于加密算法实现源码保护

    对称加密适合加密源码文件,而非对称加密适合加密密钥。如果将两者结合,就能达到加解密源码的目的。

    2.1 在构建环境进行加密

    我们发行出去安装包中,源码应该是被加密过的,那么就需要在构建阶段对源码进行加密。加密的过程如下:

    1. 随机生成一个密钥。这个密钥实际上是一个用于对称加密的密码。
    2. 使用该密钥对源代码进行对称加密,生成加密后的代码。
    3. 使用公钥(生成方法见 非对称密钥加密算法)对该密钥进行非对称加密,生成加密后的密钥。

    不论是加密后的代码还是加密后的密钥,都会放在安装包中。它们能够被用户看到,却无法被破译。而 Python 解释器该如何执行加密后的代码呢?

    2.2 Python 解释器进行解密

    假定我们发行的 Python 解释器中内置了与公钥相对应的私钥,有了它就有了解密的可能。而由于 Python 解释器本身是二进制文件,所以不需要担心内置的私钥会被看到。解密的过程如下:

    1. Python 解释器执行加密代码时需要被传入指示加密密钥的参数,通过这个参数,解释器获取到了加密密钥
    2. Python 解释器使用内置的私钥,对该加密密钥进行非对称解密,得到原始密钥
    3. Python 解释器使用原始密钥对加密代码进行对称解密,得到原始代码
    4. Python 解释器执行这段原始代码

    可以看到,通过改造构建环节、定制 Python 解释器的执行过程,便可以实现保护源码的目的。改造构建环节是容易的,但是如何定制 Python 解释器呢?我们需要深入了解解释器执行脚本和模块的方式,才能在特定的入口进行控制。

    3 脚本、模块的执行与解密

    3.1 执行 Python 代码的几种方式

    为了找到 Python 解释器执行 Python 代码时的所有入口,我们需要首先执行 Python 解释器都能以怎样的方式执行代码。

    直接运行脚本

    python test.py

    直接运行语句

    python -c "print 'hello'"

    直接运行模块

    python -m test

    导入、重载模块

    python
    >>> import test  # 导入模块
    >>> reload(test)  # 重载模块

    直接运行语句 的方式接收的就是明文的代码,我们也无需对这种方式做额外处理。 直接运行模块和导入、重载模块这两种方式在流程上是殊途同归的,所以接下来会一起来看。 因此我们将分两种情况:运行脚本和加载模块来进一步探究各自的过程和解密方式。

    3.2 运行脚本时解密

    运行脚本的过程 Python 解释器在运行脚本时的代码调用逻辑如下:

           main            WinMain
    [Modules/python.c] [PC/WinMain.c]
                 \         /
                  \       /
                   \     /
                    \   /
                     \ /
                   Py_Main
               [Moduls/main.c]

    Python 解释器运行脚本的入口函数因操作系统而异,在 Linux/Unix 系统上,主入口函数是 Modules/python.c 中的 main 函数,在 Windows系统上,则是 PC/WinMain.c 中的 WinMain 函数。不过这两个函数最终都会调用 Moduls/main.c 中的 Py_Main 函数。

    我们不妨来看看 Py_Main 函数中的相关逻辑:

    [Modules/Main.c]
    --------------------------------------
    
    int
    Py_Main(int argc, char **argv)
    {
        if (command) {
            // 处理 python -c <command>
        } else if (module) {
            // 处理 python -m <module>
        }
        else {
            // 处理 python <file>
            ...
            fp = fopen(filename, "r");
            ...
        }
    }

    处理<command><module>的部分我们暂且先不管,在处理文件(通过直接运行脚本的方式)的逻辑中,可以看到解释打开了文件,获得了文件指针。那么如果我们把这里的 fopen 换成是自定义的 decrypt_open 函数,这个函数用来打开一个加密文件,然后进行解密,并返回一个文件指针,这个指针指向解密后的文件。那么,不就可以实现解密脚本的目的了吗?

    自定义 decrypt_open 我们不妨新增一个 Modules/crypt.c 文件,用来存放一些自定义的加解密函数。

    decrypt_open 函数大概实现如下:

    [Modules/crypt.c]
    --------------------------------------
    
    /* 以解密方式打开文件 */
    FILE *
    decrypt_open(const char *filename, const char *mode)
    {
        int plainlen = -1;
        char *plaintext = NULL;
        FILE *fp = NULL;
    
        if (aes_passwd == NULL)
            fp = fopen(filename, "r");
        else {
            plainlen = aes_decrypt(filename, aes_passwd, &plaintext);
            // 如果无法解密,返回源文件描述符
            if (plainlen < 0)
                fp = fopen(filename, "r");
            // 否则,转换为内存文件描述符
            else
                fp = fmemopen(plaintext, plainlen, "r");
        }
        return fp;
    }

    这里的 aes_passwd 是一个全局变量,代表对称加密算法中的密钥。我们暂时假定已经获取该密钥了,后文会说明如何获得。而 aes_decrypt 是自定义的一个使用AES算法进行对称解密的函数,限于篇幅,此函数的实现不再贴出。

    decrypt_open 逻辑如下: - 判断是否获得了对称密钥,如果没获得,直接打开该文件并返回文件指针 - 如果获得了,则尝试使用对称算法进行解密 - 如果解密失败,可能就是一段非加密的脚本,直接打开该文件并返回文件指针 - 如果解密成功,我们通过解密后的内容创建一个内存文件对象,并返回该文件指针

    实现了上述这些函数后,我们就能够实现在直接运行脚本时,解密执行被加密代码的目的。

    3.3 加载模块时解密

    加载模块的过程 加载模块的逻辑主要实现在 Python/import.c 文件中,其过程如下:

    Py_Main
                                             [Moduls/main.c]
                                                    |
        builtin___import__                      RunModule
                |                                   |
    PyImport_ImportModuleLevel <----┐     PyImport_ImportModule
                |                   |               |
        import_module_level         └------- PyImport_Import
                |
             load_next                         builtin_reload
                |                                   |
          import_submodule                PyImport_ReloadModule
                |                                   |
            find_module <---------------------------┘
    • 通过 python -m <module> 的方式来加载模块时,其入口函数是 Py_Main 函数
    • 通过 import <module> 的方式来加载模块时,其入口函数是 builtin___import__ 函数
    • 通过 reload(<module>) 的方式来加载模块时,其入口函数是 builtin_reload 函数

    但不论是哪种方式,最终都会调用 find_module 函数,我们看看这个函数中是否暗藏乾坤呢?

    [Python/import.c]
    --------------------------------------
    
    static struct filedescr *
    find_module(char *fullname, char *subname, PyObject *path, char *buf,
                size_t buflen, FILE **p_fp, PyObject **p_loader)
    {
        ...
        fp = fopen(buf, filemode);
        ...
    }

    我们在 find_module 函数中找到了打开文件的逻辑,如果直接改成前文实现的 decrypt_open,岂不是就能达成加载模块时解密的目的了?

    总体思路是这样的,但有个细节需要注意,buf 不一定就是 .py 文件,也可能是 .pyc 文件,我们只对 .py 文件做改动,则可以这么写:

    [Python/import.c]
    --------------------------------------
    
    static struct filedescr *
    find_module(char *fullname, char *subname, PyObject *path, char *buf,
                size_t buflen, FILE **p_fp, PyObject **p_loader)
    {
        ...
        if (fdp->type == PY_SOURCE) {
            fp = decrypt_open(buf, filemode);
        }
        else {
            fp = fopen(buf, filemode);
        }
        ...
    }

    经过上述改动,就实现了加载模块时解密的目的了。

    3.4 支持指定密钥文件

    前文中还留有一个待解决的问题:我们一开始是假定解释器已获取到了密钥内容并存放在了全局变量 aes_passwd 中,那么密钥内容怎么获取呢?

    我们需要 Python 解释器能支持一个新的参数选项,通过它来指定已加密的密钥文件,然后再通过非对称算法进行解密,得到 aes_passed。 假定这个参数选项是 -k <filename>,则可使用如 python -k enpasswd.txt 的方式来告知解释器加密密钥的文件路径。其实现如下:

    [Modules/main.c]
    --------------------------------------
    
    /* 命令行选项,注意k:是新增的内容 */
    #define BASE_OPTS "3bBc:dEhiJk:m:OQ:RsStuUvVW:xX?"
    ...
    /* Long usage message, split into parts < 512 bytes */
    static char *usage_1 = "\
    ...
    -k key : decrypt source file by using key file\n\
    ...
    ";
    ...
    int
    Py_Main(int argc, char **argv)
    {
        ...
        char *keyfilename = NULL;
        ...
        while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) {
            ...
            case 'k':
                keyfilename = (char *)malloc(strlen(_PyOS_optarg) + 1);
                if (keyfilename == NULL)
                    Py_FatalError(
                       "not enough memory to copy -k argument");
                strcpy(keyfilename, _PyOS_optarg);
                keyfilename[strlen(_PyOS_optarg)] = '\0';
                break;
            ...
        }
        ...
        if (keyfilename != NULL) {
            int passwdlen;
            char *passwd = NULL;
    
            passwdlen = rsa_decrypt(keyfilename, &passwd);
            set_aes_passwd(passwd);
            if (passwdlen < 0) {
                fprintf(stderr, "%s: parsing key file '%s' error\n", argv[0], keyfilename);
                free(keyfilename);
                return 2;
            } else {
                free(keyfilename);
            }
        }
        ...
    }

    其逻辑如下: - k:中的 k 表示支持 -k 选项;: 表示选项后跟一个参数,即这里的已加密密钥文件的路径 - 解释器在处理到 -k 参数时,获取其后所跟的文件路径,记录在 keyfilename中 - 使用自定义的 rsa_decrypt 函数(限于篇幅,不列出如何实现的逻辑)对已加密密钥文件进行非对称解密,获得密钥的原始内容 - 将该密钥内容写入到 aes_passwd 中

    由此,通过显示地指定已加密密钥文件,解释器获得了原始密钥,进而通过该密钥解密已加密代码,再执行原始代码。但是,这里面还潜藏着一个风险:执行代码的过程中会生成 .pyc 文件,通过它反编译出的 .py 文件是未加密的。换句话说,恶意用户可以通过这种手段绕过限制。所以,我们需要 禁用字节码。

    4 禁用字节码

    4.1 不生成 .pyc 文件

    首先要做的就是不生成 .pyc 文件,这样,恶意用户就没法直接根据 .pyc 文件来得到源码。

    我们知道,通过 -B 选项可以告知 Python 解释器不生成 .pyc 文件。既然定制的 Python 解释器就不生成 .pyc 我们干脆禁用这个选项:

    [Modules/main.c]
    --------------------------------------
    
    /* 命令行选项,注意移除了B */
    #define BASE_OPTS "3bc:dEhiJm:OQ:RsStuUvVW:xX?"
    ...
    /* Long usage message, split into parts < 512 bytes */
    static char *usage_1 = "\
    ...
    //-B     : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x\n\
    ...
    ";
    ...
    int
    Py_Main(int argc, char **argv)
    {
        ...
        // 不生成 py[co]
        Py_DontWriteBytecodeFlag++;
        ...
    }

    除此以外,Python 解释器还会从环境变量中获取是否不生成 .pyc 文件,因此也需要做处理:

    [Python/pythonrun.c]
    --------------------------------------
    
    void
    Py_InitializeEx(int install_sigs)
    {
        ...
        f ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0')
            Py_DebugFlag = add_flag(Py_DebugFlag, p);
        if ((p = Py_GETENV("PYTHONVERBOSE")) && *p != '\0')
            Py_VerboseFlag = add_flag(Py_VerboseFlag, p);
        if ((p = Py_GETENV("PYTHONOPTIMIZE")) && *p != '\0')
            Py_OptimizeFlag = add_flag(Py_OptimizeFlag, p);
        // 移除对 PYTHONDONTWRITEBYTECODE 的处理
        if ((p = Py_GETENV("PYTHONDONTWRITEBYTECODE")) && *p != '\0')
            Py_DontWriteBytecodeFlag = add_flag(Py_DontWriteBytecodeFlag, p);
        ...
    }

    4.2 禁止访问字节码对象 co_code

    仅仅是不生成 .pyc 文件还是不够的,恶意用户已然可以访问对象的 co_code 属性来获取字节码,进而通过反编译的手段获取到源码。因此,我们也需要禁止用户访问字节码对象:

    [Objects/codeobject.c]
    --------------------------------------
    
    static PyMemberDef code_memberlist[] = {
        {"co_argcount",     T_INT,          OFF(co_argcount),       READONLY},
        {"co_nlocals",      T_INT,          OFF(co_nlocals),        READONLY},
        {"co_stacksize",T_INT,              OFF(co_stacksize),      READONLY},
        {"co_flags",        T_INT,          OFF(co_flags),          READONLY},
        // {"co_code",         T_OBJECT,       OFF(co_code),           READONLY},
        {"co_consts",       T_OBJECT,       OFF(co_consts),         READONLY},
        {"co_names",        T_OBJECT,       OFF(co_names),          READONLY},
        {"co_varnames",     T_OBJECT,       OFF(co_varnames),       READONLY},
        {"co_freevars",     T_OBJECT,       OFF(co_freevars),       READONLY},
        {"co_cellvars",     T_OBJECT,       OFF(co_cellvars),       READONLY},
        {"co_filename",     T_OBJECT,       OFF(co_filename),       READONLY},
        {"co_name",         T_OBJECT,       OFF(co_name),           READONLY},
        {"co_firstlineno", T_INT,           OFF(co_firstlineno),    READONLY},
        {"co_lnotab",       T_OBJECT,       OFF(co_lnotab),         READONLY},
        {NULL}      /* Sentinel */
    };

    到此,一个定制的 Python 解释器完成了。

    5 演示

    5.1 运行脚本

    通过 -k 选项执行已加密密钥文件,Python 解释器可以运行已加密和未加密的 Python 文件。

    5.2 加载模块

    可以通过 -m <module> 的方式加载已加密和未加密的模块,也可以通过 import <module> 的方式来加载已加密和未加密的模块。

    5.3 禁用字节码

    通过禁用字节码,我们达到以下效果: - 不会生成 .pyc 文件 - 可以访问函数的 func_code - 无法访问代码对象的 co_code,即本示例中的 f.func_code.co_code - 无法使用dis模块来获取字节码

    5.4 异常堆栈信息

    尽管代码是加密的,但是不会影响异常时的堆栈信息。

    5.5 调试

    加密的代码也是允许调试的,但是输出的代码内容会是加密的,这正是我们所期望的。

    6 思考

    1. 如何防止通过内存操作的方式找到对象的co_code?
    2. 如何进一步提升私钥被逆向工程探知的难度?
    3. 如何能在调试并希望看到源码的时候看到?
    展开全文
  • 后者可以直接在Python解释器中使用,而前者的目标是在提供基于PythonQt的控制台与btk*对象进行交互的独立应用程序中使用。 在内部,绑定由管理。 ##执照## BTK使用慷慨的开源New BSD许可证。 是的,您可以在...
  • 该存储库包含python解释器服务器和Emacs comint-mode客户端的快速n-dirty实现。 我专门构建它来与IDA Pro中的python解释器进行交互。 它使我能够在Emacs中编写代码并直接在IDA python解释器的上下文中执行(或多或少...
  • RustPython 一个用Rust编写的Python解释器 RustPython 一个用 Rust 编写的 Python-3 (CPython >= 3.5.0) 解释器 :snake: :face_screaming_in_fear: :sign_of_the_horns: 。 用法查看我们在 WebAssembly 上运行的...
  • Tonkadur的Python3解释,一种叙事脚本语言。 该解释器主要用于测试目的,可能不应该在游戏引擎中直接使用。
  • embedPython 教程-在Qt应用程序中嵌入Python解释器 在回购我的教程
  • PyO3: Python解释器的Rust绑定 Python 解释器的 PyO3 Rust 绑定。 用户指南 API 文档 Cargo 包:pyo3 PyO3 在 Apache-2.0 许可下获得许可。 Python 是根据 Python 许可证获得许可的。 支持的 Python 版本:Python...
  • 用Rust编写的Python-3(CPython> = 3.8.0)解释器 :snake: :face_screaming_in_fear: :sign_of_the_horns: 。 用法 查看我们在WebAssembly上运行的。 RustPython需要Rust最新的稳定版本(例如2020年5月24日的1.43.0...
  • gpythonPython 3.4解释器到Go语言(“不包括电池”)的部分重新实现/部分端口。 这包括: 运行时-对python3.4使用兼容的字节码 词法分析器 解析器 编译器 互动模式(REPL)( ) 它不包含很多python模块,因为...
  • pyo3:Python解释器的Rust绑定
  • 我在上完第一门Python课程后的第一年就开始了这一工作,老实说,我对此并不记得很多。 我认为Swift重写是有条理的-增加了对macOS(?)的兼容性。 我知道这个小项目的全部目的是在资源最少的设备上PoC一个合成...
  • pluspy是使用Python编写的TLA +解释器。 运行“ ./pluspy -h”以获得基本帮助。 输出应该是这样的: Usage: pluspy [options] [module] options: -c cnt: #times that Next should be evaluated -h: help -i:...
  • PyO3Python 的Rust绑定。这包括从 Rust 二进制文件运行 Python 代码并与之交互,以及编写原生 Python 模块。用户指南:稳定|掌握API 文档:稳定|掌握贡献笔记:github可以在指南中找到与 rust-cpython 的比较。用法...
  • QtPython控制台 在PySide中实现的Python解释器小部件
  • pylite是一个爱好python解释器,可以告诉您python编译器和虚拟机如何工作。 它用于教育目的,因此使代码保持尽可能简单。 pylite由两个可执行文件组成:pycom和pyvm。 pycom是编译器,它具有5000 LOC。 pyvm是...
  • 使用Jison作为解析器生成器和词法分析器,用JavaScript编写的简单python解释器。 Bastardized,写得不好,几乎没有功能,但剥夺了该语言的所有有用功能。 纯粹是学术活动。 关于 用生成的解析器和词法分析器使用和...
  • 一般情况下, linux系统自带Python, 我的centos7自带的是Python2, 这里记录一下, 源码安装Python3, 先看完再操作, 可以节省时间 1. 下载对应版本的源码压缩包,版本需求不同的话直接更改后面的版本即可, 大体...
    一般情况下, linux系统自带Python, 我的centos7自带的是Python2, 这里记录一下, 源码安装Python3, 先看完再操作, 可以节省时间
    1. 下载对应版本的源码压缩包,版本需求不同的话直接更改后面的版本即可, 大体地址不变, 我这里是在Python官网下载的, 网站在国外, 可能很慢, 有时候网速还可以, 实在慢的话就换个阿里的地址, 自己百度。。。(wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tgz)

    在这里插入图片描述

    2. 解压(tar -zxvf Python-3.6.1.tgz 或 tar -zxf Python-3.6.1.tgz)
    3. 为了方便管理你的Python3, 可以在/usr/local/下面创建一个文件夹, 我这里创建了一个python3文件夹, 也可以和python2当在相同的路径中

    在这里插入图片描述

    4. 编译, 安装

    如果报错没有gcc或make的话, 直接yum安装就ok, 否则直接下面
    ./configure --prefix=/usr/local/python3
    make && make install
    在这里插入图片描述

    5. 第四步安装报错, 缺少依赖 zipimport.ZipImportError: can’t decompress data; zlib not available

    解决: yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel(源码安装的话, 这个依赖才是重点, 每一个都有用, 如果安装之后的python有问题, 就是这个依赖少装了)

    6. 配置环境变量

    vim ~/.bash_profile
    配置下面的11行, 然后刷新生效就ok了
    source ~/.bash_profile
    在这里插入图片描述
    7. 一般我们都知道安装python第三方包是用pip install, 上面的依赖已经安装了pip3, 这里我们不用再源码安装了, 只是没有pip install 这个命令pip3只是一个脚本文件, 只要复制一份pip3改名为pip, 这样的话, 两个都能使用
    在这里插入图片描述

    展开全文
  • helpy显示内置 Python 文档,而无需使用交互式 Python 解释器控制台。 按 Python 模块、类、方法、函数或关键字查看文档。 在 cPython 2.6、2.7、3.2、3.3 和 pypy 2.2 (Python 2.7.3) 中测试。 文档来自用于执行...
  • CSCW_project M2R-I 协作式 Python 解释器项目
  • Python 的在线编辑和代码共享站点。 获取代码并运行它 git clone git://github.com/yuguang/pythonfiddle.git git clone git://github.com/yuguang/fiddlesalad.git git clone git://github....
  • [Python源码学习] 之 Python解释器

    万次阅读 2011-08-13 01:08:07
    下载Python源码,解压,即可看到源码的目录结构。 奇怪:Python2.7.2根目录下的 README 文件中有 各个目录的说明,在 Python3.2.1根目录下的README文件中却没有相应的介绍了。 Include/
  • 该工具将嵌入式Python解释器(Python DLL)注入应用程序并执行脚本。 而且,这还提供了一个方便的挂钩库,供您在脚本中使用。 该工具使用mayhem python_injector作为注入器。 参见 克隆此存储库时,还必须克隆子...
  • PyVenvManage是一个用于管理Pycharm Projects的Python解释器的插件。 一个普遍的问题是,对于不同版本的语言,Python项目可能在不同的虚拟环境中具有多个解释器。 使用tox可以轻松管理这些venv,但是在Pycharm中...
  • python-python解释器

    千次阅读 2020-01-25 15:32:21
    就是相当Linux下直接运行脚本的命令,这里需要注意的是,python解释器会读取命令行参数,转化为字符串列表存入sys模块的argv变量中。argv是一个列表,列表的第一项默认为空字符串,如果使用-c或者-m,那么argv[0]...
  • 在Haskell中实现的玩具Python 3解释器。 介绍 我想学习Haskell,并且想要一个大项目,所以我决定编写一个Python 3解释器。 结果是极富教育意义的,并且很容易成为我从事过的最酷的项目。 因为它以幼稚的方式实现,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 44,323
精华内容 17,729
关键字:

python解释器源码

python 订阅