feat: 增加导出表解析的功能和测试用例

This commit is contained in:
381848900@qq.com 2024-12-18 12:10:55 +08:00
parent f48803b3c5
commit 3e0a858318
7 changed files with 182 additions and 1 deletions

3
.gitignore vendored
View File

@ -5,3 +5,6 @@
# Generated by Tauri
# will have schema files for capabilities auto-completion
/gen/schemas
/tests/*.dll
/tests/*.exe

1
Cargo.lock generated
View File

@ -32,6 +32,7 @@ version = "0.1.0"
dependencies = [
"bitflags",
"memmap2",
"pe_parse",
"serde",
"thiserror",
]

View File

@ -12,3 +12,6 @@ thiserror = "2.0.4"
[features]
default = [] # 默认不启用任何特性
memmap2_impl = [] # 定义一个 feature并启用第三方库
[dev-dependencies]
pe_parse = { path = ".", features = ["memmap2_impl"] }

View File

@ -18,6 +18,8 @@ pub enum PEParseError {
RvaToEmptySection(u32),
#[error("错误的数据目录索引")]
InvalidDataDirectoryIndex,
#[error("错误的导出函数序号: {0}")]
ExportOrdinalNotFound(u16),
}
/// PE操作的错误
#[derive(Error, Debug)]

View File

@ -451,3 +451,35 @@ bitflags! {
}
}
/// 导出目录表
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ImageExportDirectoryTable {
pub characteristics: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub name: u32,
pub base: u32,
pub number_of_functions: u32,
pub number_of_names: u32,
pub address_of_functions: u32,
pub address_of_names: u32,
pub address_of_name_ordinals: u32,
}
/// 导出地址表
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub enum ExportAddressTable {
// 导出函数的RVA
ExportFunctionRVA(u32),
// 转发函数字符串的RVA
ForwarderStringRVA(u32),
}
pub enum ProcName {
Named(String),
Ordinal(u16),
}

View File

@ -291,6 +291,71 @@ pub trait ReadOnlyPE: Deref<Target = [u8]> + Sized + AsRef<[u8]> {
let empty_space_size = size_of_headers - last_section_header_offset as u32;
Ok(empty_space_size)
}
/// 以传入的RVA为开始偏移从中解析导出表
fn parse_export_table(&self, rva: u32) -> Result<&ImageExportDirectoryTable, PEParseError> {
let foa = self.rva_to_foa(rva)?;
let export_table = self.get_ref::<ImageExportDirectoryTable>(foa as usize)?;
Ok(export_table)
}
/// 获取导出函数的RVA地址类似于GetProcAddress
fn get_export_function_rva(&self, proc_name: ProcName) -> Result<u32, PEParseError>{
let export_table_dir = self.get_data_directory(ImageDirectoryEntry::Export)?;
let export_table_rva = export_table_dir.virtual_address;
let export_table = self.parse_export_table(export_table_rva)?;
let base = export_table.base; // 序号基数
let number_of_fun = export_table.number_of_functions;
let number_of_names = export_table.number_of_names;
// 名称指针表的FOA
let names_table_foa = self.rva_to_foa(export_table.address_of_names)?;
match proc_name{
ProcName::Named(proc_name_str) => {
// 遍历名称表
for index in 0..number_of_names {
let name_item_foa = names_table_foa + index * std::mem::size_of::<u32>() as u32;
let name_rva = self.get_ref::<u32>(name_item_foa as usize)?.clone();
let name_foa = self.rva_to_foa(name_rva)?;
let name = unsafe {
std::ffi::CStr::from_ptr(
self.get_ref::<i8>(name_foa as usize)? as *const i8
)
.to_str()
.unwrap()
};
// 比较
if name == proc_name_str {
// 找到了这个函数 那么需要找到这个函数的序号
let ordinals_table_foa = self.rva_to_foa(export_table.address_of_name_ordinals)?;
let ordinal_item_foa = ordinals_table_foa + index * std::mem::size_of::<u16>() as u32;
let ordinal = self.get_ref::<u16>(ordinal_item_foa as usize)?.clone();
// 找到了序号 那么就可以找到函数地址
let functions_table_foa = self.rva_to_foa(export_table.address_of_functions)?;
let function_item_foa = functions_table_foa + ordinal as u32 * std::mem::size_of::<u32>() as u32;
let rva = self.get_ref::<u32>(function_item_foa as usize)?.clone();
return Ok(rva);
}
}
}
ProcName::Ordinal(order) => {
// 通过序号查找
// 序号就在序号表的第order - base的下标位置
let find_ordinal = order as u32 - base;
if find_ordinal >= number_of_fun {
return Err(PEParseError::ExportOrdinalNotFound(order));
}
let functions_table_foa = self.rva_to_foa(export_table.address_of_functions)?;
let function_item_foa = functions_table_foa + find_ordinal * std::mem::size_of::<u32>() as u32;
let rva = self.get_ref::<u32>(function_item_foa as usize)?.clone();
return Ok(rva);
}
}
todo!()
}
}
/// 可修改的PE trait
pub trait MutablePE: ReadOnlyPE + DerefMut<Target = [u8]> + AsMut<[u8]> {

75
tests/test_pe.rs Normal file
View File

@ -0,0 +1,75 @@
mod test {
use super::*;
use memmap2::Mmap;
use pe_parse::header::ProcName;
#[test]
fn test_header() {
println!("This is a test for the header");
}
/// 测试导出表的解析
#[test]
#[cfg(feature = "memmap2_impl")]
fn test_export_table() {
use core::ffi;
// 1. 创建映射
use pe_parse::pe::ReadOnlyPE;
let file = std::fs::OpenOptions::new()
.read(true)
.open("tests/test_dll.dll")
.unwrap();
let mmap = unsafe { Mmap::map(&file).unwrap() };
// 2. 解析导出表
let export_table_dir = mmap
.get_data_directory(pe_parse::header::ImageDirectoryEntry::Export)
.unwrap();
let export_table_rva = export_table_dir.virtual_address;
let export_table = mmap.parse_export_table(export_table_rva).unwrap();
println!("{:?}", export_table);
// 3. 遍历导出表,打印导出函数
let number_of_fun = export_table.number_of_functions;
let functions_table_foa = mmap.rva_to_foa(export_table.address_of_functions).unwrap();
for i in 0..number_of_fun {
let functions_item_foa = functions_table_foa + i * std::mem::size_of::<u32>() as u32;
let rva = mmap
.get_ref::<u32>(functions_item_foa as usize)
.unwrap()
.clone();
// 如果rva在[export_table_dir.virtual_address, export_table_dir.virtual_address + export_table_dir.size]之间,则是一个内部导出
if rva >= export_table_dir.virtual_address
&& rva < export_table_dir.virtual_address + export_table_dir.size
{
// 这是一个转发
let forward_str_foa = mmap.rva_to_foa(rva).unwrap();
let forward_str = unsafe {
ffi::CStr::from_ptr(
mmap.get_ref::<i8>(forward_str_foa as usize).unwrap() as *const i8
)
.to_str()
.unwrap()
};
println!("i: {i}, Forward: {}", forward_str);
} else {
// 这是一个内部导出 打印函数名
println!("i: {i}, Function RVA: {:#x}", rva);
}
}
}
#[test]
fn test_find_export_function(){
let find_function_str = ProcName::Named("test_def_add".to_string());
// 1. 还是先映射
use pe_parse::pe::ReadOnlyPE;
let file = std::fs::OpenOptions::new()
.read(true)
.open("tests/test_dll.dll")
.unwrap();
let mmap = unsafe { Mmap::map(&file).unwrap() };
let result_rva = mmap.get_export_function_rva(find_function_str).unwrap();
println!("Find Function RVA: {:#x}", result_rva);
}
}