feat: 增加导入表的解析

This commit is contained in:
381848900@qq.com 2024-12-15 13:22:36 +08:00
parent a89a5ae376
commit ce85787be9
9 changed files with 393 additions and 35 deletions

View File

@ -7,6 +7,9 @@ use crate::pe_parse::error::{
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum AppError { pub enum AppError {
#[error(transparent)]
Utf8Error(#[from] std::str::Utf8Error),
#[error(transparent)] #[error(transparent)]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),

View File

@ -1,11 +1,15 @@
use std::mem::{self};
use crate::app_error::AppError; use crate::app_error::AppError;
use crate::pe_parse::header::{ImageSectionHeader, SectionCharacteristics}; use crate::pe_parse::header::{
ImageDirectoryEntry, ImageImportDescriptor, ImageSectionHeader, ImportLookupTable32,
ImportLookupTable64, SectionCharacteristics,
};
use crate::pe_parse::pe::MutablePE; use crate::pe_parse::pe::MutablePE;
use crate::services::file::expand_file_size; use crate::services::file::expand_file_size;
use crate::{pe_parse::pe::ReadOnlyPE, services::file}; use crate::{pe_parse::pe::ReadOnlyPE, services::file};
use memmap2::Mmap; use memmap2::Mmap;
use serde::Serialize;
use std::ffi::CStr;
use std::mem::{self};
/// 应用的状态结构体 /// 应用的状态结构体
#[derive(Default)] #[derive(Default)]
@ -100,7 +104,10 @@ impl AppState {
// 2. 以可读可写的方式重新映射文件 // 2. 以可读可写的方式重新映射文件
rw_mmap_option = Some(file::mmap_file_rw(file_path)?); rw_mmap_option = Some(file::mmap_file_rw(file_path)?);
// 拓展头部大小 拓展一个节的大小 // 拓展头部大小 拓展一个节的大小
rw_mmap_option.as_mut().unwrap().expand_headers(file_alignment)?; rw_mmap_option
.as_mut()
.unwrap()
.expand_headers(file_alignment)?;
} }
// 2. 添加节 // 2. 添加节
// 先拓展文件大小 // 先拓展文件大小
@ -120,4 +127,141 @@ impl AppState {
Ok(()) Ok(())
} }
pub fn get_import_tables(&self) -> Result<Vec<ImportModuleTable>, AppError> {
let mmap = self.get_mmap_ref()?;
let is_64_bit = self.is_64_bit.unwrap();
// 1. 获取导入表
let import_table = mmap.get_data_directory(ImageDirectoryEntry::Import)?;
// 2. 获取导入表的RVA
let import_table_rva = import_table.virtual_address;
// 3. RVA转FOA
let import_table_foa = mmap.rva_to_foa(import_table_rva)?;
// 从文件import_table_foa循环解析ImageImportDescriptor当解析出的导入表==ImageImportDescriptor::default()时,结束循环
let mut origin_import_module_tables: Vec<&ImageImportDescriptor> = Vec::new();
let mut base_offset = import_table_foa as usize;
loop {
let import_module_item = unsafe {
let ptr = mmap.as_ptr().add(base_offset) as *const ImageImportDescriptor;
ptr.as_ref().unwrap()
};
if import_module_item == &ImageImportDescriptor::default() {
break;
}
origin_import_module_tables.push(import_module_item);
base_offset += mem::size_of::<ImageImportDescriptor>();
}
// 4. 解析导入表 主要是来格式化一些数据 先创建一个ImportModuleTable 的变长数组
let mut import_module_tables: Vec<ImportModuleTable> = Vec::new();
// 循环处理origin_import_module_tables
for origin_item in origin_import_module_tables.iter() {
let mut import_module = ImportModuleTable::default();
// 1. 获取模块名称
// 先获取模块名称的FOA
let module_name_foa = mmap.rva_to_foa(origin_item.name)?;
// 获取模块名称 需要unsafe读取
let module_name = unsafe {
let ptr = mmap[module_name_foa as usize..].as_ptr() as *const i8;
CStr::from_ptr(ptr).to_str()?.to_string()
};
import_module.module_name = module_name;
// 其他成员
import_module.forwarder_chain = origin_item.forwarder_chain;
import_module.iat_rva = origin_item.first_thunk;
import_module.int_rva = origin_item.original_first_thunk;
import_module.module_name_rva = origin_item.name;
import_module.timestamp = origin_item.time_date_stamp;
// 2. 获取函数表
let mut functions: Vec<ImportFunctionTableItem> = Vec::new();
// 获取函数表的FOA
let mut base_offset = mmap.rva_to_foa(origin_item.original_first_thunk)? as usize;
// 循环解析函数表
loop {
// TODO: 这里需要判断是64位还是32位
// 如果是64位每次读取8字节 否则读取4字节
let import_lookup_table = if is_64_bit {
let ptr = mmap[base_offset as usize..].as_ptr() as *const u64;
unsafe { ptr.read() as u64 }
} else {
let ptr = mmap[base_offset as usize..].as_ptr() as *const u32;
unsafe { ptr.read() as u64 }
};
// 如果import_lookup_table == 0 则表示结束
if import_lookup_table == 0 {
break;
}
// 1. 判断是序号还是命名函数
let is_named = match is_64_bit {
true => {
import_lookup_table & ImportLookupTable64::IMPORT_BY_ORDINAL.bits() == 0
}
false => {
import_lookup_table as u32 & ImportLookupTable32::IMPORT_BY_ORDINAL.bits()
== 0
}
};
let mut import_function_table = ImportFunctionTable::default();
let hint_name_table_rva = import_lookup_table as u32 & 0x7FFFFFFF;
let hint_name_table_foa = mmap.rva_to_foa(hint_name_table_rva)?;
// 1. 读hint 两个字节
let funtion_hint: u16 = unsafe {
let ptr = mmap[hint_name_table_foa as usize..].as_ptr() as *const u16;
ptr.read()
};
import_function_table.function_hint = funtion_hint;
import_function_table.function_address = hint_name_table_rva;
match is_named {
true => {
// 命名函数
let function_name = unsafe {
let ptr =
mmap[hint_name_table_foa as usize + 2..].as_ptr() as *const i8;
CStr::from_ptr(ptr).to_str()?.to_string()
};
import_function_table.function_name = function_name;
functions.push(ImportFunctionTableItem::Named(import_function_table));
}
false => {
functions.push(ImportFunctionTableItem::Ordinal(import_function_table));
}
}
// 下一个
base_offset += if is_64_bit { 8 } else { 4 };
}
import_module.functions = functions;
import_module_tables.push(import_module);
}
Ok(import_module_tables)
}
}
// 定义一个获取导入表后需要返回的结构
#[derive(Debug, Default, Clone, Serialize)]
pub struct ImportModuleTable {
pub module_name: String, // 模块名称
pub functions: Vec<ImportFunctionTableItem>, // 这里要另一个结构体来描述
pub timestamp: u32, // 时间戳
pub forwarder_chain: u32, // 转发链
pub module_name_rva: u32, // dll名称的RVA
pub iat_rva: u32, // IAT的RVA
pub int_rva: u32, // INT的RVA
}
// 因为导入查找表可能是函数名、也可能是序号 所以用枚举包一下最好
#[derive(Debug, Clone, Serialize)]
pub enum ImportFunctionTableItem {
// 命名的导入函数
Named(ImportFunctionTable),
// 序号表示的
Ordinal(ImportFunctionTable),
}
#[derive(Debug, Default, Clone, Serialize)]
pub struct ImportFunctionTable {
pub function_name: String, // 函数名称
pub function_address: u32, // 函数地址 IAT
pub function_hint: u16, // 函数提示
} }

