diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 67d9aea..0000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-name: Bug report
-about: 帮助定位问题所在
-title: ''
-labels: ''
-assignees: ''
-
----
-
-**问题描述**
-请在此处提供对问题的详细描述。
-
-**复现步骤**
-请提供重现问题所需的步骤。(执行的命令)
-
-1. 步骤 1
-2. 步骤 2
-3. 步骤 3
-
-**预期行为**
-请清楚地描述您预期的行为。
-
-**实际行为**
-请描述实际的行为和问题出现的地方。
-
-**环境信息**
-- 操作系统版本:
-- python版本:
-- 微信版本:
-
-
-
-**其他信息**
-请提供任何与问题相关的其他信息(文字,截图等)。
diff --git a/README.md b/README.md
index 9fafaa7..715dd64 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,13 @@
#
PyWxDump
-* 更新日志(发现[version_list.json](./Program/version_list.json)
+[](https://www.python.org/)
+[](https://github.com/xaoyaoo/PyWxDump)
+
+* 更新日志(发现[version_list.json](app/version_list.json)
缺失或错误,请提交[issues](https://github.com/xaoyaoo/PyWxDump/issues)):
- * 2023.10.11 添加"3.9.5.81"版本的偏移地址[#10](https://github.com/xaoyaoo/PyWxDump/issues/10), 感谢@**[sv3nbeast](https://github.com/sv3nbeast)**
+ * 2023.10.14 整体重构项目,优化代码,增加命令行统一操作
+ * 2023.10.11 添加"3.9.5.81"版本的偏移地址[#10](https://github.com/xaoyaoo/PyWxDump/issues/10),
+ 感谢@[sv3nbeast](https://github.com/sv3nbeast)
* 2023.10.09 获取key基址偏移可以根据微信文件夹获取,不需要输入key
* 2023.10.09 优化代码,删减没必要代码,重新修改获取基址代码,加快运行速度(需要安装新的库 pymem)
* 2023.10.07 修改获取基址内存搜索方式,防止进入死循环
@@ -11,7 +16,7 @@
* 2023.09.28 增加了数据库部分解析
* 2023.09.15 增加了3.9.7.25版本的偏移地址
-## 一、项目介绍
+# 一、项目介绍
本项目可以获取微信基本信息,以及key,通过key可以解密微信数据库,获取聊天记录,好友信息,群信息等。
@@ -21,9 +26,34 @@
超级想要star,走过路过,帮忙点个[](https://github.com/xaoyaoo/PyWxDump/)
呗,谢谢啦~
-## 二、使用方法
+**目录结构**
-### 1. 安装依赖
+```
+PyWxDump
+├─ app # 项目代码,存放各个模块
+│ ├─ analyse # 解析数据库
+│ │ └─ parse.py # 解析数据库脚本,可以解析语音、图片、聊天记录等
+│ ├─ bias_addr # 获取偏移地址
+│ │ └─ get_bias_addr.py # 获取偏移地址脚本
+│ ├─ decrypted # 解密数据库
+│ │ ├─ decrypt.py # 解密数据库脚本
+│ │ └─ get_wx_decrypted_db.py # 直接读取当前登录微信的数据库,解密后保存到当前目录下的decrypted文件夹中
+│ ├─ wx_info # 获取微信基本信息
+│ │ ├─ get_wx_info.py # 获取微信基本信息脚本
+│ │ └─ get_wx_db.py # 获取本地所有的微信相关数据库
+│ └─ version_list.json # 微信版本列表
+├─ doc # 项目文档
+│ ├─ wx数据库简述.md # wx数据库简述
+│ └─ CE获取基址.md # CE获取基址
+├─ main.py # 命令行入口
+├─ README.md
+└─ requirements.txt
+```
+
+
+# 二、使用方法
+
+## 1. 安装依赖
```shell script
pip install -r requirements.txt
@@ -35,166 +65,87 @@ pip install -r requirements.txt
2. 如果运行报错,请检查python版本,本项目使用的是python3.10
3. 安装pycryptodome时可能会报错,可以使用下面的命令安装,自行搜索解决方案(该包为解密的核心包)
-### 2. 获取微信基本信息
+## 2. 使用方法
-获取微信的信息,获取到几个,取决于现在登录的几个微信。
-
-**2.1 shell获取微信基本信息**
+### 2.1 命令行
```shell script
-cd Program
-python get_wx_info.py
+python main.py [模式] [参数]
+# 运行模式(mode):
+# bias_addr 获取微信基址偏移
+# wx_info 获取微信信息
+# wx_db 获取微信文件夹路径
+# decrypt 解密微信数据库
+# analyse 解析微信数据库(未完成)
+# all 执行所有操作(除获取基址偏移、Analyse)
```
-结果
+*示例*
+
+以下是示例命令:
```shell script
-[+] pid: 2365
-[+] version: *.*.*.*
-[+] key: ******************************************d
-[+] name: *****
-[+] account: ********
-[+] mobile: ******
-[+] mail: *****
-========================================
-[+] pid: 2365
-[+] version: *.*.*.*
-[+] key: ******************************************d
-[+] name: *****
-[+] account: ********
-[+] mobile: ******
-[+] mail: *****
-========================================
-...
+python main.py bias_addr -h
+#usage: main.py bias_addr [-h] --mobile MOBILE --name NAME --account ACCOUNT [--key KEY] [--db_path DB_PATH] [-vlp VLP]
+#options:
+# -h, --help show this help message and exit
+# --mobile MOBILE 手机号
+# --name NAME 微信昵称
+# --account ACCOUNT 微信账号
+# --key KEY (与db_path二选一)密钥
+# --db_path DB_PATH (与key二选一)已登录账号的微信文件夹路径
+# -vlp VLP (可选)微信版本偏移文件路径
+
+python main.py wx_info -h
+#usage: main.py wx_info [-h] [-vlp VLP]
+#options:
+# -h, --help show this help message and exit
+# -vlp VLP (可选)微信版本偏移文件路径
+
+python main.py wx_db -h
+#usage: main.py wx_db [-h] [-r REQUIRE_LIST] [-wf WF]
+#options:
+# -h, --help show this help message and exit
+# -r REQUIRE_LIST, --require_list REQUIRE_LIST
+# (可选)需要的数据库名称(eg: -r MediaMSG;MicroMsg;FTSMSG;MSG;Sns;Emotion )
+# -wf WF (可选)'WeChat Files'路径
+
+python main.py decrypt -h
+#usage: main.py decrypt [-h] -k KEY -i DB_PATH -o OUT_PATH
+#options:
+# -h, --help show this help message and exit
+# -k KEY, --key KEY 密钥
+# -i DB_PATH, --db_path DB_PATH
+# 数据库路径(目录or文件)
+# -o OUT_PATH, --out_path OUT_PATH
+# 输出路径(必须是目录),输出文件为 out_path/de_{original_name}
+
+python main.py analyse -h
+#usage: main.py analyse [-h] [--arg ARG]
+#options:
+# -h, --help show this help message and exit
+# --arg ARG 参数
+
+python main.py all -h
+#usage: main.py all [-h]
+#options:
+# -h, --help show this help message and exit
```
-**2.2 import 调用**
+### 2.2 python API
```python
-import json
-from Program.get_wx_info import read_info
-
-version_list = json.load(open("version_list.json", "r", encoding="utf-8"))
-data = read_info(version_list)
-print(data)
+from app import *
+# 单独使用各模块,返回值一般为字典,参数参考命令行
```
-结果:
+【注】:
-```list
-[
- {
- 'pid': 5632,
- 'version': '*.*.*.*',
- 'key': '***************************************',
- 'name': '******',
- 'account': '******',
- 'mobile': '135********',
- 'mail': '********'
- },
- {
- 'pid': 5632,
- 'version': '*.*.*.*',
- 'key': '***************************************',
- 'name': '******',
- 'account': '******',
- 'mobile': '135********',
- 'mail': '********'
- },
- ...
-]
-```
+* 关于基址使用cheat engine获取,参考[CE获取基址.md](doc/CE获取基址.md)
+* 关于数据库解析,参考[wx数据库简述.md](doc/wx数据库简述.md)
+* 关于更多使用方法,以及各个模块的使用方法,参考前一版本的[python1.0_README.md](doc/python1.0_README.md)
-**说明**: 每个字段具体含义,参看上一条shell获取微信基本信息
-
-### 3. 获取偏移地址
-
-* 该方法一般不需要,只有当[version_list.json](./Program/version_list.json)没有对应的微信版本时,可以通过该方法获取偏移地址
-* 如果需要请参考下面的方法获取
-
-**3.1 通过python脚本获取**
-
-```shell
-python get_base_addr.py --mobile 152***** --name **** --account *** --key ********** --db_path "****\WeChat Files\wxid_******"
-```
-
-参数说明:
-
- 以下参数必选
- mobile = "152********" # 手机号
- name = "******" # 微信昵称
- account = "******" # 微信账号
- # 以上信息可以通过微信客户端获取
-
- 以下参数二选一(key获取偏移更快,db_path获取偏移很慢,本地测试需要10-60s)
- key = '**********************************************'
- # 需要降低版本使用get_wx_info.py获取key,也可以通过CheatEngine等工具获取
- # 最好是保存之前同微、同设备信使用过的key,非常方便
- db_path = "****\WeChat Files\wxid_******"
- # 微信文件夹,通过微信客户端,设置-文件管理-微信文件的默认保存位置获取
-
-return:{'3.9.7.29': [63486984, 63488320, 63486792, 0, 63488256, 56006136]}
-
- (十进制)按顺序代表:微信昵称、微信账号、微信手机号、微信邮箱(默认0)、微信KEY、版本信息
-
-[注]:如果参数错误,得到的对应地址偏移为0,邮箱高版本失效,默认为0
-
-**3.2 通过CheatEngine等工具获取**
-
-具体请查看:[CE获取基址.md](./CE%E8%8E%B7%E5%8F%96%E5%9F%BA%E5%9D%80.md)
-
-* 该方法获取到的偏移地址需要手动添加到[version_list.json](./Program/version_list.json)中
-
-**3.3 最简单获取方法**
-
-最简单的方法当然是运行
-
-```shell
-git clone https://github.com/xaoyaoo/PyWxDump.git
-```
-
-重新拉取一份新的啦~
-
-* ps: 该方法不一定能获取到最新的版本
-* 如果需要最新的版本,可以通过上面的方法获取
-* 你也可以提交Issues,分分钟给你更新
-
-## 三、获取解密数据库
-
-* [decrypt.py](./decrypted/decrypt.py) : 数据库解密脚本
-* [get_wx_decrypted_db.py](./decrypted/get_wx_decrypted_db.py) :直接读取当前登录微信的数据库,解密后保存到当前目录下的decrypted文件夹中
-
-[注]:每台设备、每个微信账号对应一个key,切换设备或者微信账号,key都会变化
-
-
-
-* 解密后可拖入数据库工具查找敏感信息
-* 还有一份数据的说明文档,但是我累了,不想写了
-
-**方法**
-
-进入目录[decrypted](./decrypted)
-
-```shell
-python decrypt.py --key ******** --db_path ./decrypted/decrypted.db --out_path ./decrypted/decrypted.db
-```
-
-[注]:--key为数据库密钥,--db_path为数据库路径,--out_path为解密后的数据库路径(解密后的路径目录必须存在)
-
-自动根据注册表读取本地微信聊天记录文件夹,解密后保存到当前目录下的decrypted文件夹中
-
-```shell
-python get_wx_decrypted_db.py --key ********
-```
-
-## 四、解析数据库
-
-* [parse.py](./parse_db/parse.py) : 数据库解析脚本,可以解析语音、图片、聊天记录等
-* 关于各个数据库的说明文档,请查看[parse_db](./parse_db)目录下的[README.md](./parse_db/README.md)
-
-未完待续...
-
-## 五、支持功能
+## 三、支持功能
1. 支持微信多开场景,获取多用户信息等
2. 微信需要登录状态才能获取数据库密钥
@@ -212,7 +163,7 @@ python get_wx_decrypted_db.py --key ********
4. 自行备份(日常备份自己留存)
5. 等等...............
-## 六、免责声明(非常重要!!!!!!!)
+## 四、免责声明(非常重要!!!!!!!)
本项目仅允许在授权情况下对数据库进行备份,严禁用于非法目的,否则自行承担所有相关责任。使用该工具则代表默认同意该条款;
diff --git a/decrypted/__init__.py b/app/__init__.py
similarity index 68%
rename from decrypted/__init__.py
rename to app/__init__.py
index 7e65b3e..fb76081 100644
--- a/decrypted/__init__.py
+++ b/app/__init__.py
@@ -3,9 +3,9 @@
# Name: __init__.py.py
# Description:
# Author: xaoyaoo
-# Date: 2023/08/21
+# Date: 2023/10/14
# -------------------------------------------------------------------------------
-
-
-if __name__ == '__main__':
- pass
+from .bias_addr import *
+from .wx_info import *
+from .decrypted import *
+from .analyse import *
\ No newline at end of file
diff --git a/app/analyse/__init__.py b/app/analyse/__init__.py
new file mode 100644
index 0000000..7b74b1c
--- /dev/null
+++ b/app/analyse/__init__.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-#
+# -------------------------------------------------------------------------------
+# Name: __init__.py.py
+# Description:
+# Author: xaoyaoo
+# Date: 2023/09/27
+# -------------------------------------------------------------------------------
+from .parse import read_img_dat, read_emoji, decompress_CompressContent, read_audio_buf, read_audio
diff --git a/parse_db/parse.py b/app/analyse/parse.py
similarity index 100%
rename from parse_db/parse.py
rename to app/analyse/parse.py
diff --git a/parse_db/__init__.py b/app/bias_addr/__init__.py
similarity index 80%
rename from parse_db/__init__.py
rename to app/bias_addr/__init__.py
index a69538a..de2c860 100644
--- a/parse_db/__init__.py
+++ b/app/bias_addr/__init__.py
@@ -3,9 +3,6 @@
# Name: __init__.py.py
# Description:
# Author: xaoyaoo
-# Date: 2023/09/27
+# Date: 2023/10/14
# -------------------------------------------------------------------------------
-
-
-if __name__ == '__main__':
- pass
+from .get_bias_addr import BiasAddr
diff --git a/Program/get_base_addr.py b/app/bias_addr/get_bias_addr.py
similarity index 94%
rename from Program/get_base_addr.py
rename to app/bias_addr/get_bias_addr.py
index ee1b848..9a46409 100644
--- a/Program/get_base_addr.py
+++ b/app/bias_addr/get_bias_addr.py
@@ -42,7 +42,7 @@ def validate_key(key, salt, first, mac_salt):
return False
-class BaseAddr:
+class BiasAddr:
def __init__(self, account, mobile, name, key, db_path):
self.account = account.encode("utf-8")
self.mobile = mobile.encode("utf-8")
@@ -201,22 +201,22 @@ class BaseAddr:
mobile_bias = self.search_memory_value(self.mobile)
name_bias = self.search_memory_value(self.name)
account_bias = self.search_memory_value(self.account)
- version_bias = self.search_memory_value(self.version.encode("utf-8"))
+ # version_bias = self.search_memory_value(self.version.encode("utf-8"))
if self.key:
key_bias = self.search_key(self.key)
elif self.db_path:
key_bias = self.get_key_bias(self.db_path, account_bias)
else:
key_bias = 0
- return {self.version: [name_bias, account_bias, mobile_bias, 0, key_bias, version_bias]}
+ return {self.version: [name_bias, account_bias, mobile_bias, 0, key_bias]}
if __name__ == '__main__':
# 创建命令行参数解析器
parser = argparse.ArgumentParser()
- parser.add_argument("--mobile", type=str, help="手机号")
- parser.add_argument("--name", type=str, help="微信昵称")
- parser.add_argument("--account", type=str, help="微信账号")
+ parser.add_argument("--mobile", type=str, help="手机号", required=True)
+ parser.add_argument("--name", type=str, help="微信昵称", required=True)
+ parser.add_argument("--account", type=str, help="微信账号", required=True)
parser.add_argument("--key", type=str, help="密钥")
parser.add_argument("--db_path", type=str, help="微信文件夹(已经登录微信)路径")
@@ -232,16 +232,16 @@ if __name__ == '__main__':
mobile = args.mobile
name = args.name
account = args.account
- key = None # args.key
+ key = args.key
db_path = args.db_path
# 调用 run 函数,并传入参数
- rdata = BaseAddr(account, mobile, name, key, db_path).run()
+ rdata = BiasAddr(account, mobile, name, key, db_path).run()
print(rdata)
# 添加到version_list.json
- with open("version_list.json", "r", encoding="utf-8") as f:
+ with open("../version_list.json", "r", encoding="utf-8") as f:
data = json.load(f)
data.update(rdata)
- with open("version_list.json", "w", encoding="utf-8") as f:
+ with open("../version_list.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
diff --git a/app/decrypted/__init__.py b/app/decrypted/__init__.py
new file mode 100644
index 0000000..59542dc
--- /dev/null
+++ b/app/decrypted/__init__.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-#
+# -------------------------------------------------------------------------------
+# Name: __init__.py.py
+# Description:
+# Author: xaoyaoo
+# Date: 2023/08/21
+# -------------------------------------------------------------------------------
+from .decrypt import batch_decrypt, decrypt
+from .get_wx_decrypted_db import all_decrypt, merge_copy_msg_db, merge_msg_db, merge_media_msg_db
diff --git a/app/decrypted/decrypt.py b/app/decrypted/decrypt.py
new file mode 100644
index 0000000..c3e0e70
--- /dev/null
+++ b/app/decrypted/decrypt.py
@@ -0,0 +1,137 @@
+import argparse
+import hmac
+import hashlib
+import os
+
+from Cryptodome.Cipher import AES
+
+# from Crypto.Cipher import AES # 如果上面的导入失败,可以尝试使用这个
+
+SQLITE_FILE_HEADER = "SQLite format 3\x00" # SQLite文件头
+
+KEY_SIZE = 32
+DEFAULT_PAGESIZE = 4096
+DEFAULT_ITER = 64000
+
+
+# 通过密钥解密数据库
+def decrypt(key: str, db_path, out_path):
+ if not os.path.exists(db_path):
+ return f"[-] db_path:'{db_path}' File not found!"
+ if not os.path.exists(os.path.dirname(out_path)):
+ return f"[-] out_path:'{out_path}' File not found!"
+
+ password = bytes.fromhex(key.strip())
+ with open(db_path, "rb") as file:
+ blist = file.read()
+
+ salt = blist[:16]
+ byteKey = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE)
+ first = blist[16:DEFAULT_PAGESIZE]
+
+ mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])
+ mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE)
+ hash_mac = hmac.new(mac_key, first[:-32], hashlib.sha1)
+ hash_mac.update(b'\x01\x00\x00\x00')
+
+ if hash_mac.digest() != first[-32:-12]:
+ return f"[-] Password Error! (key:'{key}'; db_path:'{db_path}'; out_path:'{out_path}' )"
+
+ newblist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]
+
+ with open(out_path, "wb") as deFile:
+ deFile.write(SQLITE_FILE_HEADER.encode())
+ t = AES.new(byteKey, AES.MODE_CBC, first[-48:-32])
+ decrypted = t.decrypt(first[:-48])
+ deFile.write(decrypted)
+ deFile.write(first[-48:])
+
+ for i in newblist:
+ t = AES.new(byteKey, AES.MODE_CBC, i[-48:-32])
+ decrypted = t.decrypt(i[:-48])
+ deFile.write(decrypted)
+ deFile.write(i[-48:])
+ return [True, db_path, out_path, key]
+
+
+def batch_decrypt(key: str, db_path: [str | list], out_path: str):
+ if not isinstance(key, str) or not isinstance(out_path, str) or not os.path.exists(out_path) or len(key) != 64:
+ return f"[-] (key:'{key}' or out_path:'{out_path}') Error!"
+
+ process_list = []
+
+ if isinstance(db_path, str):
+ if not os.path.exists(db_path):
+ return f"[-] db_path:'{db_path}' not found!"
+
+ if os.path.isfile(db_path):
+ inpath = db_path
+ outpath = os.path.join(out_path, 'de_' + os.path.basename(db_path))
+ process_list.append([key, inpath, outpath])
+
+ elif os.path.isdir(db_path):
+ for root, dirs, files in os.walk(db_path):
+ for file in files:
+ inpath = os.path.join(root, file)
+ rel = os.path.relpath(root, db_path)
+ outpath = os.path.join(out_path, rel, 'de_' + file)
+
+ if not os.path.exists(os.path.dirname(outpath)):
+ os.makedirs(os.path.dirname(outpath))
+ process_list.append([key, inpath, outpath])
+ else:
+ return f"[-] db_path:'{db_path}' Error "
+ elif isinstance(db_path, list):
+ rt_path = os.path.commonprefix(db_path)
+ if not os.path.exists(rt_path):
+ rt_path = os.path.dirname(rt_path)
+
+ for inpath in db_path:
+ if not os.path.exists(inpath):
+ return f"[-] db_path:'{db_path}' not found!"
+
+ inpath = os.path.normpath(inpath)
+ rel = os.path.relpath(os.path.dirname(inpath), rt_path)
+ outpath = os.path.join(out_path, rel, 'de_' + os.path.basename(inpath))
+ if not os.path.exists(os.path.dirname(outpath)):
+ os.makedirs(os.path.dirname(outpath))
+ process_list.append([key, inpath, outpath])
+ else:
+ return f"[-] db_path:'{db_path}' Error "
+
+ result = []
+ for i in process_list:
+ result.append(decrypt(*i)) # 解密
+
+ # 删除空文件夹
+ for root, dirs, files in os.walk(out_path, topdown=False):
+ for dir in dirs:
+ if not os.listdir(os.path.join(root, dir)):
+ os.rmdir(os.path.join(root, dir))
+
+ return result
+
+
+if __name__ == '__main__':
+ # 创建命令行参数解析器
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-k", "--key", type=str, help="密钥", required=True)
+ parser.add_argument("-i", "--db_path", type=str, help="数据库路径(目录or文件)", required=True)
+ parser.add_argument("-o", "--out_path", type=str,
+ help="输出路径(必须是目录),输出文件为 out_path/de_{original_name}", required=True)
+
+ # 解析命令行参数
+ args = parser.parse_args()
+
+ # 从命令行参数获取值
+ key = args.key
+ db_path = args.db_path
+ out_path = args.out_path
+
+ # 调用 decrypt 函数,并传入参数
+ result = batch_decrypt(key, db_path, out_path)
+ for i in result:
+ if isinstance(i, str):
+ print(i)
+ else:
+ print(f'[+] "{i[1]}" -> "{i[2]}"')
diff --git a/decrypted/get_wx_decrypted_db.py b/app/decrypted/get_wx_decrypted_db.py
similarity index 99%
rename from decrypted/get_wx_decrypted_db.py
rename to app/decrypted/get_wx_decrypted_db.py
index 082ea32..32953ca 100644
--- a/decrypted/get_wx_decrypted_db.py
+++ b/app/decrypted/get_wx_decrypted_db.py
@@ -248,12 +248,26 @@ def merge_media_msg_db(db_path: list, save_path: str):
return save_path
-def main(keys=None):
+if __name__ == '__main__':
+ # 创建命令行参数解析器
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-k", "--key", help="解密密钥", nargs="+", required=True)
+
+ # 解析命令行参数
+ args = parser.parse_args()
+
+ # 检查是否缺少必要参数,并抛出错误
+ if not args.key:
+ raise ValueError("缺少必要的命令行参数!请提供密钥。")
+
+ # 从命令行参数获取值
+ keys = args.key
+
decrypted_ROOT = os.path.join(os.getcwd(), "decrypted")
if keys is None:
print("keys is None")
- return False
+ exit(0)
if isinstance(keys, str):
keys = [keys]
@@ -293,22 +307,3 @@ def main(keys=None):
shutil.rmtree(decrypted_path_tmp) # 删除临时文件
print(f"解密完成:{user}, {decrypted_path}")
- return True
-
-
-if __name__ == '__main__':
- # 创建命令行参数解析器
- parser = argparse.ArgumentParser()
- parser.add_argument("-k", "--key", help="解密密钥", nargs="+", required=True)
-
- # 解析命令行参数
- args = parser.parse_args()
-
- # 检查是否缺少必要参数,并抛出错误
- if not args.key:
- raise ValueError("缺少必要的命令行参数!请提供密钥。")
-
- # 从命令行参数获取值
- keys = args.key
-
- main(keys)
diff --git a/Program/version_list.json b/app/version_list.json
similarity index 99%
rename from Program/version_list.json
rename to app/version_list.json
index 195ced8..9464729 100644
--- a/Program/version_list.json
+++ b/app/version_list.json
@@ -319,7 +319,6 @@
63488320,
63486792,
0,
- 63488256,
- 56006136
+ 63488256
]
}
\ No newline at end of file
diff --git a/Program/__init__.py b/app/wx_info/__init__.py
similarity index 80%
rename from Program/__init__.py
rename to app/wx_info/__init__.py
index 7e65b3e..2e9671c 100644
--- a/Program/__init__.py
+++ b/app/wx_info/__init__.py
@@ -5,7 +5,5 @@
# Author: xaoyaoo
# Date: 2023/08/21
# -------------------------------------------------------------------------------
-
-
-if __name__ == '__main__':
- pass
+from .get_wx_info import read_info
+from .get_wx_db import get_wechat_db
\ No newline at end of file
diff --git a/app/wx_info/get_wx_db.py b/app/wx_info/get_wx_db.py
new file mode 100644
index 0000000..db33374
--- /dev/null
+++ b/app/wx_info/get_wx_db.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-#
+# -------------------------------------------------------------------------------
+# Name: get_wx_db.py
+# Description:
+# Author: xaoyaoo
+# Date: 2023/10/14
+# -------------------------------------------------------------------------------
+import os
+import re
+import winreg
+
+
+def get_wechat_db(require_list: [list | str] = "all", msg_dir: str = None):
+ if not msg_dir:
+ try:
+ key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Tencent\WeChat", 0, winreg.KEY_READ)
+ value, _ = winreg.QueryValueEx(key, "FileSavePath")
+ winreg.CloseKey(key)
+ w_dir = value
+ except Exception as e:
+ w_dir = "MyDocument:"
+
+ if w_dir == "MyDocument:":
+ profile = os.path.expanduser("~")
+ msg_dir = os.path.join(profile, "Documents", "WeChat Files")
+ else:
+ msg_dir = os.path.join(w_dir, "WeChat Files")
+
+ if not os.path.exists(msg_dir):
+ return "[-] 目录不存在"
+
+ user_dirs = {} # wx用户目录
+ files = os.listdir(msg_dir)
+ for file_name in files:
+ if file_name == "All Users" or file_name == "Applet" or file_name == "WMPF":
+ continue
+ user_dirs[file_name] = os.path.join(msg_dir, file_name)
+
+ if isinstance(require_list, str):
+ require_list = require_list.split(";")
+
+ if "all" in require_list:
+ pattern = {"all": re.compile(r".*\.db$")}
+ elif isinstance(require_list, list):
+ pattern = {}
+ for require in require_list:
+ pattern[require] = re.compile(r".*%s.*\.db$" % require)
+ else:
+ return "[-] 参数错误"
+
+ # 获取数据库路径
+ for user, user_dir in user_dirs.items(): # 遍历用户目录
+ user_dirs[user] = {n: [] for n in pattern.keys()}
+ for root, dirs, files in os.walk(user_dir):
+ for file_name in files:
+ for n, p in pattern.items():
+ if p.match(file_name):
+ src_path = os.path.join(root, file_name)
+ user_dirs[user][n].append(src_path)
+ return user_dirs
+
+
+if __name__ == '__main__':
+ require_list = ["MediaMSG", "MicroMsg", "FTSMSG", "MSG", "Sns", "Emotion"]
+ # require_list = "all"
+ user_dirs = get_wechat_db(require_list)
+ if isinstance(user_dirs, str):
+ print(user_dirs)
+ else:
+ for user, user_dir in user_dirs.items():
+ print(f"[+] {user}")
+ for n, paths in user_dir.items():
+ print(f" {n}:")
+ for path in paths:
+ print(f" {path}")
diff --git a/Program/get_wx_info.py b/app/wx_info/get_wx_info.py
similarity index 97%
rename from Program/get_wx_info.py
rename to app/wx_info/get_wx_info.py
index f9f441d..ede8d7a 100644
--- a/Program/get_wx_info.py
+++ b/app/wx_info/get_wx_info.py
@@ -84,7 +84,7 @@ def read_info(version_list):
if __name__ == "__main__":
# 读取微信各版本偏移
- version_list = json.load(open("version_list.json", "r", encoding="utf-8"))
+ version_list = json.load(open("../version_list.json", "r", encoding="utf-8"))
result = read_info(version_list) # 读取微信信息
print("=" * 32)
diff --git a/decrypted/decrypt.py b/decrypted/decrypt.py
deleted file mode 100644
index fcd265c..0000000
--- a/decrypted/decrypt.py
+++ /dev/null
@@ -1,99 +0,0 @@
-import argparse
-import hmac
-import hashlib
-import os
-
-from Cryptodome.Cipher import AES
-# from Crypto.Cipher import AES # 如果上面的导入失败,可以尝试使用这个
-
-SQLITE_FILE_HEADER = "SQLite format 3\x00" # SQLite文件头
-
-KEY_SIZE = 32
-DEFAULT_PAGESIZE = 4096
-DEFAULT_ITER = 64000
-
-
-# 通过密钥解密数据库
-def decrypt(key, db_path, out_path):
- if not os.path.exists(db_path):
- print("[-] db_path File not found!")
- return False
- if not os.path.exists(os.path.dirname(out_path)):
- print("[-] out_path File Path not found!")
- return False
-
- password = bytes.fromhex(key.strip())
- with open(db_path, "rb") as file:
- blist = file.read()
-
- salt = blist[:16]
- byteKey = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE)
- first = blist[16:DEFAULT_PAGESIZE]
-
- mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])
- mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE)
- hash_mac = hmac.new(mac_key, first[:-32], hashlib.sha1)
- hash_mac.update(b'\x01\x00\x00\x00')
-
- if hash_mac.digest() == first[-32:-12]:
- print("[+] Decryption Success")
- else:
- print("[-] Password Error")
- return False
-
- newblist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]
-
- with open(out_path, "wb") as deFile:
- deFile.write(SQLITE_FILE_HEADER.encode())
- t = AES.new(byteKey, AES.MODE_CBC, first[-48:-32])
- decrypted = t.decrypt(first[:-48])
- deFile.write(decrypted)
- deFile.write(first[-48:])
-
- for i in newblist:
- t = AES.new(byteKey, AES.MODE_CBC, i[-48:-32])
- decrypted = t.decrypt(i[:-48])
- deFile.write(decrypted)
- deFile.write(i[-48:])
- return True
-
-
-def batch_decrypt(key, db_path, out_path):
- if not os.path.exists(db_path):
- print("[-] db_path File not found!")
- return False
- if not os.path.exists(os.path.dirname(out_path)):
- print("[-] out_path File Path not found!")
- return False
-
- if os.path.isfile(db_path) and not os.path.isdir(out_path):
- return decrypt(key, db_path, out_path)
- if os.path.isdir(db_path) and not os.path.isfile(out_path):
- for root, dirs, files in os.walk(db_path):
- for file in files:
- decrypt(key, os.path.join(root, file), os.path.join(out_path, "decrypted" + file))
- return True
-
-
-if __name__ == '__main__':
- # 创建命令行参数解析器
- parser = argparse.ArgumentParser()
- parser.add_argument("--key", type=str, help="密钥")
- parser.add_argument("--db_path", type=str, help="加密数据库路径")
- parser.add_argument("--out_path", type=str, help="解密后的数据库路径")
-
- # 解析命令行参数
- args = parser.parse_args()
-
- # 检查是否缺少必要参数,并抛出错误
- if not args.key or not args.db_path or not args.out_path:
- raise ValueError("缺少必要的命令行参数!请提供密钥、加密数据库路径和解密后的数据库路径。")
-
- # 从命令行参数获取值
- key = args.key
- db_path = args.db_path
- out_path = args.out_path
-
- # 调用 decrypt 函数,并传入参数
- result = batch_decrypt(key, db_path, out_path)
- print(f"{result} done!")
diff --git a/CE获取基址.md b/doc/CE获取基址.md
similarity index 100%
rename from CE获取基址.md
rename to doc/CE获取基址.md
diff --git a/doc/python1.0_README.md b/doc/python1.0_README.md
new file mode 100644
index 0000000..41d04b5
--- /dev/null
+++ b/doc/python1.0_README.md
@@ -0,0 +1,219 @@
+# PyWxDump
+
+* 更新日志(发现[version_list.json](./Program/version_list.json)
+ 缺失或错误,请提交[issues](https://github.com/xaoyaoo/PyWxDump/issues)):
+ * 2023.10.11 添加"3.9.5.81"版本的偏移地址[#10](https://github.com/xaoyaoo/PyWxDump/issues/10), 感谢@**[sv3nbeast](https://github.com/sv3nbeast)**
+ * 2023.10.09 获取key基址偏移可以根据微信文件夹获取,不需要输入key
+ * 2023.10.09 优化代码,删减没必要代码,重新修改获取基址代码,加快运行速度(需要安装新的库 pymem)
+ * 2023.10.07 修改获取基址内存搜索方式,防止进入死循环
+ * 2023.10.07 增加了3.9.7.29版本的偏移地址
+ * 2023.10.06 增加命令行解密数据库
+ * 2023.09.28 增加了数据库部分解析
+ * 2023.09.15 增加了3.9.7.25版本的偏移地址
+
+## 一、项目介绍
+
+本项目可以获取微信基本信息,以及key,通过key可以解密微信数据库,获取聊天记录,好友信息,群信息等。
+
+该分支是[SharpWxDump](https://github.com/AdminTest0/SharpWxDump)的经过重构python语言版本,同时添加了一些新的功能。
+
+
+超级想要star,走过路过,帮忙点个[](https://github.com/xaoyaoo/PyWxDump/)
+呗,谢谢啦~
+
+## 二、使用方法
+
+### 1. 安装依赖
+
+```shell script
+pip install -r requirements.txt
+```
+
+**说明**:
+
+1. requirements.txt中的包可能不全,如果运行报错,请自行安装缺少的包
+2. 如果运行报错,请检查python版本,本项目使用的是python3.10
+3. 安装pycryptodome时可能会报错,可以使用下面的命令安装,自行搜索解决方案(该包为解密的核心包)
+
+### 2. 获取微信基本信息
+
+获取微信的信息,获取到几个,取决于现在登录的几个微信。
+
+**2.1 shell获取微信基本信息**
+
+```shell script
+cd Program
+python get_wx_info.py
+```
+
+结果
+
+```shell script
+[+] pid: 2365
+[+] version: *.*.*.*
+[+] key: ******************************************d
+[+] name: *****
+[+] account: ********
+[+] mobile: ******
+[+] mail: *****
+========================================
+[+] pid: 2365
+[+] version: *.*.*.*
+[+] key: ******************************************d
+[+] name: *****
+[+] account: ********
+[+] mobile: ******
+[+] mail: *****
+========================================
+...
+```
+
+**2.2 import 调用**
+
+```python
+import json
+from Program.get_wx_info import read_info
+
+version_list = json.load(open("version_list.json", "r", encoding="utf-8"))
+data = read_info(version_list)
+print(data)
+```
+
+结果:
+
+```list
+[
+ {
+ 'pid': 5632,
+ 'version': '*.*.*.*',
+ 'key': '***************************************',
+ 'name': '******',
+ 'account': '******',
+ 'mobile': '135********',
+ 'mail': '********'
+ },
+ {
+ 'pid': 5632,
+ 'version': '*.*.*.*',
+ 'key': '***************************************',
+ 'name': '******',
+ 'account': '******',
+ 'mobile': '135********',
+ 'mail': '********'
+ },
+ ...
+]
+```
+
+**说明**: 每个字段具体含义,参看上一条shell获取微信基本信息
+
+### 3. 获取偏移地址
+
+* 该方法一般不需要,只有当[version_list.json](./Program/version_list.json)没有对应的微信版本时,可以通过该方法获取偏移地址
+* 如果需要请参考下面的方法获取
+
+**3.1 通过python脚本获取**
+
+```shell
+python get_base_addr.py --mobile 152***** --name **** --account *** --key ********** --db_path "****\WeChat Files\wxid_******"
+```
+
+参数说明:
+
+ 以下参数必选
+ mobile = "152********" # 手机号
+ name = "******" # 微信昵称
+ account = "******" # 微信账号
+ # 以上信息可以通过微信客户端获取
+
+ 以下参数二选一(key获取偏移更快,db_path获取偏移很慢,本地测试需要10-60s)
+ key = '**********************************************'
+ # 需要降低版本使用get_wx_info.py获取key,也可以通过CheatEngine等工具获取
+ # 最好是保存之前同微、同设备信使用过的key,非常方便
+ db_path = "****\WeChat Files\wxid_******"
+ # 微信文件夹,通过微信客户端,设置-文件管理-微信文件的默认保存位置获取
+
+return:{'3.9.7.29': [63486984, 63488320, 63486792, 0, 63488256, 56006136]}
+
+ (十进制)按顺序代表:微信昵称、微信账号、微信手机号、微信邮箱(默认0)、微信KEY、版本信息
+
+[注]:如果参数错误,得到的对应地址偏移为0,邮箱高版本失效,默认为0
+
+**3.2 通过CheatEngine等工具获取**
+
+具体请查看:[CE获取基址.md](./CE%E8%8E%B7%E5%8F%96%E5%9F%BA%E5%9D%80.md)
+
+* 该方法获取到的偏移地址需要手动添加到[version_list.json](./Program/version_list.json)中
+
+**3.3 最简单获取方法**
+
+最简单的方法当然是运行
+
+```shell
+git clone https://github.com/xaoyaoo/PyWxDump.git
+```
+
+重新拉取一份新的啦~
+
+* ps: 该方法不一定能获取到最新的版本
+* 如果需要最新的版本,可以通过上面的方法获取
+* 你也可以提交Issues,分分钟给你更新
+
+## 三、获取解密数据库
+
+* [decrypt.py](./decrypted/decrypt.py) : 数据库解密脚本
+* [get_wx_decrypted_db.py](./decrypted/get_wx_decrypted_db.py) :直接读取当前登录微信的数据库,解密后保存到当前目录下的decrypted文件夹中
+
+[注]:每台设备、每个微信账号对应一个key,切换设备或者微信账号,key都会变化
+
+
+
+* 解密后可拖入数据库工具查找敏感信息
+* 还有一份数据的说明文档,但是我累了,不想写了
+
+**方法**
+
+进入目录[decrypted](./decrypted)
+
+```shell
+python decrypt.py --key ******** --db_path ./decrypted/decrypted.db --out_path ./decrypted/decrypted.db
+```
+
+[注]:--key为数据库密钥,--db_path为数据库路径,--out_path为解密后的数据库路径(解密后的路径目录必须存在)
+
+自动根据注册表读取本地微信聊天记录文件夹,解密后保存到当前目录下的decrypted文件夹中
+
+```shell
+python get_wx_decrypted_db.py --key ********
+```
+
+## 四、解析数据库
+
+* [parse.py](./parse_db/parse.py) : 数据库解析脚本,可以解析语音、图片、聊天记录等
+* 关于各个数据库的说明文档,请查看[parse_db](./parse_db)目录下的[README.md](./parse_db/README.md)
+
+未完待续...
+
+## 五、支持功能
+
+1. 支持微信多开场景,获取多用户信息等
+2. 微信需要登录状态才能获取数据库密钥
+
+**版本差异**
+
+1. 版本 < 3.7.0.30 只运行不登录能获取个人信息,登录后可以获取数据库密钥
+2. 版本 > 3.7.0.30 只运行不登录不能获取个人信息,登录后都能获取
+
+**利用场景**
+
+1. 钓鱼攻击(通过钓鱼控到的机器通常都是登录状态)
+2. 渗透到运维机器(有些运维机器会日常登录自己的微信)
+3. 某些工作需要取证(数据库需要拷贝到本地)
+4. 自行备份(日常备份自己留存)
+5. 等等...............
+
+## 六、免责声明(非常重要!!!!!!!)
+
+本项目仅允许在授权情况下对数据库进行备份,严禁用于非法目的,否则自行承担所有相关责任。使用该工具则代表默认同意该条款;
+
+请勿利用本项目的相关技术从事非法测试,如因此产生的一切不良后果与项目作者无关。
\ No newline at end of file
diff --git a/parse_db/README.md b/doc/wx数据库简述.md
similarity index 100%
rename from parse_db/README.md
rename to doc/wx数据库简述.md
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..f4082e3
--- /dev/null
+++ b/main.py
@@ -0,0 +1,250 @@
+# -*- coding: utf-8 -*-#
+# -------------------------------------------------------------------------------
+# Name: main.py.py
+# Description:
+# Author: xaoyaoo
+# Date: 2023/10/14
+# -------------------------------------------------------------------------------
+import json
+import argparse
+import os
+
+from app import *
+
+
+class MainBiasAddr():
+ def init_parses(self, parser):
+ # 添加 'bias_addr' 子命令解析器
+ sb_bias_addr = parser.add_parser("bias_addr", 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="(与db_path二选一)密钥")
+ sb_bias_addr.add_argument("--db_path", type=str, help="(与key二选一)已登录账号的微信文件夹路径")
+ sb_bias_addr.add_argument("-vlp", type=str, help="(可选)微信版本偏移文件路径",
+ default="./app/version_list.json")
+ return sb_bias_addr
+
+ def run(self, args):
+ # 判断是否至少输入一个参数
+ if not args.key and not args.db_path:
+ sb_bias_addr.error("必须至少指定 --key 或 --db_path 参数中的一个")
+
+ # 从命令行参数获取值
+ mobile = args.mobile
+ name = args.name
+ account = args.account
+ key = args.key
+ db_path = args.db_path
+ version_list_path = args.vlp
+ # 调用 run 函数,并传入参数
+ rdata = BiasAddr(account, mobile, name, key, db_path).run()
+ print(rdata)
+
+ # 添加到version_list.json
+ version_list = json.load(open(version_list_path, "r", encoding="utf-8"))
+ version_list.update(rdata)
+ json.dump(version_list, open(version_list_path, "w", encoding="utf-8"), ensure_ascii=False, indent=4)
+
+ return rdata
+
+
+class MainWxInfo():
+ def init_parses(self, parser):
+ # 添加 'wx_info' 子命令解析器
+ sb_wx_info = parser.add_parser("wx_info", help="获取微信信息")
+ sb_wx_info.add_argument("-vlp", type=str, help="(可选)微信版本偏移文件路径", default="./app/version_list.json")
+ return sb_wx_info
+
+ def run(self, args):
+ # 读取微信各版本偏移
+ version_list_path = args.vlp
+ version_list = json.load(open(version_list_path, "r", encoding="utf-8"))
+ result = read_info(version_list) # 读取微信信息
+
+ print("=" * 32)
+ if isinstance(result, str): # 输出报错
+ print(result)
+ else: # 输出结果
+ for i, rlt in enumerate(result):
+ for k, v in rlt.items():
+ print(f"[+] {k:>7}: {v}")
+ print(end="-" * 32 + "\n" if i != len(result) - 1 else "")
+ print("=" * 32)
+ return result
+
+
+class MainWxDbPath():
+ def init_parses(self, parser):
+ # 添加 'wx_db_path' 子命令解析器
+ sb_wx_db_path = parser.add_parser("wx_db", help="获取微信文件夹路径")
+ sb_wx_db_path.add_argument("-r", "--require_list", type=str,
+ help="(可选)需要的数据库名称(eg: -r MediaMSG;MicroMsg;FTSMSG;MSG;Sns;Emotion )",
+ default="all")
+ sb_wx_db_path.add_argument("-wf", type=str, help="(可选)'WeChat Files'路径", default=None)
+ return sb_wx_db_path
+
+ def run(self, args):
+ # 从命令行参数获取值
+ require_list = args.require_list
+ msg_dir = args.wf
+
+ user_dirs = get_wechat_db(require_list, msg_dir)
+
+ if isinstance(user_dirs, str):
+ print(user_dirs)
+ else:
+ for user, user_dir in user_dirs.items():
+ print(f"[+] {user}")
+ for n, paths in user_dir.items():
+ print(f" {n}:")
+ for path in paths[:2]:
+ print(f" {path}")
+ if len(paths) > 2:
+ print(f" ...")
+ print("-" * 32)
+ print(f"[+] 共 {len(user_dirs)} 个微信账号")
+
+ return user_dirs
+
+
+class MainDecrypt():
+ def init_parses(self, parser):
+ # 添加 'decrypt' 子命令解析器
+ sb_decrypt = parser.add_parser("decrypt", help="解密微信数据库")
+ sb_decrypt.add_argument("-k", "--key", type=str, help="密钥", required=True)
+ sb_decrypt.add_argument("-i", "--db_path", type=str, help="数据库路径(目录or文件)", required=True)
+ sb_decrypt.add_argument("-o", "--out_path", type=str,
+ help="输出路径(必须是目录),输出文件为 out_path/de_{original_name}", required=True)
+ return sb_decrypt
+
+ def run(self, args):
+ # 从命令行参数获取值
+ key = args.key
+ db_path = args.db_path
+ out_path = args.out_path
+
+ # 调用 decrypt 函数,并传入参数
+ result = batch_decrypt(key, db_path, out_path)
+ if isinstance(result, list):
+ for i in result:
+ if isinstance(i, str):
+ print(i)
+ else:
+ print(f'[+] "{i[1]}" -> "{os.path.relpath(i[2], out_path)}"')
+ else:
+ print(result)
+
+
+class MainAnalyseWxDb():
+ def init_parses(self, parser):
+ # 添加 'parse_wx_db' 子命令解析器
+ sb_parse_wx_db = parser.add_parser("analyse", help="解析微信数据库(未完成)")
+ sb_parse_wx_db.add_argument("--arg", type=str, help="参数")
+ return sb_parse_wx_db
+
+ def run(self, args):
+ print(f"解析微信数据库(未完成)")
+
+
+class MainAll():
+ def init_parses(self, parser):
+ # 添加 'all' 子命令解析器
+ sb_all = parser.add_parser("all", help="执行所有操作(除获取基址偏移、Analyse)")
+ return sb_all
+
+ def run(self, args):
+ # 获取微信信息
+ args.vlp = "./app/version_list.json"
+ result_WxInfo = MainWxInfo().run(args)
+ keys = [i.get('key', "") for i in result_WxInfo]
+
+ args.require_list = 'all'
+ args.wf = None
+ result_WxDbPath = MainWxDbPath().run(args)
+ wxdbpaths = [path for user_dir in result_WxDbPath.values() for paths in user_dir.values() for path in paths]
+ wxdblen = len(wxdbpaths)
+ print(f"[+] 共 {wxdblen} 个微信数据库(包含所有本地曾登录的微信)")
+ print("=" * 32)
+
+ out_path = os.path.join(os.getcwd(), "decrypted")
+ print(f"[*] 解密后文件夹:{out_path} ")
+ print(f"[*] 解密中...(用时较久,耐心等待)")
+ if not os.path.exists(out_path):
+ os.makedirs(out_path)
+
+ rd = {}
+ for key in keys:
+ rd[key] = batch_decrypt(key, wxdbpaths, out_path)
+
+ result_Decrypt = [None] * wxdblen
+ for i in range(wxdblen):
+ for k, v in rd.items():
+ if isinstance(v[i], list):
+ result_Decrypt[i] = v[i]
+ break
+ else:
+ result_Decrypt[i] = v[i]
+
+ print("[+] 解密完成")
+ print("-" * 32)
+
+ errors = []
+ for i in range(wxdblen):
+ if isinstance(result_Decrypt[i], str):
+ errors.append(i)
+ else:
+ print(
+ f'[+] success "{os.path.relpath(result_Decrypt[i][1], os.path.commonprefix(wxdbpaths))}" -> "{os.path.relpath(result_Decrypt[i][2], os.getcwd())}"')
+ print("-" * 32)
+ print("[-] " + f"共 {len(errors)} 个文件解密失败;")
+ # print("; ".join([f'"{wxdbpaths[i]}"' for i in errors]))
+ print("=" * 32)
+
+
+if __name__ == '__main__':
+ # 创建命令行参数解析器
+ parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
+
+ # 添加子命令解析器
+ subparsers = parser.add_subparsers(dest="mode", help="""运行模式:""")
+
+ # 添加 'bias_addr' 子命令解析器
+ main_bias_addr = MainBiasAddr()
+ sb_bias_addr = main_bias_addr.init_parses(subparsers)
+
+ # 添加 'wx_info' 子命令解析器
+ main_wx_info = MainWxInfo()
+ sb_wx_info = main_wx_info.init_parses(subparsers)
+
+ # 添加 'wx_db_path' 子命令解析器
+ main_wx_db_path = MainWxDbPath()
+ sb_wx_db_path = main_wx_db_path.init_parses(subparsers)
+
+ # 添加 'decrypt' 子命令解析器
+ main_decrypt = MainDecrypt()
+ sb_decrypt = main_decrypt.init_parses(subparsers)
+
+ # 添加 'parse_wx_db' 子命令解析器
+ main_parse_wx_db = MainAnalyseWxDb()
+ sb_parse_wx_db = main_parse_wx_db.init_parses(subparsers)
+
+ # 添加 'all' 子命令解析器
+ main_all = MainAll()
+ sb_all = main_all.init_parses(subparsers)
+
+ args = parser.parse_args() # 解析命令行参数
+
+ # 根据不同的 'mode' 参数,执行不同的操作
+ if args.mode == "bias_addr":
+ main_bias_addr.run(args)
+ elif args.mode == "wx_info":
+ main_wx_info.run(args)
+ elif args.mode == "wx_db":
+ main_wx_db_path.run(args)
+ elif args.mode == "decrypt":
+ main_decrypt.run(args)
+ elif args.mode == "parse":
+ main_parse_wx_db.run(args)
+ elif args.mode == "all":
+ main_all.run(args)