446 lines
20 KiB
Rust
446 lines
20 KiB
Rust
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<Vec<&str>>) -> 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::<object::pe::ImageImportDescriptor>())
|
||
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::<object::pe::ImageThunkData32>())
|
||
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::<object::pe::ImageThunkData64>())
|
||
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::<u32>() * 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::<u16>())
|
||
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(())
|
||
}
|