使用PyInstaller打包含Capstone的Python脚本为.exe时动态库加载失败求助
解决PyInstaller打包Capstone依赖脚本的动态库加载错误
问题背景
我编写了一个可提取任意类型文件字节码并进行反汇编的Python脚本,依赖Capstone库。使用PyInstaller打包为单文件.exe后,运行时提示无法加载动态库,手动添加dll路径重新打包仍未解决问题。
报错信息
Traceback (most recent call last):
File "Extract.py", line 6, in
File "PyInstaller\loader\pyimode2_importers.py", line 419, in exec_module
File "capstone_init_.py", line 428, in
ImportError: ERROR: fail to load the dynamic library.
[4748] Failed to execute script 'Extract' due to unhandled exception!
已尝试的打包命令
- 初始打包命令:
pyinstaller --onefile Ectract.py
- 添加dll路径后的命令:
pyinstaller --onefile --add-binary "C:/Users/admin/AppData/Local/Programs/Python/Python311/Lib/site-packages/capstone/lib/capstone.dll;." Extract.py
脚本代码
import os import random import string import time import shutil from capstone import* def extract_hex_from_file(file_path, output_folder, bytes_per_line): with open(file_path, 'rb') as f: random_name = generate_random_name() + '.bytes' output_file = os.path.join(output_folder, random_name) with open(output_file, 'w') as output: address = 0x00401000 # Start address while True: chunk = f.read(bytes_per_line) if not chunk: break hex_bytes = ' '.join(f"{byte:02X}" for byte in chunk) output.write(f"{address:08X} {hex_bytes}\n") address += bytes_per_line return random_name[:-6] def generate_random_name(length=20): letters_and_digits = string.ascii_letters + string.digits return ''.join(random.choice(letters_and_digits) for _ in range(length)) def check_disk_space(output_folder): free_space = shutil.disk_usage(output_folder).free return free_space def process_files(file_path, output_folder, bytes_per_line, arch): try: generated_name = extract_hex_from_file(file_path, output_folder, bytes_per_line) if check_disk_space(output_folder) < os.path.getsize(file_path): print(f"Not enough space available to process file: {file_path}. Skipping.") return None else: disassemble_file(os.path.join(output_folder, generated_name + '.bytes'), output_folder, arch) return generated_name except PermissionError: print(f"{file_path}. .") return None except Exception as e: print(f"Error processing file: {file_path}. Error: {str(e)}") return None def extract_hex_from_folder(folder_path, output_folder, bytes_per_line=16, arch='x86'): if not os.path.exists(output_folder): os.makedirs(output_folder) file_paths = [] for root, _, files in os.walk(folder_path): for file_name in files: file_paths.append(os.path.join(root, file_name)) start_time = time.time() for file_path in file_paths: process_files(file_path, output_folder, bytes_per_line, arch) end_time = time.time() total_time = end_time - start_time print(f"Total execution time: {total_time} seconds") def disassemble_file(filename, output_folder, arch): arch_map = { 'x86': CS_ARCH_X86, 'x86-64': CS_ARCH_X86, 'arm': CS_ARCH_ARM, } md = Cs(arch_map[arch], CS_MODE_64 if arch == 'x86-64' else CS_MODE_ARM) with open(filename, 'rb') as f: code = f.read() disassembly_output = "" for instr in md.disasm(code, 0x1000): disassembly_output += f"0x{instr.address:x}:\t{instr.mnemonic}\t{instr.op_str}\n" section_rva = 0x1000 section_size = 0x100 disassembly_output += "\nDisassembly of specified code section:\n" for instr in md.disasm(code[section_rva:section_rva+section_size], section_rva): disassembly_output += f"0x{instr.address:x}:\t{instr.mnemonic}\t{instr.op_str}\n" base_name, _ = os.path.splitext(os.path.basename(filename)) output_filename = os.path.join(output_folder, base_name + '.asm') with open(output_filename, 'w') as output_file: output_file.write(disassembly_output) if __name__ == '__main__': folder_path = '/path/' output_folder = '/path/' bytes_per_line = 16 arch = 'x86' extract_hex_from_folder(folder_path, output_folder, bytes_per_line, arch)
解决方案
方法1:修正打包命令的dll路径结构
Capstone库会从capstone/lib目录下加载dll,之前的命令把dll放到根目录不符合其预期路径。修改打包命令,将dll放到对应的子目录:
pyinstaller --onefile --add-binary "C:/Users/admin/AppData/Local/Programs/Python/Python311/Lib/site-packages/capstone/lib/capstone.dll;capstone/lib" Extract.py
方法2:使用.spec文件手动配置
- 生成基础.spec文件:
pyinstaller --onefile Extract.py
- 打开生成的
Extract.spec,找到binaries参数,添加dll的路径配置:
binaries=[('C:/Users/admin/AppData/Local/Programs/Python/Python311/Lib/site-packages/capstone/lib/capstone.dll', 'capstone/lib')],
- 基于修改后的spec文件重新打包:
pyinstaller Extract.spec
方法3:在脚本中手动指定dll路径
在脚本开头添加代码,适配PyInstaller打包后的环境,手动指向dll的正确位置:
import os import sys from capstone import * # 适配打包后的dll路径 if getattr(sys, 'frozen', False): # 打包后临时目录中的dll路径 capstone_dll_path = os.path.join(sys._MEIPASS, 'capstone', 'lib', 'capstone.dll') # 手动加载dll import ctypes ctypes.CDLL(capstone_dll_path)
添加后再使用方法1或方法2的命令重新打包即可。
内容的提问来源于stack exchange,提问作者MayurD
相关产品推荐
相关产品推荐

