Skip to content

Commit 2467d51

Browse files
committed
Revert "fix(decrypt): 修复 SQLCipher 密钥兼容性回归"
This reverts commit bfa468e.
1 parent bfa468e commit 2467d51

2 files changed

Lines changed: 11 additions & 78 deletions

File tree

src/wechat_decrypt_tool/wechat_decrypt.py

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,6 @@ def _derive_mac_key(raw_key: bytes, salt: bytes) -> bytes:
4343
return hashlib.pbkdf2_hmac("sha512", raw_key, mac_salt, 2, dklen=KEY_SIZE)
4444

4545

46-
def _derive_sqlcipher_enc_key(key_material: bytes, salt: bytes) -> bytes:
47-
return hashlib.pbkdf2_hmac("sha512", key_material, salt, 256000, dklen=KEY_SIZE)
48-
49-
50-
def _resolve_page1_key_material(key_material: bytes, page1: bytes) -> tuple[bytes, bytes, str] | None:
51-
salt = page1[:SALT_SIZE]
52-
stored_page1_hmac = page1[PAGE_SIZE - HMAC_SIZE : PAGE_SIZE]
53-
54-
candidates = [
55-
("raw_enc_key", key_material, _derive_mac_key(key_material, salt)),
56-
]
57-
58-
derived_key = _derive_sqlcipher_enc_key(key_material, salt)
59-
candidates.append(("sqlcipher_passphrase", derived_key, _derive_mac_key(derived_key, salt)))
60-
61-
for mode, enc_key, mac_key in candidates:
62-
expected_page1_hmac = _compute_page_hmac(mac_key, page1, 1)
63-
if stored_page1_hmac == expected_page1_hmac:
64-
return enc_key, mac_key, mode
65-
66-
return None
67-
68-
6946
def _compute_page_hmac(mac_key: bytes, page: bytes, page_num: int) -> bytes:
7047
offset = SALT_SIZE if page_num == 1 else 0
7148
data_end = PAGE_SIZE - RESERVE_SIZE + IV_SIZE
@@ -346,9 +323,8 @@ def _clear_last_error(self) -> None:
346323
def decrypt_database(self, db_path: str, output_path: str) -> bool:
347324
"""解密微信4.x版本数据库
348325
349-
兼容两种输入形态:
350-
- raw enc_key(部分内存扫描/工具直接返回)
351-
- SQLCipher 口令/基础 key(需先用数据库 salt 做一轮 PBKDF2)
326+
这里传入的 key 已经是从微信进程内存提取出的 raw enc_key,
327+
不是 SQLCipher 的口令,因此不能再做一轮 PBKDF2。
352328
"""
353329
from .logging_config import get_logger
354330
logger = get_logger(__name__)
@@ -394,14 +370,15 @@ def decrypt_database(self, db_path: str, output_path: str) -> bool:
394370
tmp_output_path = ""
395371
return True
396372

397-
resolved_key_material = _resolve_page1_key_material(self.key_bytes, page1)
398-
if resolved_key_material is None:
373+
salt = page1[:SALT_SIZE]
374+
mac_key = _derive_mac_key(self.key_bytes, salt)
375+
expected_page1_hmac = _compute_page_hmac(mac_key, page1, 1)
376+
stored_page1_hmac = page1[PAGE_SIZE - HMAC_SIZE : PAGE_SIZE]
377+
if stored_page1_hmac != expected_page1_hmac:
399378
message = f"当前数据库密钥不正确,或该密钥不属于当前账号/当前设备: {db_path}"
400379
self._set_last_error("key_mismatch", message)
401380
logger.error(f"页面 1 HMAC验证失败,密钥与数据库不匹配: {db_path}")
402381
return False
403-
enc_key, mac_key, key_mode = resolved_key_material
404-
logger.info(f"页面 1 HMAC验证通过: mode={key_mode} path={db_path}")
405382

406383
total_pages = (file_size + PAGE_SIZE - 1) // PAGE_SIZE
407384
successful_pages = 0
@@ -429,7 +406,7 @@ def decrypt_database(self, db_path: str, output_path: str) -> bool:
429406
logger.error(f"页面 {page_num} HMAC验证失败,终止解密: {db_path}")
430407
return False
431408

