Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/daemon/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,6 @@ impl DbCache {
}

pub async fn get_with_mode(&self, rel_key: &str) -> Result<Option<CacheResolve>> {
let enc_key_hex = match self.all_keys.get(rel_key) {
Some(k) => k.clone(),
None => return Ok(None),
};

let db_path = self.db_dir.join(
rel_key
.replace('\\', std::path::MAIN_SEPARATOR_STR)
Expand All @@ -214,6 +209,21 @@ impl DbCache {
return Ok(None);
}

let enc_key_hex = match self.all_keys.get(rel_key) {
Some(k) => k.clone(),
None => {
// Fallback: look up by DB salt for keys saved as "_by_salt/<salt>"
// when the DB file wasn't accessible during `wx init`.
let salt = crate::scanner::read_db_salt(&db_path);
match salt.and_then(|s| {
self.all_keys.get(&format!("_by_salt/{}", s)).cloned()
}) {
Some(k) => k,
None => return Ok(None),
}
}
};

let wal_path = wal_path_for(&db_path);
let db_mt = mtime_nanos(&db_path);
let wal_mt = if wal_path.exists() {
Expand Down
35 changes: 24 additions & 11 deletions src/scanner/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/// 通过 /proc/<pid>/mem 读取内存内容,
/// 搜索 x'<64hex><32hex>' 格式的 SQLCipher 密钥
use anyhow::{Context, Result};
use std::collections::HashMap;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

Expand Down Expand Up @@ -89,21 +90,33 @@ pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> {
}
eprintln!("找到 {} 个候选密钥", raw_keys.len());

// 无法匹配 DB 文件的密钥用 _by_salt/<salt> 保存,daemon 会做 salt 回退查找,
// 确保所有在内存中找到的密钥都不会因 DB 文件权限或路径问题而丢失。
let db_salt_map: HashMap<&str, &str> = db_salts
.iter()
.map(|(salt, name)| (salt.as_str(), name.as_str()))
.collect();

let mut entries = Vec::new();
for (key_hex, salt_hex) in &raw_keys {
for (db_salt, db_name) in &db_salts {
if salt_hex == db_salt {
entries.push(KeyEntry {
db_name: db_name.clone(),
enc_key: key_hex.clone(),
salt: salt_hex.clone(),
});
break;
}
}
let db_name = db_salt_map
.get(salt_hex.as_str())
.map(|s| s.to_string())
.unwrap_or_else(|| format!("_by_salt/{}", salt_hex));
entries.push(KeyEntry {
db_name,
enc_key: key_hex.clone(),
salt: salt_hex.clone(),
});
}

eprintln!("匹配到 {}/{} 个密钥", entries.len(), raw_keys.len());
let matched = entries.iter().filter(|e| !e.db_name.starts_with("_by_salt/")).count();
eprintln!(
"匹配到 {}/{} 个密钥(另有 {} 个按 salt 保存)",
matched,
raw_keys.len(),
entries.len() - matched
);
Ok(entries)
}

Expand Down
36 changes: 24 additions & 12 deletions src/scanner/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/// 2. WeChat 需要进行 ad-hoc 签名
/// 3. 在内存中搜索 x'<64hex><32hex>' 格式的 SQLCipher 密钥
use anyhow::{bail, Context, Result};
use std::collections::HashMap;
use std::path::Path;

use super::{collect_db_salts, KeyEntry};
Expand Down Expand Up @@ -141,22 +142,33 @@ pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> {
let raw_keys = scan_memory(task)?;
eprintln!("找到 {} 个候选密钥", raw_keys.len());

// 5. 将密钥与数据库 salt 匹配
// 5. 将密钥与数据库 salt 匹配,无法匹配的用 _by_salt/<salt> 保存,
// 确保所有在内存中找到的密钥都不会因 DB 文件权限或路径问题而丢失。
let db_salt_map: HashMap<&str, &str> = db_salts
.iter()
.map(|(salt, name)| (salt.as_str(), name.as_str()))
.collect();

let mut entries = Vec::new();
for (key_hex, salt_hex) in &raw_keys {
for (db_salt, db_name) in &db_salts {
if salt_hex == db_salt {
entries.push(KeyEntry {
db_name: db_name.clone(),
enc_key: key_hex.clone(),
salt: salt_hex.clone(),
});
break;
}
}
let db_name = db_salt_map
.get(salt_hex.as_str())
.map(|s| s.to_string())
.unwrap_or_else(|| format!("_by_salt/{}", salt_hex));
entries.push(KeyEntry {
db_name,
enc_key: key_hex.clone(),
salt: salt_hex.clone(),
});
}

eprintln!("匹配到 {}/{} 个密钥", entries.len(), raw_keys.len());
let matched = entries.iter().filter(|e| !e.db_name.starts_with("_by_salt/")).count();
eprintln!(
"匹配到 {}/{} 个密钥(另有 {} 个按 salt 保存)",
matched,
raw_keys.len(),
entries.len() - matched
);
Ok(entries)
}

Expand Down
36 changes: 25 additions & 11 deletions src/scanner/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/// - VirtualQueryEx: 枚举内存区域
/// - ReadProcessMemory: 读取内存内容
use anyhow::{Context, Result};
use std::collections::HashMap;
use std::path::Path;
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory;
Expand Down Expand Up @@ -79,20 +80,33 @@ pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> {
let _ = CloseHandle(process);
}

// 无法匹配 DB 文件的密钥用 _by_salt/<salt> 保存,daemon 会做 salt 回退查找,
// 确保所有在内存中找到的密钥都不会因 DB 文件权限或路径问题而丢失。
let db_salt_map: HashMap<&str, &str> = db_salts
.iter()
.map(|(salt, name)| (salt.as_str(), name.as_str()))
.collect();

let mut entries = Vec::new();
for (key_hex, salt_hex) in &raw_keys {
for (db_salt, db_name) in &db_salts {
if salt_hex == db_salt {
entries.push(KeyEntry {
db_name: db_name.clone(),
enc_key: key_hex.clone(),
salt: salt_hex.clone(),
});
break;
}
}
let db_name = db_salt_map
.get(salt_hex.as_str())
.map(|s| s.to_string())
.unwrap_or_else(|| format!("_by_salt/{}", salt_hex));
entries.push(KeyEntry {
db_name,
enc_key: key_hex.clone(),
salt: salt_hex.clone(),
});
}
eprintln!("匹配到 {}/{} 个密钥", entries.len(), raw_keys.len());

let matched = entries.iter().filter(|e| !e.db_name.starts_with("_by_salt/")).count();
eprintln!(
"匹配到 {}/{} 个密钥(另有 {} 个按 salt 保存)",
matched,
raw_keys.len(),
entries.len() - matched
);
Ok(entries)
}

Expand Down