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::{
15 io::{ Read, Seek, SeekFrom},
16};
17use url::Url;
18
19#[derive(Debug)]
20pub struct DllReader<'config, R> {
21 options: &'config DllReadConfig,
23 reader: pe_assembler::formats::dll::reader::DllReader<R>,
24 clr_header: Option<ClrHeader>,
26 metadata_header: Option<MetadataHeader>,
28 stream_headers: Option<Vec<StreamHeader>>,
30 assembly_info: Option<DotNetAssemblyInfo>,
32 clr_program: Option<ClrProgram>,
34}
35
36impl<'config, R: Read + Seek> PeReader<R> for DllReader<'config, R> {
37 fn get_viewer(&mut self) -> &mut R {
38 self.reader.get_viewer()
39 }
40
41 fn add_diagnostics(&mut self, error: impl Into<GaiaError>) {
42 self.reader.add_diagnostics(error)
43 }
44
45 fn get_section_headers(&mut self) -> Result<&[SectionHeader], GaiaError> {
46 self.reader.get_section_headers()
47 }
48
49 fn get_pe_header(&mut self) -> Result<&PeHeader, GaiaError> {
50 self.reader.get_pe_header()
51 }
52
53 fn get_program(&mut self) -> Result<&PeProgram, GaiaError> {
54 self.reader.get_program()
55 }
56}
57
58impl<'config, R> DllReader<'config, R> {
59 pub fn new(reader: R, options: &'config DllReadConfig) -> Self {
63 Self {
64 reader: pe_assembler::formats::dll::reader::DllReader::new(reader),
65 clr_header: None,
66 metadata_header: None,
67 stream_headers: None,
68 assembly_info: None,
69 clr_program: None,
70 options,
71 }
72 }
73}
74
75impl<'config, R> DllReader<'config, R>
76where
77 R: Read + Seek,
78{
79 pub fn get_assembly_info(&mut self) -> Result<DotNetAssemblyInfo, GaiaError> {
119 if self.assembly_info.is_none() {
120 self.ensure_assembly_info_parsed()?;
121 }
122
123 self.assembly_info
124 .as_ref()
125 .cloned()
126 .ok_or_else(|| GaiaError::syntax_error("程序集信息未解析".to_string(), SourceLocation::default()))
127 }
128
129 pub fn to_clr_program(&mut self) -> Result<ClrProgram, GaiaError> {
138 if let Some(ref program) = self.clr_program {
139 return Ok(program.clone());
140 }
141
142 let program = self.parse_full_program()?;
144 self.clr_program = Some(program.clone());
145 Ok(program)
146 }
147
148 pub fn validate_assembly(&mut self) -> Result<Vec<String>, GaiaError> {
159 let mut warnings = Vec::new();
160
161 self.ensure_assembly_info_parsed()?;
163
164 if self.clr_header.is_none() {
166 warnings.push("缺少 CLR 头".to_string());
167 }
168
169 if self.metadata_header.is_none() {
171 warnings.push("缺少元数据头".to_string());
172 }
173
174 if self.stream_headers.as_ref().map_or(true, |h| h.is_empty()) {
176 warnings.push("缺少元数据流".to_string());
177 }
178
179 Ok(warnings)
180 }
181
182 pub fn get_assembly_summary(&mut self) -> String {
190 match self.get_assembly_info() {
191 Ok(info) => {
192 format!(
193 "程序集: {}\n版本: {}\n文化: {}\n公钥标记: {}\n运行时版本: {}",
194 info.name,
195 info.version,
196 info.culture.as_deref().unwrap_or("neutral"),
197 info.public_key_token.as_deref().unwrap_or("null"),
198 info.runtime_version.as_deref().unwrap_or("unknown")
199 )
200 }
201 Err(_) => "无法获取程序集信息".to_string(),
202 }
203 }
204
205 fn ensure_assembly_info_parsed(&mut self) -> Result<(), GaiaError> {
207 if self.assembly_info.is_some() {
208 return Ok(());
209 }
210
211 self.parse_clr_header()?;
213 self.parse_metadata()?;
214 self.extract_assembly_info()?;
215
216 Ok(())
217 }
218
219 fn parse_clr_header(&mut self) -> Result<(), GaiaError> {
228 self.clr_header = self.find_and_read_clr_header()?;
229 Ok(())
230 }
231
232 fn parse_metadata(&mut self) -> Result<(), GaiaError> {
243 if let Some(ref clr_header) = self.clr_header {
244 let metadata_offset = self.rva_to_file_offset(clr_header.metadata_rva)?;
246 self.metadata_header = Some(self.read_metadata_header(metadata_offset)?);
248 self.stream_headers = Some(self.read_stream_headers(metadata_offset)?);
250 }
251
252 Ok(())
253 }
254
255 fn extract_assembly_info(&mut self) -> Result<(), GaiaError> {
264 let clr_header = match &self.clr_header {
266 Some(h) => *h,
267 None => return Ok(()),
268 };
269 let metadata_offset = self.rva_to_file_offset(clr_header.metadata_rva)?;
270
271 let mut tables_stream: Option<StreamHeader> = None;
273 let mut strings_stream: Option<StreamHeader> = None;
274 if let Some(ref stream_headers) = self.stream_headers {
275 for sh in stream_headers {
276 match sh.name.as_str() {
277 "#~" => tables_stream = Some(sh.clone()),
278 "#Strings" => strings_stream = Some(sh.clone()),
279 _ => {}
280 }
281 }
282 }
283 if tables_stream.is_none() || strings_stream.is_none() {
284 return Ok(());
285 }
286 let tables_stream = tables_stream.unwrap();
287 let strings_stream = strings_stream.unwrap();
288
289 let tables_start = metadata_offset + tables_stream.offset;
290 let strings_start = metadata_offset + strings_stream.offset;
291
292 let mut cur = self.reader.get_viewer();
294 cur.seek(SeekFrom::Start(tables_start as u64))
295 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://table").unwrap()))?;
296
297 let _reserved =
298 cur.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
299 let _major = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
300 let _minor = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
301 let heap_sizes = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
302 let _reserved2 = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
303 let valid_mask =
304 cur.read_u64::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
305 let _sorted_mask =
306 cur.read_u64::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_hdr").unwrap()))?;
307
308 let str_idx_sz = if (heap_sizes & 0x01) != 0 { 4 } else { 2 };
310 let guid_idx_sz = if (heap_sizes & 0x02) != 0 { 4 } else { 2 };
311 let blob_idx_sz = if (heap_sizes & 0x04) != 0 { 4 } else { 2 };
312
313 let mut row_counts: [u32; 64] = [0; 64];
315 for tid in 0..64u8 {
316 if (valid_mask >> tid) & 1 == 1 {
317 row_counts[tid as usize] = cur
318 .read_u32::<LittleEndian>()
319 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_rows").unwrap()))?;
320 }
321 }
322
323 fn coded_size(rows: &[u32; 64], tags: &[u8]) -> u32 {
325 let max_rows = tags.iter().map(|&t| rows[t as usize]).max().unwrap_or(0);
326 let tag_bits = (tags.len() as f32).log2().ceil() as u32;
327 if (max_rows << tag_bits) < (1 << 16) {
328 2
329 }
330 else {
331 4
332 }
333 }
334 let type_def_or_ref_sz = coded_size(&row_counts, &[0x02, 0x01, 0x18]);
335 let resolution_scope_sz = coded_size(&row_counts, &[0x00, 0x01, 0x17, 0x23]);
336
337 let module_row_size = 2 + str_idx_sz + guid_idx_sz + guid_idx_sz + guid_idx_sz;
339 let type_def_row_size = 4
340 + str_idx_sz
341 + str_idx_sz
342 + type_def_or_ref_sz
343 + (if row_counts[0x04] < (1 << 16) { 2 } else { 4 })
344 + (if row_counts[0x06] < (1 << 16) { 2 } else { 4 });
345 let methoddef_row_size = 4 + 2 + 2 + str_idx_sz + blob_idx_sz + (if row_counts[0x07] < (1 << 16) { 2 } else { 4 });
346 let typeref_row_size = resolution_scope_sz + str_idx_sz + str_idx_sz;
347 let assembly_row_size = 4 + 2 + 2 + 2 + 2 + 4 + blob_idx_sz + str_idx_sz + str_idx_sz;
348
349 let tables_data_start = cur.stream_position().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables_data").unwrap()))? as u32;
351 let mut table_start: [Option<u32>; 64] = [None; 64];
353 let mut table_row_size: [u32; 64] = [0; 64];
354 let mut running = tables_data_start;
355 for tid in 0..64u8 {
356 if (valid_mask >> tid) & 1 == 1 {
357 let rows = row_counts[tid as usize];
358 let row_size = match tid {
359 0x00 => module_row_size,
360 0x01 => typeref_row_size,
361 0x02 => type_def_row_size,
362 0x06 => methoddef_row_size,
363 0x1D => assembly_row_size,
364 _ => 0,
365 } as u32;
366 table_start[tid as usize] = Some(running);
367 table_row_size[tid as usize] = row_size;
368 running += rows * row_size;
369 }
370 }
371
372 let mut name = String::from("Unknown");
374 let mut version = ClrVersion { major: 0, minor: 0, build: 0, revision: 0 };
375 let strings_size = strings_stream.size;
376 if let Some(asm_start) = table_start[0x1D] {
377 if row_counts[0x1D] > 0 {
379 let mut c = self.reader.get_viewer();
380 c.seek(SeekFrom::Start(asm_start as u64))
381 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
382 let _hash_alg =
383 c.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
384 version.major =
385 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
386 version.minor =
387 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
388 version.build =
389 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
390 version.revision =
391 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
392 let _flags =
393 c.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://asm").unwrap()))?;
394 let _pk_idx = read_heap_index(&mut c, blob_idx_sz)?;
395 let name_idx = read_heap_index(&mut c, str_idx_sz)?;
396 let _culture_idx = read_heap_index(&mut c, str_idx_sz)?;
397 let n = self.read_string_from_strings_heap(strings_start, strings_size, name_idx)?;
398 if !n.is_empty() {
399 name = n;
400 }
401 }
402 }
403 else if let Some(mod_start) = table_start[0x00] {
404 if row_counts[0x00] > 0 {
406 let mut c = self.reader.get_viewer();
407 c.seek(SeekFrom::Start(mod_start as u64))
408 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://mod").unwrap()))?;
409 let _generation =
410 c.read_u16::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://mod").unwrap()))?;
411 let name_idx = read_heap_index(&mut c, str_idx_sz)?;
412 let _mvid_idx = read_heap_index(&mut c, guid_idx_sz)?;
413 let _enc_id_idx = read_heap_index(&mut c, guid_idx_sz)?;
414 let _enc_base_id_idx = read_heap_index(&mut c, guid_idx_sz)?;
415 let n = self.read_string_from_strings_heap(strings_start, strings_size, name_idx)?;
416 if !n.is_empty() {
417 name = n;
418 }
419 }
420 }
421
422 let runtime_version = self.metadata_header.as_ref().map(|h| h.version_string.clone());
424
425 self.assembly_info = Some(DotNetAssemblyInfo {
427 name,
428 version: format!("{}.{}.{}.{}", version.major, version.minor, version.build, version.revision),
429 culture: None,
430 public_key_token: None,
431 runtime_version,
432 });
433
434 Ok(())
435 }
436
437 fn parse_full_program(&mut self) -> Result<ClrProgram, GaiaError> {
446 let metadata_rva = self
448 .clr_header
449 .as_ref()
450 .ok_or_else(|| GaiaError::syntax_error("缺少 CLR 头".to_string(), SourceLocation::default()))?
451 .metadata_rva;
452 let _version_string = self
453 .metadata_header
454 .as_ref()
455 .ok_or_else(|| GaiaError::syntax_error("缺少元数据头".to_string(), SourceLocation::default()))?
456 .version_string
457 .clone();
458
459 let metadata_base = self.rva_to_file_offset(metadata_rva)?;
461
462 let mut tables_stream: Option<StreamHeader> = None;
464 let mut strings_stream: Option<StreamHeader> = None;
465 if let Some(ref stream_headers) = self.stream_headers {
466 for sh in stream_headers {
467 match sh.name.as_str() {
468 "#~" | "#-" => tables_stream = Some(sh.clone()),
469 "#Strings" => strings_stream = Some(sh.clone()),
470 _ => {}
471 }
472 }
473 }
474
475 let tables_stream = tables_stream
476 .ok_or_else(|| GaiaError::syntax_error("缺少元数据表流(#~/#-)".to_string(), SourceLocation::default()))?;
477 let strings_stream = strings_stream
478 .ok_or_else(|| GaiaError::syntax_error("缺少字符串流(#Strings)".to_string(), SourceLocation::default()))?;
479
480 let mut cur = self.reader.get_viewer();
482 let tables_start = metadata_base + tables_stream.offset;
484 let strings_start = metadata_base + strings_stream.offset;
485
486 cur.seek(SeekFrom::Start(tables_start as u64))
488 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
489 let _reserved =
490 cur.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
491 let _major = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
492 let _minor = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
493 let heap_sizes = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
494 let _reserved2 = cur.read_u8().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
495 let valid_mask =
496 cur.read_u64::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
497 let _sorted_mask =
498 cur.read_u64::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
499
500 let mut row_counts: [u32; 64] = [0; 64];
502 for tid in 0..64u8 {
503 if (valid_mask >> tid) & 1 == 1 {
504 let cnt = cur
505 .read_u32::<LittleEndian>()
506 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://tables").unwrap()))?;
507 row_counts[tid as usize] = cnt;
508 }
509 }
510
511 let str_idx_sz = if (heap_sizes & 0x01) != 0 { 4 } else { 2 };
513 let guid_idx_sz = if (heap_sizes & 0x02) != 0 { 4 } else { 2 };
514 let blob_idx_sz = if (heap_sizes & 0x04) != 0 { 4 } else { 2 };
515 let _ = guid_idx_sz; let tables_data_start = 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.stream_position().map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
1036 let aligned_pos = (current_pos + 3) & !3;
1037 cursor
1038 .seek(SeekFrom::Start(aligned_pos))
1039 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://stream_headers").unwrap()))?;
1040
1041 stream_headers.push(StreamHeader { offset, size, name });
1042 }
1043 }
1044
1045 Ok(stream_headers)
1046 }
1047
1048 fn read_strings_heap_data(&mut self, strings_start: u32, strings_size: u32) -> Result<Vec<u8>, GaiaError> {
1050 let mut reader = self.reader.get_viewer();
1051 reader
1052 .seek(SeekFrom::Start(strings_start as u64))
1053 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://strings_heap").unwrap()))?;
1054
1055 let mut buffer = vec![0u8; strings_size as usize];
1056 reader
1057 .read_exact(&mut buffer)
1058 .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 } else {
1153 cursor
1154 .read_u32::<LittleEndian>()
1155 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://type_def_or_ref_index").unwrap()))
1156 }
1157}
1158
1159fn read_heap_index<R: Read>(cursor: &mut R, idx_size: u32) -> Result<u32, GaiaError> {
1160 if idx_size == 2 {
1161 cursor
1162 .read_u16::<LittleEndian>()
1163 .map(|v| v as u32)
1164 .map_err(|e| GaiaError::io_error(e, Url::parse("memory://heap_index").unwrap()))
1165 }
1166 else if idx_size == 4 {
1167 cursor.read_u32::<LittleEndian>().map_err(|e| GaiaError::io_error(e, Url::parse("memory://heap_index").unwrap()))
1168 }
1169 else {
1170 Err(GaiaError::syntax_error("非法堆索引大小".to_string(), SourceLocation::default()))
1171 }
1172}
1173
1174fn read_string_from_heap(pe_data: &[u8], strings_start: u32, strings_size: u32, index: u32) -> Result<String, GaiaError> {
1176 if index == 0 {
1177 return Ok(String::new());
1178 }
1179 let base = strings_start + index;
1180 let end = strings_start + strings_size;
1181 if base >= end || (base as usize) >= pe_data.len() {
1182 return Ok(String::new());
1183 }
1184 let mut i = base as usize;
1185 let mut bytes = Vec::new();
1186 while i < pe_data.len() && (i as u32) < end {
1187 let b = pe_data[i];
1188 if b == 0 {
1189 break;
1190 }
1191 bytes.push(b);
1192 i += 1;
1193 }
1194 Ok(String::from_utf8_lossy(&bytes).to_string())
1195}
1196
1197fn find_subslice(haystack: &[u8], needle: &[u8]) -> bool {
1199 if needle.is_empty() {
1200 return true;
1201 }
1202 if haystack.len() < needle.len() {
1203 return false;
1204 }
1205 let n = needle.len();
1206 for i in 0..=haystack.len() - n {
1207 if &haystack[i..i + n] == needle {
1208 return true;
1209 }
1210 }
1211 false
1212}