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;
6use gaia_types::{helpers::Architecture, BinaryReader, GaiaError};
7use std::io::{Read, Seek};
8
9pub trait PeReader<R: Read + Seek> {
11 fn get_viewer(&mut self) -> &mut BinaryReader<R, LittleEndian>;
13
14 fn add_diagnostics(&mut self, error: impl Into<GaiaError>);
16
17 fn get_cached_section_headers(&self) -> Option<&Vec<SectionHeader>>;
19
20 fn set_cached_section_headers(&mut self, headers: Vec<SectionHeader>);
22
23 fn read_header_once(&mut self) -> Result<&PeHeader, GaiaError>;
25
26 fn read_header_force(&mut self) -> Result<PeHeader, GaiaError> {
28 let original_pos = self.get_viewer().get_position();
30
31 self.get_viewer().set_position(0)?;
33
34 let dos_header = DosHeader::read(self.get_viewer())?;
36
37 if dos_header.e_magic != 0x5A4D {
39 let error = GaiaError::invalid_data("无效的 DOS 签名 (MZ)");
40 self.add_diagnostics(error);
41 }
42
43 self.get_viewer().set_position(dos_header.e_lfanew as u64)?;
45
46 let nt_header = NtHeader::read(self.get_viewer())?;
48
49 if nt_header.signature != 0x00004550 {
51 let error = GaiaError::invalid_data("无效的 PE 签名 (PE)");
52 self.add_diagnostics(error);
53 }
54
55 let coff_header = CoffHeader::read(self.get_viewer())?;
57
58 if coff_header.number_of_sections == 0 {
60 let error = GaiaError::invalid_data("PE 文件必须至少有一个节");
61 self.add_diagnostics(error);
62 }
63
64 let optional_header = OptionalHeader::read(self.get_viewer())?;
66
67 match optional_header.magic {
69 0x10b => {} 0x20b => {} _ => {
72 let error = GaiaError::invalid_data("无效的可选头魔数");
73 self.add_diagnostics(error);
74 return Err(GaiaError::invalid_data("无效的可选头魔数"));
75 }
76 }
77
78 self.get_viewer().set_position(original_pos)?;
80
81 Ok(PeHeader { dos_header, nt_header, coff_header, optional_header })
82 }
83
84 fn read_section_headers(&mut self) -> Result<Vec<SectionHeader>, GaiaError> {
86 if let Some(sections) = self.get_cached_section_headers() {
87 return Ok(sections.clone());
88 }
89
90 let header = self.read_header_once()?.clone();
92 let original_pos = self.get_viewer().get_position();
93
94 let mut section_headers = Vec::with_capacity(header.coff_header.number_of_sections as usize);
96
97 let section_header_offset = header.dos_header.e_lfanew as u64
99 + 4 + std::mem::size_of::<CoffHeader>() as u64
101 + header.coff_header.size_of_optional_header as u64;
102
103 self.get_viewer().set_position(section_header_offset)?;
104
105 for _ in 0..header.coff_header.number_of_sections {
106 let section_header = SectionHeader::read(self.get_viewer())?;
107 section_headers.push(section_header);
108 }
109
110 self.get_viewer().set_position(original_pos)?;
112
113 self.set_cached_section_headers(section_headers.clone());
114 Ok(section_headers)
115 }
116
117 fn rva_to_file_offset(&self, rva: u32, sections: &[PeSection]) -> Result<u32, GaiaError> {
119 for section in sections {
120 if rva >= section.virtual_address && rva < section.virtual_address + section.virtual_size {
121 let offset_in_section = rva - section.virtual_address;
122 return Ok(section.pointer_to_raw_data + offset_in_section);
123 }
124 }
125 Err(GaiaError::invalid_data(&format!("无法将 RVA 0x{:08X} 转换为文件偏移", rva)))
126 }
127
128 fn read_section_from_header(&mut self, header: &SectionHeader) -> Result<PeSection, GaiaError> {
130 let name = String::from_utf8_lossy(&header.name).trim_end_matches('\0').to_string();
131 let viewer = self.get_viewer();
132
133 let mut data = Vec::new();
134 if header.size_of_raw_data > 0 && header.pointer_to_raw_data > 0 {
135 let original_pos = viewer.get_position();
136 viewer.set_position(header.pointer_to_raw_data as u64)?;
137 data.resize(header.size_of_raw_data as usize, 0);
138 viewer.read_exact(&mut data)?;
139 viewer.set_position(original_pos)?;
140 }
141
142 Ok(PeSection {
143 name,
144 virtual_size: header.virtual_size,
145 virtual_address: header.virtual_address,
146 size_of_raw_data: header.size_of_raw_data,
147 pointer_to_raw_data: header.pointer_to_raw_data,
148 pointer_to_relocations: header.pointer_to_relocations,
149 pointer_to_line_numbers: header.pointer_to_line_numbers,
150 number_of_relocations: header.number_of_relocations,
151 number_of_line_numbers: header.number_of_line_numbers,
152 characteristics: header.characteristics,
153 data,
154 })
155 }
156
157 fn parse_import_table(&mut self, header: &PeHeader, sections: &[PeSection]) -> Result<ImportTable, GaiaError> {
159 if header.optional_header.data_directories.len() < 2 {
161 return Ok(ImportTable::new());
162 }
163
164 let import_dir = &header.optional_header.data_directories[1]; if import_dir.virtual_address == 0 || import_dir.size == 0 {
166 return Ok(ImportTable::new());
167 }
168
169 let file_offset = self.rva_to_file_offset(import_dir.virtual_address, sections)?;
171
172 let current_pos = self.get_viewer().get_position();
174
175 self.get_viewer().set_position(file_offset as u64)?;
177
178 let mut import_table = ImportTable::new();
179
180 loop {
182 let import_lookup_table = self.get_viewer().read_u32()?;
183 let time_date_stamp = self.get_viewer().read_u32()?;
184 let forwarder_chain = self.get_viewer().read_u32()?;
185 let name_rva = self.get_viewer().read_u32()?;
186 let import_address_table = self.get_viewer().read_u32()?;
187
188 if import_lookup_table == 0
190 && time_date_stamp == 0
191 && forwarder_chain == 0
192 && name_rva == 0
193 && import_address_table == 0
194 {
195 break;
196 }
197
198 let mut dll_name = String::new();
199 let mut functions = Vec::new();
200
201 if name_rva != 0 {
203 let name_offset = self.rva_to_file_offset(name_rva, sections)?;
204 let saved_pos = self.get_viewer().get_position();
205 self.get_viewer().set_position(name_offset as u64)?;
206
207 let mut name_bytes = Vec::new();
208 loop {
209 let byte = self.get_viewer().read_u8()?;
210 if byte == 0 {
211 break;
212 }
213 name_bytes.push(byte);
214 }
215 dll_name = String::from_utf8_lossy(&name_bytes).to_string();
216 self.get_viewer().set_position(saved_pos)?;
217 }
218
219 if import_lookup_table != 0 {
221 let lookup_offset = self.rva_to_file_offset(import_lookup_table, sections)?;
222 let saved_pos = self.get_viewer().get_position();
223 self.get_viewer().set_position(lookup_offset as u64)?;
224
225 loop {
226 let entry = if header.optional_header.magic == 0x20b {
227 self.get_viewer().read_u64()?
229 }
230 else {
231 self.get_viewer().read_u32()? as u64
233 };
234
235 if entry == 0 {
236 break;
237 }
238
239 let is_ordinal = if header.optional_header.magic == 0x20b {
241 (entry & 0x8000000000000000) != 0
242 }
243 else {
244 (entry & 0x80000000) != 0
245 };
246
247 if !is_ordinal {
248 let hint_name_rva =
249 entry & if header.optional_header.magic == 0x20b { 0x7FFFFFFFFFFFFFFF } else { 0x7FFFFFFF };
250 let hint_name_offset = self.rva_to_file_offset(hint_name_rva as u32, sections)?;
251 let func_pos = self.get_viewer().get_position();
252 self.get_viewer().set_position(hint_name_offset as u64)?;
253
254 self.get_viewer().read_u16()?;
256
257 let mut func_name_bytes = Vec::new();
259 loop {
260 let byte = self.get_viewer().read_u8()?;
261 if byte == 0 {
262 break;
263 }
264 func_name_bytes.push(byte);
265 }
266 let func_name = String::from_utf8_lossy(&func_name_bytes).to_string();
267 functions.push(func_name);
268
269 self.get_viewer().set_position(func_pos)?;
270 }
271 else {
272 let ordinal = entry & 0xFFFF;
274 functions.push(format!("Ordinal_{}", ordinal));
275 }
276 }
277
278 self.get_viewer().set_position(saved_pos)?;
279 }
280
281 if !dll_name.is_empty() {
283 use crate::types::tables::ImportEntry;
284 let entry = ImportEntry { dll_name, functions };
285 import_table.entries.push(entry);
286 }
287 }
288
289 self.get_viewer().set_position(current_pos)?;
291
292 Ok(import_table)
293 }
294
295 fn parse_export_table(&mut self, header: &PeHeader, sections: &[PeSection]) -> Result<ExportTable, GaiaError> {
297 if header.optional_header.data_directories.is_empty() {
299 return Ok(ExportTable { name: String::new(), functions: Vec::new() });
300 }
301
302 let export_dir = &header.optional_header.data_directories[0]; if export_dir.virtual_address == 0 || export_dir.size == 0 {
304 return Ok(ExportTable { name: String::new(), functions: Vec::new() });
305 }
306
307 let file_offset = self.rva_to_file_offset(export_dir.virtual_address, sections)?;
309
310 let current_pos = self.get_viewer().get_position();
312
313 self.get_viewer().set_position(file_offset as u64)?;
315
316 let _export_flags = self.get_viewer().read_u32()?;
318 let _time_date_stamp = self.get_viewer().read_u32()?;
319 let _major_version = self.get_viewer().read_u16()?;
320 let _minor_version = self.get_viewer().read_u16()?;
321 let name_rva = self.get_viewer().read_u32()?;
322 let _ordinal_base = self.get_viewer().read_u32()?;
323 let _number_of_functions = self.get_viewer().read_u32()?;
324 let number_of_names = self.get_viewer().read_u32()?;
325 let _address_of_functions = self.get_viewer().read_u32()?;
326 let address_of_names = self.get_viewer().read_u32()?;
327 let _address_of_name_ordinals = self.get_viewer().read_u32()?;
328
329 let mut name = String::new();
331 if name_rva != 0 {
332 let name_offset = self.rva_to_file_offset(name_rva, sections)?;
333 let saved_pos = self.get_viewer().get_position();
334 self.get_viewer().set_position(name_offset as u64)?;
335
336 let mut name_bytes = Vec::new();
337 loop {
338 let byte = self.get_viewer().read_u8()?;
339 if byte == 0 {
340 break;
341 }
342 name_bytes.push(byte);
343 }
344 name = String::from_utf8_lossy(&name_bytes).to_string();
345 self.get_viewer().set_position(saved_pos)?;
346 }
347
348 let mut functions = Vec::new();
350 if address_of_names != 0 && number_of_names > 0 {
351 let names_offset = self.rva_to_file_offset(address_of_names, sections)?;
352 let saved_pos = self.get_viewer().get_position();
353 self.get_viewer().set_position(names_offset as u64)?;
354
355 for _ in 0..number_of_names {
356 let name_rva = self.get_viewer().read_u32()?;
357 if name_rva != 0 {
358 let func_name_offset = self.rva_to_file_offset(name_rva, sections)?;
359 let func_pos = self.get_viewer().get_position();
360 self.get_viewer().set_position(func_name_offset as u64)?;
361
362 let mut func_name_bytes = Vec::new();
363 loop {
364 let byte = self.get_viewer().read_u8()?;
365 if byte == 0 {
366 break;
367 }
368 func_name_bytes.push(byte);
369 }
370 let func_name = String::from_utf8_lossy(&func_name_bytes).to_string();
371 functions.push(func_name);
372
373 self.get_viewer().set_position(func_pos)?;
374 }
375 }
376
377 self.get_viewer().set_position(saved_pos)?;
378 }
379
380 self.get_viewer().set_position(current_pos)?;
382
383 Ok(ExportTable { name, functions })
384 }
385
386 fn create_pe_info(&mut self) -> Result<PeInfo, GaiaError> {
388 let header = self.read_header_once()?.clone();
389 let viewer = self.get_viewer();
390
391 let target_arch = match header.coff_header.machine {
393 0x014c => Architecture::X86,
394 0x8664 => Architecture::X86_64,
395 0x01c0 => Architecture::ARM32,
396 0xaa64 => Architecture::ARM64,
397 unknown => {
398 tracing::warn!("未知的机器类型: {:04x}", unknown);
399 Architecture::Unknown
400 }
401 };
402
403 let current_pos = viewer.get_position();
405 viewer.seek(std::io::SeekFrom::End(0))?;
406 let file_size = viewer.get_position();
407 viewer.set_position(current_pos)?;
408
409 Ok(PeInfo {
410 target_arch,
411 subsystem: header.optional_header.subsystem,
412 entry_point: header.optional_header.address_of_entry_point,
413 image_base: header.optional_header.image_base,
414 section_count: header.coff_header.number_of_sections,
415 file_size,
416 })
417 }
418 fn read_program_once(&mut self) -> Result<&PeProgram, GaiaError>;
420
421 fn read_program_force(&mut self) -> Result<PeProgram, GaiaError> {
422 let header = self.read_header_once()?.clone();
423 let section_headers = self.read_section_headers()?;
424
425 let mut sections = Vec::new();
427 for section_header in section_headers {
428 let section = self.read_section_from_header(§ion_header)?;
429 sections.push(section);
430 }
431
432 let imports = self.parse_import_table(&header, §ions)?;
434
435 let exports = self.parse_export_table(&header, §ions)?;
437
438 let program = PeProgram { header: header, sections, imports, exports };
439 Ok(program)
440 }
441}