loadpe-rs/src/lib.rs

446 lines
20 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {
// 如果最高位是10-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(())
}