mirror of https://github.com/jackwener/wx-cli.git
Merge 42169de2f0 into 08af894594
commit
3696d6d97d
|
|
@ -200,11 +200,6 @@ impl DbCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_with_mode(&self, rel_key: &str) -> Result<Option<CacheResolve>> {
|
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(
|
let db_path = self.db_dir.join(
|
||||||
rel_key
|
rel_key
|
||||||
.replace('\\', std::path::MAIN_SEPARATOR_STR)
|
.replace('\\', std::path::MAIN_SEPARATOR_STR)
|
||||||
|
|
@ -214,6 +209,21 @@ impl DbCache {
|
||||||
return Ok(None);
|
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 wal_path = wal_path_for(&db_path);
|
||||||
let db_mt = mtime_nanos(&db_path);
|
let db_mt = mtime_nanos(&db_path);
|
||||||
let wal_mt = if wal_path.exists() {
|
let wal_mt = if wal_path.exists() {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
/// 通过 /proc/<pid>/mem 读取内存内容,
|
/// 通过 /proc/<pid>/mem 读取内存内容,
|
||||||
/// 搜索 x'<64hex><32hex>' 格式的 SQLCipher 密钥
|
/// 搜索 x'<64hex><32hex>' 格式的 SQLCipher 密钥
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
@ -89,21 +90,33 @@ pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> {
|
||||||
}
|
}
|
||||||
eprintln!("找到 {} 个候选密钥", raw_keys.len());
|
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();
|
let mut entries = Vec::new();
|
||||||
for (key_hex, salt_hex) in &raw_keys {
|
for (key_hex, salt_hex) in &raw_keys {
|
||||||
for (db_salt, db_name) in &db_salts {
|
let db_name = db_salt_map
|
||||||
if salt_hex == db_salt {
|
.get(salt_hex.as_str())
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.unwrap_or_else(|| format!("_by_salt/{}", salt_hex));
|
||||||
entries.push(KeyEntry {
|
entries.push(KeyEntry {
|
||||||
db_name: db_name.clone(),
|
db_name,
|
||||||
enc_key: key_hex.clone(),
|
enc_key: key_hex.clone(),
|
||||||
salt: salt_hex.clone(),
|
salt: salt_hex.clone(),
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
Ok(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
/// 2. WeChat 需要进行 ad-hoc 签名
|
/// 2. WeChat 需要进行 ad-hoc 签名
|
||||||
/// 3. 在内存中搜索 x'<64hex><32hex>' 格式的 SQLCipher 密钥
|
/// 3. 在内存中搜索 x'<64hex><32hex>' 格式的 SQLCipher 密钥
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::{collect_db_salts, KeyEntry};
|
use super::{collect_db_salts, KeyEntry};
|
||||||
|
|
@ -141,22 +142,33 @@ pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> {
|
||||||
let raw_keys = scan_memory(task)?;
|
let raw_keys = scan_memory(task)?;
|
||||||
eprintln!("找到 {} 个候选密钥", raw_keys.len());
|
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();
|
let mut entries = Vec::new();
|
||||||
for (key_hex, salt_hex) in &raw_keys {
|
for (key_hex, salt_hex) in &raw_keys {
|
||||||
for (db_salt, db_name) in &db_salts {
|
let db_name = db_salt_map
|
||||||
if salt_hex == db_salt {
|
.get(salt_hex.as_str())
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.unwrap_or_else(|| format!("_by_salt/{}", salt_hex));
|
||||||
entries.push(KeyEntry {
|
entries.push(KeyEntry {
|
||||||
db_name: db_name.clone(),
|
db_name,
|
||||||
enc_key: key_hex.clone(),
|
enc_key: key_hex.clone(),
|
||||||
salt: salt_hex.clone(),
|
salt: salt_hex.clone(),
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
Ok(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
/// - VirtualQueryEx: 枚举内存区域
|
/// - VirtualQueryEx: 枚举内存区域
|
||||||
/// - ReadProcessMemory: 读取内存内容
|
/// - ReadProcessMemory: 读取内存内容
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use windows::Win32::Foundation::{CloseHandle, HANDLE};
|
use windows::Win32::Foundation::{CloseHandle, HANDLE};
|
||||||
use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory;
|
use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory;
|
||||||
|
|
@ -79,20 +80,33 @@ pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> {
|
||||||
let _ = CloseHandle(process);
|
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();
|
let mut entries = Vec::new();
|
||||||
for (key_hex, salt_hex) in &raw_keys {
|
for (key_hex, salt_hex) in &raw_keys {
|
||||||
for (db_salt, db_name) in &db_salts {
|
let db_name = db_salt_map
|
||||||
if salt_hex == db_salt {
|
.get(salt_hex.as_str())
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.unwrap_or_else(|| format!("_by_salt/{}", salt_hex));
|
||||||
entries.push(KeyEntry {
|
entries.push(KeyEntry {
|
||||||
db_name: db_name.clone(),
|
db_name,
|
||||||
enc_key: key_hex.clone(),
|
enc_key: key_hex.clone(),
|
||||||
salt: salt_hex.clone(),
|
salt: salt_hex.clone(),
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
let matched = entries.iter().filter(|e| !e.db_name.starts_with("_by_salt/")).count();
|
||||||
eprintln!("匹配到 {}/{} 个密钥", entries.len(), raw_keys.len());
|
eprintln!(
|
||||||
|
"匹配到 {}/{} 个密钥(另有 {} 个按 salt 保存)",
|
||||||
|
matched,
|
||||||
|
raw_keys.len(),
|
||||||
|
entries.len() - matched
|
||||||
|
);
|
||||||
Ok(entries)
|
Ok(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue