clr_assembler/formats/exe/reader/
mod.rs1use crate::program::{
2 ClrAccessFlags, ClrHeader, ClrMethod, ClrProgram, ClrType, ClrTypeReference, ClrVersion, DotNetAssemblyInfo,
3 MetadataHeader, StreamHeader,
4};
5use byteorder::{LittleEndian, ReadBytesExt};
6use gaia_types::{GaiaDiagnostics, GaiaError, SourceLocation};
7use pe_assembler::{
8 helpers::PeReader,
9 types::{PeHeader, PeProgram, SectionHeader},
10};
11use std::{
12 fs,
13 io::{Cursor, Read, Seek, SeekFrom},
14};
15use url::Url;
16
17#[derive(Debug)]
26pub struct ExeReader<R> {
27 exe_reader: pe_assembler::formats::exe::reader::ExeReader<R>,
28 clr_header: Option<ClrHeader>,
30 metadata_header: Option<MetadataHeader>,
32 stream_headers: Option<Vec<StreamHeader>>,
34 assembly_info: Option<DotNetAssemblyInfo>,
36 clr_program: Option<ClrProgram>,
38}
39
40impl<R: Read + Seek> PeReader<R> for ExeReader<R> {
41 fn get_viewer(&mut self) -> &mut R {
42 self.exe_reader.get_viewer()
43 }
44
45 fn add_diagnostics(&mut self, error: impl Into<GaiaError>) {
46 self.exe_reader.add_diagnostics(error)
47 }
48
49 fn get_section_headers(&mut self) -> Result<&[SectionHeader], GaiaError> {
50 self.exe_reader.get_section_headers()
51 }
52
53 fn get_pe_header(&mut self) -> Result<&PeHeader, GaiaError> {
54 self.exe_reader.get_pe_header()
55 }
56
57 fn get_program(&mut self) -> Result<&PeProgram, GaiaError> {
58 self.exe_reader.get_program()
59 }
60}
61
62impl<R> ExeReader<R>
63where
64 R: Read + Seek,
65{
66 pub fn new(reader: R) -> Self {
70 ExeReader {
71 exe_reader: pe_assembler::formats::exe::reader::ExeReader::new(reader),
72 clr_header: None,
73 metadata_header: None,
74 stream_headers: None,
75 assembly_info: None,
76 clr_program: None,
77 }
78 }
79}
80impl<R> ExeReader<R>
81where
82 R: Read + Seek,
83{
84 pub fn get_assembly_info(&mut self) -> Result<DotNetAssemblyInfo, GaiaError> {
107 if self.assembly_info.is_none() {
108 self.ensure_assembly_info_parsed()?;
109 }
110
111 self.assembly_info
112 .as_ref()
113 .cloned()
114 .ok_or_else(|| GaiaError::syntax_error("程序集信息未解析".to_string(), SourceLocation::default()))
115 }
116
117 pub fn to_clr_program(&mut self) -> Result<ClrProgram, GaiaError> {
140 if let Some(ref program) = self.clr_program {
141 return Ok(program.clone());
142 }
143
144 let program = self.parse_full_program()?;
146 self.clr_program = Some(program.clone());
147 Ok(program)
148 }
149
150 pub fn validate_assembly(&mut self) -> Result<Vec<String>, GaiaError> {
169 let mut warnings = Vec::new();
170
171 self.ensure_assembly_info_parsed()?;
173
174 if self.clr_header.is_none() {
176 warnings.push("缺少 CLR 头".to_string());
177 }
178
179 if self.metadata_header.is_none() {
181 warnings.push("缺少元数据头".to_string());
182 }
183
184 if self.stream_headers.as_ref().map_or(true, |h| h.is_empty()) {
186 warnings.push("缺少元数据流".to_string());
187 }
188
189 Ok(warnings)
190 }
191
192 pub fn get_assembly_summary(&mut self) -> Result<String, GaiaError> {
209 match self.get_assembly_info() {
210 Ok(info) => Ok(format!(
211 "程序集: {}, 版本: {}, 文化: {}",
212 info.name,
213 info.version,
214 info.culture.unwrap_or_else(|| "neutral".to_string())
215 )),
216 Err(_) => Ok("程序集信息不可用".to_string()),
217 }
218 }
219
220 fn parse_clr_header(&mut self) -> Result<(), GaiaError> {
239 self.clr_header = self.find_and_read_clr_header()?;
240 Ok(())
241 }
242
243 fn parse_metadata(&mut self) -> Result<(), GaiaError> {
261 if let Some(ref clr_header) = self.clr_header {
262 let metadata_offset = self.rva_to_file_offset(clr_header.metadata_rva)?;
264 self.metadata_header = Some(self.read_metadata_header(metadata_offset)?);
266 self.stream_headers = Some(self.read_stream_headers(metadata_offset)?);
268 }
269
270 Ok(())
271 }
272
273 fn ensure_assembly_info_parsed(&mut self) -> Result<(), GaiaError> {
283 if self.assembly_info.is_none() {
284 self.parse_clr_header()?;
285 self.parse_metadata()?;
286 self.extract_assembly_info()?;
287 }
288 Ok(())
289 }
290
291 fn extract_assembly_info(&mut self) -> Result<(), GaiaError> {
300 self.assembly_info = Some(DotNetAssemblyInfo {
307 name: "Unknown".to_string(),
308 version: "0.0.0.0".to_string(),
309 culture: None,
310 public_key_token: None,
311 runtime_version: Some("v4.0.30319".to_string()),
312 });
313
314 Ok(())
315 }
316
317 fn parse_full_program(&self) -> Result<ClrProgram, GaiaError> {
326 let mut program = ClrProgram::new("UnknownAssembly".to_string());
328
329 program.version = ClrVersion { major: 1, minor: 0, build: 0, revision: 0 };
331
332 program.access_flags =
334 ClrAccessFlags { is_public: true, is_private: false, is_security_transparent: false, is_retargetable: false };
335
336 let mut example_type = ClrType::new("ExampleClass".to_string(), Some("ExampleNamespace".to_string()));
353
354 let void_type = ClrTypeReference {
356 name: "Void".to_string(),
357 namespace: Some("System".to_string()),
358 assembly: Some("mscorlib".to_string()),
359 is_value_type: true,
360 is_reference_type: false,
361 generic_parameters: Vec::new(),
362 };
363
364 let example_method = ClrMethod::new("ExampleMethod".to_string(), void_type);
365 example_type.add_method(example_method);
366
367 program.add_type(example_type);
368
369 Ok(program)
370 }
371
372 fn find_and_read_clr_header(&mut self) -> Result<Option<ClrHeader>, GaiaError> {
385 let pe_program = self.get_program()?.clone();
387
388 if let Some(clr_dir) = pe_program.header.optional_header.data_directories.get(14) {
390 if clr_dir.virtual_address == 0 || clr_dir.size == 0 {
391 return Ok(None);
392 }
393
394 let file_offset = self.rva_to_file_offset(clr_dir.virtual_address)?;
396
397 let viewer = self.get_viewer();
399 viewer
400 .seek(SeekFrom::Start(file_offset as u64))
401 .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
402
403 let cb = viewer
404 .read_u32::<LittleEndian>()
405 .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
406 let major_runtime_version = viewer
407 .read_u16::<LittleEndian>()
408 .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
409 let minor_runtime_version = viewer
410 .read_u16::<LittleEndian>()
411 .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
412 let metadata_rva = viewer
413 .read_u32::<LittleEndian>()
414 .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
415 let metadata_size = viewer
416 .read_u32::<LittleEndian>()
417 .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
418 let flags = viewer
419 .read_u32::<LittleEndian>()
420 .map_err(|e| GaiaError::io_error(e, Url::parse("file://clr_header").unwrap()))?;
421
422 Ok(Some(ClrHeader { cb, major_runtime_version, minor_runtime_version, metadata_rva, metadata_size, flags }))
423 }
424 else {
425 Ok(None)
426 }
427 }
428
429 fn read_metadata_header(&mut self, offset: u32) -> Result<MetadataHeader, GaiaError> {
446 let viewer = self.get_viewer();
447 viewer
448 .seek(SeekFrom::Start(offset as u64))
449 .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
450
451 let signature = viewer
453 .read_u32::<LittleEndian>()
454 .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
455 let major_version = viewer
456 .read_u16::<LittleEndian>()
457 .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
458 let minor_version = viewer
459 .read_u16::<LittleEndian>()
460 .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
461 let reserved = viewer
462 .read_u32::<LittleEndian>()
463 .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
464 let version_length = viewer
465 .read_u32::<LittleEndian>()
466 .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
467
468 let mut version_bytes = vec![0u8; version_length as usize];
470 viewer
471 .read_exact(&mut version_bytes)
472 .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
473 let version_string = String::from_utf8_lossy(&version_bytes).trim_end_matches('\0').to_string();
474
475 let flags = viewer
477 .read_u16::<LittleEndian>()
478 .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
479 let streams = viewer
480 .read_u16::<LittleEndian>()
481 .map_err(|e| GaiaError::io_error(e, Url::parse("file://metadata_header").unwrap()))?;
482
483 Ok(MetadataHeader { signature, major_version, minor_version, reserved, version_length, version_string, flags, streams })
485 }
486
487 fn read_stream_headers(&mut self, metadata_offset: u32) -> Result<Vec<StreamHeader>, GaiaError> {
512 let mut stream_headers = Vec::new();
513
514 if let Some(ref metadata_header) = self.metadata_header {
515 let stream_start_offset = metadata_offset + 20 + metadata_header.version_length;
517 self.exe_reader
518 .seek(SeekFrom::Start(stream_start_offset as u64))
519 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
520
521 for _ in 0..metadata_header.streams {
523 let offset = self
524 .exe_reader
525 .read_u32::<LittleEndian>()
526 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
527 let size = self
528 .exe_reader
529 .read_u32::<LittleEndian>()
530 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
531
532 let mut name_bytes = Vec::new();
534 loop {
535 let byte = self
536 .exe_reader
537 .read_u8()
538 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
539 if byte == 0 {
540 break;
541 }
542 name_bytes.push(byte);
543 }
544
545 let current_pos = stream_start_offset + 8 + name_bytes.len() as u32 + 1; let padding = (4 - (current_pos % 4)) % 4;
548 for _ in 0..padding {
549 self.exe_reader
550 .read_u8()
551 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
552 }
553
554 let name = String::from_utf8_lossy(&name_bytes).to_string();
555 stream_headers.push(StreamHeader { offset, size, name });
556 }
557 }
558
559 Ok(stream_headers)
560 }
561
562 fn rva_to_file_offset(&mut self, rva: u32) -> Result<u32, GaiaError> {
585 let pe_program = self.get_program()?.clone();
587
588 for section in &pe_program.sections {
590 let section_start = section.virtual_address;
591 let section_end = section_start + section.virtual_size;
592
593 if rva >= section_start && rva < section_end {
595 let offset_in_section = rva - section_start;
597 return Ok(section.pointer_to_raw_data + offset_in_section);
599 }
600 }
601
602 Err(GaiaError::syntax_error(format!("无法将 RVA 0x{:x} 转换为文件偏移", rva), SourceLocation::default()))
604 }
605}