use errors::LoadPEError; use object::{ endian, pe::{ IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE, }, read::pe::{self, ImageOptionalHeader, PeFile32}, LittleEndian, Object, }; use std::{ alloc::{alloc, Layout}, arch::asm, fs }; use windows::{ core::PCSTR, Win32::System::{ LibraryLoader::{GetProcAddress, LoadLibraryA}, Memory::{ VirtualProtect, PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_PROTECTION_FLAGS, PAGE_READONLY, PAGE_READWRITE, }, }, }; pub mod errors; mod help; /// 加载并启动一个exe文件,需要支持命令行参数 /// /// /// /// pub fn load_exe(exe_path: &str, _args: Option>) -> Result<(), LoadPEError> { // 1. 检查文件是否存在 if !fs::metadata(exe_path).is_ok() { return Err(LoadPEError::FileNotFound(exe_path.to_string())); } // 2. 如果文件存在,加载文件 let data = fs::read(exe_path)?; #[cfg(target_arch = "x86")] let file: PeFile32 = pe::PeFile::parse(&data[..])?; #[cfg(target_arch = "x86_64")] let file: PeFile64 = pe::PeFile::parse(&data[..])?; // 获取镜像大小 let image_size = file .nt_headers() .optional_header .size_of_image .get(endian::LittleEndian); // 3. 为镜像分配内存 // 分配的内存,后续需要修改内存属性 // let layout = Layout::from_size_align(image_size as usize, 1)?; let buf = unsafe { alloc(layout) }; if buf.is_null() { return Err(LoadPEError::MemoryAllocFailed); } dbg!("分配的内存地址: {:?}", buf); // 进行数据的拷贝。 { // 拷贝整个头部 let head_size = file .nt_headers() .optional_header .size_of_headers .get(endian::LittleEndian); let head_data = &data[..head_size as usize]; unsafe { std::ptr::copy_nonoverlapping(head_data.as_ptr(), buf, head_size as usize); } } { // 分块拷贝节区数据 let section_alignment = file.nt_headers().optional_header.section_alignment(); let file_alignment = file.nt_headers().optional_header.file_alignment(); for section in file.sections() { // 如果节区没有映射到内存中,那么就不需要拷贝 if section.pe_section().size_of_raw_data.get(LittleEndian) == 0 { continue; } // VA对齐值 let va = align_to!( section.pe_section().virtual_address.get(LittleEndian), section_alignment ); // pointer_to_raw_data对齐值 let pointer_to_raw_data = align_to!( section.pe_section().pointer_to_raw_data.get(LittleEndian), file_alignment ); // size_of_raw_data对齐值 let size_of_raw_data = align_to!( section.pe_section().size_of_raw_data.get(LittleEndian), file_alignment ); // 从data[pointer_to_raw_data..pointer_to_raw_data + size_of_raw_data]中拷贝数据到buf[va..va + size_of_raw_data] let section_data = &data [pointer_to_raw_data as usize..(pointer_to_raw_data + size_of_raw_data) as usize]; dbg!( "拷贝节区数据: va: {:?}, size_of_raw_data: {:?}, pointer_to_raw_data: {:?}, section_data.len: {:?}", va, size_of_raw_data, pointer_to_raw_data, section_data.len() ); unsafe { let dst = (buf as usize + va as usize) as *mut u8; dbg!("dst: {:p}", dst); std::ptr::copy(section_data.as_ptr(), dst, size_of_raw_data as usize); }; } // 修复IAT表 { // 获取导入表的数据目录 let import_directory = file.data_directory(IMAGE_DIRECTORY_ENTRY_IMPORT); if import_directory.is_none() { return Err(LoadPEError::ExecutableWithoutImportTable); } let base_import_table_rva = import_directory.unwrap().virtual_address.get(LittleEndian); let mut import_table_index = 0; loop { let descriptor = unsafe { &*((buf as usize + base_import_table_rva as usize + import_table_index * std::mem::size_of::()) as *const object::pe::ImageImportDescriptor) }; if descriptor.original_first_thunk.get(LittleEndian) == 0 { break; } let module_name_rva = descriptor.name.get(LittleEndian); let p_module_name = (buf as usize + module_name_rva as usize) as *const u8; // 使用LoadLibraryA加载模块 let h_module = unsafe { LoadLibraryA(PCSTR::from_raw(p_module_name)) .map_err(|err| LoadPEError::LoadLibraryError(err))? }; dbg!(h_module); if h_module.is_invalid() { // 这个逻辑应该走不到 unimplemented!("LoadLibraryA failed"); } // 遍历IAT表,修复IAT表 // 加载每一个方法。使用GetProcAddress,获取地址。 // 然后修复IAT表。如果其中一个环节出现问题,我们应该:释放所有加载的Module,然后返回错误 // 获取Image_thunk_data数组的首地址 let base_original_first_thunk_va = if descriptor.original_first_thunk.get(LittleEndian) != 0 { descriptor.original_first_thunk.get(LittleEndian) } else { // 如果OriginalFirstThunk不存在,则使用FirstThunk 的值 descriptor.first_thunk.get(LittleEndian) }; let base_import_address_table_va = descriptor.first_thunk.get(LittleEndian); let mut index = 0; loop { // 1. 从文件的数据中取出一个ImageThunkData let origin_thunk_data = unsafe { #[cfg(target_arch = "x86")] let thunk_data = &*((buf as usize + base_original_first_thunk_va as usize + index * std::mem::size_of::()) as *const object::pe::ImageThunkData32); #[cfg(target_arch = "x86_64")] let thunk_data = &*((buf as usize + base_original_first_thunk_va as usize + index * std::mem::size_of::()) as *const object::pe::ImageThunkData64); thunk_data }; // 2. 判断是否是最后一个ImageThunkData let origin_thunk_data_value = origin_thunk_data.0.get(LittleEndian) as u64; if origin_thunk_data_value == 0 { break; } // 如果不是最后一个,现判断是序号,还是函数名 #[cfg(target_arch = "x86")] let is_ordinal = origin_thunk_data_value & 0x80000000 != 0; #[cfg(target_arch = "x86_64")] let is_ordinal = origin_thunk_data_value & 0x8000000000000000 != 0; // 函数地址指针 let function_address; if is_ordinal { // 如果最高位是1,0-15位是序号 let ordinal = origin_thunk_data_value as u16; let pstr = PCSTR(ordinal as *const u8); // 使用GetProcAddress获取函数地址 let p_function = unsafe { GetProcAddress(h_module, pstr) }; if p_function.is_none() { // 如果函数地址是null,表示获取失败 return Err(LoadPEError::GetProcAddressError(format!( "GetProcAddress failed, ordinal: {:?}", ordinal ))); } function_address = p_function.unwrap() as *const u8; } else { // 否则是名称表的RVA let name_table_rva = origin_thunk_data_value; let p_function_name = (buf as usize + name_table_rva as usize + 2) as *const u8; // 构造一个PSTR let p_function_name = PCSTR(p_function_name); // 使用GetProcAddress获取函数地址 let p_function = unsafe { GetProcAddress(h_module, p_function_name) }; if p_function.is_none() { // 如果函数地址是null,表示获取失败 return Err(LoadPEError::GetProcAddressError(format!( "GetProcAddress failed, function name: {:?}", p_function_name ))); } function_address = p_function.unwrap() as *const u8; } // 修复IAT表 // 需要获取到IAT表的地址 let iat_address = (buf as usize + base_import_address_table_va as usize + std::mem::size_of::() * index) as *mut u8; // 修复IAT表 // 将函数地址写入IAT表 如果是x86_64,需要写入8字节 // 如果是x86,需要写入4字节 unsafe { #[cfg(target_arch = "x86")] { *(iat_address as *mut u32) = function_address as u32; } #[cfg(target_arch = "x86_64")] { *(iat_address as *mut u64) = function_address as u64; } } index += 1; } import_table_index += 1; } } // 修复重定向表 { // 1. 获取重定向表的数据目录 let relocation_table_directory = file.data_directory(IMAGE_DIRECTORY_ENTRY_BASERELOC); if relocation_table_directory.is_none() { // TODO: 如果没有重定位表,应该要把这个错误返回出去 return Err(LoadPEError::NoRelocationTable); } let base_relocation_table_rva = relocation_table_directory .unwrap() .virtual_address .get(LittleEndian); let mut relocation_offset = 0; loop { // 读取一个IMAGE_BASE_RELOCATION let relocation = unsafe { &*((buf as usize + base_relocation_table_rva as usize + relocation_offset) as *const object::pe::ImageBaseRelocation) }; // 如果SizeOfBlock为0,表示已经到了最后一个IMAGE_BASE_RELOCATION if relocation.size_of_block.get(LittleEndian) == 0 { break; } // 获取重定位表的VirtualAddress和SizeOfBlock let virtual_address = relocation.virtual_address.get(LittleEndian); let size_of_block = relocation.size_of_block.get(LittleEndian); let base_block = base_relocation_table_rva as usize + relocation_offset + 8; let block_num = (size_of_block - 8) / 2; for i in 0..block_num { // 读取一个TypeOffset let type_offset = unsafe { &*((buf as usize + base_block as usize + i as usize * std::mem::size_of::()) as *mut u16) }; // 高四位 let r_type = (type_offset >> 12) as u8; // 剩余12位 let r_offset = type_offset & 0x0FFF; // 需要修复的地址偏移 let offset_va = virtual_address + r_offset as u32; // 若要应用基址重定位,会计算首选基址与在其中实际加载映像的基址之间的差异。 如果在首选基址处加载映像,则差异为零,因此无需应用基址重定位。 let reloc_offset = buf as usize - file .nt_headers() .optional_header .image_base .get(LittleEndian) as usize; let p_address = (buf as usize + offset_va as usize) as *mut u32; let address_value = unsafe { *p_address }; dbg!( "r_type: {:?}, r_offset: {:?}, offset_va: {:?}, virtual_address: {:?}", r_type, r_offset, offset_va, virtual_address ); match r_type { 0 => {} 1 => { // IMAGE_REL_BASED_HIGH 基址重定位在偏移量处将差值的高 16 位添加到 16 位字段。 16 位字段表示 32 位单词的高值。 // 取reloc_offset的高16位 let high = (reloc_offset >> 16) as u16; let new_address = address_value + high as u32; unsafe { *p_address = new_address; } dbg!("修复一个16位地址: {:?} -> {:?}", address_value, new_address); } 2 => { // IMAGE_REL_BASED_LOW 基准重定位将差值的低 16 位加到偏移的 16 位字段。 16 位字段代表 32 位字的低半部分。 // 取reloc_offset的低16位 let low = reloc_offset as u16; let new_address = address_value + low as u32; unsafe { *p_address = new_address; } dbg!("修复一个16位地址: {:?} -> {:?}", address_value, new_address); } 3 => { // 如果r_type是3,表示这个TypeOffset是一个IMAGE_REL_BASED_HIGHLOW // 这个时候需要修复一个32位的地址 let new_address = address_value + buf as u32; unsafe { *p_address = new_address; } dbg!("修复一个32位地址: {:?} -> {:?}", address_value, new_address); } 10 => { // IMAGE_REL_BASED_DIR64 基址重定位将差值添加到偏移的 64 位字段。 let new_address = address_value + reloc_offset as u32; unsafe { *(p_address as *mut u64) = new_address as u64; } dbg!("修复一个64位地址: {:?} -> {:?}", address_value, new_address); } _ => { // 其他的类型,暂时不支持 unimplemented!("未实现的重定位类型: {:?}", r_type); } } } // 计算下一个IMAGE_BASE_RELOCATION的偏移 relocation_offset += size_of_block as usize; } } // TODO: 修复TLS表 {} // TODO: 修复资源表 {} // TODO: 修复命令行参数 {} // 设置节区属性 { let section_alignment = file.nt_headers().optional_header.section_alignment(); // 遍历节区,设置节区属性 for section in file.sections() { let va = section.pe_section().virtual_address.get(LittleEndian); let size_of_raw_data = section.pe_section().size_of_raw_data.get(LittleEndian); let characteristics = section.pe_section().characteristics.get(LittleEndian); let section_va = buf as usize + va as usize; let section_size = align_to!(size_of_raw_data, section_alignment); if section_size == 0 { continue; } // 设置节区属性 let protect; if characteristics & IMAGE_SCN_MEM_EXECUTE != 0 && characteristics & IMAGE_SCN_MEM_READ == 0 && characteristics & IMAGE_SCN_MEM_WRITE == 0 { protect = PAGE_EXECUTE.0; } else if characteristics & IMAGE_SCN_MEM_EXECUTE != 0 && characteristics & IMAGE_SCN_MEM_READ != 0 && characteristics & IMAGE_SCN_MEM_WRITE == 0 { protect = PAGE_EXECUTE_READ.0; } else if characteristics & IMAGE_SCN_MEM_EXECUTE != 0 && characteristics & IMAGE_SCN_MEM_READ != 0 && characteristics & IMAGE_SCN_MEM_WRITE != 0 { protect = PAGE_EXECUTE_READWRITE.0; } else if characteristics & IMAGE_SCN_MEM_EXECUTE == 0 && characteristics & IMAGE_SCN_MEM_READ != 0 && characteristics & IMAGE_SCN_MEM_WRITE == 0 { protect = PAGE_READONLY.0; } else if characteristics & IMAGE_SCN_MEM_EXECUTE == 0 && characteristics & IMAGE_SCN_MEM_READ != 0 && characteristics & IMAGE_SCN_MEM_WRITE != 0 { protect = PAGE_READWRITE.0; } else { return Err(LoadPEError::ChangeMemoryProtectFailed); } // 设置属性 let mut old_protect = PAGE_PROTECTION_FLAGS(0); dbg!( "设置节区属性: va: {:?}, size: {:?}, protect: {:?}", section_va, section_size, protect ); let result = unsafe { VirtualProtect( section_va as *mut _, section_size as usize, PAGE_PROTECTION_FLAGS(protect), &mut old_protect, ) }; if result.is_err() { dbg!("设置节区属性失败: {:?}", result.unwrap_err()); return Err(LoadPEError::ChangeMemoryProtectFailed); } } } { let entry_point = file .nt_headers() .optional_header .address_of_entry_point .get(LittleEndian); let entry_point = buf as usize + entry_point as usize; let entry_point: extern "system" fn() = unsafe { std::mem::transmute(entry_point) }; dbg!("entry_point: {:p}", entry_point); entry_point(); } } Ok(()) }