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, LE};
6use gaia_types::{helpers::Architecture, BinaryReader, 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 set_section_headers(&mut self, headers: Vec<SectionHeader>) -> Vec<SectionHeader>;
38
39 fn get_pe_header(&mut self) -> Result<&PeHeader, GaiaError>;
41
42 fn set_pe_header(&mut self, head: PeHeader) -> Option<PeHeader>;
43
44 fn rva_to_file_offset(&self, rva: u32, sections: &[PeSection]) -> Result<u32, GaiaError> {
46 for section in sections {
47 if rva >= section.virtual_address && rva < section.virtual_address + section.virtual_size {
48 let offset_in_section = rva - section.virtual_address;
49 return Ok(section.pointer_to_raw_data + offset_in_section);
50 }
51 }
52 Err(GaiaError::invalid_data(&format!("无法将 RVA 0x{:08X} 转换为文件偏移", rva)))
53 }
54
55 fn parse_import_table(&mut self, header: &PeHeader, sections: &[PeSection]) -> Result<ImportTable, GaiaError> {
57 if header.optional_header.data_directories.len() < 2 {
59 return Ok(ImportTable::new());
60 }
61
62 let import_dir = &header.optional_header.data_directories[1]; if import_dir.virtual_address == 0 || import_dir.size == 0 {
64 return Ok(ImportTable::new());
65 }
66
67 let file_offset = self.rva_to_file_offset(import_dir.virtual_address, sections)?;
69
70 let current_pos = self.get_position()?;
72
73 self.set_position(file_offset as u64)?;
75
76 let mut import_table = ImportTable::new();
77
78 loop {
80 let import_lookup_table = self.get_viewer().read_u32::<LittleEndian>()?;
81 let time_date_stamp = self.get_viewer().read_u32::<LittleEndian>()?;
82 let forwarder_chain = self.get_viewer().read_u32::<LittleEndian>()?;
83 let name_rva = self.get_viewer().read_u32::<LittleEndian>()?;
84 let import_address_table = self.get_viewer().read_u32::<LittleEndian>()?;
85
86 if import_lookup_table == 0
88 && time_date_stamp == 0
89 && forwarder_chain == 0
90 && name_rva == 0
91 && import_address_table == 0
92 {
93 break;
94 }
95
96 let mut dll_name = String::new();
97 let mut functions = Vec::new();
98
99 if name_rva != 0 {
101 let name_offset = self.rva_to_file_offset(name_rva, sections)?;
102 let saved_pos = self.get_position()?;
103 self.set_position(name_offset as u64)?;
104
105 let mut name_bytes = Vec::new();
106 loop {
107 let byte = self.get_viewer().read_u8()?;
108 if byte == 0 {
109 break;
110 }
111 name_bytes.push(byte);
112 }
113 dll_name = String::from_utf8_lossy(&name_bytes).to_string();
114 self.set_position(saved_pos)?;
115 }
116
117 if import_lookup_table != 0 {
119 let lookup_offset = self.rva_to_file_offset(import_lookup_table, sections)?;
120 let saved_pos = self.get_position()?;
121 self.set_position(lookup_offset as u64)?;
122
123 loop {
124 let entry = if header.optional_header.magic == 0x20b {
125 self.get_viewer().read_u64::<LittleEndian>()?
127 }
128 else {
129 self.get_viewer().read_u32::<LittleEndian>()? as u64
131 };
132
133 if entry == 0 {
134 break;
135 }
136
137 let is_ordinal = if header.optional_header.magic == 0x20b {
139 (entry & 0x8000000000000000) != 0
140 }
141 else {
142 (entry & 0x80000000) != 0
143 };
144
145 if !is_ordinal {
146 let hint_name_rva =
147 entry & if header.optional_header.magic == 0x20b { 0x7FFFFFFFFFFFFFFF } else { 0x7FFFFFFF };
148 let hint_name_offset = self.rva_to_file_offset(hint_name_rva as u32, sections)?;
149 let func_pos = self.get_position()?;
150 self.set_position(hint_name_offset as u64)?;
151
152 self.get_viewer().read_u16::<LittleEndian>()?;
154
155 let mut func_name_bytes = Vec::new();
157 loop {
158 let byte = self.get_viewer().read_u8()?;
159 if byte == 0 {
160 break;
161 }
162 func_name_bytes.push(byte);
163 }
164 let func_name = String::from_utf8_lossy(&func_name_bytes).to_string();
165 functions.push(func_name);
166
167 self.set_position(func_pos)?;
168 }
169 else {
170 let ordinal = entry & 0xFFFF;
172 functions.push(format!("Ordinal_{}", ordinal));
173 }
174 }
175
176 self.set_position(saved_pos)?;
177 }
178
179 if !dll_name.is_empty() {
181 use crate::types::tables::ImportEntry;
182 let entry = ImportEntry { dll_name, functions };
183 import_table.entries.push(entry);
184 }
185 }
186
187 self.set_position(current_pos)?;
189
190 Ok(import_table)
191 }
192
193 fn parse_export_table(&mut self, header: &PeHeader, sections: &[PeSection]) -> Result<ExportTable, GaiaError> {
195 if header.optional_header.data_directories.is_empty() {
197 return Ok(ExportTable { name: String::new(), functions: Vec::new() });
198 }
199
200 let export_dir = &header.optional_header.data_directories[0]; if export_dir.virtual_address == 0 || export_dir.size == 0 {
202 return Ok(ExportTable { name: String::new(), functions: Vec::new() });
203 }
204
205 let file_offset = self.rva_to_file_offset(export_dir.virtual_address, sections)?;
207
208 let current_pos = self.get_position()?;
210
211 self.set_position(file_offset as u64)?;
213
214 let _export_flags = self.get_viewer().read_u32::<LittleEndian>()?;
216 let _time_date_stamp = self.get_viewer().read_u32::<LittleEndian>()?;
217 let _major_version = self.get_viewer().read_u16::<LittleEndian>()?;
218 let _minor_version = self.get_viewer().read_u16::<LittleEndian>()?;
219 let name_rva = self.get_viewer().read_u32::<LittleEndian>()?;
220 let _ordinal_base = self.get_viewer().read_u32::<LittleEndian>()?;
221 let _number_of_functions = self.get_viewer().read_u32::<LittleEndian>()?;
222 let number_of_names = self.get_viewer().read_u32::<LittleEndian>()?;
223 let _address_of_functions = self.get_viewer().read_u32::<LittleEndian>()?;
224 let address_of_names = self.get_viewer().read_u32::<LittleEndian>()?;
225 let _address_of_name_ordinals = self.get_viewer().read_u32::<LittleEndian>()?;
226
227 let mut name = String::new();
229 if name_rva != 0 {
230 let name_offset = self.rva_to_file_offset(name_rva, sections)?;
231 let saved_pos = self.get_position()?;
232 self.set_position(name_offset as u64)?;
233
234 let mut name_bytes = Vec::new();
235 loop {
236 let byte = self.get_viewer().read_u8()?;
237 if byte == 0 {
238 break;
239 }
240 name_bytes.push(byte);
241 }
242 name = String::from_utf8_lossy(&name_bytes).to_string();
243 self.set_position(saved_pos)?;
244 }
245
246 let mut functions = Vec::new();
248 if address_of_names != 0 && number_of_names > 0 {
249 let names_offset = self.rva_to_file_offset(address_of_names, sections)?;
250 let saved_pos = self.get_position()?;
251 self.set_position(names_offset as u64)?;
252
253 for _ in 0..number_of_names {
254 let name_rva = self.get_viewer().read_u32::<LittleEndian>()?;
255 if name_rva != 0 {
256 let func_name_offset = self.rva_to_file_offset(name_rva, sections)?;
257 let func_pos = self.get_position()?;
258 self.set_position(func_name_offset as u64)?;
259
260 let mut func_name_bytes = Vec::new();
261 loop {
262 let byte = self.get_viewer().read_u8()?;
263 if byte == 0 {
264 break;
265 }
266 func_name_bytes.push(byte);
267 }
268 let func_name = String::from_utf8_lossy(&func_name_bytes).to_string();
269 functions.push(func_name);
270
271 self.set_position(func_pos)?;
272 }
273 }
274
275 self.set_position(saved_pos)?;
276 }
277
278 self.set_position(current_pos)?;
280
281 Ok(ExportTable { name, functions })
282 }
283
284 fn create_pe_info(&mut self) -> Result<PeInfo, GaiaError> {
286 let header = self.get_pe_header()?.clone();
287
288 let target_arch = match header.coff_header.machine {
290 0x014c => Architecture::X86,
291 0x8664 => Architecture::X86_64,
292 0x01c0 => Architecture::ARM32,
293 0xaa64 => Architecture::ARM64,
294 unknown => {
295 tracing::warn!("未知的机器类型: {:04x}", unknown);
296 Architecture::Unknown
297 }
298 };
299
300 let current_pos = self.get_position()?;
302 self.get_viewer().seek(std::io::SeekFrom::End(0))?;
303 let file_size = self.get_position()?;
304 self.set_position(current_pos)?;
305
306 Ok(PeInfo {
307 target_arch,
308 subsystem: header.optional_header.subsystem,
309 entry_point: header.optional_header.address_of_entry_point,
310 image_base: header.optional_header.image_base,
311 section_count: header.coff_header.number_of_sections,
312 file_size,
313 })
314 }
315 fn get_program(&mut self) -> Result<&PeProgram, GaiaError>;
317
318 fn set_program(&mut self, program: PeProgram) -> Option<PeProgram>;
319}
320
321pub(crate) fn read_pe_head<R: Read + Seek>(reader: &mut impl PeReader<R>) -> Result<PeHeader, GaiaError> {
323 let original_pos = reader.get_position()?;
325
326 reader.set_position(0)?;
328
329 let dos_header = DosHeader::read(reader.get_viewer())?;
331
332 if dos_header.e_magic != 0x5A4D {
334 let error = GaiaError::invalid_data("无效的 DOS 签名 (MZ)");
335 reader.add_diagnostics(error);
336 }
337
338 reader.set_position(dos_header.e_lfanew as u64)?;
340
341 let nt_header = NtHeader::read(reader.get_viewer())?;
343
344 if nt_header.signature != 0x00004550 {
346 let error = GaiaError::invalid_data("无效的 PE 签名 (PE)");
347 reader.add_diagnostics(error);
348 }
349
350 let coff_header = CoffHeader::read(reader.get_viewer())?;
352
353 if coff_header.number_of_sections == 0 {
355 let error = GaiaError::invalid_data("PE 文件必须至少有一个节");
356 reader.add_diagnostics(error);
357 }
358
359 let optional_header = OptionalHeader::read(reader.get_viewer())?;
361
362 match optional_header.magic {
364 0x10b => {} 0x20b => {} _ => {
367 let error = GaiaError::invalid_data("无效的可选头魔数");
368 reader.add_diagnostics(error);
369 return Err(GaiaError::invalid_data("无效的可选头魔数"));
370 }
371 }
372
373 reader.set_position(original_pos)?;
375
376 Ok(PeHeader { dos_header, nt_header, coff_header, optional_header })
377}
378
379pub(crate) fn read_pe_section_headers<R: Read + Seek>(reader: &mut impl PeReader<R>) -> Result<(), GaiaError> {
381 let header = reader.get_pe_header()?.clone();
383 let original_pos = reader.get_position()?;
384
385 let mut section_headers = Vec::with_capacity(header.coff_header.number_of_sections as usize);
387
388 let section_header_offset = header.dos_header.e_lfanew as u64
390 + 4 + std::mem::size_of::<CoffHeader>() as u64
392 + header.coff_header.size_of_optional_header as u64;
393
394 reader.set_position(section_header_offset)?;
395
396 for _ in 0..header.coff_header.number_of_sections {
397 let section_header = SectionHeader::read(reader.get_viewer())?;
398 section_headers.push(section_header);
399 }
400
401 reader.set_position(original_pos)?;
403
404 reader.set_section_headers(section_headers);
405 Ok(())
406}
407
408pub(crate) fn read_section_from_header<R: Read + Seek>(
410 reader: &mut impl PeReader<R>,
411 header: &SectionHeader,
412) -> Result<PeSection, GaiaError> {
413 let name = String::from_utf8_lossy(&header.name).trim_end_matches('\0').to_string();
414
415 let mut data = Vec::new();
416 if header.size_of_raw_data > 0 && header.pointer_to_raw_data > 0 {
417 let original_pos = reader.get_position()?;
418 reader.set_position(header.pointer_to_raw_data as u64)?;
419 data.resize(header.size_of_raw_data as usize, 0);
420 reader.get_viewer().read_exact(&mut data)?;
421 reader.set_position(original_pos)?;
422 }
423
424 Ok(PeSection {
425 name,
426 virtual_size: header.virtual_size,
427 virtual_address: header.virtual_address,
428 size_of_raw_data: header.size_of_raw_data,
429 pointer_to_raw_data: header.pointer_to_raw_data,
430 pointer_to_relocations: header.pointer_to_relocations,
431 pointer_to_line_numbers: header.pointer_to_line_numbers,
432 number_of_relocations: header.number_of_relocations,
433 number_of_line_numbers: header.number_of_line_numbers,
434 characteristics: header.characteristics,
435 data,
436 })
437}
438
439pub(crate) fn read_pe_program<R: Read + Seek>(reader: &mut impl PeReader<R>) -> Result<(), GaiaError> {
440 let header = reader.get_pe_header()?.clone();
441 let section_headers = reader.get_section_headers()?.to_vec();
442
443 let mut sections = Vec::new();
445 for section_header in section_headers {
446 let section = read_section_from_header(reader, §ion_header)?;
447 sections.push(section);
448 }
449
450 let imports = reader.parse_import_table(&header, §ions)?;
452
453 let exports = reader.parse_export_table(&header, §ions)?;
455
456 reader.set_program(PeProgram { header, sections, imports, exports });
457 Ok(())
458}