feat: 增加节区属性恢复

This commit is contained in:
asahi 2024-12-27 12:04:51 +08:00
parent 989abab053
commit 6ffc10abe9
3 changed files with 137 additions and 30 deletions

View File

@ -26,5 +26,6 @@ pub enum LoadPEError {
NoRelocationTable, NoRelocationTable,
#[error(transparent)] #[error(transparent)]
LayoutError(#[from] LayoutError), LayoutError(#[from] LayoutError),
#[error("更改内存属性失败!")]
ChangeMemoryProtectFailed,
} }

View File

@ -1,20 +1,24 @@
use core::alloc;
use errors::LoadPEError; use errors::LoadPEError;
use object::{ use object::{
bytes_of, bytes_of_slice, endian, endian,
pe::{IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_DIRECTORY_ENTRY_IMPORT}, pe::{
read::pe::{self, ImageOptionalHeader, PeFile32, PeFile64}, IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_SCN_MEM_EXECUTE,
LittleEndian, Object, ObjectSection, Pod, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE,
},
read::pe::{self, ImageOptionalHeader, PeFile32},
LittleEndian, Object,
}; };
use std::{ use std::{
alloc::{alloc, Layout}, alloc::{alloc, Layout}, arch::asm, fs
fs,
}; };
use windows::{ use windows::{
core::PCSTR, core::PCSTR,
Win32::{ Win32::System::{
Foundation::CloseHandle, LibraryLoader::{GetProcAddress, LoadLibraryA},
System::LibraryLoader::{GetProcAddress, LoadLibraryA}, Memory::{
VirtualProtect, PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE,
PAGE_PROTECTION_FLAGS, PAGE_READONLY, PAGE_READWRITE,
},
}, },
}; };
pub mod errors; pub mod errors;
@ -25,7 +29,7 @@ mod help;
/// ///
/// ///
/// ///
pub fn load_exe(exe_path: &str, args: Option<Vec<&str>>) -> Result<(), LoadPEError> { pub fn load_exe(exe_path: &str, _args: Option<Vec<&str>>) -> Result<(), LoadPEError> {
// 1. 检查文件是否存在 // 1. 检查文件是否存在
if !fs::metadata(exe_path).is_ok() { if !fs::metadata(exe_path).is_ok() {
return Err(LoadPEError::FileNotFound(exe_path.to_string())); return Err(LoadPEError::FileNotFound(exe_path.to_string()));
@ -183,7 +187,7 @@ pub fn load_exe(exe_path: &str, args: Option<Vec<&str>>) -> Result<(), LoadPEErr
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
let is_ordinal = origin_thunk_data_value & 0x8000000000000000 != 0; let is_ordinal = origin_thunk_data_value & 0x8000000000000000 != 0;
// 函数地址指针 // 函数地址指针
let mut function_address: *const u8 = std::ptr::null(); let function_address;
if is_ordinal { if is_ordinal {
// 如果最高位是10-15位是序号 // 如果最高位是10-15位是序号
let ordinal = origin_thunk_data_value as u16; let ordinal = origin_thunk_data_value as u16;
@ -241,7 +245,7 @@ pub fn load_exe(exe_path: &str, args: Option<Vec<&str>>) -> Result<(), LoadPEErr
import_table_index += 1; import_table_index += 1;
} }
} }
// TODO: 修复重定向表 // 修复重定向表
{ {
// 1. 获取重定向表的数据目录 // 1. 获取重定向表的数据目录
let relocation_table_directory = file.data_directory(IMAGE_DIRECTORY_ENTRY_BASERELOC); let relocation_table_directory = file.data_directory(IMAGE_DIRECTORY_ENTRY_BASERELOC);
@ -285,6 +289,15 @@ pub fn load_exe(exe_path: &str, args: Option<Vec<&str>>) -> Result<(), LoadPEErr
let r_offset = type_offset & 0x0FFF; let r_offset = type_offset & 0x0FFF;
// 需要修复的地址偏移 // 需要修复的地址偏移
let offset_va = virtual_address + r_offset as u32; 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!( dbg!(
"r_type: {:?}, r_offset: {:?}, offset_va: {:?}, virtual_address: {:?}", "r_type: {:?}, r_offset: {:?}, offset_va: {:?}, virtual_address: {:?}",
r_type, r_type,
@ -292,26 +305,44 @@ pub fn load_exe(exe_path: &str, args: Option<Vec<&str>>) -> Result<(), LoadPEErr
offset_va, offset_va,
virtual_address virtual_address
); );
match r_type { match r_type {
0 => { 0 => {}
// 如果r_type是0表示这个TypeOffset是一个IMAGE_REL_BASED_ABSOLUTE 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 => { 3 => {
// 如果r_type是3表示这个TypeOffset是一个IMAGE_REL_BASED_HIGHLOW // 如果r_type是3表示这个TypeOffset是一个IMAGE_REL_BASED_HIGHLOW
// 这个时候需要修复一个32位的地址 // 这个时候需要修复一个32位的地址
let p_address = (buf as usize + offset_va as usize) as *mut u32; let new_address = address_value + buf as u32;
let address = unsafe { *p_address };
let new_address = address + buf as u32;
unsafe { unsafe {
*p_address = new_address; *p_address = new_address;
} }
dbg!("修复一个32位地址: {:?} -> {:?}", address, new_address); dbg!("修复一个32位地址: {:?} -> {:?}", address_value, new_address);
} }
10 => { 10 => {
// 需要实现64位的重定位 // IMAGE_REL_BASED_DIR64 基址重定位将差值添加到偏移的 64 位字段。
unimplemented!("未实现的重定位类型: {:?}", r_type); 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);
} }
_ => { _ => {
// 其他的类型,暂时不支持 // 其他的类型,暂时不支持
@ -329,11 +360,86 @@ pub fn load_exe(exe_path: &str, args: Option<Vec<&str>>) -> Result<(), LoadPEErr
{} {}
// TODO: 修复命令行参数 // TODO: 修复命令行参数
{} {}
// 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);
} }
todo!("未实现") // 设置属性
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(())
} }

BIN
tests/Test.exe Normal file

Binary file not shown.