parent
2f2cf58c47
commit
36f0d26e45
13
README.md
13
README.md
@ -16,6 +16,7 @@
|
||||
<details>
|
||||
<summary><strong>更新日志(点击展开):</strong></summary>
|
||||
|
||||
* 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
|
||||
|
@ -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)
|
||||
|
@ -12,9 +12,6 @@ import json
|
||||
import multiprocessing
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import winreg
|
||||
import threading
|
||||
import platform
|
||||
|
||||
import psutil
|
||||
|
@ -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()):
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user