首页 > 编程语言 >python代码混淆与编译

python代码混淆与编译

时间:2024-08-08 16:17:37浏览次数:7  
标签:混淆 python setup py add 编译 ext

python代码混淆、编译与打包

考虑到生产环境部署, 而python作为解释性语言, 对源码没有任何保护。 此文记录探索如何保护源码的过程。

代码混淆

代码混淆基本上就是把代码编译为字节码。

工具有两种:

  1. py_compile
  2. pyarmor

py_compile示例:

py_compile.compile(src_pyfile, dst_pyfile, dfile=os.path.relpath(dst_pyfile, target_directory), optimize=2)

给定一个输入文件, 及输出路径即可进行混淆。 当然还可以设置其优化级别。其中dfile用于报错时展示的名称。

而不是内置库, 需要额外安装:

pip install pyarmor

其混淆形式为:

pyarmor obfuscate test.py,

结果将会在dist目录中生成。

对当前目录进行递归执行:

pyarmor obfuscate --recursive test.py,

打包为exe

首先安装pyinstaller:

pip install pyinstaller

命令选项解释

  • -F, –onefile 打包单个文件, 适用于只有一个py脚本的情况
  • -D, –onedir 打包多个文件, 适用于多层目录的项目
  • --uac-admin 编译出来的exe需要管理员权限执行
  • -w 程序为GUI, 仅仅适用Windows
  • -c 程序为CUI, 仅仅适用Windows

如果你只需打包一个脚本, 如app.py, 执行如下命令:

pyinstaller -F app.py

如果你的项目有多个package, 多层目录。 其入口脚本为app.py。则执行如下命令:

pyinstaller -D app.py

编译为运行库

pyd即是python动态模块, 英文为Python Dynamic Module。其本质上是共享库。 所以编译过程需要依赖特定的编译器Windows上为msvc, linux为gcc。

首先安装编译器, 安装哪个版本却决于你当前python版本。 通过如下代码查看python 对应的编译器版本:

import sys
print(sys.version)

可以看到类似msvc或gcc相关描述, 其中包括版本信息。

通过cython可以将.py文件编译为.pyd。首先安装:

pip install cython

编写python脚本:

# add.py

def add(lhs, rhs):
    return lhs + rhs

编写setup.py脚本:

from setuptools import setup
from Cython.Build import cythonize

setup(
    name='addapp',
    ext_modules=cythonize("add.py")
)

执行编译脚本:

python setup.py build_ext --inplace

会在当前目录下生成add.c以及add.cp310-win_amd64.pyd。 其pyd命名是自动根据python版本与操作系统生成。

在其他目录中将pyd拷贝过去, 然后新建test.py测试脚本:

from add import add

print(add(1, 2))

最终输出结果为3

或者不编写setup.py, 对单个文件进行编译。 可通cython的命令行工具cythonize:

cythonize -i add.py

也会在目录下生成相同的文件。

编译多个文件:

setup(
    name='addapp',
    ext_modules=cythonize("*.py"),
)

如果要针对不同的模块设置不同的编译选项, 其目录结构如下:

│  setup.py
└─utils
        add.py
        subtract.py

其setup需要这样写:

from setuptools import setup
from Cython.Build import cythonize
from setuptools import Extension, setup


extensions = [
    Extension("utils.add", ["utils/add.py"]),
    Extension("utils.subtract", ["utils/subtract.py"]),
    # Extension("utils.__init__", ["utils/__init__.py"]),
]

setup(
    name='addapp',
    ext_modules=cythonize(extensions),
)

注意其中Extension的第一个参数其路径必须与文件结构一致。 第二个参数列表中放置当前py文件即可,一个python模块对应一个py文件。 如果将utils本身作为package, 可以取消对__init__.py的注释。

如何编译到其他目录?

因为上述编译命令都带了选项--inplace顾名思义即编译到当前目录。 可以通过编译选项--build-lib指定目录。

自定义编译流程

通过设置cmdclass进行实现:

from distutils.command.clean import clean
from Cython.Distutils import build_ext

class MyBuildExtCommand(build_ext):
  
    def run(self):
        super().run()
        clean_command = clean(self.distribution)
        clean_command.all = True
        clean_command.finalize_options()
        clean_command.run()

setup(
    name='addapp',
    ext_modules=cythonize(extensions),
    cmdclass={"build_ext": MyBuildExtCommand}
)

实际编译时执行的命令为基类build_ext的run方法。代码中通过super().run()进行调用。 调用完后执行clean, 此清理仅仅是清理build目录, 主要是编译过程生成的中间文件。 而在目录中生成的.c文件并未清理。 此时需要手动清理:

def clear(srcdir, exts=(".c")):

    for r,d,fs in os.walk(srcdir):
        for f in fs:
            _,ext = os.path.splitext(f)
            if not ext in exts:
                continue
            
            os.remove(os.path.join(r, f))

