├── .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 | [](https://www.python.org/)
4 | [](https://github.com/xaoyaoo/PyWxDump)
5 | [](https://github.com/xaoyaoo/PyWxDump)
6 | [](https://github.com/xaoyaoo/PyWxDump)
7 | [](https://github.com/xaoyaoo/PyWxDump/fork)
8 | [](https://github.com/xaoyaoo/PyWxDump/issues)
9 |
10 | [](https://pypi.org/project/pywxdump/)
11 | [](https://pypi.org/project/pywxdump/)
12 | [](https://pypistats.org/packages/pywxdump)
13 | [](https://github.com/xaoyaoo/PyWxDump/blob/master/LICENSE)
14 | [](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 |
--------------------------------------------------------------------------------