├── .github └── workflows │ └── publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── pywxdump_mini ├── __init__.py └── simplify_wx_info.py ├── requirements.txt ├── setup.py └── tests ├── build_exe.py ├── icon.ico └── test.py /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | # 当master分支有push时,触发action 5 | push: 6 | tags: 7 | - 'v*' # 以 'v' 开头的标签触发工作流程 8 | 9 | jobs: 10 | publish: 11 | name: Publish Pypi and Create Release 12 | # 此作业在 Linux 上运行 13 | runs-on: windows-latest 14 | 15 | steps: 16 | - name: Checkout repository # 检出仓库 17 | uses: actions/checkout@v2 # 使用 GitHub 官方的 checkout action 18 | 19 | - name: Set git fetch depth # 设置 git fetch 深度 20 | run: | 21 | git fetch --prune --unshallow # 获取完整的 git 历史记录 22 | 23 | - name: Set up Python # 设置 Python 环境 24 | uses: actions/setup-python@v4 25 | with: 26 | python-version: '3.8' 27 | - run: | 28 | python -m pip install --upgrade pip 29 | python -m pip install --upgrade twine 30 | pip install --upgrade build 31 | pip install pyinstaller 32 | pip install -r requirements.txt 33 | 34 | - name: Build package 35 | run: | 36 | python tests/build_exe.py 37 | 38 | - name: Build Wheel 39 | run: | 40 | python -m build --wheel --sdist 41 | 42 | - name: Build Executable 43 | run: | 44 | pyinstaller --clean --distpath=dist dist/pywxdump.spec 45 | 46 | - name: test 47 | run: | 48 | ls -l dist 49 | ls -l "${{ github.workspace }}" 50 | 51 | - name: Publish package with Twine # 使用 Twine 发布到 PyPI 52 | run: | 53 | twine upload dist/*.whl dist/*.tar.gz 54 | env: 55 | TWINE_USERNAME: __token__ 56 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 57 | 58 | - name: Create Release 59 | id: create_release 60 | uses: softprops/action-gh-release@v1 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 63 | with: 64 | tag_name: ${{ github.ref }} 65 | release_name: Release ${{ github.ref.tag }} 66 | body: | 67 | [Auto Release] Update PyWxDump to ${{ github.ref }} 68 | draft: false 69 | prerelease: false 70 | files: | 71 | dist/*.exe 72 | dist/*.whl -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # 基于编辑器的 HTTP 客户端请求 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | # Zeppelin 忽略的文件 10 | /ZeppelinRemoteNotebooks/ 11 | /.idea/ 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 xaoyaoo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | PyWxDump is hosted at: https://github.com/xaoyaoo/PyWxDump 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #
PyWxDump
2 | 3 | [![Python](https://img.shields.io/badge/Python-3-blue.svg)](https://www.python.org/) 4 | [![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/xaoyaoo/pywxdump)](https://github.com/xaoyaoo/PyWxDump) 5 | [![GitHub all releases](https://img.shields.io/github/downloads/xaoyaoo/pywxdump/total)](https://github.com/xaoyaoo/PyWxDump) 6 | [![GitHub stars](https://img.shields.io/github/stars/xaoyaoo/PyWxDump.svg)](https://github.com/xaoyaoo/PyWxDump) 7 | [![GitHub forks](https://img.shields.io/github/forks/xaoyaoo/PyWxDump.svg)](https://github.com/xaoyaoo/PyWxDump/fork) 8 | [![GitHub issues](https://img.shields.io/github/issues/xaoyaoo/PyWxDump)](https://github.com/xaoyaoo/PyWxDump/issues) 9 | 10 | [![PyPI](https://img.shields.io/pypi/v/pywxdump)](https://pypi.org/project/pywxdump/) 11 | [![Wheel](https://img.shields.io/pypi/wheel/pywxdump)](https://pypi.org/project/pywxdump/) 12 | [![PyPI-Downloads](https://img.shields.io/pypi/dm/pywxdump)](https://pypistats.org/packages/pywxdump) 13 | [![GitHub license](https://img.shields.io/pypi/l/pywxdump)](https://github.com/xaoyaoo/PyWxDump/blob/master/LICENSE) 14 | [![Publish](https://github.com/xaoyaoo/PyWxDump/actions/workflows/publish.yml/badge.svg)](https://github.com/xaoyaoo/PyWxDump/actions/workflows/publish.yml) 15 | 16 | # 一、项目介绍 17 | 18 | ## 1. 项目简介 19 | 20 | [PyWxDump](https://github.com/xaoyaoo/pywxdump) 超级简化版本,仅支持key以及wxid以及filepath的导出,不支持其他功能。 21 | 22 | 主要面向安全人员 23 | 24 | # 二、使用说明 25 | 26 | ```shell 27 | pip install pywxdump-mini # 或者下载exe文件(7.5M),地址 https://github.com/xaoyaoo/pywxdumpmini/releases 28 | wxinfo # 查看微信信息 包含key wxid filepath 29 | ``` 30 | 31 | 参看 https://github.com/xaoyaoo/pywxdump 32 | 33 | # 三、免责声明(非常重要!!!!!!!) 34 | 35 | 本项目仅供学习交流使用,请勿用于非法用途,否则后果自负。 36 | 37 | 您应该在下载保存,编译使用本项目的24小时内,删除本项目的源代码和(编译出的)程序。 38 | 39 | 本项目仅允许在授权情况下对数据库进行备份,严禁用于非法目的,否则自行承担所有相关责任。 40 | 41 | 下载、保存、进一步浏览源代码或者下载安装、编译使用本程序,表示你同意本警告,并承诺遵守它; 42 | 43 | 请勿利用本项目的相关技术从事非法测试,如因此产生的一切不良后果与项目作者无关。 44 | 45 | # 四、许可证 46 | 47 | ```text 48 | MIT License 49 | 50 | Copyright (c) 2023 xaoyaoo 51 | 52 | Permission is hereby granted, free of charge, to any person obtaining a copy 53 | of this software and associated documentation files (the "Software"), to deal 54 | in the Software without restriction, including without limitation the rights 55 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 56 | copies of the Software, and to permit persons to whom the Software is 57 | furnished to do so, subject to the following conditions: 58 | 59 | The above copyright notice and this permission notice shall be included in all 60 | copies or substantial portions of the Software. 61 | 62 | PyWxDump is hosted at: https://github.com/xaoyaoo/PyWxDump 63 | 64 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 65 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 66 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 67 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 68 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 69 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 70 | SOFTWARE. 71 | ``` 72 | 73 | -------------------------------------------------------------------------------- /pywxdump_mini/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*-# 2 | # ------------------------------------------------------------------------------- 3 | # Name: __init__.py.py 4 | # Description: 5 | # Author: xaoyaoo 6 | # Date: 2023/10/14 7 | # ------------------------------------------------------------------------------- 8 | from .simplify_wx_info import read_info 9 | 10 | -------------------------------------------------------------------------------- /pywxdump_mini/simplify_wx_info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*-# 2 | # ------------------------------------------------------------------------------- 3 | # Name: simplify_wx_info.py 4 | # Description: 5 | # Author: xaoyaoo 6 | # Date: 2023/12/07 7 | # ------------------------------------------------------------------------------- 8 | import hmac 9 | import hashlib 10 | import ctypes 11 | import os 12 | import winreg 13 | import pymem 14 | import psutil 15 | import sys 16 | 17 | ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory 18 | void_p = ctypes.c_void_p 19 | 20 | 21 | # 获取exe文件的位数 22 | def get_exe_bit(file_path): 23 | """ 24 | 获取 PE 文件的位数: 32 位或 64 位 25 | :param file_path: PE 文件路径(可执行文件) 26 | :return: 如果遇到错误则返回 64 27 | """ 28 | try: 29 | with open(file_path, 'rb') as f: 30 | dos_header = f.read(2) 31 | if dos_header != b'MZ': 32 | print('get exe bit error: Invalid PE file') 33 | return 64 34 | # Seek to the offset of the PE signature 35 | f.seek(60) 36 | pe_offset_bytes = f.read(4) 37 | pe_offset = int.from_bytes(pe_offset_bytes, byteorder='little') 38 | 39 | # Seek to the Machine field in the PE header 40 | f.seek(pe_offset + 4) 41 | machine_bytes = f.read(2) 42 | machine = int.from_bytes(machine_bytes, byteorder='little') 43 | 44 | if machine == 0x14c: 45 | return 32 46 | elif machine == 0x8664: 47 | return 64 48 | else: 49 | print('get exe bit error: Unknown architecture: %s' % hex(machine)) 50 | return 64 51 | except IOError: 52 | print('get exe bit error: File not found or cannot be opened') 53 | return 64 54 | 55 | 56 | def pattern_scan_all(handle, pattern, *, return_multiple=False, find_num=100): 57 | next_region = 0 58 | found = [] 59 | user_space_limit = 0x7FFFFFFF0000 if sys.maxsize > 2 ** 32 else 0x7fff0000 60 | while next_region < user_space_limit: 61 | try: 62 | next_region, page_found = pymem.pattern.scan_pattern_page( 63 | handle, 64 | next_region, 65 | pattern, 66 | return_multiple=return_multiple 67 | ) 68 | except Exception as e: 69 | print(e) 70 | break 71 | if not return_multiple and page_found: 72 | return page_found 73 | if page_found: 74 | found += page_found 75 | if len(found) > find_num: 76 | break 77 | return found 78 | 79 | 80 | def get_info_wxid(h_process): 81 | find_num = 100 82 | addrs = pattern_scan_all(h_process, br'\\Msg\\FTSContact', return_multiple=True, find_num=find_num) 83 | wxids = [] 84 | for addr in addrs: 85 | array = ctypes.create_string_buffer(80) 86 | if ReadProcessMemory(h_process, void_p(addr - 30), array, 80, 0) == 0: return "None" 87 | array = bytes(array) # .split(b"\\")[0] 88 | array = array.split(b"\\Msg")[0] 89 | array = array.split(b"\\")[-1] 90 | wxids.append(array.decode('utf-8', errors='ignore')) 91 | wxid = max(wxids, key=wxids.count) if wxids else "None" 92 | return wxid 93 | 94 | 95 | def get_info_filePath_base_wxid(h_process, wxid=""): 96 | find_num = 10 97 | addrs = pattern_scan_all(h_process, wxid.encode() + br'\\Msg\\FTSContact', return_multiple=True, find_num=find_num) 98 | filePath = [] 99 | for addr in addrs: 100 | win_addr_len = 260 101 | array = ctypes.create_string_buffer(win_addr_len) 102 | if ReadProcessMemory(h_process, void_p(addr - win_addr_len + 50), array, win_addr_len, 0) == 0: return "None" 103 | array = bytes(array).split(b"\\Msg")[0] 104 | array = array.split(b"\00")[-1] 105 | filePath.append(array.decode('utf-8', errors='ignore')) 106 | filePath = max(filePath, key=filePath.count) if filePath else "None" 107 | return filePath 108 | 109 | 110 | def get_info_filePath(wxid="all"): 111 | if not wxid: 112 | return "None" 113 | w_dir = "MyDocument:" 114 | is_w_dir = False 115 | 116 | try: 117 | key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Tencent\WeChat", 0, winreg.KEY_READ) 118 | value, _ = winreg.QueryValueEx(key, "FileSavePath") 119 | winreg.CloseKey(key) 120 | w_dir = value 121 | is_w_dir = True 122 | except Exception as e: 123 | w_dir = "MyDocument:" 124 | 125 | if not is_w_dir: 126 | try: 127 | user_profile = os.environ.get("USERPROFILE") 128 | path_3ebffe94 = os.path.join(user_profile, "AppData", "Roaming", "Tencent", "WeChat", "All Users", "config", 129 | "3ebffe94.ini") 130 | with open(path_3ebffe94, "r", encoding="utf-8") as f: 131 | w_dir = f.read() 132 | is_w_dir = True 133 | except Exception as e: 134 | w_dir = "MyDocument:" 135 | 136 | if w_dir == "MyDocument:": 137 | try: 138 | # 打开注册表路径 139 | key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 140 | r"Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders") 141 | documents_path = winreg.QueryValueEx(key, "Personal")[0] # 读取文档实际目录路径 142 | winreg.CloseKey(key) # 关闭注册表 143 | documents_paths = os.path.split(documents_path) 144 | if "%" in documents_paths[0]: 145 | w_dir = os.environ.get(documents_paths[0].replace("%", "")) 146 | w_dir = os.path.join(w_dir, os.path.join(*documents_paths[1:])) 147 | # print(1, w_dir) 148 | else: 149 | w_dir = documents_path 150 | except Exception as e: 151 | profile = os.environ.get("USERPROFILE") 152 | w_dir = os.path.join(profile, "Documents") 153 | 154 | msg_dir = os.path.join(w_dir, "WeChat Files") 155 | 156 | if wxid == "all" and os.path.exists(msg_dir): 157 | return msg_dir 158 | 159 | filePath = os.path.join(msg_dir, wxid) 160 | return filePath if os.path.exists(filePath) else "None" 161 | 162 | 163 | def get_key(pid, db_path, addr_len): 164 | def read_key_bytes(h_process, address, address_len=8): 165 | array = ctypes.create_string_buffer(address_len) 166 | if ReadProcessMemory(h_process, void_p(address), array, address_len, 0) == 0: return "None" 167 | address = int.from_bytes(array, byteorder='little') # 逆序转换为int地址(key地址) 168 | key = ctypes.create_string_buffer(32) 169 | if ReadProcessMemory(h_process, void_p(address), key, 32, 0) == 0: return "None" 170 | key_bytes = bytes(key) 171 | return key_bytes 172 | 173 | def verify_key(key, wx_db_path): 174 | KEY_SIZE = 32 175 | DEFAULT_PAGESIZE = 4096 176 | DEFAULT_ITER = 64000 177 | with open(wx_db_path, "rb") as file: 178 | blist = file.read(5000) 179 | salt = blist[:16] 180 | byteKey = hashlib.pbkdf2_hmac("sha1", key, salt, DEFAULT_ITER, KEY_SIZE) 181 | first = blist[16:DEFAULT_PAGESIZE] 182 | 183 | mac_salt = bytes([(salt[i] ^ 58) for i in range(16)]) 184 | mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE) 185 | hash_mac = hmac.new(mac_key, first[:-32], hashlib.sha1) 186 | hash_mac.update(b'\x01\x00\x00\x00') 187 | 188 | if hash_mac.digest() != first[-32:-12]: 189 | return False 190 | return True 191 | 192 | phone_type1 = "iphone\x00" 193 | phone_type2 = "android\x00" 194 | phone_type3 = "ipad\x00" 195 | 196 | pm = pymem.Pymem(pid) 197 | module_name = "WeChatWin.dll" 198 | 199 | MicroMsg_path = os.path.join(db_path, "MSG", "MicroMsg.db") 200 | 201 | type1_addrs = pm.pattern_scan_module(phone_type1.encode(), module_name, return_multiple=True) 202 | type2_addrs = pm.pattern_scan_module(phone_type2.encode(), module_name, return_multiple=True) 203 | type3_addrs = pm.pattern_scan_module(phone_type3.encode(), module_name, return_multiple=True) 204 | 205 | # print(type1_addrs, type2_addrs, type3_addrs) 206 | 207 | type_addrs = [] 208 | if len(type1_addrs) >= 2: type_addrs += type1_addrs 209 | if len(type2_addrs) >= 2: type_addrs += type2_addrs 210 | if len(type3_addrs) >= 2: type_addrs += type3_addrs 211 | if len(type_addrs) == 0: return "None" 212 | 213 | type_addrs.sort() # 从小到大排序 214 | 215 | for i in type_addrs[::-1]: 216 | for j in range(i, i - 2000, -addr_len): 217 | key_bytes = read_key_bytes(pm.process_handle, j, addr_len) 218 | if key_bytes == "None": 219 | continue 220 | if verify_key(key_bytes, MicroMsg_path): 221 | return key_bytes.hex() 222 | return "None" 223 | 224 | 225 | # 读取微信信息(account,mobile,name,mail,wxid,key) 226 | def read_info(is_logging=False, is_save=False): 227 | wechat_process = [] 228 | result = [] 229 | for process in psutil.process_iter(['name', 'exe', 'pid', 'cmdline']): 230 | if process.name() == 'WeChat.exe': 231 | wechat_process.append(process) 232 | 233 | if len(wechat_process) == 0: 234 | error = "[-] WeChat No Run" 235 | if is_logging: print(error) 236 | return error 237 | 238 | for process in wechat_process: 239 | tmp_rd = {} 240 | 241 | tmp_rd['pid'] = process.pid 242 | # tmp_rd['version'] = Dispatch("Scripting.FileSystemObject").GetFileVersion(process.exe()) 243 | 244 | Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, process.pid) 245 | 246 | addrLen = get_exe_bit(process.exe()) // 8 247 | 248 | tmp_rd['wxid'] = get_info_wxid(Handle) 249 | tmp_rd['filePath'] = get_info_filePath_base_wxid(Handle, tmp_rd['wxid']) if tmp_rd['wxid'] != "None" else "None" 250 | tmp_rd['filePath'] = get_info_filePath(tmp_rd['wxid']) if tmp_rd['wxid'] != "None" and tmp_rd[ 251 | 'filePath'] == "None" else tmp_rd['filePath'] 252 | tmp_rd['key'] = get_key(tmp_rd['pid'], tmp_rd['filePath'], addrLen) if tmp_rd['filePath'] != "None" else "None" 253 | result.append(tmp_rd) 254 | 255 | if is_logging: 256 | print("=" * 32) 257 | if isinstance(result, str): # 输出报错 258 | print(result) 259 | else: # 输出结果 260 | for i, rlt in enumerate(result): 261 | for k, v in rlt.items(): 262 | print(f"[+] {k:>8}: {v}") 263 | print(end="-" * 32 + "\n" if i != len(result) - 1 else "") 264 | print("=" * 32) 265 | 266 | if is_save: 267 | with open("wx_info.txt", "w", encoding="utf-8") as f: 268 | f.write(str(result)) 269 | return result 270 | 271 | 272 | if __name__ == '__main__': 273 | a = read_info(is_logging=True, is_save=True) 274 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | wheel 2 | pymem 3 | psutil -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | with open("README.md", "r", encoding="utf-8") as fh: 4 | long_description = fh.read() 5 | 6 | version = "2.4.2" 7 | 8 | install_requires = [ 9 | "pymem", 10 | "psutil" 11 | ] 12 | 13 | setup( 14 | name="pywxdump_mini", 15 | author="xaoyaoo", 16 | version=version, 17 | author_email="xaoyaoo@gmail.com", 18 | description="微信信息获取工具", 19 | long_description=long_description, 20 | long_description_content_type="text/markdown", 21 | url="https://github.com/xaoyaoo/PyWxDumpMini", 22 | license='MIT', 23 | 24 | packages=['pywxdump_mini'], 25 | package_dir={'pywxdump_mini': 'pywxdump_mini'}, 26 | 27 | package_data={}, 28 | classifiers=[ 29 | "Programming Language :: Python :: 3", 30 | "Operating System :: OS Independent", 31 | ], 32 | python_requires='>=3.6, <4', 33 | install_requires=install_requires, 34 | entry_points={ 35 | 'console_scripts': [ 36 | 'wxinfo = pywxdump_mini.simplify_wx_info:read_info', 37 | ], 38 | }, 39 | setup_requires=['wheel'] 40 | ) 41 | -------------------------------------------------------------------------------- /tests/build_exe.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*-# 2 | # ------------------------------------------------------------------------------- 3 | # Name: gen_exe.py 4 | # Description: 5 | # Author: xaoyaoo 6 | # Date: 2023/11/10 7 | # ------------------------------------------------------------------------------- 8 | import shutil 9 | import os 10 | 11 | spec_content = ''' 12 | # -*- mode: python ; coding: utf-8 -*- 13 | 14 | block_cipher = None 15 | 16 | a = Analysis(['tmp.py'], 17 | pathex=[], 18 | binaries=[], 19 | hiddenimports={hidden_imports}, 20 | hookspath=[], 21 | runtime_hooks=[], 22 | excludes=[], 23 | win_no_prefer_redirects=False, 24 | win_private_assemblies=False, 25 | cipher=block_cipher, 26 | noarchive=False) 27 | 28 | pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) 29 | 30 | exe = EXE(pyz, 31 | a.scripts, 32 | a.binaries, 33 | a.zipfiles, 34 | a.datas, 35 | [], 36 | name='wxdump', 37 | debug=False, 38 | bootloader_ignore_signals=False, 39 | strip=False, 40 | upx=True, # 启用压缩 41 | console=True, # 使用控制台 42 | disable_windowed_traceback=True, # 不禁用堆栈跟踪 43 | argv_emulation=False, # 不模拟命令行参数 44 | target_arch=None, # 自动检测目标 CPU 架构 45 | codesign_identity=None, # 不签名应用程序 46 | entitlements_file=None, # 不使用 entitlements 文件 47 | onefile=True, # 生成单个可执行文件 48 | icon="icon.ico" 49 | ) 50 | 51 | coll = COLLECT(exe, 52 | a.binaries, 53 | a.zipfiles, 54 | a.datas, 55 | strip=False, 56 | upx=True, 57 | upx_exclude=[], 58 | name='wxdump') 59 | 60 | ''' 61 | 62 | # 创建文件夹 63 | os.makedirs("dist", exist_ok=True) 64 | 65 | # 当前文件所在目录 66 | current_path = os.path.dirname(os.path.abspath(__file__)) 67 | shutil.copy(os.path.join(os.path.dirname(current_path), "pywxdump_mini", "simplify_wx_info.py"), "dist/tmp.py") # 复制代码 68 | shutil.copy(os.path.join(current_path, "icon.ico"), "dist/icon.ico") # 复制图标 69 | 70 | require_path = os.path.join(os.path.dirname(current_path), "requirements.txt") # requirements.txt 路径 71 | with open(require_path, "r", encoding="utf-8") as f: 72 | hidden_imports = f.read().splitlines() 73 | hidden_imports = [i for i in hidden_imports if i not in ["setuptools", "wheel"]] # 去掉setuptools、wheel 74 | 75 | # 生成 spec 文件 76 | spec_content = spec_content.format(hidden_imports=hidden_imports) 77 | spec_file = os.path.join("dist", "pywxdump.spec") 78 | with open(spec_file, 'w', encoding="utf-8") as f: 79 | f.write(spec_content.strip()) 80 | 81 | # 执行打包命令 82 | cmd = f'pyinstaller --clean --distpath=dist {spec_file}' 83 | print(cmd) 84 | # os.system(cmd) 85 | -------------------------------------------------------------------------------- /tests/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaoyaoo/PyWxDumpMini/915c09121aa8290d31182e70288c286f498d19f9/tests/icon.ico -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*-# 2 | # ------------------------------------------------------------------------------- 3 | # Name: test.py 4 | # Description: 5 | # Author: xaoyaoo 6 | # Date: 2024/01/29 7 | # ------------------------------------------------------------------------------- 8 | from pywxdump_mini import read_info 9 | 10 | a = read_info(is_logging=True, is_save=True) 11 | print(a) 12 | 13 | if __name__ == '__main__': 14 | pass 15 | --------------------------------------------------------------------------------