├── .gitignore ├── Cargo.toml ├── README.md └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.idea/ 3 | *.iml 4 | Cargo.lock 5 | .vscode/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "WeChatDB" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0" 10 | log = "0.4" 11 | simple_logger = "4.0.0" 12 | byteorder = "1" 13 | 14 | [target.'cfg(windows)'.dependencies.windows] 15 | version = "0.44.0" 16 | features = [ 17 | "Win32_Foundation", 18 | "Win32_Globalization", 19 | "Win32_Security", 20 | "Win32_Security_Authorization", 21 | "Win32_Security_Cryptography", 22 | "Win32_System", 23 | "Win32_System_Diagnostics_Debug", 24 | "Win32_System_Diagnostics_ToolHelp", 25 | "Win32_System_LibraryLoader", 26 | "Win32_System_Memory", 27 | "Win32_System_ProcessStatus", 28 | "Win32_System_Threading", 29 | ] 30 | 31 | [profile.release] 32 | lto = true 33 | opt-level = 'z' 34 | codegen-units = 1 35 | strip = true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WeChatDB-Rust 2 | 3 | 用Rust语言编写,使用特征值从微信内存中提取数据库密钥的工具 4 | 5 | ## 使用方法 6 | ### 编译 7 | 安装Rust语言编译器,然后执行以下命令: 8 | ```bash 9 | cargo build --release 10 | ``` 11 | 编译出的文件在`target/release`目录下 12 | 13 | ### 运行 14 | ```bash 15 | WeChatDB.exe 即可 16 | ``` 17 | 18 | ## 原理说明 19 | ### 特征值 20 | 微信的内存中存在用户的公私钥对,我们通过搜索`-----BEGIN PUBLIC KEY-----`关键字找到其在内存中的位置, 21 | 并二次搜索其所在的内存地址,这样我们就可以找到用户信息的上下文。原理参考[1] 22 | 23 | ### 搜索算法 24 | 搜索算法采用Sunday算法,思路来自[2],可以在O(n)的时间复杂度内完成搜索,原理参考[3] 25 | 26 | 27 | ## 参考 28 | 1. https://github.com/x1hy9/WeChatUserDB 29 | 2. https://www.jianshu.com/p/2e6eb7386cd3 30 | 3. https://github.com/baiyies/CppWeixinHunter -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; 2 | use log::{debug, info, Level}; 3 | use std::io::{Cursor, Read}; 4 | use std::mem::size_of; 5 | use windows::core::PCSTR; 6 | use windows::s; 7 | use windows::Win32::Foundation::{CloseHandle, GetLastError, HANDLE, HINSTANCE, MAX_PATH}; 8 | use windows::Win32::Globalization::lstrcmpiA; 9 | use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory; 10 | use windows::Win32::System::Diagnostics::ToolHelp::{ 11 | CreateToolhelp32Snapshot, Module32First, Module32Next, Process32First, Process32Next, 12 | MODULEENTRY32, PROCESSENTRY32, TH32CS_SNAPMODULE, TH32CS_SNAPPROCESS, 13 | }; 14 | use windows::Win32::System::LibraryLoader::GetModuleFileNameA; 15 | use windows::Win32::System::Memory::{ 16 | VirtualQueryEx, MEMORY_BASIC_INFORMATION, PAGE_EXECUTE, PAGE_EXECUTE_READ, 17 | PAGE_EXECUTE_READWRITE, PAGE_NOCACHE, PAGE_READONLY, PAGE_READWRITE, 18 | }; 19 | use windows::Win32::System::ProcessStatus::{ 20 | K32EnumProcessModulesEx, K32GetModuleBaseNameA, K32GetModuleInformation, LIST_MODULES_ALL, 21 | MODULEINFO, 22 | }; 23 | use windows::Win32::System::Threading::{ 24 | OpenProcess, PROCESS_ALL_ACCESS, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, 25 | }; 26 | 27 | pub static SEARCH_STR: &str = "-----BEGIN PUBLIC KEY-----"; 28 | 29 | fn read_process_memory( 30 | process_handle: HANDLE, 31 | addr: usize, 32 | size: usize, 33 | ) -> anyhow::Result> { 34 | let mut buffer = vec![0u8; size]; 35 | unsafe { 36 | ReadProcessMemory( 37 | process_handle, 38 | addr as _, 39 | buffer.as_mut_ptr() as _, 40 | size, 41 | None, 42 | ) 43 | .ok()? 44 | }; 45 | Ok(buffer) 46 | } 47 | 48 | fn sunday(pattern: &[u8], text: &[u8]) -> Option { 49 | let mut i = 0; 50 | let mut j = 0; 51 | let mut skip = [pattern.len() + 1; 256]; 52 | for (i, &c) in pattern.iter().enumerate() { 53 | skip[c as usize] = pattern.len() - i; 54 | } 55 | while i < text.len() - pattern.len() { 56 | j = 0; 57 | while j < pattern.len() && pattern[j] == text[i + j] { 58 | j += 1; 59 | } 60 | if j == pattern.len() { 61 | return Some(i); 62 | } 63 | i += skip[text[i + pattern.len()] as usize]; 64 | } 65 | None 66 | } 67 | 68 | unsafe fn search_memory( 69 | pattern: &[u8], 70 | process_handle: HANDLE, 71 | start_addr: usize, 72 | ) -> anyhow::Result { 73 | let mut mem_basic_info = MEMORY_BASIC_INFORMATION::default(); 74 | let mut start_addr = start_addr; 75 | loop { 76 | let ret = VirtualQueryEx( 77 | process_handle, 78 | Some(start_addr as _), 79 | &mut mem_basic_info, 80 | size_of::(), 81 | ); 82 | if ret == 0 { 83 | GetLastError().ok()?; 84 | } else { 85 | } 86 | 87 | if (mem_basic_info.Protect == PAGE_READONLY 88 | || mem_basic_info.Protect == PAGE_READWRITE 89 | || mem_basic_info.Protect == PAGE_EXECUTE_READ 90 | || mem_basic_info.Protect == PAGE_EXECUTE_READWRITE) 91 | && mem_basic_info.RegionSize != 1 92 | { 93 | let buf_length = 94 | mem_basic_info.BaseAddress as usize + mem_basic_info.RegionSize - start_addr; 95 | if buf_length > 0xF000000 { 96 | start_addr += mem_basic_info.RegionSize; 97 | continue; 98 | } 99 | let mut buf = vec![0u8; buf_length]; 100 | ReadProcessMemory( 101 | process_handle, 102 | start_addr as _, 103 | buf.as_mut_ptr() as _, 104 | buf_length, 105 | None, 106 | ) 107 | .ok()?; 108 | if let Some(idx) = sunday(pattern, &buf) { 109 | return Ok(start_addr + idx); 110 | } 111 | } 112 | start_addr += mem_basic_info.RegionSize; 113 | } 114 | } 115 | 116 | fn main() -> anyhow::Result<()> { 117 | simple_logger::init_with_level(Level::Info)?; 118 | 119 | unsafe { 120 | let handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)?; 121 | let mut entry = PROCESSENTRY32::default(); 122 | entry.dwSize = size_of::() as u32; 123 | Process32First(handle, &mut entry).ok()?; 124 | 125 | loop { 126 | if lstrcmpiA(PCSTR(entry.szExeFile.as_ptr() as _), s!("WeChat.exe")) == 0 { 127 | debug!("found target process pid={}", entry.th32ProcessID); 128 | break; 129 | } 130 | Process32Next(handle, &mut entry).ok()?; 131 | } 132 | 133 | let process_handle = OpenProcess(PROCESS_ALL_ACCESS, false, entry.th32ProcessID)?; 134 | debug!("open process success pHandle={:?}", process_handle); 135 | let mut wechatwin_base_addr = 0; 136 | let mut instance = [HINSTANCE::default(); 1024]; 137 | let mut lpcb_needed = 0; 138 | K32EnumProcessModulesEx( 139 | process_handle, 140 | instance.as_mut_ptr(), 141 | size_of::() as u32 * 1024, 142 | &mut lpcb_needed, 143 | LIST_MODULES_ALL, 144 | ) 145 | .ok()?; 146 | debug!("lpcb_needed={}", lpcb_needed); 147 | for module in instance.iter() { 148 | let mut info = MODULEINFO::default(); 149 | debug!("module={:?}", module); 150 | K32GetModuleInformation( 151 | process_handle, 152 | *module, 153 | &mut info, 154 | size_of::() as _, 155 | ) 156 | .ok()?; 157 | let mut name = vec![0 as u8; MAX_PATH as usize]; 158 | let length = K32GetModuleBaseNameA(process_handle, *module, &mut name); 159 | let name = String::from_utf8(name[..length as usize].to_vec())?; 160 | if name == "WeChatWin.dll" { 161 | wechatwin_base_addr = info.lpBaseOfDll as usize; 162 | debug!("WeChatWin=0x{:x}", wechatwin_base_addr); 163 | break; 164 | } 165 | } 166 | 167 | let mut mem_basic_info = MEMORY_BASIC_INFORMATION::default(); 168 | let mut start_addr = 0x327000; 169 | let reference; 170 | loop { 171 | let pubkey_addr = search_memory(SEARCH_STR.as_bytes(), process_handle, start_addr)?; 172 | debug!("pubkey_addr=0x{:x}", pubkey_addr); 173 | start_addr = pubkey_addr + 4; 174 | 175 | let mut buf = Vec::new(); 176 | buf.write_u32::(pubkey_addr as u32)?; 177 | debug!("buf={:?}", buf); 178 | reference = match search_memory(&buf, process_handle, wechatwin_base_addr) { 179 | Ok(reference) => reference, 180 | Err(_) => continue, 181 | }; 182 | debug!("reference=0x{:x}", reference); 183 | break; 184 | } 185 | 186 | let mut cursor = Cursor::new(read_process_memory(process_handle, reference - 0x5c, 4)?); 187 | let username_length = cursor.read_u32::()?; 188 | let username = 189 | read_process_memory(process_handle, reference - 0x6c, username_length as usize)?; 190 | info!("username={}", String::from_utf8(username)?); 191 | 192 | let mut cursor = Cursor::new(read_process_memory(process_handle, reference - 0x44, 4)?); 193 | let wxid_length = cursor.read_u32::()?; 194 | let mut cursor = Cursor::new(read_process_memory(process_handle, reference - 0x54, 4)?); 195 | let wxid_addr = cursor.read_u32::()?; 196 | 197 | let wxid = read_process_memory(process_handle, wxid_addr as usize, wxid_length as usize)?; 198 | info!("wxid={}", String::from_utf8(wxid)?); 199 | 200 | let mobile_type_length = 201 | Cursor::new(read_process_memory(process_handle, reference - 0xc, 4)?) 202 | .read_u32::()?; 203 | let mobile_type = read_process_memory( 204 | process_handle, 205 | reference - 0x1c, 206 | mobile_type_length as usize, 207 | )?; 208 | info!("mobile_type={}", String::from_utf8(mobile_type)?); 209 | 210 | // let tel_length = Cursor::new(read_process_memory(process_handle, reference - 0x47c, 4)?).read_u32::()?; 211 | // let tel = read_process_memory(process_handle, reference - 0x48c, tel_length as usize)?; 212 | // info!("tel={}", String::from_utf8(tel)?); 213 | 214 | let sqlite_key_length = 215 | Cursor::new(read_process_memory(process_handle, reference - 0x8c, 4)?) 216 | .read_u32::()?; 217 | let sqlite_key_addr = 218 | Cursor::new(read_process_memory(process_handle, reference - 0x90, 4)?) 219 | .read_u32::()?; 220 | let sqlite_key = read_process_memory( 221 | process_handle, 222 | sqlite_key_addr as usize, 223 | sqlite_key_length as usize, 224 | )?; 225 | // info!("sqlite_key={:?}", sqlite_key); 226 | let mut output = String::new(); 227 | output += "["; 228 | for byte in sqlite_key.iter() { 229 | output += &format!("0x{:02x},", byte); 230 | } 231 | output += "]"; 232 | info!("sqlite_key={}", output); 233 | 234 | let pubkey_length = Cursor::new(read_process_memory(process_handle, reference + 0x10, 4)?) 235 | .read_u32::()?; 236 | let pubkey_addr = Cursor::new(read_process_memory(process_handle, reference, 4)?) 237 | .read_u32::()?; 238 | let pubkey = 239 | read_process_memory(process_handle, pubkey_addr as usize, pubkey_length as usize)?; 240 | info!("pubkey={:?}", String::from_utf8(pubkey)?); 241 | 242 | let private_key_length = 243 | Cursor::new(read_process_memory(process_handle, reference + 0x28, 4)?) 244 | .read_u32::()?; 245 | let private_key_addr = 246 | Cursor::new(read_process_memory(process_handle, reference + 0x18, 4)?) 247 | .read_u32::()?; 248 | let private_key = read_process_memory( 249 | process_handle, 250 | private_key_addr as usize, 251 | private_key_length as usize, 252 | )?; 253 | info!("private_key={:?}", String::from_utf8(private_key)?); 254 | 255 | CloseHandle(handle); 256 | CloseHandle(process_handle); 257 | 258 | Ok(()) 259 | } 260 | } 261 | --------------------------------------------------------------------------------