1use crate::{
2 formats::dll::DllReadConfig,
3 program::{
4 ClrAccessFlags, ClrHeader, ClrMethod, ClrProgram, ClrType, ClrTypeReference, ClrVersion, DotNetAssemblyInfo,
5 MetadataHeader, StreamHeader,
6 },
7};
8use byteorder::{LittleEndian, ReadBytesExt};
9use gaia_types::{GaiaDiagnostics, GaiaError, SourceLocation};
10use pe_assembler::{
11 helpers::PeReader,
12 types::{PeHeader, PeProgram, SectionHeader},
13};
14use std::io::{Read, Seek, SeekFrom};
15use url::Url;
16
17#[derive(Debug)]
18pub struct DllReader<'config, R> {
19 options: &'config DllReadConfig,
21 reader: pe_assembler::formats::dll::reader::DllReader<R>,
22 clr_header: Option<ClrHeader>,
24 metadata_header: Option<MetadataHeader>,
26 stream_headers: Option<Vec<StreamHeader>>,
28 assembly_info: Option<DotNetAssemblyInfo>,
30 clr_program: Option<ClrProgram>,
32}
33
34impl<'config, R: Read + Seek> PeReader<R> for DllReader<'config, R> {
35 fn get_viewer(&mut self) -> &mut R {
36 self.reader.get_viewer()
37 }
38
39 fn add_diagnostics(&mut self, error: impl Into<GaiaError>) {
40 self.reader.add_diagnostics(error)
41 }
42
43 fn get_section_headers(&mut self) -> Result<&[SectionHeader], GaiaError> {
44 self.reader.get_section_headers()
45 }
46
47 fn get_pe_header(&mut self) -> Result<&PeHeader, GaiaError> {
48 self.reader.get_pe_header()
49 }
50
51 fn get_program(&mut self) -> Result<&PeProgram, GaiaError> {
52 self.reader.get_program()
53 }
54}
55
56impl<'config, R> DllReader<'config, R> {
57 pub fn new(reader: R, options: &'config DllReadConfig) -> Self {
61 Self {
62 reader: pe_assembler::formats::dll::reader::DllReader::new(reader),
63 clr_header: None,
64 metadata_header: None,
65 stream_headers: None,
66 assembly_info: None,
67 clr_program: None,
68 options,
69 }
70 }
71}
72
73impl<'config, R> DllReader<'config, R>
74where
75 R: Read + Seek,
76{
77 pub fn get_assembly_info(&mut self) -> Result<DotNetAssemblyInfo, GaiaError> {
117 if self.assembly_info.is_none() {
118 self.ensure_assembly_info_parsed()?;
119 }
120
121 self.assembly_info
122 .as_ref()
123 .cloned()
124 .ok_or_else(|| GaiaError::syntax_error("程序集信息未解析".to_string(), SourceLocation::default()))
125 }
126
127 pub fn to_clr_program(&mut self) -> Result<ClrProgram, GaiaError> {
136 if let Some(ref program) = self.clr_program {
137 return Ok(program.clone());
138 }
139
140 let program = self.parse_full_program()?;
142 self.clr_program = Some(program.clone());
143 Ok(program)
144 }
145
146 pub fn validate_assembly(&mut self) -> Result<Vec<String>, GaiaError> {
157 let mut warnings = Vec::new();
158
159 self.ensure_assembly_info_parsed()?;
161
162 if self.clr_header.is_none() {
164 warnings.push("缺少 CLR 头".to_string());
165 }
166
167 if self.metadata_header.is_none() {
169 warnings.push("缺少元数据头".to_string());
170 }
171
172 if self.stream_headers.as_ref().map_or(true, |h| h.is_empty()) {
174 warnings.push("缺少元数据流".to_string());
175 }
176
177 Ok(warnings)
178 }
179
180 pub fn get_assembly_summary(&mut self) -> String {
188 match self.get_assembly_info() {
189 Ok(info) => {
190 format!(
191 "程序集: {}\n版本: {}\n文化: {}\n公钥标记: {}\n运行时版本: {}",
192 info.name,
193 info.version,
194 info.culture.as_deref().unwrap_or("neutral"),
195 info.public_key_token.as_deref().unwrap_or("null"),
196 info.runtime_version.as_deref().unwrap_or("unknown")
197 )
198 }
199 Err(_) => "无法获取程序集信息".to_string(),
200 }
201 }
202
203 fn ensure_assembly_info_parsed(&mut self) -> Result<(), GaiaError> {
205 if self.assembly_info.is_some() {
206 return Ok(());
207 }
208
209 self.parse_clr_header()?;
211 self.parse_metadata()?;
212 self.extract_assembly_info()?;
213
214 Ok(())
215 }
216
217 fn parse_clr_header(&mut self) -> Result<(), GaiaError> {
226 self.clr_header = self.find_and_read_clr_header()?;
227 Ok(())
228 }
229
230 fn parse_metadata(&mut self) -> Result<(), GaiaError> {
241 if let Some(ref clr_header) = self.clr_header {
242 let metadata_offset = self.rva_to_file_offset(clr_header.metadata_rva)?;
244 self.metadata_header = Some(self.read_metadata_header(metadata_offset)?);
246 self.stream_headers = Some(self.read_stream_headers(metadata_offset)?);
248 }
249
250 Ok(())
251 }
252
253 fn extract_assembly_info(&mut self) -> Result<(), GaiaError> {
262 let clr_header = match &self.clr_header {
264 Some(h) => *h,
265 None => return Ok(()),
266 };
267 let metadata_offset = self.rva_to_file_offset(clr_header.metadata_rva)?;
268
269 let mut tables_stream: Option<StreamHeader> = None;
271 let mut strings_stream: Option<StreamHeader> = None;
272 if let Some(ref stream_headers) = self.stream_headers {
273 for sh in stream_headers {
274 match sh.name.as_str() {
275 "#~" => tables_stream = Some(sh.clone()),
276 "#Strings" => strings_stream = Some(sh.clone()),
277 _ => {}
278 }
279 }
280 }
281 if tables_stream.is_none() || strings_stream.is_none() {
282 return Ok(());
283 }
284 let tables_stream = tables_stream.unwrap();
285 let strings_stream = strings_stream.unwrap();
286
287 let tables_start = metadata_offset + tables_stream.offset;
288 let strings_start = metadata_offset + strings_stream.offset;
289
290 let mut cur = self.reader.get_viewer();
292 cur.seek(SeekFrom::Start(tables_start as u64))
293 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://table").unwrap()))?;
294
295 let _reserved =
296 cur.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
297 let _major = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
298 let _minor = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
299 let heap_sizes = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
300 let _reserved2 = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
301 let valid_mask =
302 cur.read_u64::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
303 let _sorted_mask =
304 cur.read_u64::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
305
306 let str_idx_sz = if (heap_sizes & 0x01) != 0 { 4 } else { 2 };
308 let guid_idx_sz = if (heap_sizes & 0x02) != 0 { 4 } else { 2 };
309 let blob_idx_sz = if (heap_sizes & 0x04) != 0 { 4 } else { 2 };
310
311 let mut row_counts: [u32; 64] = [0; 64];
313 for tid in 0..64u8 {
314 if (valid_mask >> tid) & 1 == 1 {
315 row_counts[tid as usize] = cur
316 .read_u32::<LittleEndian>()
317 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_rows").unwrap()))?;
318 }
319 }
320
321 fn coded_size(rows: &[u32; 64], tags: &[u8]) -> u32 {
323 let max_rows = tags.iter().map(|&t| rows[t as usize]).max().unwrap_or(0);
324 let tag_bits = (tags.len() as f32).log2().ceil() as u32;
325 if (max_rows << tag_bits) < (1 << 16) {
326 2
327 }
328 else {
329 4
330 }
331 }
332 let type_def_or_ref_sz = coded_size(&row_counts, &[0x02, 0x01, 0x18]);
333 let resolution_scope_sz = coded_size(&row_counts, &[0x00, 0x01, 0x17, 0x23]);
334
335 let module_row_size = 2 + str_idx_sz + guid_idx_sz + guid_idx_sz + guid_idx_sz;
337 let type_def_row_size = 4
338 + str_idx_sz
339 + str_idx_sz
340 + type_def_or_ref_sz
341 + (if row_counts[0x04] < (1 << 16) { 2 } else { 4 })
342 + (if row_counts[0x06] < (1 << 16) { 2 } else { 4 });
343 let methoddef_row_size = 4 + 2 + 2 + str_idx_sz + blob_idx_sz + (if row_counts[0x07] < (1 << 16) { 2 } else { 4 });
344 let typeref_row_size = resolution_scope_sz + str_idx_sz + str_idx_sz;
345 let assembly_row_size = 4 + 2 + 2 + 2 + 2 + 4 + blob_idx_sz + str_idx_sz + str_idx_sz;
346
347 let tables_data_start =
349 cur.stream_position().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_data").unwrap()))? as u32;
350 let mut table_start: [Option<u32>; 64] = [None; 64];
352 let mut table_row_size: [u32; 64] = [0; 64];
353 let mut running = tables_data_start;
354 for tid in 0..64u8 {
355 if (valid_mask >> tid) & 1 == 1 {
356 let rows = row_counts[tid as usize];
357 let row_size = match tid {
358 0x00 => module_row_size,
359 0x01 => typeref_row_size,
360 0x02 => type_def_row_size,
361 0x06 => methoddef_row_size,
362 0x1D => assembly_row_size,
363 _ => 0,
364 } as u32;
365 table_start[tid as usize] = Some(running);
366 table_row_size[tid as usize] = row_size;
367 running += rows * row_size;
368 }
369 }
370
371 let mut name = String::from("Unknown");
373 let mut version = ClrVersion { major: 0, minor: 0, build: 0, revision: 0 };
374 let strings_size = strings_stream.size;
375 if let Some(asm_start) = table_start[0x1D] {
376 if row_counts[0x1D] > 0 {
378 let mut c = self.reader.get_viewer();
379 c.seek(SeekFrom::Start(asm_start as u64))
380 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
381 let _hash_alg =
382 c.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
383 version.major =
384 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
385 version.minor =
386 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
387 version.build =
388 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
389 version.revision =
390 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
391 let _flags =
392 c.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
393 let _pk_idx = read_heap_index(&mut c, blob_idx_sz)?;
394 let name_idx = read_heap_index(&mut c, str_idx_sz)?;
395 let _culture_idx = read_heap_index(&mut c, str_idx_sz)?;
396 let n = self.read_string_from_strings_heap(strings_start, strings_size, name_idx)?;
397 if !n.is_empty() {
398 name = n;
399 }
400 }
401 }
402 else if let Some(mod_start) = table_start[0x00] {
403 if row_counts[0x00] > 0 {
405 let mut c = self.reader.get_viewer();
406 c.seek(SeekFrom::Start(mod_start as u64))
407 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://mod").unwrap()))?;
408 let _generation =
409 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://mod").unwrap()))?;
410 let name_idx = read_heap_index(&mut c, str_idx_sz)?;
411 let _mvid_idx = read_heap_index(&mut c, guid_idx_sz)?;
412 let _enc_id_idx = read_heap_index(&mut c, guid_idx_sz)?;
413 let _enc_base_id_idx = read_heap_index(&mut c, guid_idx_sz)?;
414 let n = self.read_string_from_strings_heap(strings_start, strings_size, name_idx)?;
415 if !n.is_empty() {
416 name = n;
417 }
418 }
419 }
420
421 let runtime_version = self.metadata_header.as_ref().map(|h| h.version_string.clone());
423
424 self.assembly_info = Some(DotNetAssemblyInfo {
426 name,
427 version: format!("{}.{}.{}.{}", version.major, version.minor, version.build, version.revision),
428 culture: None,
429 public_key_token: None,
430 runtime_version,
431 });
432
433 Ok(())
434 }
435
436 fn parse_full_program(&mut self) -> Result<ClrProgram, GaiaError> {
445 let metadata_rva = self
447 .clr_header
448 .as_ref()
449 .ok_or_else(|| GaiaError::syntax_error("缺少 CLR 头".to_string(), SourceLocation::default()))?
450 .metadata_rva;
451 let _version_string = self
452 .metadata_header
453 .as_ref()
454 .ok_or_else(|| GaiaError::syntax_error("缺少元数据头".to_string(), SourceLocation::default()))?
455 .version_string
456 .clone();
457
458 let metadata_base = self.rva_to_file_offset(metadata_rva)?;
460
461 let mut tables_stream: Option<StreamHeader> = None;
463 let mut strings_stream: Option<StreamHeader> = None;
464 if let Some(ref stream_headers) = self.stream_headers {
465 for sh in stream_headers {
466 match sh.name.as_str() {
467 "#~" | "#-" => tables_stream = Some(sh.clone()),
468 "#Strings" => strings_stream = Some(sh.clone()),
469 _ => {}
470 }
471 }
472 }
473
474 let tables_stream = tables_stream
475 .ok_or_else(|| GaiaError::syntax_error("缺少元数据表流(#~/#-)".to_string(), SourceLocation::default()))?;
476 let strings_stream = strings_stream
477 .ok_or_else(|| GaiaError::syntax_error("缺少字符串流(#Strings)".to_string(), SourceLocation::default()))?;
478
479 let mut cur = self.reader.get_viewer();
481 let tables_start = metadata_base + tables_stream.offset;
483 let strings_start = metadata_base + strings_stream.offset;
484
485 cur.seek(SeekFrom::Start(tables_start as u64))
487 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
488 let _reserved =
489 cur.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
490 let _major = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
491 let _minor = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
492 let heap_sizes = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
493 let _reserved2 = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
494 let valid_mask =
495 cur.read_u64::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
496 let _sorted_mask =
497 cur.read_u64::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
498
499 let mut row_counts: [u32; 64] = [0; 64];
501 for tid in 0..64u8 {
502 if (valid_mask >> tid) & 1 == 1 {
503 let cnt = cur
504 .read_u32::<LittleEndian>()
505 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
506 row_counts[tid as usize] = cnt;
507 }
508 }
509
510 let str_idx_sz = if (heap_sizes & 0x01) != 0 { 4 } else { 2 };
512 let guid_idx_sz = if (heap_sizes & 0x02) != 0 { 4 } else { 2 };
513 let blob_idx_sz = if (heap_sizes & 0x04) != 0 { 4 } else { 2 };
514 let _ = guid_idx_sz; let tables_data_start =
518 cur.stream_position().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_data").unwrap()))? as u32;
519
520 let mut simple_index_size = |table_id: u8| -> u32 {
522 let rows = row_counts[table_id as usize];
523 if rows < (1 << 16) {
524 2
525 }
526 else {
527 4
528 }
529 };
530
531 let param_index_sz = simple_index_size(0x07); let methoddef_row_size = 4 + 2 + 2 + str_idx_sz + blob_idx_sz + param_index_sz;
534
535 let field_row_size = 2 + str_idx_sz + blob_idx_sz ;
537 let fieldptr_row_size = simple_index_size(0x04);
538 let methodptr_row_size = simple_index_size(0x06);
539 let rs_candidates = [0x00u8, 0x17u8, 0x20u8, 0x01u8];
542 let mut max_rs_rows = 0u32;
543 for &t in &rs_candidates {
544 max_rs_rows = max_rs_rows.max(row_counts[t as usize]);
545 }
546 let rs_tag_bits = 2u32;
547 let resolution_scope_sz = if max_rs_rows < (1 << (16 - rs_tag_bits)) { 2 } else { 4 };
548 let typeref_row_size = resolution_scope_sz + str_idx_sz + str_idx_sz;
549 let tdr_candidates = [0x02u8, 0x01u8, 0x18u8];
552 let mut max_tdr_rows = 0u32;
553 for &t in &tdr_candidates {
554 max_tdr_rows = max_tdr_rows.max(row_counts[t as usize]);
555 }
556 let tdr_tag_bits = 2u32;
557 let type_def_or_ref_sz = if max_tdr_rows < (1 << (16 - tdr_tag_bits)) { 2 } else { 4 };
558 let type_def_row_size =
559 4 + str_idx_sz + str_idx_sz + type_def_or_ref_sz + simple_index_size(0x04) + simple_index_size(0x06);
560 let module_row_size = 2 + str_idx_sz + guid_idx_sz + guid_idx_sz + guid_idx_sz;
562
563 let mut table_start: [Option<u32>; 64] = [None; 64];
565 let mut table_row_size: [u32; 64] = [0; 64];
566 let mut running = tables_data_start;
567 for tid in 0..64u8 {
568 if (valid_mask >> tid) & 1 == 1 {
569 let rows = row_counts[tid as usize];
570 let row_size = match tid {
571 0x00 => module_row_size,
572 0x01 => typeref_row_size,
573 0x02 => type_def_row_size,
574 0x03 => fieldptr_row_size,
575 0x04 => field_row_size,
576 0x05 => methodptr_row_size,
577 0x06 => methoddef_row_size,
578 0x07 => 2 + str_idx_sz + blob_idx_sz, 0x08 => simple_index_size(0x02) + simple_index_size(0x01), 0x09 => resolution_scope_sz + str_idx_sz + blob_idx_sz, 0x0A => 2 + blob_idx_sz, 0x0B => simple_index_size(0x02) + simple_index_size(0x0A) + simple_index_size(0x0C), 0x0C => simple_index_size(0x04) + simple_index_size(0x07), 0x0D => 2 + blob_idx_sz, 0x0E => 2 + 4 + 4, 0x0F => simple_index_size(0x04) + 4, 0x10 => blob_idx_sz, 0x11 => simple_index_size(0x02) + simple_index_size(0x12), 0x12 => 2 + str_idx_sz + simple_index_size(0x10), 0x13 => simple_index_size(0x02) + simple_index_size(0x14), 0x14 => 2 + str_idx_sz + blob_idx_sz, 0x15 => 2 + simple_index_size(0x06) + simple_index_size(0x14), 0x16 => simple_index_size(0x02) + simple_index_size(0x06) + simple_index_size(0x01), 0x17 => str_idx_sz, 0x18 => blob_idx_sz, 0x19 => 2 + simple_index_size(0x17) + str_idx_sz, 0x1A => 4 + simple_index_size(0x04), 0x1B => 4, 0x1C => 4, 0x1D => 4 + 2 + 2 + 2 + 2 + 4 + blob_idx_sz + str_idx_sz + str_idx_sz, 0x1E => 4 + 4, 0x1F => 4 + 4 + 4, 0x20 => 2 + 2 + 2 + 2 + 4 + blob_idx_sz + str_idx_sz + str_idx_sz + blob_idx_sz, _ => 0,
605 } as u32;
606 table_start[tid as usize] = Some(running);
607 table_row_size[tid as usize] = row_size;
608 running += rows * row_size;
609 }
610 }
611 let methoddef_offset = table_start[0x06].unwrap_or(tables_data_start);
612
613 let mut program = ClrProgram::new("UnknownAssembly");
615 program.version = ClrVersion { major: 1, minor: 0, build: 0, revision: 0 };
616 program.access_flags =
617 ClrAccessFlags { is_public: true, is_private: false, is_security_transparent: false, is_retargetable: false };
618
619 if let Some(asm_start) = table_start[0x1D] {
621 let asm_rows = row_counts[0x1D];
623 if asm_rows > 0 {
624 let asm0 = asm_start; let mut c2 = self.reader.get_viewer();
626 c2.seek(SeekFrom::Start(asm0 as u64))
627 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
628 let _hash_alg =
629 c2.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
630 let ver_major =
631 c2.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
632 let ver_minor =
633 c2.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
634 let ver_build =
635 c2.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
636 let ver_rev =
637 c2.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
638 let _flags =
639 c2.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
640 let _pk_idx = read_heap_index(&mut c2, blob_idx_sz)?;
641 let name_idx = read_heap_index(&mut c2, str_idx_sz)?;
642 let culture_idx = read_heap_index(&mut c2, str_idx_sz)?;
643 let _hash_idx = read_heap_index(&mut c2, blob_idx_sz)?;
644
645 let name = self.read_string_from_strings_heap(strings_start, strings_stream.size, name_idx)?;
646 let culture = if culture_idx != 0 {
647 Some(self.read_string_from_strings_heap(strings_start, strings_stream.size, culture_idx)?)
648 }
649 else {
650 None
651 };
652
653 if !name.is_empty() {
654 program.name = name;
655 }
656 program.version = ClrVersion { major: ver_major, minor: ver_minor, build: ver_build, revision: ver_rev };
657 }
658 }
659 else if let Some(module_start) = table_start[0x00] {
660 let mut cm = self.reader.get_viewer();
662 cm.seek(SeekFrom::Start(module_start as u64))
663 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://module").unwrap()))?;
664 let _generation =
665 cm.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://module").unwrap()))?;
666 let name_idx = read_heap_index(&mut cm, str_idx_sz)?;
667 let _mvid_idx = read_heap_index(&mut cm, guid_idx_sz)?;
668 let _encid = read_heap_index(&mut cm, guid_idx_sz)?;
669 let _encbase = read_heap_index(&mut cm, guid_idx_sz)?;
670 let mod_name = self.read_string_from_strings_heap(strings_start, strings_stream.size, name_idx)?;
671 if !mod_name.is_empty() {
672 program.name = mod_name;
673 }
674 }
675
676 if let Some(typedef_start) = table_start[0x02] {
678 for i in 0..row_counts[0x02] {
679 let mut ct = self.reader.get_viewer();
680 ct.seek(SeekFrom::Start((typedef_start + i * type_def_row_size) as u64))
681 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://typedef").unwrap()))?;
682 let flags = ct
683 .read_u32::<LittleEndian>()
684 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://typedef").unwrap()))?;
685 let name_idx = read_heap_index(&mut ct, str_idx_sz)?;
686 let ns_idx = read_heap_index(&mut ct, str_idx_sz)?;
687 let _extends_idx = read_type_def_or_ref_index(&mut ct, type_def_or_ref_sz)?;
688 let _field_list_idx = read_heap_index(&mut ct, (if row_counts[0x04] < (1 << 16) { 2 } else { 4 }))?;
689 let _method_list_idx = read_heap_index(&mut ct, (if row_counts[0x06] < (1 << 16) { 2 } else { 4 }))?;
690
691 let type_name = self.read_string_from_strings_heap(strings_start, strings_stream.size, name_idx)?;
692 let namespace = if ns_idx != 0 {
693 Some(self.read_string_from_strings_heap(strings_start, strings_stream.size, ns_idx)?)
694 }
695 else {
696 None
697 };
698
699 if !type_name.is_empty() {
700 let mdef = ClrMethod::new(
701 "DefaultMethod".to_string(),
702 ClrTypeReference {
703 name: "Void".to_string(),
704 namespace: Some("System".to_string()),
705 assembly: Some("mscorlib".to_string()),
706 is_value_type: true,
707 is_reference_type: false,
708 generic_parameters: Vec::new(),
709 },
710 );
711 let mut clr_type = ClrType::new(type_name, namespace);
712 clr_type.access_flags.is_public = true;
713 clr_type.add_method(mdef);
714 }
715 }
716 }
717
718 if let Some(methoddef_start) = table_start[0x06] {
720 for i in 0..row_counts[0x06] {
721 let mut c3 = self.reader.get_viewer();
722 c3.seek(SeekFrom::Start((methoddef_start + i * methoddef_row_size) as u64))
723 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://methoddef").unwrap()))?;
724 let _rva = c3
725 .read_u32::<LittleEndian>()
726 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://methoddef").unwrap()))?;
727 let _impl_flags = c3
728 .read_u16::<LittleEndian>()
729 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://methoddef").unwrap()))?;
730 let _flags = c3
731 .read_u16::<LittleEndian>()
732 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://methoddef").unwrap()))?;
733 let name_idx = read_heap_index(&mut c3, str_idx_sz)?;
734 let _sig_idx = read_heap_index(&mut c3, blob_idx_sz)?;
735 let _param_list_idx = read_heap_index(&mut c3, (if row_counts[0x07] < (1 << 16) { 2 } else { 4 }))?;
736
737 let method_name = self.read_string_from_strings_heap(strings_start, strings_stream.size, name_idx)?;
738
739 if !method_name.is_empty() {
740 let mdef = ClrMethod::new(
741 method_name,
742 ClrTypeReference {
743 name: "Void".to_string(),
744 namespace: Some("System".to_string()),
745 assembly: Some("mscorlib".to_string()),
746 is_value_type: true,
747 is_reference_type: false,
748 generic_parameters: Vec::new(),
749 },
750 );
751 }
755 }
756 }
757
758 if let Some(field_start) = table_start[0x04] {
760 for i in 0..row_counts[0x04] {
761 let mut c4 = self.reader.get_viewer();
762 c4.seek(SeekFrom::Start((field_start + i * field_row_size) as u64))
763 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://field").unwrap()))?;
764 let _flags =
765 c4.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://field").unwrap()))?;
766 let name_idx = read_heap_index(&mut c4, str_idx_sz)?;
767 let _sig_idx = read_heap_index(&mut c4, blob_idx_sz)?;
768
769 let name = self.read_string_from_strings_heap(strings_start, strings_stream.size, name_idx)?;
770
771 if !name.is_empty() {
772 let mdef = ClrMethod::new(
773 "DefaultMethod".to_string(),
774 ClrTypeReference {
775 name: "Void".to_string(),
776 namespace: Some("System".to_string()),
777 assembly: Some("mscorlib".to_string()),
778 is_value_type: true,
779 is_reference_type: false,
780 generic_parameters: Vec::new(),
781 },
782 );
783 let mut clr_type = ClrType::new(name, None);
784 clr_type.access_flags.is_public = true;
785 clr_type.add_method(mdef);
786 }
787 }
788 }
789
790 let mut external_assemblies: Vec<crate::program::ClrExternalAssembly> = Vec::new();
792 if let Some(asmref_start) = table_start[0x20] {
794 let assemblyref_rows = row_counts[0x20];
796
797 let row_size = table_row_size[0x20];
799 for i in 0..assemblyref_rows {
800 let row_off = asmref_start + i * row_size;
801 let mut c4 = self.reader.get_viewer();
802 c4.seek(SeekFrom::Start(row_off as u64))
803 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://asmref").unwrap()))?;
804 let ver_major = c4
805 .read_u16::<LittleEndian>()
806 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://asmref").unwrap()))?;
807 let ver_minor = c4
808 .read_u16::<LittleEndian>()
809 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://asmref").unwrap()))?;
810 let ver_build = c4
811 .read_u16::<LittleEndian>()
812 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://asmref").unwrap()))?;
813 let ver_rev = c4
814 .read_u16::<LittleEndian>()
815 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://asmref").unwrap()))?;
816 let _flags = c4
817 .read_u32::<LittleEndian>()
818 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://asmref").unwrap()))?;
819 let _pkt_idx = read_heap_index(&mut c4, blob_idx_sz)?;
820 let name_idx = read_heap_index(&mut c4, str_idx_sz)?;
821 let culture_idx = read_heap_index(&mut c4, str_idx_sz)?;
822 let _hash_idx = read_heap_index(&mut c4, blob_idx_sz)?;
823 let name = self.read_string_from_strings_heap(strings_start, strings_stream.size, name_idx)?;
824 if !name.is_empty() {
825 external_assemblies.push(crate::program::ClrExternalAssembly {
826 name,
827 version: ClrVersion { major: ver_major, minor: ver_minor, build: ver_build, revision: ver_rev },
828 public_key_token: None,
829 culture: None,
830 hash_algorithm: None,
831 });
832 }
833 }
834 }
835
836 if external_assemblies.is_empty() {
838 let cfg = &self.options.assembly_ref_fallback_names;
839 let heap = self.read_strings_heap_data(strings_start, strings_stream.size)?;
840 for name in cfg.iter() {
841 if find_subslice(&heap, name.as_bytes()) {
842 external_assemblies.push(crate::program::ClrExternalAssembly {
843 name: name.to_string(),
844 version: ClrVersion { major: 0, minor: 0, build: 0, revision: 0 },
845 public_key_token: None,
846 culture: None,
847 hash_algorithm: None,
848 });
849 }
850 }
851 }
852
853 for ea in external_assemblies {
854 program.add_external_assembly(ea);
855 }
856
857 let _ = _version_string.as_str();
859
860 Ok(program)
861 }
862
863 fn find_and_read_clr_header(&mut self) -> Result<Option<ClrHeader>, GaiaError> {
876 let pe_program = self.reader.get_program()?.clone();
878
879 if let Some(clr_dir) = pe_program.header.optional_header.data_directories.get(14) {
881 if clr_dir.virtual_address == 0 || clr_dir.size == 0 {
882 return Ok(None);
883 }
884
885 let file_offset = self.rva_to_file_offset(clr_dir.virtual_address)?;
887
888 let mut cursor = self.reader.get_viewer();
890 cursor
891 .seek(SeekFrom::Start(file_offset as u64))
892 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://clr_header").unwrap()))?;
893
894 let cb = cursor
895 .read_u32::<LittleEndian>()
896 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://clr_header").unwrap()))?;
897 let major_runtime_version = cursor
898 .read_u16::<LittleEndian>()
899 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://clr_header").unwrap()))?;
900 let minor_runtime_version = cursor
901 .read_u16::<LittleEndian>()
902 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://clr_header").unwrap()))?;
903 let metadata_rva = cursor
904 .read_u32::<LittleEndian>()
905 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://clr_header").unwrap()))?;
906 let metadata_size = cursor
907 .read_u32::<LittleEndian>()
908 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://clr_header").unwrap()))?;
909 let flags = cursor
910 .read_u32::<LittleEndian>()
911 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://clr_header").unwrap()))?;
912
913 Ok(Some(ClrHeader { cb, major_runtime_version, minor_runtime_version, metadata_rva, metadata_size, flags }))
914 }
915 else {
916 Ok(None)
917 }
918 }
919
920 fn read_metadata_header(&mut self, offset: u32) -> Result<MetadataHeader, GaiaError> {
937 let mut cursor = self.reader.get_viewer();
938 cursor
939 .seek(SeekFrom::Start(offset as u64))
940 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://metadata_header").unwrap()))?;
941
942 let signature = cursor
944 .read_u32::<LittleEndian>()
945 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://metadata_header").unwrap()))?;
946 let major_version = cursor
947 .read_u16::<LittleEndian>()
948 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://metadata_header").unwrap()))?;
949 let minor_version = cursor
950 .read_u16::<LittleEndian>()
951 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://metadata_header").unwrap()))?;
952 let reserved = cursor
953 .read_u32::<LittleEndian>()
954 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://metadata_header").unwrap()))?;
955 let version_length = cursor
956 .read_u32::<LittleEndian>()
957 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://metadata_header").unwrap()))?;
958
959 let mut version_bytes = vec![0u8; version_length as usize];
961 cursor
962 .read_exact(&mut version_bytes)
963 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://metadata_header").unwrap()))?;
964 let version_string = String::from_utf8_lossy(&version_bytes).trim_end_matches('\0').to_string();
965
966 let flags = cursor
968 .read_u16::<LittleEndian>()
969 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://metadata_header").unwrap()))?;
970 let streams = cursor
971 .read_u16::<LittleEndian>()
972 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://metadata_header").unwrap()))?;
973
974 Ok(MetadataHeader { signature, major_version, minor_version, reserved, version_length, version_string, flags, streams })
976 }
977
978 fn read_stream_headers(&mut self, metadata_offset: u32) -> Result<Vec<StreamHeader>, GaiaError> {
1003 let mut stream_headers = Vec::new();
1004
1005 if let Some(ref metadata_header) = self.metadata_header {
1006 let mut cursor = self.reader.get_viewer();
1007 let stream_start_offset = metadata_offset + 20 + metadata_header.version_length;
1009 cursor
1010 .seek(SeekFrom::Start(stream_start_offset as u64))
1011 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
1012
1013 for _ in 0..metadata_header.streams {
1015 let offset = cursor
1016 .read_u32::<LittleEndian>()
1017 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
1018 let size = cursor
1019 .read_u32::<LittleEndian>()
1020 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
1021
1022 let mut name_bytes = Vec::new();
1024 loop {
1025 let byte =
1026 cursor.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
1027 if byte == 0 {
1028 break;
1029 }
1030 name_bytes.push(byte);
1031 }
1032 let name = String::from_utf8_lossy(&name_bytes).to_string();
1033
1034 let current_pos = cursor
1036 .stream_position()
1037 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
1038 let aligned_pos = (current_pos + 3) & !3;
1039 cursor
1040 .seek(SeekFrom::Start(aligned_pos))
1041 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
1042
1043 stream_headers.push(StreamHeader { offset, size, name });
1044 }
1045 }
1046
1047 Ok(stream_headers)
1048 }
1049
1050 fn read_strings_heap_data(&mut self, strings_start: u32, strings_size: u32) -> Result<Vec<u8>, GaiaError> {
1052 let mut reader = self.reader.get_viewer();
1053 reader
1054 .seek(SeekFrom::Start(strings_start as u64))
1055 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://strings_heap").unwrap()))?;
1056
1057 let mut buffer = vec![0u8; strings_size as usize];
1058 reader.read_exact(&mut buffer).map_err(|e| GaiaError::io_error(e, Url::parse("memory://strings_heap").unwrap()))?;
1059
1060 Ok(buffer)
1061 }
1062
1063 fn read_string_from_strings_heap(
1065 &mut self,
1066 strings_start: u32,
1067 strings_size: u32,
1068 index: u32,
1069 ) -> Result<String, GaiaError> {
1070 if index == 0 {
1071 return Ok(String::new());
1072 }
1073
1074 let base = strings_start + index;
1075 let end = strings_start + strings_size;
1076
1077 if base >= end {
1078 return Err(GaiaError::syntax_error(format!("字符串索引 {} 超出堆范围", index), SourceLocation::default()));
1079 }
1080
1081 let viewer = self.reader.get_viewer();
1083 viewer
1084 .seek(SeekFrom::Start(base as u64))
1085 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://strings_heap").unwrap()))?;
1086
1087 let mut bytes = Vec::new();
1089 loop {
1090 let byte = viewer.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://strings_heap").unwrap()))?;
1091 if byte == 0 {
1092 break;
1093 }
1094 bytes.push(byte);
1095 }
1096
1097 Ok(String::from_utf8_lossy(&bytes).to_string())
1098 }
1099
1100 fn rva_to_file_offset(&mut self, rva: u32) -> Result<u32, GaiaError> {
1123 let pe_program = self.reader.get_program()?.clone();
1125
1126 for section in &pe_program.sections {
1128 let section_start = section.virtual_address;
1129 let section_end = section_start + section.virtual_size;
1130
1131 if rva >= section_start && rva < section_end {
1133 let offset_in_section = rva - section_start;
1135 return Ok(section.pointer_to_raw_data + offset_in_section);
1137 }
1138 }
1139
1140 Err(GaiaError::syntax_error(format!("无法将 RVA 0x{:x} 转换为文件偏移", rva), SourceLocation::default()))
1142 }
1143}
1144
1145fn read_type_def_or_ref_index<R: Read>(cursor: &mut R, idx_size: u32) -> Result<u32, GaiaError> {
1147 if idx_size == 2 {
1148 cursor
1149 .read_u16::<LittleEndian>()
1150 .map(|v| v as u32)
1151 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://type_def_or_ref_index").unwrap()))
1152 }
1153 else {
1154 cursor
1155 .read_u32::<LittleEndian>()
1156 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://type_def_or_ref_index").unwrap()))
1157 }
1158}
1159
1160fn read_heap_index<R: Read>(cursor: &mut R, idx_size: u32) -> Result<u32, GaiaError> {
1161 if idx_size == 2 {
1162 cursor
1163 .read_u16::<LittleEndian>()
1164 .map(|v| v as u32)
1165 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://heap_index").unwrap()))
1166 }
1167 else if idx_size == 4 {
1168 cursor.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://heap_index").unwrap()))
1169 }
1170 else {
1171 Err(GaiaError::syntax_error("非法堆索引大小".to_string(), SourceLocation::default()))
1172 }
1173}
1174
1175fn read_string_from_heap(pe_data: &[u8], strings_start: u32, strings_size: u32, index: u32) -> Result<String, GaiaError> {
1177 if index == 0 {
1178 return Ok(String::new());
1179 }
1180 let base = strings_start + index;
1181 let end = strings_start + strings_size;
1182 if base >= end || (base as usize) >= pe_data.len() {
1183 return Ok(String::new());
1184 }
1185 let mut i = base as usize;
1186 let mut bytes = Vec::new();
1187 while i < pe_data.len() && (i as u32) < end {
1188 let b = pe_data[i];
1189 if b == 0 {
1190 break;
1191 }
1192 bytes.push(b);
1193 i += 1;
1194 }
1195 Ok(String::from_utf8_lossy(&bytes).to_string())
1196}
1197
1198fn find_subslice(haystack: &[u8], needle: &[u8]) -> bool {
1200 if needle.is_empty() {
1201 return true;
1202 }
1203 if haystack.len() < needle.len() {
1204 return false;
1205 }
1206 let n = needle.len();
1207 for i in 0..=haystack.len() - n {
1208 if &haystack[i..i + n] == needle {
1209 return true;
1210 }
1211 }
1212 false
1213}