diff --git a/README.md b/README.md index b94f680..3f7c65d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@
更新日志(点击展开): +* 2023.11.22 添加all命令中解密错误数据日志写入文件,修复部分bug * 2023.11.16 增加聊天记录导出为html * 2023.11.15 添加test文件,添加自动构建可执行文件的脚本,添加版本描述 * 2023.11.15 [v2.2.5变化较大]重构解密脚本的返回值,重构命令行参数 @@ -49,7 +50,7 @@ ## 1. 项目简介 -PyWxDump可用于:获取用户个人信息(昵称/账号/手机/邮箱/数据库密钥(用来解密聊天记录));数据库读取、解密脚本;聊天记录查看、聊天记录导出为html。 +[PyWxDump](https://github.com/xaoyaoo/PyWxDump)可用于:获取用户个人信息(昵称/账号/手机/邮箱/数据库密钥(用来解密聊天记录));数据库读取、解密脚本;聊天记录查看、聊天记录导出为html。 支持多账户信息获取,支持所有微信版本。 @@ -67,7 +68,7 @@ PyWxDump可用于:获取用户个人信息(昵称/账号/手机/邮箱/数据 * (6)提供数据库部分字段说明 * (7)支持微信多开场景,获取多用户信息等 * (8)微信需要登录状态才能获取数据库密钥 -* (9)支持导出聊天记录为html +* (9)支持导出聊天记录为html,备份微信聊天记录,方便查看 **版本差异** @@ -121,6 +122,7 @@ PyWxDump [PyWxDump](https://github.com/xaoyaoo/PyWxDump)是[SharpWxDump](https://github.com/AdminTest0/SharpWxDump) 的经过重构python语言版本,同时添加了一些新的功能。 +* 项目地址:https://github.com/xaoyaoo/PyWxDump * 目前只在windows下测试过,linux下可能会存在问题。 * 如发现[version_list.json](pywxdump/version_list.json)缺失或错误, 请提交[issues](https://github.com/xaoyaoo/PyWxDump/issues). @@ -331,10 +333,15 @@ else: 本项目仅供学习交流使用,请勿用于非法用途,否则后果自负。 -本项目仅允许在授权情况下对数据库进行备份,严禁用于非法目的,否则自行承担所有相关责任。使用该工具则代表默认同意该条款; +您应该在下载保存,编译使用本项目的24小时内,删除本项目的源代码和(编译出的)程序。 + +本项目仅允许在授权情况下对数据库进行备份,严禁用于非法目的,否则自行承担所有相关责任。 + +下载、保存、进一步浏览源代码或者下载安装、编译使用本程序,表示你同意本警告,并承诺遵守它; 请勿利用本项目的相关技术从事非法测试,如因此产生的一切不良后果与项目作者无关。 + # 四、许可证 ```text diff --git a/pywxdump/analyse/parse.py b/pywxdump/analyse/parse.py index fe8ae02..fd9d8af 100644 --- a/pywxdump/analyse/parse.py +++ b/pywxdump/analyse/parse.py @@ -5,6 +5,7 @@ # Author: xaoyaoo # Date: 2023/09/27 # ------------------------------------------------------------------------------- +import os.path import sqlite3 import pysilk from io import BytesIO @@ -245,5 +246,43 @@ def read_audio(MsgSvrID, is_play=False, is_wave=False, DB_PATH: str = "", rate=2 return pcm_data +def wordcloud_generator(text, out_path="", is_show=False, img_path="", font="C:\Windows\Fonts\simhei.ttf"): + """ + 词云 + :param is_show: 是否显示 + :param img_path: 背景图片路径 + :param text: 文本 + :param font: 字体路径 + :return: + """ + try: + from wordcloud import WordCloud + import jieba + import numpy as np + import matplotlib.pyplot as plt + from matplotlib.font_manager import fontManager + except ImportError as e: + print("error", e) + raise ImportError("请安装wordcloud,jieba,numpy,matplotlib,pillow库") + words = jieba.lcut(text) # 精确分词 + newtxt = ' '.join(words) # 空格拼接 + # 字体路径 + + # 创建WordCloud对象 + wordcloud1 = WordCloud(width=800, height=400, background_color='white', font_path=font) + wordcloud1.generate(newtxt) + + if out_path and out_path != "": + wordcloud1.to_file("wordcloud.png") # 保存图片 + if img_path and os.path.exists(img_path): # 设置背景图片 + img_color = np.array(Image.open(img_path)) # 读取背景图片 + img_color = img_color.reshape((img_color.shape[0] * img_color.shape[1], 3)) + wordcloud1.recolor(color_func=img_color) # 设置背景图片颜色 + if is_show: + # 显示词云 + wordcloud_img = wordcloud1.to_image() + wordcloud_img.show() + + if __name__ == '__main__': - pass + wordcloud_generator("我是中国人,我喜欢吃饭", is_show=True) diff --git a/pywxdump/bias_addr/get_bias_addr.py b/pywxdump/bias_addr/get_bias_addr.py index a4e4f08..4e03b33 100644 --- a/pywxdump/bias_addr/get_bias_addr.py +++ b/pywxdump/bias_addr/get_bias_addr.py @@ -12,9 +12,6 @@ import json import multiprocessing import os import re -import time -import winreg -import threading import platform import psutil diff --git a/pywxdump/command.py b/pywxdump/command.py index 0adc664..96534dc 100644 --- a/pywxdump/command.py +++ b/pywxdump/command.py @@ -7,6 +7,8 @@ # ------------------------------------------------------------------------------- import argparse import importlib.metadata +import sys +import textwrap from . import * @@ -19,12 +21,12 @@ class MainBiasAddr(): self.mode = "bias" # 添加 'bias_addr' 子命令解析器 sb_bias_addr = parser.add_parser(self.mode, help="获取微信基址偏移") - sb_bias_addr.add_argument("--mobile", type=str, help="手机号", required=True) - sb_bias_addr.add_argument("--name", type=str, help="微信昵称", required=True) - sb_bias_addr.add_argument("--account", type=str, help="微信账号", required=True) - sb_bias_addr.add_argument("--key", type=str, help="(可选)密钥") - sb_bias_addr.add_argument("--db_path", type=str, help="(可选)已登录账号的微信文件夹路径") - sb_bias_addr.add_argument("-vlp", '--version_list_path', type=str, + sb_bias_addr.add_argument("--mobile", type=str, help="手机号", metavar="", required=True) + sb_bias_addr.add_argument("--name", type=str, help="微信昵称", metavar="", required=True) + sb_bias_addr.add_argument("--account", type=str, help="微信账号", metavar="", required=True) + sb_bias_addr.add_argument("--key", type=str, metavar="", help="(可选)密钥") + sb_bias_addr.add_argument("--db_path", type=str, metavar="", help="(可选)已登录账号的微信文件夹路径") + sb_bias_addr.add_argument("-vlp", '--version_list_path', type=str, metavar="", help="(可选)微信版本偏移文件路径,如有,则自动更新", default=None) self.sb_bias_addr = sb_bias_addr @@ -52,12 +54,13 @@ class MainWxInfo(): self.mode = "info" # 添加 'wx_info' 子命令解析器 sb_wx_info = parser.add_parser(self.mode, help="获取微信信息") - sb_wx_info.add_argument("-vlp", type=str, help="(可选)微信版本偏移文件路径", default=VERSION_LIST_PATH) + sb_wx_info.add_argument("-vlp", '--version_list_path', metavar="", type=str, + help="(可选)微信版本偏移文件路径", default=VERSION_LIST_PATH) return sb_wx_info def run(self, args): # 读取微信各版本偏移 - path = args.vlp + path = args.version_list_path version_list = json.load(open(path, "r", encoding="utf-8")) result = read_info(version_list, True) # 读取微信信息 return result @@ -173,7 +176,7 @@ class MainExportChatRecords(): def init_parses(self, parser): self.mode = "export" # 添加 'decrypt' 子命令解析器 - sb_decrypt = parser.add_parser(self.mode, help="聊天记录导出为html") + sb_decrypt = parser.add_parser(self.mode, help="聊天记录导出为html[需要安装flask]") sb_decrypt.add_argument("-u", "--username", type=str, help="微信账号", required=True, metavar="") sb_decrypt.add_argument("-o", "--outpath", type=str, help="导出路径", required=True, metavar="") sb_decrypt.add_argument("-msg", "--msg_path", type=str, help="解密后的 MSG.db 的路径", required=True, @@ -274,8 +277,11 @@ class MainAll(): f'[+] success "{os.path.relpath(ret1[0], os.path.commonprefix(wxdbpaths))}" -> "{os.path.relpath(ret1[1], os.getcwd())}"') out_dbs.append(ret1[1]) print("-" * 32) - print("[-] " + f"共 {len(errors)} 个文件解密失败;") + print( + "[-] " + f"共 {len(errors)} 个文件解密失败(可能原因:非当前登录用户数据库;非加密数据库),详见{out_path}下‘解密失败.txt’;") # print("; ".join([f'"{wxdbpaths[i]}"' for i in errors])) + with open(os.path.join(out_path, "解密失败.txt"), "w", encoding="utf-8") as f: + f.write("\n".join([f'{i}' for i in errors])) print("=" * 32) if len(out_dbs) <= 0: @@ -300,13 +306,31 @@ class MainAll(): MainShowChatRecords().run(args) +PYWXDUMP_VERSION = importlib.metadata.version('pywxdump') + + +class CustomArgumentParser(argparse.ArgumentParser): + def format_help(self): + # 首先显示软件简介 + # 定义软件简介文本并进行格式化 + PYWXDUMP_VERSION = importlib.metadata.version('pywxdump') + first_line = f'\033[36m{" PyWxDump v" + PYWXDUMP_VERSION + " ":=^80}\033[0m' + brief = 'PyWxDump是一款用于获取账号信息(昵称/账号/手机/邮箱/数据库密钥)、解密数据库、查看\n聊天记录、备份导出聊天记录为html的工具。' + other = '更多详情请查看: \033[4m\033[1mhttps://github.com/xaoyaoo/PyWxDump\033[0m' + + separator = f'{"options":-^80}' + + # 获取帮助信息并添加到软件简介下方 + help_text = super().format_help().strip() + + return f'{first_line}\n{brief}\n{separator}\n{help_text}\n{separator}\n{other}\n{first_line}\n' + + def console_run(): # 创建命令行参数解析器 - parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) - - version = importlib.metadata.version('pywxdump') - version = f"PyWxDump {version}" - parser.add_argument('-V', '--version', action='version', version=version) + parser = CustomArgumentParser(formatter_class=argparse.RawTextHelpFormatter) + PYWXDUMP_VERSION = importlib.metadata.version('pywxdump') + parser.add_argument('-V', '--version', action='version', version=f"PyWxDump v{PYWXDUMP_VERSION}") # 添加子命令解析器 subparsers = parser.add_subparsers(dest="mode", help="""运行模式:""", required=True, metavar="mode") @@ -347,6 +371,12 @@ def console_run(): sb_all = main_all.init_parses(subparsers) modes[main_all.mode] = main_all + # 检查是否需要显示帮助信息 + if len(sys.argv) == 1: + sys.argv.append('-h') + elif len(sys.argv) == 2 and sys.argv[1] in modes.keys(): + sys.argv.append('-h') + args = parser.parse_args() # 解析命令行参数 if not any(vars(args).values()): diff --git a/pywxdump/wx_info/get_wx_info.py b/pywxdump/wx_info/get_wx_info.py index 030d9a2..998bc53 100644 --- a/pywxdump/wx_info/get_wx_info.py +++ b/pywxdump/wx_info/get_wx_info.py @@ -24,9 +24,35 @@ def get_info_without_key(h_process, address, n_size=64): return text.strip() if text.strip() != "" else "None" +def pattern_scan_all(handle, pattern, *, return_multiple=False): + import sys + next_region = 0 + found = [] + user_space_limit = 0x7FFFFFFF0000 if sys.maxsize > 2 ** 32 else 0x7fff0000 + while next_region < user_space_limit: + try: + next_region, page_found = pymem.pattern.scan_pattern_page( + handle, + next_region, + pattern, + return_multiple=return_multiple + ) + except Exception as e: + print(e) + break + if not return_multiple and page_found: + return page_found + if page_found: + found += page_found + if not return_multiple: + return None + return found + + def get_info_wxid(h_process, n_size=64): pm = pymem.Pymem("WeChat.exe") - addrs = pymem.pattern.pattern_scan_all(pm.process_handle, b'wxid_', return_multiple=True) + # addrs = pymem.pattern.pattern_scan_all(pm.process_handle, b'wxid_', return_multiple=True) + addrs = pattern_scan_all(pm.process_handle, b'wxid_', return_multiple=True) for addr in addrs: wxidtmp = get_info_without_key(h_process, addr, n_size) if wxidtmp.startswith("wxid_") and r'\FileStorage\MsgAttach' in wxidtmp: diff --git a/setup.py b/setup.py index eee8f94..b30d12f 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() -version = "2.2.7" +version = "2.2.8" install_requires = [ "psutil",