@[TOC]python程序加密

加密方法对比

加密手段

优点

缺点

发行 .pyc 文件

简单方便,提高了一点源码门槛。平台兼容性好,.py 能在哪里运行,.pyc 就能在哪里运行

解释器兼容性差,.pyc 只能在特定版本的解释器上运行。有现成的反编译工具成本低

代码混淆

简单方便,提高了一点源码门槛。兼容性好,只要源码逻辑能做到兼容,混淆代码亦能

只能对单个文件混淆,无法做到多个互相有联系的源码文件的联动混淆。代码结构未发生变化,也能获取字节码

使用 py2exe

能够直接打包成 exe,方便分发和执行。门槛比 .pyc 更高一些

兼容性差,只能运行在 Windows 系统上。生成的可执行文件内的布局是明确、公开的,可以找到源码对应的 .pyc 文件,进而反编译出源码

使用 Cpython

生成的二进制 .so 或 .pyd 文件难以。同时带来了性能提升

兼容性稍差,对于不同版本的操作系统,可能需要重新编译。虽然支持大多数 Python 代码

Cpython 加密流程如下:

基础环境准备:

yum install gcc -y 
pip install Cython

使用 Cython 进行开发的步骤

构建一个测试项目test
1)编写文件hellotest.py cat …/test/hello/hellotest.py
class Hello:
  def test(self):
    return 'Hello World'

main.py 文件cat …/test/main.py

from hello.hellotest import Hello
h = Hello()
str = h.test()
print (t)
2)编写setup.py
from distutils.core import setup
from Cython.Build import cythonize


setup(name='Hello World app',
     ext_modules=cythonize('hello/hellotest.py')) #注意这里推荐使用相对路径,编译出的so文件在引用其他模块时可能会出现路径问题
3)编译为 .c 在进一步编译为 .so 或 .pyd

python setup.py build_ext --生成.so 文件
python setup.py build_ext --inplace

加密前目录结构
test/
├── hello
│   └── hellotest.py
├── main.py
└── setup.py
加密后目录结构

python setup.py build_ext

test/
├── build
│   ├── lib.linux-aarch64-3.7
│   │   └── hellotest.cpython-37m-aarch64-linux-gnu.so   
│   └── temp.linux-aarch64-3.7
│       └── hello
│           └── hellotest.o
├── hello
│   ├── hellotest.c
│   └── hellotest.py
├── main.py
└── setup.py
添加参数后加密后目录结构

python setup.py build_ext --inplace

test/
├── build
│   └── temp.linux-aarch64-3.7
│       └── hello
│           └── hellotest.o
├── hello
│   ├── hellotest.c
│   └── hellotest.py
├── hellotest.cpython-37m-aarch64-linux-gnu.so
├── main.py
└── setup.py
加密后期望结果:
test/
├── hello
│   └── hellotest.cpython-37m-aarch64-linux-gnu.so
├── main.py
└── setup.py
加密后测试结果
python main.py
Hello World

加密步骤: 加密 *.py 文件 ,so, 替换源文件,删除加密过程中生成的相关依赖
方法:蛮力法,逐个遍历
特殊要求: 排除文件,排除目录

1.获取当前目录所有py 文件相对路径
2.切分为 路径 文件
3.切换至 路径 执行加密 操作
4.删除生成的临时文件

梳理工具脚本如下:

encrypt.py

import os
import sys
from distutils.core import setup
from Cython.Build import cythonize






# 不需要编译的文件
global  exclude_list
exclude_list=['encrypt.py','main.py']
global pylist
pylist = []


# 功能,遍历搜索目录下所有 py 文件并返回列表
def search(basedir, target, exclude):
    # 主目录下的所有文件,文件夹集合
    items = os.listdir(basedir)
    for item in items:
        # 拼接
        path = os.path.join(basedir, item)
        # path 是目录,继续搜索
        if os.path.isdir(path):
            #print('[-]', path)
            search(path, target, exclude)
        # 不是目录,取最后一列值,判断是否以 target 结尾,并且为非排除文件
        elif path.split('/')[-1].endswith(target) and path.split('/')[-1] not in exclude:
            #print('[+]', path)
            pylist.append(str(path))
        else:
             pass
             # print('[!]',path)
    #print (pylist)
    return pylist


# 根据输入构建 setup.py 文件
def newSetupFile(exclude_list,filename):
    str = '''
import os
import sys
from distutils.core import setup
from Cython.Build import cythonize


exclude_list = {} # 不需要编译的py文件
if __name__ == '__main__':
    setup(ext_modules = cythonize('{}',exclude=exclude_list))


    '''.format(exclude_list,filename)
    file = open('setup.py',"w")
    file.write(str)
    file.close


# 清理setup 构建生成临时文件
def cleanSetupFile(filepath, filename):
    os.chdir(filepath)
    print('{}/{}'.format(filepath,filename))
    os.system("rm -rf build")
    os.remove(filename.split('.')[0]+'.c')
    os.remove('setup.py')
    os.remove(filename)


def main():
    # list = search('./', '.py', exclude_list)


    list = search(os.getcwd(), '.py', exclude_list)
    for filepath in list:
        os.path.split(filepath)
        filename=os.path.split(filepath)[1]
        filepath=os.path.split(filepath)[0]
        # 切换目录
        os.chdir(filepath)
        # 写入setup.py 文件
        newSetupFile(exclude_list,filename)
        os.system("python setup.py build_ext --inplace")
        cleanSetupFile(filepath, filename)


if __name__ == '__main__':
    print('\033[1;31;40m 加密py文件为{} 路径下所有py文件,如有排除请更新exclude_list 列表 \033[0m'.format(os.getcwd()))


    tag = input('请确认: \033[1;31;40m 加密后会删除源文件,请确认执行前是否备份: yes/no \033[0m')
    if tag == 'yes':
        main()
    else:
        print('\033[1;31;40m 请备份后执行加密操作 \033[0m')