diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 4b4099f..b19531a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -520,6 +520,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -753,6 +772,12 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "embed-resource" version = "2.5.1" @@ -864,11 +889,13 @@ dependencies = [ "memmap2", "serde", "serde_json", + "sysinfo", "tauri", "tauri-build", "tauri-plugin-dialog", "tauri-plugin-shell", "thiserror 2.0.4", + "windows 0.58.0", ] [[package]] @@ -2047,6 +2074,15 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -2809,6 +2845,26 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.7" @@ -3395,6 +3451,20 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "sysinfo" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -3441,7 +3511,7 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows", + "windows 0.58.0", "windows-core 0.58.0", "windows-version", "x11-dl", @@ -3511,7 +3581,7 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows", + "windows 0.58.0", ] [[package]] @@ -3672,7 +3742,7 @@ dependencies = [ "tauri-utils", "thiserror 2.0.4", "url", - "windows", + "windows 0.58.0", ] [[package]] @@ -3697,7 +3767,7 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.58.0", "wry", ] @@ -4416,10 +4486,10 @@ checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", + "windows 0.58.0", "windows-core 0.58.0", - "windows-implement", - "windows-interface", + "windows-implement 0.58.0", + "windows-interface 0.58.0", ] [[package]] @@ -4440,7 +4510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" dependencies = [ "thiserror 1.0.69", - "windows", + "windows 0.58.0", "windows-core 0.58.0", ] @@ -4489,6 +4559,16 @@ dependencies = [ "windows-version", ] +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.58.0" @@ -4508,19 +4588,42 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", "windows-strings", "windows-targets 0.52.6", ] +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -4532,6 +4635,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -4549,11 +4663,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-strings", "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -4569,7 +4692,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] @@ -4873,7 +4996,7 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", + "windows 0.58.0", "windows-core 0.58.0", "windows-version", "x11-dl", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 09b1c5c..f378f9b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -18,11 +18,23 @@ name = "test_tauri_lib" tauri-build = {version = "2", features = [] } [dependencies] -bitflags = {version= "2.6.0", features = ["serde"] } +bitflags = {version = "2.6.0", features = ["serde"] } memmap2 = "0.9.5" serde = {version = "1", features = ["derive"] } serde_json = "1" +sysinfo = "0.33.0" tauri = {version = "2", features = [] } tauri-plugin-dialog = "2" tauri-plugin-shell = "2" thiserror = "2.0.4" + +[dependencies.windows] +version = "0.58.0" +features = [ + "Data_Xml_Dom", + "Win32_Foundation", + "Win32_Security", + "Win32_System_Threading", + "Win32_UI_WindowsAndMessaging", + "Win32_System_ProcessStatus" +] diff --git a/src-tauri/src/app_error.rs b/src-tauri/src/app_error.rs index 5f1afcc..33d25f5 100644 --- a/src-tauri/src/app_error.rs +++ b/src-tauri/src/app_error.rs @@ -38,6 +38,11 @@ pub enum AppError { /// 节名过长 #[error("节名过长!")] SectionNameTooLong, + + #[error("打开进程失败!: {0}")] + OpenProcessError(String), + #[error("查询进程路径失败!: {0}")] + QueryFullProcessImageNameWError(String), } impl serde::Serialize for AppError { diff --git a/src-tauri/src/app_state.rs b/src-tauri/src/app_state.rs index 7ddec0b..0b343a0 100644 --- a/src-tauri/src/app_state.rs +++ b/src-tauri/src/app_state.rs @@ -128,7 +128,6 @@ impl AppState { Ok(()) } - pub fn get_import_tables(&self) -> Result, AppError> { let mmap = self.get_mmap_ref()?; let is_64_bit = self.is_64_bit.unwrap(); @@ -204,31 +203,29 @@ impl AppState { } }; let mut import_function_table = ImportFunctionTable::default(); - let hint_name_table_rva = import_lookup_table as u32 & 0x7FFFFFFF; - let hint_name_table_foa = mmap.rva_to_foa(hint_name_table_rva)?; - // 1. 读hint 两个字节 - let funtion_hint: u16 = unsafe { - let ptr = mmap[hint_name_table_foa as usize..].as_ptr() as *const u16; - ptr.read() - }; - import_function_table.function_hint = funtion_hint; - import_function_table.function_address = hint_name_table_rva; - match is_named { - true => { - // 命名函数 - let function_name = unsafe { - let ptr = - mmap[hint_name_table_foa as usize + 2..].as_ptr() as *const i8; - CStr::from_ptr(ptr).to_str()?.to_string() - }; - import_function_table.function_name = function_name; - functions.push(import_function_table); - } - false => { - import_function_table.function_type = ImportFunctionTableItem::Ordinal; - functions.push(import_function_table); - } + + if is_named == true { + // TODO: 仅当序号/名称标志位字段为 0(按名称导入)时,才使用此字段。 + let hint_name_table_rva = import_lookup_table as u32 & 0x7FFFFFFF; + let hint_name_table_foa = mmap.rva_to_foa(hint_name_table_rva)?; + // 1. 读hint 两个字节 + let funtion_hint: u16 = unsafe { + let ptr = mmap[hint_name_table_foa as usize..].as_ptr() as *const u16; + ptr.read() + }; + import_function_table.function_hint = funtion_hint; + import_function_table.function_address = hint_name_table_rva; + let function_name = unsafe { + let ptr = mmap[hint_name_table_foa as usize + 2..].as_ptr() as *const i8; + CStr::from_ptr(ptr).to_str()?.to_string() + }; + import_function_table.function_name = function_name; + } else { + let function_ordianl = import_lookup_table as u32 & ImportLookupTable32::IMPORT_BY_ORDINAL_MASK.bits(); + import_function_table.function_hint = function_ordianl as u16; + import_function_table.function_type = ImportFunctionTableItem::Ordinal; } + functions.push(import_function_table); // 下一个 base_offset += if is_64_bit { 8 } else { 4 }; } @@ -242,13 +239,13 @@ impl AppState { // 定义一个获取导入表后需要返回的结构 #[derive(Debug, Default, Clone, Serialize)] pub struct ImportModuleTable { - pub module_name: String, // 模块名称 + pub module_name: String, // 模块名称 pub functions: Vec, // 这里要另一个结构体来描述 - pub timestamp: u32, // 时间戳 - pub forwarder_chain: u32, // 转发链 - pub module_name_rva: u32, // dll名称的RVA - pub iat_rva: u32, // IAT的RVA - pub int_rva: u32, // INT的RVA + pub timestamp: u32, // 时间戳 + pub forwarder_chain: u32, // 转发链 + pub module_name_rva: u32, // dll名称的RVA + pub iat_rva: u32, // IAT的RVA + pub int_rva: u32, // INT的RVA } // 因为导入查找表可能是函数名、也可能是序号 所以用枚举包一下最好 @@ -266,5 +263,5 @@ pub struct ImportFunctionTable { pub function_name: String, // 函数名称 pub function_address: u32, // 函数地址 IAT pub function_hint: u16, // 函数提示 - pub function_type: ImportFunctionTableItem + pub function_type: ImportFunctionTableItem, } diff --git a/src-tauri/src/services/mod.rs b/src-tauri/src/services/mod.rs index 026a865..c22ebee 100644 --- a/src-tauri/src/services/mod.rs +++ b/src-tauri/src/services/mod.rs @@ -4,6 +4,7 @@ use crate::pe_parse::pe::{ MutablePE }; pub mod file; +pub mod win_proc; // 为文件映射实现PE结构 impl ReadOnlyPE for Mmap {} diff --git a/src-tauri/src/services/win_proc.rs b/src-tauri/src/services/win_proc.rs new file mode 100644 index 0000000..087aec7 --- /dev/null +++ b/src-tauri/src/services/win_proc.rs @@ -0,0 +1,213 @@ +use std::{alloc::{alloc, Layout}, io::{Seek, Write}}; + +use crate::{ + app_error::AppError, + pe_parse::{header::ImageDosHeader, pe::ReadOnlyPE}, +}; +use memmap2::Mmap; +use sysinfo::{self, System}; +use windows::{ + core::PWSTR, + Win32::{ + Foundation::{CloseHandle, HMODULE}, + System::{ + Diagnostics::Debug::ReadProcessMemory, + ProcessStatus::EnumProcessModules, + Threading::{ + OpenProcess, QueryFullProcessImageNameW, PROCESS_ACCESS_RIGHTS, PROCESS_ALL_ACCESS, + PROCESS_NAME_WIN32, + }, + }, + }, +}; + +/// 遍历进程并返回进程名\PID +pub fn get_process_list() -> Vec<(String, u32)> { + let mut sys = System::new_all(); + sys.refresh_all(); + let mut process_list = Vec::new(); + for (pid, process) in sys.processes() { + let pid = pid.as_u32(); + process_list.push((process.name().to_str().unwrap().to_string(), pid)); + } + process_list +} +pub fn dump_process(pid: u32, save_file_path: &str) -> Result<(), AppError> { + #[warn(unused_assignments)] + let mut file_path = String::new(); + // 1. 打开进程 + + let mut image_base = 0; // 模块基址 + let h_process = unsafe { + OpenProcess(PROCESS_ACCESS_RIGHTS(PROCESS_ALL_ACCESS.0), false, pid) + .map_err(|e| AppError::OpenProcessError(e.to_string()))? + }; + // 2. 获取进程对应的文件路径 + let mut buffer = [0u16; 1024]; + let pwstr = PWSTR(buffer.as_mut_ptr()); + let mut size = buffer.len() as u32; + unsafe { + let result = QueryFullProcessImageNameW(h_process, PROCESS_NAME_WIN32, pwstr, &mut size); + if result.is_err() { + return Err(AppError::QueryFullProcessImageNameWError( + result.unwrap_err().to_string(), + )); + } + } + unsafe { + file_path = pwstr.to_string().unwrap(); + println!("进程路径: {:?}", file_path); + } + // 创建新文件 + let mut save_file = std::fs::OpenOptions::new() + .write(true) + .create(true) + .open(save_file_path) + .map_err(|e| AppError::Io(e))?; + + // 3. 读取进程的文件,读取它的PE头 + // 3.1 映射文件 + let file = std::fs::OpenOptions::new() + .read(true) + .open(file_path) + .map_err(|e| AppError::Io(e))?; + let mmap = unsafe { Mmap::map(&file).map_err(|e| AppError::Io(e)) }?; + // 3.2 读取PE头 + { + // 3.2.1 读取DOS头 + let dos_header = mmap.get_dos_header()?; + let write_size = std::mem::size_of::() as u64; + save_file.write_all(&mmap[..write_size as usize])?; + // 3.2.2 读取PE头 + let nt_header = mmap.get_nt_header()?; + let nt_header_size = match nt_header { + crate::pe_parse::header::ImageNTHeader::NTHeader32(_) => { + std::mem::size_of::() as u64 + } + crate::pe_parse::header::ImageNTHeader::NTHeader64(_) => { + std::mem::size_of::() as u64 + } + }; + let nt_header_offset = dos_header.e_lfanew; + // 移动文件指针 + save_file.seek(std::io::SeekFrom::Start(nt_header_offset.0 as u64))?; + // 写入PE头 + save_file.write_all( + &mmap[nt_header_offset.0 as usize + ..(nt_header_offset.0 as usize + nt_header_size as usize) as usize], + )?; + // 写入nt_header_offset.0 - sizeof(dos_header)的数据 + // 移动文件指针到dos_header之后 + save_file.seek(std::io::SeekFrom::Start(write_size))?; + save_file.write_all(&mmap[write_size as usize..nt_header_offset.0 as usize])?; + // 移动文件指针到最后 + save_file.seek(std::io::SeekFrom::Start( + nt_header_offset.0 as u64 + nt_header_size, + ))?; + // 数据目录 + let data_directory_offset = nt_header_offset.0 as usize + nt_header_size as usize; + save_file.write_all(&mmap[data_directory_offset..data_directory_offset + 16 * 17])?; + // 节区头 + let sections_num = mmap.get_section_headers()?.len(); + let section_header_offset = mmap.get_section_headers_offset()?; + let section_header_size = std::mem::size_of::() + as u64 + * sections_num as u64; + save_file.seek(std::io::SeekFrom::Start(section_header_offset as u64))?; + save_file.write_all( + &mmap[section_header_offset as usize + ..(section_header_offset as usize + section_header_size as usize) as usize], + )?; + save_file.flush()?; + } + // 获取模块基址 + { + let mut h_module = HMODULE::default(); + let mut cb_needed = 0; + unsafe { + EnumProcessModules( + h_process, + &mut h_module, + std::mem::size_of::() as u32, + &mut cb_needed, + ) + .unwrap(); + // 获取模块基址 + image_base = h_module.0 as u64; + } + } + + // 5. 遍历节表头,从进程中读取节的内容,写入文件 + { + let sections = mmap.get_section_headers()?; + for section in sections.iter() { + let section_va = section.virtual_address as u64; + let section_size = section.size_of_raw_data as u64; + let section_size_align = mmap.align_size_with_section_alignment(section_size as u32)?; + let section_offset = image_base + section_va; + let section_foa = section.pointer_to_raw_data as u64; + + let data_buf = unsafe { + let layout = Layout::array::(section_size_align as usize).unwrap(); + alloc(layout) + }; + + let mut read_size = 0; + + unsafe { + ReadProcessMemory( + h_process, + section_offset as *const _, + data_buf as *mut _, + section_size_align as usize, + Some(&mut read_size), + ) + .unwrap(); + } + println!( + "读取节: VA: {:#x}, Size: {:#x}, FOA: {:#x}", + section_va, section_size, section_foa + ); + println!( + "读取节: Size: {:#x}, ReadSize: {:#x}", + section_size_align, read_size + ); + IMAGE_SECTION_HEADER; + // 如果文件长度不够,则先扩容 + let file_size = save_file.metadata().unwrap().len(); + if file_size < section_foa + section_size_align as u64 { + // 文件指针要移动到末尾 + save_file.seek(std::io::SeekFrom::End(0))?; + save_file.set_len(section_foa + section_size_align as u64)?; + } + + save_file.seek(std::io::SeekFrom::Start(section_foa))?; + save_file.write_all(unsafe { + std::slice::from_raw_parts(data_buf as *const u8, section_size_align as usize) + })?; + } + } + // 6. 关闭进程 + unsafe { + CloseHandle(h_process).unwrap(); + }; + // 7. 返回 + Ok(()) +} + +// 来个测试 +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_get_process_list() { + let process_list = get_process_list(); + for (name, pid) in process_list { + println!("进程名: {}, PID: {}", name, pid); + } + } + #[test] + fn test_dump_process() { + dump_process(11696, "D:\\download\\a.exe").unwrap(); + } +} diff --git a/tsconfig.json b/tsconfig.json index e5b04e4..221bfa7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,8 +16,8 @@ "noImplicitAny": false, /* Linting */ "strict": false, - "noUnusedLocals": true, - "noUnusedParameters": true, + "noUnusedLocals": false, + "noUnusedParameters": false, "noFallthroughCasesInSwitch": true }, "include": ["src"],