View File

@ -97,6 +97,11 @@ pub fn command_get_file_pe_node_tree_data(
key: "section_header".to_string(), key: "section_header".to_string(),
children: vec![], children: vec![],
}, },
PeNodeTreeData {
title: "导入目录".to_string(),
key: "import_directory".to_string(),
children: vec![],
},
], ],
}; };
Ok(vec![result]) Ok(vec![result])
@ -179,6 +184,19 @@ pub fn command_get_pe_data_section_headers(
})) }))
} }
// 命令,获取导入表数据
#[tauri::command(async)]
pub fn command_get_pe_data_import_directory(
app_state: State<'_, Mutex<AppState>>,
) -> Result<Value, AppError> {
let app_state = app_state.lock().unwrap();
let import_directory = app_state.get_import_tables()?;
Ok(json!({
"fields": import_directory,
"base_offset": 0,
}))
}
// 命令,修改偏移处数据 // 命令,修改偏移处数据
#[tauri::command(async)] #[tauri::command(async)]
pub fn command_write_data( pub fn command_write_data(

View File

@ -24,6 +24,7 @@ pub fn run() {
commands::command_get_pe_data_file_header, commands::command_get_pe_data_file_header,
commands::command_get_pe_data_optional_header, commands::command_get_pe_data_optional_header,
commands::command_get_pe_data_section_headers, commands::command_get_pe_data_section_headers,
commands::command_get_pe_data_import_directory,
commands::command_write_data, commands::command_write_data,
commands::command_add_section, commands::command_add_section,
commands::command_test, commands::command_test,

View File

@ -12,6 +12,12 @@ pub enum PEParseError {
InvalidOptionalSize, InvalidOptionalSize,
#[error("解析超出了文件范围")] #[error("解析超出了文件范围")]
OutOfBounds, OutOfBounds,
#[error("错误的RVA: {0}")]
RvaToFoaError(u32),
#[error("RVA对应的是一个空节区数据RVA: {0}")]
RvaToEmptySection(u32),
#[error("错误的数据目录索引")]
InvalidDataDirectoryIndex,
} }
/// PE操作的错误 /// PE操作的错误
#[derive(Error, Debug)] #[derive(Error, Debug)]

View File

@ -45,7 +45,7 @@ pub struct ImageNTHeader64 {
#[repr(C)] #[repr(C)]
#[derive(Serialize, Clone, Copy)] #[derive(Serialize, Clone, Copy)]
#[serde(untagged)] #[serde(untagged)]
pub enum ImageNTHeader{ pub enum ImageNTHeader {
NTHeader32(ImageNTHeader32), NTHeader32(ImageNTHeader32),
NTHeader64(ImageNTHeader64), NTHeader64(ImageNTHeader64),
} }
@ -53,21 +53,24 @@ pub enum ImageNTHeader{
#[repr(C)] #[repr(C)]
#[derive(Serialize)] #[derive(Serialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum ImageNTHeaderMut<'a>{ pub enum ImageNTHeaderMut<'a> {
NTHeader32(&'a mut ImageNTHeader32), NTHeader32(&'a mut ImageNTHeader32),
NTHeader64(&'a mut ImageNTHeader64), NTHeader64(&'a mut ImageNTHeader64),
} }
impl <'a> ImageNTHeaderMut<'a>{ impl<'a> ImageNTHeaderMut<'a> {
pub fn get_optional_header_mut(&mut self) -> ImageOptionalHeaderMut{ pub fn get_optional_header_mut(&mut self) -> ImageOptionalHeaderMut {
match self{ match self {
ImageNTHeaderMut::NTHeader32(header) => ImageOptionalHeaderMut::OptionalHeader32(&mut header.optional_header), ImageNTHeaderMut::NTHeader32(header) => {
ImageNTHeaderMut::NTHeader64(header) => ImageOptionalHeaderMut::OptionalHeader64(&mut header.optional_header), ImageOptionalHeaderMut::OptionalHeader32(&mut header.optional_header)
}
ImageNTHeaderMut::NTHeader64(header) => {
ImageOptionalHeaderMut::OptionalHeader64(&mut header.optional_header)
}
} }
} }
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, Serialize)] #[derive(Debug, Clone, Copy, Serialize)]
pub struct ImageFileHeader { pub struct ImageFileHeader {
@ -374,9 +377,27 @@ impl ImageOptionalHeader {
ImageOptionalHeader::OptionalHeader64(header) => header.size_of_image, ImageOptionalHeader::OptionalHeader64(header) => header.size_of_image,
} }
} }
pub fn get_size_of_optional_header(&self) -> u32 {
match self {
ImageOptionalHeader::OptionalHeader32(_) => {
size_of::<ImageOptionalHeader32>() as u32
}
ImageOptionalHeader::OptionalHeader64(_) => {
size_of::<ImageOptionalHeader64>() as u32
}
}
}
/// 获取数据目录数量
pub fn get_data_directory_count(&self) -> u32 {
// 如果数据目录数量大于16则返回16
match self {
ImageOptionalHeader::OptionalHeader32(header) => header.number_of_rva_and_sizes,
ImageOptionalHeader::OptionalHeader64(header) => header.number_of_rva_and_sizes,
}
.min(16)
}
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, Serialize)] #[derive(Debug, Clone, Copy, Serialize)]
pub struct ImageSectionHeader { pub struct ImageSectionHeader {
@ -391,3 +412,42 @@ pub struct ImageSectionHeader {
pub number_of_linenumbers: u16, pub number_of_linenumbers: u16,
pub characteristics: SectionCharacteristics, pub characteristics: SectionCharacteristics,
} }
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
pub struct ImageImportDescriptor {
pub original_first_thunk: u32,
pub time_date_stamp: u32,
pub forwarder_chain: u32,
pub name: u32,
pub first_thunk: u32,
}
bitflags! {
/// 导入查找表 32位
/// [Import Lookup Table](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#import-lookup-table)
#[derive(Debug, Clone, Copy, Serialize)]
pub struct ImportLookupTable32: u32 {
/// 第31位为1表示导入函数通过序号导入
const IMPORT_BY_ORDINAL = 0x80000000;
/// 15-0 位表示导入函数的序号
const IMPORT_BY_ORDINAL_MASK = 0x0000FFFF;
/// 30-0 位表示导入函数的RVA
const IMPORT_BY_RVA_MASK = 0x7FFFFFFF;
}
/// 导入查找表 64位
/// [Import Lookup Table](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#import-lookup-table)
#[derive(Debug, Clone, Copy, Serialize)]
pub struct ImportLookupTable64: u64 {
/// 第63位为1表示导入函数通过序号导入
const IMPORT_BY_ORDINAL = 0x8000000000000000;
/// 15-0 位表示导入函数的序号
const IMPORT_BY_ORDINAL_MASK = 0x0000FFFF;
/// 30-0 位表示导入函数的RVA
const IMPORT_BY_RVA_MASK = 0x7FFFFFFFFFFFFFFF;
/// 62-31 must be zero.
const RESERVED = 0x7FFFFFFF00000000;
}
}

View File

@ -26,11 +26,86 @@ pub trait ReadOnlyPE: Deref<Target = [u8]> + Sized + AsRef<[u8]> {
Ok(is_64_bit) Ok(is_64_bit)
} }
// TODO: 需要一个RVA->FOA的转换函数 /// 获取数据目录
// TODO: 需要一个FOA->RVA的转换函数 fn get_data_directories(&self) -> Result<&[ImageDataDirectory], PEParseError> {
// TODO: 获取数据目录、可变数据目录 // 1. 获取数据目录偏移
// TODO: 通过枚举获取某个数据目录的数据
let data_directories_offset = self.get_data_directories_offset()?;
// 2. 解析数据目录
let data_directories = unsafe {
let ptr = self
.as_ptr()
.wrapping_offset(data_directories_offset as isize)
as *const ImageDataDirectory;
let data_directories_count = self.get_optional_header()?.get_data_directory_count();
let result = std::slice::from_raw_parts(ptr, data_directories_count as usize);
result
};
Ok(data_directories)
}
/// 通过枚举获取某项数据目录的成员
/// data_directory: 数据目录枚举
fn get_data_directory(
&self,
data_directory: ImageDirectoryEntry,
) -> Result<&ImageDataDirectory, PEParseError> {
let data_directories = self.get_data_directories()?;
let data_directory = data_directories
.get(data_directory as usize)
.ok_or(PEParseError::InvalidDataDirectoryIndex)?;
Ok(data_directory)
}
/// 获取数据目录的偏移
fn get_data_directories_offset(&self) -> Result<u32, PEParseError> {
// 1. 获取可选头和可选头的偏移
let optional_header: ImageOptionalHeader = self.get_optional_header()?;
let optional_header_offset = self.get_optional_header_offset()? as u32;
// 2. 数据目录的偏移=可选头的偏移+可选头的大小
let data_directories_offset =
optional_header_offset + optional_header.get_size_of_optional_header();
Ok(data_directories_offset)
}
/// 将RVA转换为FOA
/// rva: 需要转换的RVA
fn rva_to_foa(&self, rva: u32) -> Result<u32, PEParseError> {
let sections = self.get_section_headers()?;
for section in sections.iter() {
let section_start = section.virtual_address;
let section_end =
section_start + self.align_size_with_file_alignment(section.size_of_raw_data)?;
if rva >= section_start && rva < section_end {
if section.size_of_raw_data == 0 {
// 如果节区的SizeOfRawData是0那么这个节区是没有数据的 映射到这个节区的RVA一定是无效的
return Err(PEParseError::RvaToEmptySection(rva));
}
let foa = rva - section_start + section.pointer_to_raw_data;
return Ok(foa);
}
}
Err(PEParseError::RvaToFoaError(rva))
}
/// 将FOA转换为RVA
/// foa: 需要转换的FOA
fn foa_to_rva(&self, foa: u32) -> Result<u32, PEParseError> {
let sections = self.get_section_headers()?;
for section in sections.iter() {
// 如果section的PointerToRawData是0那么这个节区是没有数据的,直接跳过
if section.pointer_to_raw_data == 0 {
continue;
}
let section_start = section.pointer_to_raw_data;
let section_end = section_start + section.size_of_raw_data;
if foa >= section_start && foa < section_end {
let rva = foa - section_start + section.virtual_address;
return Ok(rva);
}
}
Err(PEParseError::RvaToFoaError(foa))
}
/// 将size与节对齐值进行对齐返回对齐后的值 /// 将size与节对齐值进行对齐返回对齐后的值
/// size: 需要对齐的值 /// size: 需要对齐的值
@ -275,6 +350,36 @@ pub trait MutablePE: ReadOnlyPE + DerefMut<Target = [u8]> + AsMut<[u8]> {
Ok(result) Ok(result)
} }
/// 获取可变的数据目录引用
fn get_data_directories_mut(&mut self) -> Result<&mut [ImageDataDirectory], PEParseError> {
// 1. 获取数据目录偏移
let data_directories_offset = self.get_data_directories_offset()?;
// 2. 解析数据目录
let data_directories = unsafe {
let ptr = self
.as_mut_ptr()
.wrapping_offset(data_directories_offset as isize)
as *mut ImageDataDirectory;
let data_directories_count = self.get_optional_header()?.get_data_directory_count();
let result = std::slice::from_raw_parts_mut(ptr, data_directories_count as usize);
result
};
Ok(data_directories)
}
/// 通过枚举获取某项数据目录的可变引用
/// data_directory: 数据目录枚举
fn get_data_directory_mut(
&mut self,
data_directory: ImageDirectoryEntry,
) -> Result<&mut ImageDataDirectory, PEParseError> {
let data_directories = self.get_data_directories_mut()?;
let data_directory = data_directories
.get_mut(data_directory as usize)
.ok_or(PEParseError::InvalidDataDirectoryIndex)?;
Ok(data_directory)
}
/// 扩大头映射大小,调用者必须要保证当挪动所有节区数据时,不会超出文件范围 /// 扩大头映射大小,调用者必须要保证当挪动所有节区数据时,不会超出文件范围
/// 也就是,调用者必须要在扩大头映射大小之前,先扩大文件大小 /// 也就是,调用者必须要在扩大头映射大小之前,先扩大文件大小
/// add_size: 需要扩大的大小,不需要进行对齐,会自动对齐 /// add_size: 需要扩大的大小,不需要进行对齐,会自动对齐

