Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3ba05a8d9 | ||
|
|
f6850656e7 |
No files matched your search
@@ -1,5 +0,0 @@
|
|||||||
.*
|
|
||||||
!.gitignore
|
|
||||||
|
|
||||||
*.pyc
|
|
||||||
__pycache__
|
|
||||||
@@ -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)
|
|
||||||
@@ -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}")
|
|
||||||
@@ -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,40 +1,3 @@
|
|||||||
# Hack WeChat DB
|
# 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>
|
|
||||||
|
|
||||||

|
|
||||||
</center>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
segno
|
|
||||||
pycryptodome
|
|
||||||
pymem
|
|
||||||
pywin32
|
|
||||||
argparse
|
|
||||||
Reference in new issue
Block a user