wx-cli/src/scanner/mod.rs

85 lines
2.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "windows")]
mod windows;
/// 扫描到的一条密钥记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyEntry {
/// 相对路径,如 "message/message_0.db"
pub db_name: String,
/// 32字节 AES 密钥hex
pub enc_key: String,
/// 16字节 salthex来自数据库文件头
pub salt: String,
}
/// 从进程内存中扫描所有 SQLCipher 密钥
///
/// 需要以 root/Administrator 权限运行
pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> {
#[cfg(target_os = "macos")]
return macos::scan_keys(db_dir);
#[cfg(target_os = "linux")]
return linux::scan_keys(db_dir);
#[cfg(target_os = "windows")]
return windows::scan_keys(db_dir);
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
{
anyhow::bail!("当前平台不支持自动密钥扫描")
}
}
/// 读取 DB 文件前 16 字节作为 salthex如果是明文 SQLite 则返回 None
pub fn read_db_salt(path: &Path) -> Option<String> {
let mut buf = [0u8; 16];
let mut f = std::fs::File::open(path).ok()?;
use std::io::Read;
f.read_exact(&mut buf).ok()?;
// 明文 SQLite头部是 "SQLite format 3"
if &buf[..15] == b"SQLite format 3" {
return None;
}
Some(hex::encode(&buf))
}
/// 遍历 db_dir收集所有 .db 文件的 salt -> 相对路径 映射
pub fn collect_db_salts(db_dir: &Path) -> Vec<(String, String)> {
let mut result = Vec::new();
collect_recursive(db_dir, db_dir, &mut result);
result
}
fn collect_recursive(base: &Path, dir: &Path, out: &mut Vec<(String, String)>) {
let entries = match std::fs::read_dir(dir) {
Ok(e) => e,
Err(_) => return,
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_recursive(base, &path, out);
} else if path.extension().map(|e| e == "db").unwrap_or(false) {
if let Some(salt) = read_db_salt(&path) {
if let Ok(rel) = path.strip_prefix(base) {
let rel_str = rel.to_string_lossy().replace('\\', "/");
out.push((salt, rel_str));
}
}
}
}
}
// hex encoding helper (avoid adding hex crate by implementing inline)
mod hex {
pub fn encode(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{:02x}", b)).collect()
}
}