编程与调试 -- PyInstaller & Extractor

PyInstaller 是开源的,可以对其深度订制(改进 Bootloader)。 PyInstaller Manual

遇到的最大问题就是启动慢的问题。 每次都会生成临时文件夹并释放文件,有点烦人。如果崩溃了,临时文件就残留了。本文尝试修改启动器,改掉这个问题。 This can be done using the --runtime-tmpdir option.

我的调整:hawkhai / pyinstaller (还未完成)

升级 pyinstaller

pip install --upgrade pyinstaller

运行时检测

#!/usr/bin/env python3
import sys, os
frozen = 'not'
if getattr(sys, 'frozen', False):
    # we are running in a bundle
    frozen = 'ever so'
    bundle_dir = sys._MEIPASS
else:
    # we are running in a normal Python environment
    bundle_dir = os.path.dirname(os.path.abspath(__file__))
print( 'we are', frozen, 'frozen' )
print( 'bundle dir is', bundle_dir )
print( 'sys.argv[0] is', sys.argv[0] )
print( 'sys.executable is', sys.executable )
print( 'os.getcwd is', os.getcwd() )

Bootloader

这里 描述详细的启动过程。


Structure of the ZlibArchive

Structure of the CArchive

Structure of the Self Extracting Executable

双进程实现(除了 Windows one-folder 模式)。

  1. 主进程:bootloader 启动,在准备工作。
    • 如果是 one-file 模式,释放文件到:temppath/_MEIxxxxxx
    • 修改大量环境变量。
    • 设置 handle signals,便于两个进程通信。
    • 运行子进程。
    • 等待子进程结束。
    • 如果是 one-file 模式,删除清理 temppath/_MEIxxxxxx
  2. 子进程。
    • 如果是 Windows,设置 激活上下文
    • 加载 Python 动态库。
    • 初始化 Python 解释器:set sys.path, sys.prefix, sys.executable.
    • Run python code.

编译

python ./waf all
python ./waf all --target-arch=32bit

改造计划

不能对现有流程造成影响,保持将来的升级能力。 把附加配置通过后期资源打到最终 pe 文件里面。 然后 Bootloader 读取配置。

  1. 指定释放临时目录。 pyi_launch_need_to_extract_binaries
    • swprintf(prefix, 16, L"_MEI%d", getpid()); – 这里一个固定的版本号接上去。
    • pyi_get_temp_path pyi_create_temp_path
  2. 如果发现临时目录文件存在了,就跳过。 pyi_launch_extract_binaries
    • 怎么判断文件完整? pyi_arch_extract2fs(ARCHIVE_STATUS *status, TOC *ptoc)
  3. 进程结束,不要清理文件夹(就需要每个版本指定的临时文件夹唯一)。 pyi_remove_temp_path

加密与解密

from

python pyinstxtractor.py xx.exe
uncompyle6

set CL=-FI"Full-Path\stdint.h" (use real value for Full-Path for the environment)
pip install pycrypto
pyinstaller.exe -F --key 123456 xxx.py

PyInstaller Extractor

PyInstaller Extractor

conda or pyenv

$ conda create -n my-py3.6-environment python=3.6
$ conda activate my-py3.6-environment
pipenv install

[[source]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
verify_ssl = true
name = "pypi"

# 把 pyinstaller 安装到开发环境中
pipenv install pyinstaller --dev

# 进入虚拟环境
pipenv shell

import os
import sys
os.chdir(os.path.dirname(__file__))
sys.path.append("..")
import settings

# 直接运行 js 代码
import js2py

context = js2py.EvalJs()
with open("./ids-encrypt.js") as f:
    js_content = f.read()

def encryptAES(data, salt):
    # 执行整段 JS 代码
    context.execute(js_content)
    result = context.encryptAES(data, salt)
    return result

A cross-version Python bytecode decompiler

# 通过清华镜像源,下载快
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple uncompyle
uncompyle test.pyc > test.py

代码加密

https://pyob.oxyry.com/ https://blog.csdn.net/ir0nf1st/article/details/61650984

Pyinstaller 加密打包应用的示例代码

抓取真实 api 后

def obfuscation(py_file, save_path):
    print("读取文件:", py_file)
    with open(py_file, "r", encoding="utf-8") as f:
        py_content = f.read()

    print("进行混淆中 ...")
    url = "https://pyob.oxyry.com/obfuscate"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
        "Referer": "http://pyob.oxyry.com/",
        "content-type": "application/json",
        "cookie": "_ga=GA1.2.1306886713.1588752647; _gid=GA1.2.46944674.1588899118"
    }
    data = json.dumps({
        "append_source": "false",
        "preserve": "",
        "remove_docstrings": "true",
        "rename_default_parameters": "false",
        "rename_nondefault_parameters": "true",
        "source": py_content
    })
    result = json.loads(requests.post(url, data=data, headers=headers).text)["dest"]
    result = "# cython: language_level=3\n" + result
    print("混淆成功 ...")

    with open(save_path, "w", encoding="utf-8") as f:
        f.write(result)
    print("混淆文件已写入 {}\n".format(save_path))

