pe_assembler/helpers/pe_reader/
mod.rs1use crate::types::{
2 CoffHeader, DosHeader, ExportTable, ImportTable, NtHeader, OptionalHeader, PeHeader, PeInfo, PeProgram, PeSection,
3 SectionHeader,
4};
5use byteorder::{LittleEndian, ReadBytesExt};
6use gaia_types::{helpers::Architecture, GaiaError};
7use std::io::{Read, Seek, SeekFrom};
8
9pub trait PeReader<R: Read + Seek> {
11 fn get_viewer(&mut self) -> &mut R;
13
14 fn add_diagnostics(&mut self, error: impl Into<GaiaError>);
16
17 fn get_position(&mut self) -> Result<u64, GaiaError>
18 where
19 R: Seek,
20 {
21 let position = self.get_viewer().stream_position()?;
22 Ok(position)
23 }
24
25 fn set_position(&mut self, offset: u64) -> Result<u64, GaiaError>
26 where
27 R: Seek,
28 {
29 let position = self.get_viewer().seek(SeekFrom::Start(offset))?;
30 Ok(position)
31 }
32
33 fn get_section_headers(&mut self) -> Result<&[SectionHeader], GaiaError>;
35
36 fn get_pe_header(&mut self) -> Result<&PeHeader, GaiaError>;
38
39 fn rva_to_file_offset(&self, rva: u32, sections: &[PeSection]) -> Result<u32, GaiaError> {
41 for section in sections {
42 if rva >= section.virtual_address && rva < section.virtual_address + section.virtual_size {
43 let offset_in_section = rva - section.virtual_address;
44 return Ok(section.pointer_to_raw_data + offset_in_section);
45 }
46 }
47 Err(GaiaError::invalid_data(&format!("无法将 RVA 0x{:08X} 转换为文件偏移", rva)))
48 }
49
50 fn parse_import_table(&mut self, header: &PeHeader, sections: &[PeSection]) -> Result<ImportTable, GaiaError> {
52 if header.optional_header.data_directories.len() < 2 {
54 return Ok(ImportTable::new());
55 }
56
57 let import_dir = &header.optional_header.data_directories[1]; if import_dir.virtual_address == 0 || import_dir.size == 0 {
59 return Ok(ImportTable::new());
60 }
61
62 let file_offset = self.rva_to_file_offset(import_dir.virtual_address, sections)?;
64
65 let current_pos = self.get_position()?;
67
68 self.set_position(file_offset as u64)?;
70
71 let mut import_table = ImportTable::new();
72
73 loop {
75 let import_lookup_table = self.get_viewer().read_u32::<LittleEndian>()?;
76 let time_date_stamp = self.get_viewer().read_u32::<LittleEndian>()?;
77 let forwarder_chain = self.get_viewer().read_u32::<LittleEndian>()?;
78 let name_rva = self.get_viewer().read_u32::<LittleEndian>()?;
79 let import_address_table = self.get_viewer().read_u32::<LittleEndian>()?;
80
81 if import_lookup_table == 0
83 && time_date_stamp == 0
84 && forwarder_chain == 0
85 && name_rva == 0
86 && import_address_table == 0
87 {
88 break;
89 }
90
91 let mut dll_name = String::new();
92 let mut functions = Vec::new();
93
94 if name_rva != 0 {
96 let name_offset = self.rva_to_file_offset(name_rva, sections)?;
97 let saved_pos = self.get_position()?;
98 self.set_position(name_offset as u64)?;
99
100 let mut name_bytes = Vec::new();
101 loop {
102 let byte = self.get_viewer().read_u8()?;
103 if byte == 0 {
104 break;
105 }
106 name_bytes.push(byte);
107 }
108 dll_name = String::from_utf8_lossy(&name_bytes).to_string();
109 self.set_position(saved_pos)?;
110 }
111
112 if import_lookup_table != 0 {
114 let lookup_offset = self.rva_to_file_offset(import_lookup_table, sections)?;
115 let saved_pos = self.get_position()?;
116 self.set_position(lookup_offset as u64)?;
117
118 loop {
119 let entry = if header.optional_header.magic == 0x20b {
120 self.get_viewer().read_u64::<LittleEndian>()?
122 }
123 else {
124 self.get_viewer().read_u32::<LittleEndian>()? as u64
126 };
127
128 if entry == 0 {
129 break;
130 }
131
132 let is_ordinal = if header.optional_header.magic == 0x20b {
134 (entry & 0x8000000000000000) != 0
135 }
136 else {
137 (entry & 0x80000000) != 0
138 };
139
140 if !is_ordinal {
141 let hint_name_rva =
142 entry & if header.optional_header.magic == 0x20b { 0x7FFFFFFFFFFFFFFF } else { 0x7FFFFFFF };
143 let hint_name_offset = self.rva_to_file_offset(hint_name_rva as u32, sections)?;
144 let func_pos = self.get_position()?;
145 self.set_position(hint_name_offset as u64)?;
146
147 self.get_viewer().read_u16::<LittleEndian>()?;
149
150 let mut func_name_bytes = Vec::new();
152 loop {
153 let byte = self.get_viewer().read_u8()?;
154 if byte == 0 {
155 break;
156 }
157 func_name_bytes.push(byte);
158 }
159 let func_name = String::from_utf8_lossy(&func_name_bytes).to_string();
160 functions.push(func_name);
161
162 self.set_position(func_pos)?;
163 }
164 else {
165 let ordinal = entry & 0xFFFF;
167 functions.push(format!("Ordinal_{}", ordinal));
168 }
169 }
170
171 self.set_position(saved_pos)?;
172 }
173
174 if !dll_name.is_empty() {
176 use crate::types::tables::ImportEntry;
177 let entry = ImportEntry { dll_name, functions };
178 import_table.entries.push(entry);
179 }
180 }
181
182 self.set_position(current_pos)?;
184
185 Ok(import_table)
186 }
187
188 fn parse_export_table(&mut self, header: &PeHeader, sections: &[PeSection]) -> Result<ExportTable, GaiaError> {
190 if header.optional_header.data_directories.is_empty() {
192 return Ok(ExportTable { name: String::new(), functions: Vec::new() });
193 }
194
195 let export_dir = &header.optional_header.data_directories[0]; if export_dir.virtual_address == 0 || export_dir.size == 0 {
197 return Ok(ExportTable { name: String::new(), functions: Vec::new() });
198 }
199
200 let file_offset = self.rva_to_file_offset(export_dir.virtual_address, sections)?;
202
203 let current_pos = self.get_position()?;
205
206 self.set_position(file_offset as u64)?;
208
209 let _export_flags = self.get_viewer().read_u32::<LittleEndian>()?;
211 let _time_date_stamp = self.get_viewer().read_u32::<LittleEndian>()?;
212 let _major_version = self.get_viewer().read_u16::<LittleEndian>()?;
213 let _minor_version = self.get_viewer().read_u16::<LittleEndian>()?;
214 let name_rva = self.get_viewer().read_u32::<LittleEndian>()?;
215 let _ordinal_base = self.get_viewer().read_u32::<LittleEndian>()?;
216 let _number_of_functions = self.get_viewer().read_u32::<LittleEndian>()?;
217 let number_of_names = self.get_viewer().read_u32::<LittleEndian>()?;
218 let _address_of_functions = self.get_viewer().read_u32::<LittleEndian>()?;
219 let address_of_names = self.get_viewer().read_u32::<LittleEndian>()?;
220 let _address_of_name_ordinals = self.get_viewer().read_u32::<LittleEndian>()?;
221
222 let mut name = String::new();
224 if name_rva != 0 {
225 let name_offset = self.rva_to_file_offset(name_rva, sections)?;
226 let saved_pos = self.get_position()?;
227 self.set_position(name_offset as u64)?;
228
229 let mut name_bytes = Vec::new();
230 loop {
231 let byte = self.get_viewer().read_u8()?;
232 if byte == 0 {
233 break;
234 }
235 name_bytes.push(byte);
236 }
237 name = String::from_utf8_lossy(&name_bytes).to_string();
238 self.set_position(saved_pos)?;
239 }
240
241 let mut functions = Vec::new();
243 if address_of_names != 0 && number_of_names > 0 {
244 let names_offset = self.rva_to_file_offset(address_of_names, sections)?;
245 let saved_pos = self.get_position()?;
246 self.set_position(names_offset as u64)?;
247
248 for _ in 0..number_of_names {
249 let name_rva = self.get_viewer().read_u32::<LittleEndian>()?;
250 if name_rva != 0 {
251 let func_name_offset = self.rva_to_file_offset(name_rva, sections)?;
252 let func_pos = self.get_position()?;
253 self.set_position(func_name_offset as u64)?;
254
255 let mut func_name_bytes = Vec::new();
256 loop {
257 let byte = self.get_viewer().read_u8()?;
258 if byte == 0 {
259 break;
260 }
261 func_name_bytes.push(byte);
262 }
263 let func_name = String::from_utf8_lossy(&func_name_bytes).to_string();
264 functions.push(func_name);
265
266 self.set_position(func_pos)?;
267 }
268 }
269
270 self.set_position(saved_pos)?;
271 }
272
273 self.set_position(current_pos)?;
275
276 Ok(ExportTable { name, functions })
277 }
278
279 fn create_pe_info(&mut self) -> Result<PeInfo, GaiaError> {
281 let header = self.get_pe_header()?.clone();
282
283 let target_arch = match header.coff_header.machine {
285 0x014c => Architecture::X86,
286 0x8664 => Architecture::X86_64,
287 0x01c0 => Architecture::ARM32,
288 0xaa64 => Architecture::ARM64,
289 unknown => {
290 tracing::warn!("未知的机器类型: {:04x}", unknown);
291 Architecture::Unknown
292 }
293 };
294
295 let current_pos = self.get_position()?;
297 self.get_viewer().seek(std::io::SeekFrom::End(0))?;
298 let file_size = self.get_position()?;
299 self.set_position(current_pos)?;
300
301 Ok(PeInfo {
302 target_arch,
303 subsystem: header.optional_header.subsystem,
304 entry_point: header.optional_header.address_of_entry_point,
305 image_base: header.optional_header.image_base,
306 section_count: header.coff_header.number_of_sections,
307 file_size,
308 })
309 }
310 fn get_program(&mut self) -> Result<&PeProgram, GaiaError>;
312}
313
314pub fn read_pe_head<R: Read + Seek>(reader: &mut impl PeReader<R>) -> Result<PeHeader, GaiaError> {
316 let original_pos = reader.get_position()?;
318
319 reader.set_position(0)?;
321
322 let dos_header = DosHeader::read(reader.get_viewer())?;
324
325 if dos_header.e_magic != 0x5A4D {
327 let error = GaiaError::invalid_data("无效的 DOS 签名 (MZ)");
328 reader.add_diagnostics(error);
329 }
330
331 reader.set_position(dos_header.e_lfanew as u64)?;
333
334 let nt_header = NtHeader::read(reader.get_viewer())?;
336
337 if nt_header.signature != 0x00004550 {
339 let error = GaiaError::invalid_data("无效的 PE 签名 (PE)");
340 reader.add_diagnostics(error);
341 }
342
343 let coff_header = CoffHeader::read(reader.get_viewer())?;
345
346 if coff_header.number_of_sections == 0 {
348 let error = GaiaError::invalid_data("PE 文件必须至少有一个节");
349 reader.add_diagnostics(error);
350 }
351
352 let optional_header = OptionalHeader::read(reader.get_viewer())?;
354
355 match optional_header.magic {
357 0x10b => {} 0x20b => {} _ => {
360 let error = GaiaError::invalid_data("无效的可选头魔数");
361 reader.add_diagnostics(error);
362 return Err(GaiaError::invalid_data("无效的可选头魔数"));
363 }
364 }
365
366 reader.set_position(original_pos)?;
368
369 Ok(PeHeader { dos_header, nt_header, coff_header, optional_header })
370}
371
372pub fn read_pe_section_headers<R: Read + Seek>(reader: &mut impl PeReader<R>) -> Result<Vec<SectionHeader>, GaiaError> {
374 let header = reader.get_pe_header()?.clone();
376 let original_pos = reader.get_position()?;
377
378 let mut section_headers = Vec::with_capacity(header.coff_header.number_of_sections as usize);
380
381 let section_header_offset = header.dos_header.e_lfanew as u64
383 + 4 + std::mem::size_of::<CoffHeader>() as u64
385 + header.coff_header.size_of_optional_header as u64;
386
387 reader.set_position(section_header_offset)?;
388
389 for _ in 0..header.coff_header.number_of_sections {
390 let section_header = SectionHeader::read(reader.get_viewer())?;
391 section_headers.push(section_header);
392 }
393
394 reader.set_position(original_pos)?;
396
397 Ok(section_headers)
398}
399
400pub fn read_section_from_header<R: Read + Seek>(
402 reader: &mut impl PeReader<R>,
403 header: &SectionHeader,
404) -> Result<PeSection, GaiaError> {
405 let name = String::from_utf8_lossy(&header.name).trim_end_matches('\0').to_string();
406
407 let mut data = Vec::new();
408 if header.size_of_raw_data > 0 && header.pointer_to_raw_data > 0 {
409 let original_pos = reader.get_position()?;
410 reader.set_position(header.pointer_to_raw_data as u64)?;
411 data.resize(header.size_of_raw_data as usize, 0);
412 reader.get_viewer().read_exact(&mut data)?;
413 reader.set_position(original_pos)?;
414 }
415
416 Ok(PeSection {
417 name,
418 virtual_size: header.virtual_size,
419 virtual_address: header.virtual_address,
420 size_of_raw_data: header.size_of_raw_data,
421 pointer_to_raw_data: header.pointer_to_raw_data,
422 pointer_to_relocations: header.pointer_to_relocations,
423 pointer_to_line_numbers: header.pointer_to_line_numbers,
424 number_of_relocations: header.number_of_relocations,
425 number_of_line_numbers: header.number_of_line_numbers,
426 characteristics: header.characteristics,
427 data,
428 })
429}
430
431pub fn read_pe_program<R: Read + Seek>(reader: &mut impl PeReader<R>) -> Result<PeProgram, GaiaError> {
432 let header = reader.get_pe_header()?.clone();
433 let section_headers = reader.get_section_headers()?.to_vec();
434
435 let mut sections = Vec::new();
437 for section_header in section_headers {
438 let section = read_section_from_header(reader, §ion_header)?;
439 sections.push(section);
440 }
441
442 let imports = reader.parse_import_table(&header, §ions)?;
444
445 let exports = reader.parse_export_table(&header, §ions)?;
447 Ok(PeProgram { header, sections, imports, exports })
448}