mirror of https://github.com/jackwener/wx-cli.git
Merge 9870977d0a into 08af894594
commit
34d84d16c1
|
|
@ -9,6 +9,9 @@
|
|||
/// 1. 需要以 root (sudo) 运行
|
||||
/// 2. WeChat 需要进行 ad-hoc 签名
|
||||
/// 3. 在内存中搜索 x'<64hex><32hex>' 格式的 SQLCipher 密钥
|
||||
///
|
||||
/// v1.1: 支持扫描 WeChat 主进程 + WeChatAppEx Helper 进程。
|
||||
/// 微信 3.8+ 将数据库操作分离到 WeChatAppEx,两个进程都需要扫描。
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::path::Path;
|
||||
|
||||
|
|
@ -77,9 +80,10 @@ extern "C" {
|
|||
) -> kern_return_t;
|
||||
}
|
||||
|
||||
/// 查找 WeChat 进程的 PID
|
||||
fn find_wechat_pid() -> Option<libc::pid_t> {
|
||||
// 使用 pgrep -x WeChat 查找(与 C 版本一致)
|
||||
/// 查找所有 WeChat 相关进程的 PID
|
||||
/// 微信 3.8+ 将数据库操作分离到 WeChatAppEx Helper 进程,
|
||||
/// 需要扫描主 WeChat 进程和所有 WeChatAppEx 进程才能找到密钥。
|
||||
fn find_wechat_pids() -> Option<Vec<libc::pid_t>> {
|
||||
let output = std::process::Command::new("pgrep")
|
||||
.args(["-x", "WeChat"])
|
||||
.output()
|
||||
|
|
@ -88,7 +92,15 @@ fn find_wechat_pid() -> Option<libc::pid_t> {
|
|||
return None;
|
||||
}
|
||||
let s = String::from_utf8_lossy(&output.stdout);
|
||||
s.trim().parse().ok()
|
||||
let pids: Vec<libc::pid_t> = s
|
||||
.lines()
|
||||
.filter_map(|l| l.trim().parse().ok())
|
||||
.collect();
|
||||
if pids.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(pids)
|
||||
}
|
||||
}
|
||||
|
||||
/// 判断字节是否是 ASCII 十六进制字符
|
||||
|
|
@ -98,14 +110,59 @@ fn is_hex_char(c: u8) -> bool {
|
|||
}
|
||||
|
||||
pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> {
|
||||
// 1. 查找 WeChat PID
|
||||
let pid = find_wechat_pid()
|
||||
// 1. 查找所有 WeChat 相关进程
|
||||
let pids = find_wechat_pids()
|
||||
.context("找不到 WeChat 进程,请确认 WeChat 正在运行")?;
|
||||
eprintln!("WeChat PID: {}", pid);
|
||||
eprintln!("找到 {} 个 WeChat 相关进程: {:?}", pids.len(), pids);
|
||||
|
||||
// 2. 获取 task port
|
||||
// SAFETY: task_for_pid 是标准 Mach API,参数合法
|
||||
let task = unsafe {
|
||||
// 2. 收集数据库 salt 映射
|
||||
eprintln!("扫描数据库文件...");
|
||||
let db_salts = collect_db_salts(db_dir);
|
||||
eprintln!("找到 {} 个加密数据库", db_salts.len());
|
||||
|
||||
// 3. 扫描所有进程内存,收集所有候选密钥
|
||||
let mut all_raw_keys: Vec<(String, String)> = Vec::new();
|
||||
for pid in &pids {
|
||||
eprintln!("扫描进程 {} 内存...", pid);
|
||||
match scan_single_process(*pid) {
|
||||
Ok(keys) => {
|
||||
eprintln!(" 进程 {}: 找到 {} 个候选密钥", pid, keys.len());
|
||||
all_raw_keys.extend(keys);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(" 进程 {}: 跳过 ({})", pid, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 去重(跨进程可能找到相同密钥)
|
||||
all_raw_keys.sort_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1.cmp(&b.1)));
|
||||
all_raw_keys.dedup();
|
||||
|
||||
eprintln!("候选密钥去重后: {} 个", all_raw_keys.len());
|
||||
|
||||
// 4. 将密钥与数据库 salt 匹配
|
||||
let mut entries = Vec::new();
|
||||
for (key_hex, salt_hex) in &all_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("匹配到 {}/{} 个密钥", entries.len(), all_raw_keys.len());
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
/// 获取单个进程的 task port
|
||||
fn get_task_port(pid: libc::pid_t) -> Result<mach_port_t> {
|
||||
unsafe {
|
||||
let mut task: mach_port_t = 0;
|
||||
let kr = task_for_pid(mach_task_self(), pid, &mut task);
|
||||
if kr != KERN_SUCCESS {
|
||||
|
|
@ -127,37 +184,15 @@ pub fn scan_keys(db_dir: &Path) -> Result<Vec<KeyEntry>> {
|
|||
kr
|
||||
);
|
||||
}
|
||||
task
|
||||
};
|
||||
eprintln!("Got task port: {}", task);
|
||||
|
||||
// 3. 收集数据库 salt 映射
|
||||
eprintln!("扫描数据库文件...");
|
||||
let db_salts = collect_db_salts(db_dir);
|
||||
eprintln!("找到 {} 个加密数据库", db_salts.len());
|
||||
|
||||
// 4. 扫描进程内存
|
||||
eprintln!("扫描进程内存寻找密钥...");
|
||||
let raw_keys = scan_memory(task)?;
|
||||
eprintln!("找到 {} 个候选密钥", raw_keys.len());
|
||||
|
||||
// 5. 将密钥与数据库 salt 匹配
|
||||
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;
|
||||
}
|
||||
}
|
||||
Ok(task)
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("匹配到 {}/{} 个密钥", entries.len(), raw_keys.len());
|
||||
Ok(entries)
|
||||
/// 扫描单个进程的内存
|
||||
fn scan_single_process(pid: libc::pid_t) -> Result<Vec<(String, String)>> {
|
||||
let task = get_task_port(pid)?;
|
||||
eprintln!(" Got task port: {}", task);
|
||||
scan_memory(task)
|
||||
}
|
||||
|
||||
/// 扫描进程内存,返回 (key_hex, salt_hex) 列表
|
||||
|
|
|
|||
Loading…
Reference in New Issue