if __name__ == '__main__':
    obfuscation("my.py", "../ 混淆 /my.py")
    obfuscation("approach.py", "../ 混淆 /approach.py")

编译 pyd

build_pyd.py

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

setup(
    name='any words.....',
    ext_modules=cythonize(["my.py","approach.py" ])
)

执行打包

import json
import os
# 清理旧 pyd 文件
import uuid
import requests

def clearPyd():
    for file in os.listdir():
        if ".pyd" in file:
            print("删除 .pyd:", file)
            os.remove(file)
    print("***********************************************************************")

# 构建 pyd 文件
def buildPyd():
    os.system("python build_pyd.py build_ext --inplace")

# 重命名 pyd 文件
def renamePyd():
    print("***********************************************************************")
    for file in os.listdir():
        if ".pyd" in file:
            print("重新命名 pyd:", file)
            os.rename(file, file[:file.find(".")] + ".pyd")
    for file in os.listdir():
        if ".c" in file:
            print("删除 .c 文件:", file)
            os.remove(file)
    print("***********************************************************************")

# 执行打包
def pyinstaller(key, ico):
    os.system("pyinstaller -F --key {} -i {} main.py".format(key, ico))

# 删除 bulid 和 spec 文件
def clearBuildAndSpec():
    import shutil
    shutil.rmtree('build')
    print("删除 bulid 文件夹")
    os.remove("main.spec")
    print("删除 spec 文件")

if __name__ == '__main__':
    clearPyd() # 清理旧 pyd 文件
    buildPyd() # 构建 pyd 文件
    renamePyd() # 重命名 pyd 文件
    pyinstaller(uuid.uuid4()[0:16], "1.ico") # 执行打包
    clearPyd() # 清理 pyd 文件
    clearBuildAndSpec() # 删除 bulid 和 spec 文件

Windows cmd: piping python 3.5 py file results works but pyinstaller exe's leads to UnicodeEncodeError

sys.stdout.buffer.write('\u5000'.encode('utf8'))

https://stackoverflow.com/questions/44780476/windows-cmd-piping-python-3-5-py-file-results-works-but-pyinstaller-exes-leads https://github.com/chriskiehl/Gooey/issues/520 https://blog.csdn.net/hello_crayon/article/details/80940390

# determine if application is a script file or frozen exe
if getattr(sys, 'frozen', False):
    PATH_CUR = os.path.dirname(sys.executable)
elif __file__:
    PATH_CUR = os.path.dirname(__file__)
sys.stdout.reconfigure(encoding='utf-8')
sys.stdin.reconfigure(encoding='utf-8')

import codecs
if sys.stdout.encoding != 'UTF-8':
    sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict')
if sys.stderr.encoding != 'UTF-8':
    sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict')

windows pipe

https://www.cnblogs.com/MMLoveMeMM/articles/3811166.html

其它问题

貌似不同 python 版本打包出来的文件不能混用一个文件夹。 https://juejin.cn/s/python%20failed%20to%20execute%20script%20pyi_rth_win32comgenpy loader2.exe 多余的文件要重命名。

simple_launcher

https://bitbucket.org/vinay.sajip/simple_launcher


参考资料快照
参考资料快照

本文短链接:
If you have any questions or feedback, please reach out .