-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathconfig.py
More file actions
220 lines (193 loc) · 8.01 KB
/
config.py
File metadata and controls
220 lines (193 loc) · 8.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
"""
配置加载器 - 从 config.json 读取路径配置
首次运行时自动检测微信数据目录,检测失败则提示手动配置
"""
import glob
import json
import os
import platform
import sys
CONFIG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")
_SYSTEM = platform.system().lower()
if _SYSTEM == "linux":
_DEFAULT_TEMPLATE_DIR = os.path.expanduser("~/Documents/xwechat_files/your_wxid/db_storage")
_DEFAULT_PROCESS = "wechat"
elif _SYSTEM == "darwin":
# macOS 使用独立的 C 扫描器 (find_all_keys_macos.c),此处仅提供 config 默认值
_DEFAULT_TEMPLATE_DIR = os.path.expanduser("~/Documents/xwechat_files/your_wxid/db_storage")
_DEFAULT_PROCESS = "WeChat"
else:
_DEFAULT_TEMPLATE_DIR = r"D:\xwechat_files\your_wxid\db_storage"
_DEFAULT_PROCESS = "Weixin.exe"
_DEFAULT = {
"db_dir": _DEFAULT_TEMPLATE_DIR,
"keys_file": "all_keys.json",
"decrypted_dir": "decrypted",
"decoded_image_dir": "decoded_images",
"wechat_process": _DEFAULT_PROCESS,
}
def _choose_candidate(candidates):
"""在多个候选目录中选择一个。"""
if len(candidates) == 1:
return candidates[0]
if len(candidates) > 1:
if not sys.stdin.isatty():
return candidates[0]
print("[!] 检测到多个微信数据目录(请选择当前正在运行的微信账号):")
for i, c in enumerate(candidates, 1):
print(f" {i}. {c}")
print(" 0. 跳过,稍后手动配置")
try:
while True:
choice = input("请选择 [0-{}]: ".format(len(candidates))).strip()
if choice == "0":
return None
if choice.isdigit() and 1 <= int(choice) <= len(candidates):
return candidates[int(choice) - 1]
print(" 无效输入,请重新选择")
except (EOFError, KeyboardInterrupt):
print()
return None
return None
def _auto_detect_db_dir_windows():
"""从微信本地配置自动检测 Windows db_storage 路径。
读取 %APPDATA%\\Tencent\\xwechat\\config\\*.ini,
找到数据存储根目录,然后匹配 xwechat_files\\*\\db_storage。
"""
appdata = os.environ.get("APPDATA", "")
config_dir = os.path.join(appdata, "Tencent", "xwechat", "config")
if not os.path.isdir(config_dir):
return None
# 从 ini 文件中找到有效的目录路径
data_roots = []
for ini_file in glob.glob(os.path.join(config_dir, "*.ini")):
try:
# 微信 ini 可能是 utf-8 或 gbk 编码(中文路径)
content = None
for enc in ("utf-8", "gbk"):
try:
with open(ini_file, "r", encoding=enc) as f:
content = f.read(1024).strip()
break
except UnicodeDecodeError:
continue
if not content or any(c in content for c in "\n\r\x00"):
continue
if os.path.isdir(content):
data_roots.append(content)
except OSError:
continue
# 在每个根目录下搜索 xwechat_files\*\db_storage
seen = set()
candidates = []
for root in data_roots:
pattern = os.path.join(root, "xwechat_files", "*", "db_storage")
for match in glob.glob(pattern):
normalized = os.path.normcase(os.path.normpath(match))
if os.path.isdir(match) and normalized not in seen:
seen.add(normalized)
candidates.append(match)
return _choose_candidate(candidates)
def _auto_detect_db_dir_linux():
"""自动检测 Linux 微信 db_storage 路径。
优先搜索当前用户的 home 目录。以 sudo 运行时通过 SUDO_USER 回退到
实际用户的 home,避免只搜索 /root 而遗漏真实数据目录。
"""
seen = set()
candidates = []
search_roots = [
os.path.expanduser("~/Documents/xwechat_files"),
]
# sudo 运行时,~ 展开为 /root;回退到实际用户的 home
sudo_user = os.environ.get("SUDO_USER")
if sudo_user:
# 验证 SUDO_USER 是合法系统用户,防止路径注入
import pwd
try:
sudo_home = pwd.getpwnam(sudo_user).pw_dir
except KeyError:
sudo_home = None
if sudo_home:
fallback = os.path.join(sudo_home, "Documents", "xwechat_files")
if fallback not in search_roots:
search_roots.append(fallback)
for root in search_roots:
if not os.path.isdir(root):
continue
pattern = os.path.join(root, "*", "db_storage")
for match in glob.glob(pattern):
normalized = os.path.normcase(os.path.normpath(match))
if os.path.isdir(match) and normalized not in seen:
seen.add(normalized)
candidates.append(match)
# 早期 Linux 微信版本(wine/容器方案)使用的数据路径
old_path = os.path.expanduser("~/.local/share/weixin/data/db_storage")
if os.path.isdir(old_path):
normalized = os.path.normcase(os.path.normpath(old_path))
if normalized not in seen:
candidates.append(old_path)
# 优先使用最近活跃账号:按 message 目录 mtime 降序(近似排序,best-effort)
def _mtime(path):
msg_dir = os.path.join(path, "message")
target = msg_dir if os.path.isdir(msg_dir) else path
try:
return os.path.getmtime(target)
except OSError:
return 0
candidates.sort(key=_mtime, reverse=True)
return _choose_candidate(candidates)
def auto_detect_db_dir():
if _SYSTEM == "windows":
return _auto_detect_db_dir_windows()
if _SYSTEM == "linux":
return _auto_detect_db_dir_linux()
return None
def load_config():
cfg = {}
if os.path.exists(CONFIG_FILE):
try:
with open(CONFIG_FILE) as f:
cfg = json.load(f)
except json.JSONDecodeError:
print(f"[!] {CONFIG_FILE} 格式损坏,将使用默认配置")
cfg = {}
# db_dir 缺失或仍为模板值时,尝试自动检测
db_dir = cfg.get("db_dir", "")
if not db_dir or db_dir == _DEFAULT_TEMPLATE_DIR or "your_wxid" in db_dir:
detected = auto_detect_db_dir()
if detected:
print(f"[+] 自动检测到微信数据目录: {detected}")
cfg = {**_DEFAULT, **cfg, "db_dir": detected}
with open(CONFIG_FILE, "w") as f:
json.dump(cfg, f, indent=4, ensure_ascii=False)
print(f"[+] 已保存到: {CONFIG_FILE}")
else:
if not os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, "w") as f:
json.dump(_DEFAULT, f, indent=4, ensure_ascii=False)
print(f"[!] 未能自动检测微信数据目录")
print(f" 请手动编辑 {CONFIG_FILE} 中的 db_dir 字段")
if _SYSTEM == "linux":
print(" Linux 默认路径类似: ~/Documents/xwechat_files/<wxid>/db_storage")
else:
print(f" 路径可在 微信设置 → 文件管理 中找到")
sys.exit(1)
else:
cfg = {**_DEFAULT, **cfg}
# 将相对路径转为绝对路径
base = os.path.dirname(os.path.abspath(__file__))
for key in ("keys_file", "decrypted_dir", "decoded_image_dir"):
if key in cfg and not os.path.isabs(cfg[key]):
cfg[key] = os.path.join(base, cfg[key])
# 自动推导微信数据根目录(db_dir 的上级目录)
# db_dir 格式: D:\xwechat_files\<wxid>\db_storage
# base_dir 格式: D:\xwechat_files\<wxid>
db_dir = cfg.get("db_dir", "")
if db_dir and os.path.basename(db_dir) == "db_storage":
cfg["wechat_base_dir"] = os.path.dirname(db_dir)
else:
cfg["wechat_base_dir"] = db_dir
# decoded_image_dir 默认值
if "decoded_image_dir" not in cfg:
cfg["decoded_image_dir"] = os.path.join(base, "decoded_images")
return cfg