|
10 | 10 | /// 2. WeChat 需要进行 ad-hoc 签名 |
11 | 11 | /// 3. 在内存中搜索 x'<64hex><32hex>' 格式的 SQLCipher 密钥 |
12 | 12 | use anyhow::{bail, Context, Result}; |
| 13 | +use std::collections::HashMap; |
13 | 14 | use std::path::Path; |
14 | 15 |
|
15 | 16 | use super::{collect_db_salts, KeyEntry}; |
@@ -141,22 +142,34 @@ pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> { |
141 | 142 | let raw_keys = scan_memory(task)?; |
142 | 143 | eprintln!("找到 {} 个候选密钥", raw_keys.len()); |
143 | 144 |
|
144 | | - // 5. 将密钥与数据库 salt 匹配 |
| 145 | + // 5. 将密钥与数据库 salt 匹配,无法匹配的用 _by_salt/<salt> 保存 |
| 146 | + // 与 khipuchat 脚本行为一致:所有在内存中找到的密钥都保留, |
| 147 | + // 避免因 DB 文件权限或路径问题导致密钥丢失。 |
| 148 | + let db_salt_map: HashMap<&str, &str> = db_salts |
| 149 | + .iter() |
| 150 | + .map(|(salt, name)| (salt.as_str(), name.as_str())) |
| 151 | + .collect(); |
| 152 | + |
145 | 153 | let mut entries = Vec::new(); |
146 | 154 | for (key_hex, salt_hex) in &raw_keys { |
147 | | - for (db_salt, db_name) in &db_salts { |
148 | | - if salt_hex == db_salt { |
149 | | - entries.push(KeyEntry { |
150 | | - db_name: db_name.clone(), |
151 | | - enc_key: key_hex.clone(), |
152 | | - salt: salt_hex.clone(), |
153 | | - }); |
154 | | - break; |
155 | | - } |
156 | | - } |
| 155 | + let db_name = db_salt_map |
| 156 | + .get(salt_hex.as_str()) |
| 157 | + .map(|s| s.to_string()) |
| 158 | + .unwrap_or_else(|| format!("_by_salt/{}", salt_hex)); |
| 159 | + entries.push(KeyEntry { |
| 160 | + db_name, |
| 161 | + enc_key: key_hex.clone(), |
| 162 | + salt: salt_hex.clone(), |
| 163 | + }); |
157 | 164 | } |
158 | 165 |
|
159 | | - eprintln!("匹配到 {}/{} 个密钥", entries.len(), raw_keys.len()); |
| 166 | + let matched = entries.iter().filter(|e| !e.db_name.starts_with("_by_salt/")).count(); |
| 167 | + eprintln!( |
| 168 | + "匹配到 {}/{} 个密钥(另有 {} 个按 salt 保存)", |
| 169 | + matched, |
| 170 | + raw_keys.len(), |
| 171 | + entries.len() - matched |
| 172 | + ); |
160 | 173 | Ok(entries) |
161 | 174 | } |
162 | 175 |
|
|
0 commit comments