View File

@ -0,0 +1,19 @@
import { invoke } from "@tauri-apps/api/core";
import { useEffect } from "react";
export default function ImportDirectory() {
const getData = () => {
invoke("command_get_pe_data_import_directory")
.then((res) => {
console.log("res", res);
})
.catch((err) => {
console.log("err", err);
});
};
useEffect(() => {
getData();
}, []);
return <div>ImportDirectory</div>;
}

View File

@ -12,6 +12,7 @@ import FileHeader from "../components/FileHeader/FileHeader";
import SectionHeaders from "../components/SectionHeaders/SectionHeaders"; import SectionHeaders from "../components/SectionHeaders/SectionHeaders";
import OptionalHeader from "../components/OptionalHeader/OptionalHeader"; import OptionalHeader from "../components/OptionalHeader/OptionalHeader";
import { open } from "@tauri-apps/plugin-dialog"; import { open } from "@tauri-apps/plugin-dialog";
import ImportDirectory from "../components/ImportDirectory/ImportDirectory";
const SelectNodeMap = { const SelectNodeMap = {
dos_header: <DosHeader />, dos_header: <DosHeader />,
@ -19,6 +20,7 @@ const SelectNodeMap = {
file_header: <FileHeader />, file_header: <FileHeader />,
optional_header: <OptionalHeader />, optional_header: <OptionalHeader />,
section_header: <SectionHeaders />, section_header: <SectionHeaders />,
import_directory: <ImportDirectory />,
}; };
export default function MainPage() { export default function MainPage() {