class MyBuildExtCommand(build_ext):
  
    def run(self):
        # ...
        # ...
        clear(".")

不通过命令行工具执行, 直接执行setup.py?

秩序添加参数script_args即可:

setup(
    name='addapp',
    ext_modules=cythonize(extensions),
    cmdclass={"build_ext": MyBuildExtCommand},
    script_args=['build_ext', '--build-lib', "dist"]
)

此时直接python setup.py即可。 可将setup.py改名为compile.py。

将打包后的pyd通过pyinstaller打包成exe?

可以打包成exe, 但是pyinstaller无法获取pyd的依赖包。 只有.py文件,pyinstaller才能获取其依赖, 并打包依赖。

结论

打包为动态链接库其保密程度最高, pyinstaller简单打包及代码混淆, 都非常容易反编译, 且其还原度较高。

参考

  1. py_compile --- 编译 Python 源文件
  2. Bundling to One Folder
  3. Cython Users Guide

标签:混淆,python,setup,py,add,编译,ext
From: https://www.cnblogs.com/quenwaz/p/18349154

相关文章

  • 在python中将二维数组转换为彩色图像
    我有像这样的2d整数列表:list1=[[1,30,50],[21,45,9],[97,321,100]]下一步我要将其转换为numpy数组:myarr=np.asarray(list1)下一步我将使用PIL将其转换为图像,如下所示:img=Image.fromarray(myarr,"I")img.save("my.png")问题是我不想要灰......
  • Python 汉字区位码、字符串 相互转换
    Python汉字区位码、字符串相互转换区位码简介GB2312所有字符放在一张94x94的矩阵中,矩阵中的每个位置对应一个字符(有的位置是空的,没有字符)。区位码为十进制四位数,前后两位分别代表该字符在矩阵中的行、列坐标(均从1算起),如4528“图”字,为45行、28列上的字符。能通过对区位码进......
  • python3如何使用‘pexpect’自动与串口交互?
    如何在Windows中使用python'pexpect'自动与串口(例如:COM3)交互并在开机时通过串口登录嵌入式开发板时输入用户密码?ser=serial.Serial(port=serial_port,baudrate=baudrate,bytesize=bytesize,parity=parity,stopbits=stopbits,timeout=timeout)channel=pexpect.fdpexp......
  • Python 熊猫迭代
    代码:#Importcarsdataimportpandasaspdcars=pd.read_csv('cars.csv',index_col=0)#Adaptforloopforlab,rowincars.iterrows():print(f"{[lab]}:{row['cars_per_cap']}")输出:['US']:809['A......
  • 【第九节】python中xml解析和json编解码
    目录一、PythonXML解析1.1什么是XML1.2Python对XML的解析方法1.3SAX解析xml1.4xml.dom解析xml1.6ElementTree解析XML二、Python编解码json2.1什么是json2.2使用json库2.3使用第三方库Demjson一、PythonXML解析1.1什么是XML        XML,......
  • 从 python 设置运算符符号到方法名称的映射是什么?它们与文档不匹配
    我创建了自己的Customset类,它实现了python集合的几乎所有方法。当我使用此自定义集的实例时,许多集合运算符都会失败。它们会失败,并显示类似以下内容的内容:TypeError:unsupportedoperandtype(s)for-:'Customset'andCustomset'orTypeError:'<='......
  • 21.python函数(return)
    return一、return语句1、return是指定一个返回值2、在python中创建一个函数,可以用return语句指定返回的的值,这个返回值可以是任意的类型3、return语句在同一个函数中可以出现多次,但是只有有一个得到执行,就会直接结束函数的执行。return后面的语句不执行了4、return的格式re......
  • 【Python代码】如何根据出生日期计算出年龄?
    哈喽,大家好,木易巷来啦!假设我们有一个包含出生日期的Excel文件,需要计算每个人的年龄,你会怎么做呢?具体情况如下图:今天木易巷分享通过Python及其强大的pandas库,来实现从Excel文件中读取日期数据,计算年龄,并将结果存储回Excel文件中的过程。话不多说,开干!▍1、环境准备在开......
  • python 无法解密重音字符(如 è),我该怎么办?
    我正在解决这个ctf,您必须使用PyCryptodome模块通过DES加密消息。消息是“Launghezzadiquestafrasenonèdivisibileper8”(意大利语)。我无法将此消息转换为字节,因为重音字符不包含在utf-8中(我认为这就是解释)。有人可以帮我加密消息吗?fromCrypto.Util.Paddingimp......
  • Python笔记
    Python1变量1、什么是变量变量是关联一个对象的标识符变量可以绑定一个对象,并可以通过变量名使用这个对象2、变量的命名方法:以字母或下划线开头,后面跟字母、下划线或者数字3、python中的关键字不能用作变量名4、变量无类型,对象有类型5、在交互模式下查看当前作用域内的......