@@ -43,6 +43,29 @@ 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+
4669def _compute_page_hmac (mac_key : bytes , page : bytes , page_num : int ) -> bytes :
4770 offset = SALT_SIZE if page_num == 1 else 0
4871 data_end = PAGE_SIZE - RESERVE_SIZE + IV_SIZE
@@ -323,8 +346,9 @@ def _clear_last_error(self) -> None:
323346 def decrypt_database (self , db_path : str , output_path : str ) -> bool :
324347 """解密微信4.x版本数据库
325348
326- 这里传入的 key 已经是从微信进程内存提取出的 raw enc_key,
327- 不是 SQLCipher 的口令,因此不能再做一轮 PBKDF2。
349+ 兼容两种输入形态:
350+ - raw enc_key(部分内存扫描/工具直接返回)
351+ - SQLCipher 口令/基础 key(需先用数据库 salt 做一轮 PBKDF2)
328352 """
329353 from .logging_config import get_logger
330354 logger = get_logger (__name__ )
@@ -370,15 +394,14 @@ def decrypt_database(self, db_path: str, output_path: str) -> bool:
370394 tmp_output_path = ""
371395 return True
372396
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 :
397+ resolved_key_material = _resolve_page1_key_material (self .key_bytes , page1 )
398+ if resolved_key_material is None :
378399 message = f"当前数据库密钥不正确,或该密钥不属于当前账号/当前设备: { db_path } "
379400 self ._set_last_error ("key_mismatch" , message )
380401 logger .error (f"页面 1 HMAC验证失败,密钥与数据库不匹配: { db_path } " )
381402 return False
403+ enc_key , mac_key , key_mode = resolved_key_material
404+ logger .info (f"页面 1 HMAC验证通过: mode={ key_mode } path={ db_path } " )
382405
383406 total_pages = (file_size + PAGE_SIZE - 1 ) // PAGE_SIZE
384407 successful_pages = 0
@@ -406,7 +429,7 @@ def decrypt_database(self, db_path: str, output_path: str) -> bool:
406429 logger .error (f"页面 { page_num } HMAC验证失败,终止解密: { db_path } " )
407430 return False
408431
409- target .write (_decrypt_page (self . key_bytes , page , page_num ))
432+ target .write (_decrypt_page (enc_key , page , page_num ))
410433 successful_pages += 1
411434
412435 logger .info (f"解密完成: 成功 { successful_pages } 页, 失败 0 页" )
0 commit comments