Skip to content

Commit

Permalink
v1.0.13
Browse files Browse the repository at this point in the history
- Support converting db key to sqlcipher rawkey.
  • Loading branch information
0xlane committed Nov 14, 2024
1 parent 36ce739 commit 58abbd9
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wechat-dump-rs"
version = "1.0.12"
version = "1.0.13"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
> **可能存在封号风险,后果自负!!!**
>
> **使用需知**
> 微信4.0 重构后改用 HMAC_SHA512 算法,寻找 key 的方式和 v3 不同,工具内仍然采用内存暴力搜索的方式,对于 v4 解密时将使用多线程加速,会导致 cpu 飙到 100%,等后期优化下,介意勿用!!
> 微信4.0 重构后改用 HMAC_SHA512 算法,寻找 key 的方式和 v3 不同,工具内仍然采用内存暴力搜索的方式,对于 v4 解密时将使用多线程加速,可能会导致 cpu 飙到 100%,取决于 key 离起始查找点的距离。
## 工具用法

```bash
wechat-dump-rs (1.0.11) - REinject
wechat-dump-rs (1.0.13) - REinject
A wechat db dump tool
Options:
-p, --pid <PID> pid of wechat
Expand All @@ -20,6 +20,7 @@ Options:
-o, --output <PATH> decrypted database output path
-a, --all dump key and decrypt db files
--vv <VERSION> wechat db file version [default: 4] [possible values: 3, 4]
-r, --rawkey convert db key to sqlcipher raw key (file is required)
-h, --help Print help
```

Expand All @@ -39,6 +40,24 @@ key: f11fd83bxxxxxx4f3f4x4ddxxxxxe417696b4axx19e09489ad48c

使用参数 `-a` 可以直接导出所有数据库文件。

### 使用 sqlcipher browser 浏览数据库

工具目前对稍微大点的库文件解密后可能存在畸形问题,建议使用 [DB Browser for SQLCipher](https://sqlitebrowser.org/) 进行浏览。

打开 sqlcipher 数据库时,选择 “原始密钥”,微信 V3 选择 sqlcipher3,V4 选择 sqlcipher4,每个数据库文件对应的原始密钥都是不一样的,获取方式如下:

微信 V3 数据库文件 rawkey:

```bash
wechat-dump-rs.exe -k xxxxxxxxxxxxxxxxx -f c:\users\xxxx\xxxx\contact.db -r -vv 3
```

微信 V4 数据库文件 rawkey:

```bash
wechat-dump-rs.exe -k xxxxxxxxxxxxxxxxx -f c:\users\xxxx\xxxx\contact.db -r -vv 4
```

## 原理

一般情况下,key 要在运行的微信进程内存中拿到,内存偏移在每个版本都不一样,大部分工具是对每个版本维护一套偏移,但是当出现新版本的时候都要重新找偏移,方法见后面有简单记录。
Expand All @@ -64,6 +83,7 @@ key: f11fd83bxxxxxx4f3f4x4ddxxxxxe417696b4axx19e09489ad48c
- 3.9.12.15
- 3.9.12.17
- 4.0.0.26
- 4.0.0.32

## 如何手动寻找偏移

Expand Down
38 changes: 37 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,26 @@ fn decrypt_db_file_v4(path: &PathBuf, pkey: &String) -> Result<Vec<u8>> {
Ok(decrypted_buf)
}

fn convert_to_sqlcipher_rawkey(pkey: &str, path: &PathBuf, is_v4: bool) -> Result<String> {
const KEY_SIZE: usize = 32;
const ROUND_COUNT_V4: u32 = 256000;
const ROUND_COUNT_V3: u32 = 64000;
const SALT_SIZE: usize = 16;

let mut file = File::open(path)?;
let mut salt = vec![0; SALT_SIZE];
file.read(salt.as_mut())?;

let pass = hex::decode(pkey)?;
let key = if is_v4 {
pbkdf2_hmac_array::<Sha512, KEY_SIZE>(&pass, &salt, ROUND_COUNT_V4)
} else {
pbkdf2_hmac_array::<Sha1, KEY_SIZE>(&pass, &salt, ROUND_COUNT_V3)
};
let rawkey = [key.as_slice(), &salt].concat();
Ok(format!("0x{}", hex::encode(rawkey)))
}

fn dump_all_by_pid(wechat_info: &WechatInfo, output: &PathBuf) {
let msg_dir = if wechat_info.version.starts_with("4.0") {
wechat_info.data_dir.clone() + "db_storage"
Expand Down Expand Up @@ -990,7 +1010,7 @@ fn cli() -> clap::Command {
use clap::{arg, value_parser, Command};

Command::new("wechat-dump-rs")
.version("1.0.12")
.version("1.0.13")
.about("A wechat db dump tool")
.author("REinject")
.help_template("{name} ({version}) - {author}\n{about}\n{all-args}")
Expand All @@ -1015,6 +1035,7 @@ fn cli() -> clap::Command {
.value_parser(["3", "4"])
.default_value("4"),
)
.arg(arg!(-r --rawkey "convert db key to sqlcipher raw key (file is required)"))
}

fn main() {
Expand Down Expand Up @@ -1068,12 +1089,27 @@ fn main() {
if !file.exists() {
panic!("the target file does not exist");
}

let is_v4 = if matches.get_one::<String>("vv").unwrap() == "4" {
true
} else {
false
};

// convert db key to sqlcipher rawkey
let b_rawkey = matches.get_flag("rawkey");
if b_rawkey {
if file.is_dir() {
panic!("the target file is a directory.");
}

let rawkey = convert_to_sqlcipher_rawkey(&key, &file, is_v4).unwrap();
println!("{}", rawkey);

return;
}
// convert end

match file.is_dir() {
true => {
let dbfiles = scan_db_files(file.to_str().unwrap().to_string()).unwrap();
Expand Down

0 comments on commit 58abbd9

Please sign in to comment.