feat: 实现了节表导入和IAT的修复

This commit is contained in:
asahi 2024-12-26 21:07:34 +08:00
parent cbd1b5b6a5
commit aabbc73ee5
7 changed files with 155 additions and 34 deletions

2
.cargo/config.toml Normal file
View File

@ -0,0 +1,2 @@
[build]
target = "i686-pc-windows-msvc"

9
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,9 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{}
]
}

View File

@ -7,9 +7,6 @@ edition = "2021"
object = "0.36.7"
thiserror = "2.0.9"
[build]
target = "i686-pc-windows-msvc"
[dependencies.windows]
version = "0.58.0"
features=[

18
src/bin/main.rs Normal file
View File

@ -0,0 +1,18 @@
use loadpe_rs::load_exe;
#[cfg(target_arch = "x86_64")]
const EXE_PATH: &str = r#"E:\work\逆向工程学习\PE学习\HelloWorld.exe"#;
#[cfg(target_arch = "x86")]
const EXE_PATH: &str = r#"E:\work\rust\loadpe-rs\tests\Test.exe"#;
fn main() {
match load_exe(EXE_PATH, None) {
Ok(_) => {
println!("Load exe success!");
}
Err(e) => {
eprintln!("Load exe failed: {}", e);
}
}
}

View File

@ -18,4 +18,6 @@ pub enum LoadPEError {
ReadCStringError(#[from] std::str::Utf8Error),
#[error("LoadLibraryA错误, {0}")]
LoadLibraryError(#[from] WindowsError),
#[error("GetProcAddress错误, {0}")]
GetProcAddressError(String),
}

View File

@ -12,7 +12,10 @@ use std::{
};
use windows::{
core::PCSTR,
Win32::{Foundation::CloseHandle, System::LibraryLoader::LoadLibraryA},
Win32::{
Foundation::CloseHandle,
System::LibraryLoader::{GetProcAddress, LoadLibraryA},
},
};
pub mod errors;
mod help;
@ -98,59 +101,149 @@ pub fn load_exe(exe_path: &str, args: Option<Vec<&str>>) -> Result<(), LoadPEErr
}
// 修复IAT表
{
let import_table = file.import_table()?;
if import_table.is_none() {
// 获取导入表的数据目录
let import_directory = file.data_directory(IMAGE_DIRECTORY_ENTRY_IMPORT);
if import_directory.is_none() {
return Err(LoadPEError::ExecutableWithoutImportTable);
}
let import_table = import_table.unwrap();
// 1. 加载模块
let descriptors = import_table.descriptors().unwrap();
for descriptor_result in descriptors {
// 当descriptor 是Ok(null)的时候表示结束
let descriptor = descriptor_result?;
if descriptor.is_null() {
let base_import_table_rva = import_directory.unwrap().virtual_address.get(LittleEndian);
let mut import_table_index = 0;
loop {
let descriptor = unsafe {
&*(buf.add(base_import_table_rva as usize)
as *const object::pe::ImageImportDescriptor)
.add(import_table_index)
};
if descriptor.original_first_thunk.get(LittleEndian) == 0 {
break;
}
let module_name_rva = descriptor.name.get(LittleEndian);
let p_module_name = unsafe {
// 从data[module_name_rva..]中读取一个c风格字符串
data.as_ptr().add(module_name_rva as usize) as *const u8
// buf[module_name_rva..]中读取一个c风格字符串
buf.add(module_name_rva as usize) as *const u8
};
// 使用LoadLibraryA加载模块
let h_module = unsafe {
LoadLibraryA(PCSTR(p_module_name))
LoadLibraryA(PCSTR::from_raw(p_module_name))
.map_err(|err| LoadPEError::LoadLibraryError(err))?
};
dbg!(h_module);
if h_module.is_invalid() {
// 这个逻辑应该走不到
unimplemented!("LoadLibraryA failed");
}
// TODO: 遍历IAT表修复IAT表
// 遍历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)
};
// 如果OriginalFirstThunk不存在则使用FirstThunk 的值
let original_first_thunk = descriptor.original_first_thunk.get(LittleEndian);
if original_first_thunk == 0 {
}else {
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.add(base_original_first_thunk_va as usize)
as *const object::pe::ImageThunkData32)
.add(index);
#[cfg(target_arch = "x86_64")]
let thunk_data = &*(buf.add(base_original_first_thunk_va as usize)
as *const object::pe::ImageThunkData64)
.add(index);
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 mut function_address: *const u8 = std::ptr::null();
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 =
unsafe { buf.add(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 =
unsafe { buf.add(base_import_address_table_va as usize + 4 * index) };
// 修复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;
}
}
// TODO: 修复重定向表
{
}
// TODO: 修复TLS表
{
}
// TODO: 修复资源表
{
}
// TODO: 修复命令行参数
{
}
}
todo!()
todo!("未实现")
}

BIN
tests/Test.exe Normal file

Binary file not shown.