432-
target.write(_decrypt_page(enc_key, page, page_num))
409+
target.write(_decrypt_page(self.key_bytes, page, page_num))
433410
successful_pages += 1
434411

435412
logger.info(f"解密完成: 成功 {successful_pages} 页, 失败 0 页")

tests/test_wechat_decrypt_raw_key.py

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,11 @@
1919
SQLITE_HEADER,
2020
WeChatDatabaseDecryptor,
2121
_derive_mac_key,
22-
_derive_sqlcipher_enc_key,
2322
decrypt_wechat_databases,
2423
)
2524

2625

27-
def _encrypt_page(
28-
raw_key: bytes,
29-
plain_page: bytes,
30-
page_num: int,
31-
salt: bytes,
32-
iv: bytes,
33-
*,
34-
sqlcipher_passphrase: bool = False,
35-
) -> bytes:
36-
enc_key = _derive_sqlcipher_enc_key(raw_key, salt) if sqlcipher_passphrase else raw_key
37-
26+
def _encrypt_page(raw_key: bytes, plain_page: bytes, page_num: int, salt: bytes, iv: bytes) -> bytes:
3827
if page_num == 1:
3928
encrypted_input = plain_page[SALT_SIZE : PAGE_SIZE - RESERVE_SIZE]
4029
prefix = salt
@@ -43,15 +32,15 @@ def _encrypt_page(
4332
prefix = b""
4433

4534
cipher = Cipher(
46-
algorithms.AES(enc_key),
35+
algorithms.AES(raw_key),
4736
modes.CBC(iv),
4837
backend=default_backend(),
4938
)
5039
encryptor = cipher.encryptor()
5140
encrypted = encryptor.update(encrypted_input) + encryptor.finalize()
5241

5342
page_without_hmac = prefix + encrypted + iv
54-
mac = hmac.new(_derive_mac_key(enc_key, salt), digestmod=hashlib.sha512)
43+
mac = hmac.new(_derive_mac_key(raw_key, salt), digestmod=hashlib.sha512)
5544
mac.update(page_without_hmac[SALT_SIZE if page_num == 1 else 0 :])
5645
mac.update(page_num.to_bytes(4, "little"))
5746
return page_without_hmac + mac.digest()
@@ -85,39 +74,6 @@ def test_decrypt_database_uses_raw_enc_key(self):
8574
self.assertTrue(decryptor.decrypt_database(str(src), str(dst)))
8675
self.assertEqual(dst.read_bytes(), page1 + page2)
8776

88-
def test_decrypt_database_falls_back_to_sqlcipher_passphrase_mode(self):
89-
passphrase_key = bytes.fromhex("9f5dd0d3b6d0477ea5045c9e380ee272e53927993eb548dd98a022e842d5f7bd")
90-
salt = bytes.fromhex("50f4090ef6897e146f94109f13743e34")
91-
iv1 = bytes.fromhex("0102030405060708090a0b0c0d0e0f10")
92-
iv2 = bytes.fromhex("1112131415161718191a1b1c1d1e1f20")
93-
94-
page1 = _build_plain_page(0x41, first_page=True)
95-
page2 = _build_plain_page(0x42, first_page=False)
96-
encrypted_db = _encrypt_page(
97-
passphrase_key,
98-
page1,
99-
1,
100-
salt,
101-
iv1,
102-
sqlcipher_passphrase=True,
103-
) + _encrypt_page(
104-
passphrase_key,
105-
page2,
106-
2,
107-
salt,
108-
iv2,
109-
sqlcipher_passphrase=True,
110-
)
111-
112-
with tempfile.TemporaryDirectory() as tmpdir:
113-
src = Path(tmpdir) / "source.db"
114-
dst = Path(tmpdir) / "out.db"
115-
src.write_bytes(encrypted_db)
116-
117-
decryptor = WeChatDatabaseDecryptor(passphrase_key.hex())
118-
self.assertTrue(decryptor.decrypt_database(str(src), str(dst)))
119-
self.assertEqual(dst.read_bytes(), page1 + page2)
120-
12177
def test_decrypt_database_keeps_existing_output_on_hmac_failure(self):
12278
good_key = bytes.fromhex("00112233445566778899aabbccddeefffedcba98765432100123456789abcdef")
12379
bad_key_hex = "ffeeddccbbaa998877665544332211000123456789abcdeffedcba9876543210"

0 commit comments

Comments
 (0)