1#![allow(clippy::trivially_copy_pass_by_ref)]
5#![allow(clippy::new_ret_no_self)]
8
9use serde::{Deserialize, Serialize};
10
11pub mod error;
12pub mod lang;
13pub mod resource;
14pub mod signatures;
21pub mod stream;
22pub mod utils;
23
24use crate::{
25 error::Error,
26 stream::meta_data_tables::mdtables::{enums::*, *},
27};
28
29pub type Result<T> = std::result::Result<T, Error>;
30
31#[derive(Debug, Serialize)]
47pub struct DnPe<'a> {
48 #[serde(skip_serializing)]
49 data: &'a [u8],
50 #[serde(skip_serializing)]
54 sections: Vec<goblin::pe::section_table::SectionTable>,
55 #[serde(skip_serializing)]
57 file_alignment: u32,
58 #[serde(skip_serializing)]
61 pub(crate) resources_rva: u32,
62 #[serde(skip_serializing)]
64 pub(crate) resources_size: u32,
65 net: Option<ClrData<'a>>,
66}
67
68impl<'a> DnPe<'a> {
69 pub fn net(&self) -> Result<&ClrData<'a>> {
70 self.net.as_ref().ok_or(Error::NotImplementedError)
71 }
72
73 pub fn pe(&self) -> Result<goblin::pe::PE<'_>> {
81 Ok(goblin::pe::PE::parse(self.data)?)
82 }
83
84 pub fn parse(data: &'a [u8]) -> Result<Self> {
89 let (sections, file_alignment, clr_directory) = {
93 let pe = goblin::pe::PE::parse(data)?;
94 let opt_header = pe
95 .header
96 .optional_header
97 .ok_or(Error::UnsupportedBinaryFormat("optional header absence"))?;
98 let file_alignment = opt_header.windows_fields.file_alignment;
99 let clr_directory = opt_header
100 .data_directories
101 .get_clr_runtime_header()
102 .copied()
103 .ok_or(Error::UnsupportedBinaryFormat("ClR runtime header absence"))?;
104 (pe.sections.clone(), file_alignment, clr_directory)
105 };
106
107 let mut res = DnPe {
108 data,
109 sections,
110 file_alignment,
111 resources_rva: 0,
112 resources_size: 0,
113 net: None,
114 };
115 let clr_struct: ClrStruct = res.get_data(
116 &clr_directory.virtual_address,
117 &(clr_directory.size as usize),
118 )?;
119 res.resources_rva = clr_struct.resources_rva;
120 res.resources_size = clr_struct.resources_size;
121 res.net = Some(res.new_clrdata(clr_struct)?);
122 Ok(res)
123 }
124
125 fn offset(&self, rva: u32) -> Result<usize> {
126 goblin::pe::utils::find_offset(
127 rva as usize,
128 &self.sections,
129 self.file_alignment,
130 &goblin::pe::options::ParseOptions::default(),
131 )
132 .ok_or(Error::UnresolvedRvaError(rva))
133 }
134
135 fn get_data<'b, T>(&'b self, rva: &'b u32, size: &'b usize) -> Result<T>
136 where
137 T: scroll::ctx::TryFromCtx<'b, goblin::container::Endian, Error = scroll::Error>,
138 {
139 Ok(goblin::pe::utils::get_data(
140 self.data,
141 &self.sections,
142 goblin::pe::data_directories::DataDirectory {
143 virtual_address: *rva,
144 size: *size as u32,
145 },
146 self.file_alignment,
147 )?)
148 }
149
150 fn get_nullterminated_string(&self, rva: &u32) -> Result<String> {
155 const MAX_LEN: usize = 1024;
156 let mut res_buf = Vec::with_capacity(32);
157 let mut rrva = *rva;
158 loop {
159 let off = self.offset(rrva)?;
160 let c = *self.data.get(off).ok_or(Error::UnresolvedRvaError(rrva))?;
161 if c == 0 {
162 break;
163 }
164 if res_buf.len() >= MAX_LEN {
165 return Err(Error::UnresolvedRvaError(rrva));
166 }
167 res_buf.push(c);
168 rrva = rrva.checked_add(1).ok_or(Error::UnresolvedRvaError(rrva))?;
169 }
170 Ok(String::from_utf8(res_buf)?)
171 }
172
173 fn get_slice(&self, rva: &u32, size: usize) -> Result<&'a [u8]> {
176 let offset = self.offset(*rva)?;
177 let end = offset
178 .checked_add(size)
179 .ok_or(Error::UnresolvedRvaError(*rva))?;
180 self.data
181 .get(offset..end)
182 .ok_or(Error::UnresolvedRvaError(*rva))
183 }
184
185 fn get_vec(&self, rva: &u32, size: &usize) -> Result<Vec<u8>> {
187 Ok(self.get_slice(rva, *size)?.to_vec())
188 }
189
190 fn get_dword_at_rva(&self, rva: &u32) -> Result<u32> {
191 self.get_data(rva, &4)
192 }
193
194 #[must_use]
197 pub fn resources_rva(&self) -> u32 {
198 self.resources_rva
199 }
200
201 #[must_use]
203 pub fn resources_size(&self) -> u32 {
204 self.resources_size
205 }
206
207 fn new_clrdata(&self, clr_struct: ClrStruct) -> Result<ClrData<'a>> {
208 let metadata_struct: MetaDataStruct = self.get_data(
209 &clr_struct.meta_data_rva,
210 &(clr_struct.meta_data_size as usize),
211 )?;
212 let metadata = self.new_metadata(&clr_struct.meta_data_rva, metadata_struct)?;
213 let flags = ClrHeaderFlags::new(clr_struct.flags as usize);
214 let functions = self.parse_functions(&metadata)?;
215 Ok(ClrData {
216 metadata,
217 flags,
218 functions,
219 })
220 }
221
222 fn parse_functions(
223 &self,
224 metadata: &MetaData<'a>,
225 ) -> Result<Vec<lang::cil::function::Function>> {
226 let mut res = vec![];
227 let method_def_table = metadata.md_table("MethodDef")?;
228 for i in 0..method_def_table.row_count() {
229 let row = method_def_table.row::<MethodDef>(i)?;
230 if !row
231 .impl_flags
232 .contains(&ClrMethodImpl::MethodCodeType(CorMethodCodeType::IL))
233 || row
234 .flags
235 .contains(&ClrMethodAttr::AttrFlag(CorMethodAttrFlag::Abstract))
236 || row
237 .flags
238 .contains(&ClrMethodAttr::AttrFlag(CorMethodAttrFlag::PinvokeImpl))
239 {
240 continue;
241 }
242 res.push(self.parse_function(row)?);
243 }
244 Ok(res)
245 }
246
247 fn parse_function(&self, row: &MethodDef) -> Result<lang::cil::function::Function> {
248 let mut reader = lang::cil::function::reader::Reader::new(self.data);
249 reader.seek(self.offset(row.rva)?)?;
250 lang::cil::function::Function::new(&mut reader)
251 }
252
253 fn new_metadata(
254 &self,
255 metadata_rva: &u32,
256 metadata_struct: MetaDataStruct,
257 ) -> Result<MetaData<'a>> {
258 let after_signature_rva = metadata_rva
260 .checked_add(16)
261 .ok_or(Error::UnresolvedRvaError(*metadata_rva))?;
262 let version_offset = self.offset(after_signature_rva)?;
263 let version_end = version_offset
264 .checked_add(metadata_struct.version_length as usize)
265 .ok_or(Error::UnresolvedRvaError(after_signature_rva))?;
266 let version = self
267 .data
268 .get(version_offset..version_end)
269 .ok_or(Error::UnresolvedRvaError(after_signature_rva))?
270 .to_vec();
271 let after_version_rva = after_signature_rva
272 .checked_add(metadata_struct.version_length)
273 .ok_or(Error::UnresolvedRvaError(after_signature_rva))?;
274 let flags: u16 = self.get_data(&after_version_rva, &2)?;
275 let after_flags_rva = after_version_rva
276 .checked_add(2)
277 .ok_or(Error::UnresolvedRvaError(after_version_rva))?;
278 let number_of_streams: u16 = self.get_data(&after_flags_rva, &2)?;
279 let streams_table_rva = after_flags_rva
280 .checked_add(2)
281 .ok_or(Error::UnresolvedRvaError(after_flags_rva))?;
282 let streams = if number_of_streams > 0 {
283 self.new_streams(
284 metadata_rva,
285 &streams_table_rva,
286 &(number_of_streams as usize),
287 )?
288 } else {
289 std::collections::HashMap::new()
290 };
291 Ok(MetaData {
292 _version: String::from_utf8(version)?,
293 flags,
294 streams,
295 })
296 }
297
298 fn new_streams(
299 &self,
300 metadata_rva: &u32,
301 streams_table_rva: &u32,
302 number_of_streams: &usize,
303 ) -> Result<std::collections::HashMap<String, stream::ClrStream<'a>>> {
304 const MAX_STREAMS: usize = 64;
308 let n = (*number_of_streams).min(MAX_STREAMS);
309 let mut res = std::collections::HashMap::with_capacity(n);
310 let mut stream_entry_rva = *streams_table_rva;
311 for _i in 0..n {
312 let stream = self.new_clr_stream(&stream_entry_rva, metadata_rva)?;
313 stream_entry_rva = stream_entry_rva
314 .checked_add(stream.stream_table_entry_size as u32)
315 .ok_or(Error::UnresolvedRvaError(stream_entry_rva))?;
316 res.insert(stream.name().to_string(), stream);
317 }
318 let mut rres = std::collections::HashMap::with_capacity(res.len());
319 for (n, s) in &res {
320 rres.insert(n.to_string(), self.parse_clr_stream(s, &res)?);
321 }
322 Ok(rres)
323 }
324
325 fn new_clr_stream(
326 &self,
327 stream_table_entry_rva: &u32,
328 metadata_rva: &u32,
329 ) -> Result<stream::ClrStream<'a>> {
330 let stream_offset: u32 = self.get_data(stream_table_entry_rva, &4)?;
331 let size_rva = stream_table_entry_rva
332 .checked_add(4)
333 .ok_or(Error::UnresolvedRvaError(*stream_table_entry_rva))?;
334 let stream_size: u32 = self.get_data(&size_rva, &4)?;
335 let name_rva = stream_table_entry_rva
336 .checked_add(8)
337 .ok_or(Error::UnresolvedRvaError(*stream_table_entry_rva))?;
338 let stream_name = self.get_nullterminated_string(&name_rva)?;
339 let data_rva = metadata_rva
340 .checked_add(stream_offset)
341 .ok_or(Error::UnresolvedRvaError(*metadata_rva))?;
342 let stream_data = self.get_slice(&data_rva, stream_size as usize)?;
343 self.nnew_clr_stream(
344 metadata_rva,
345 &stream_offset,
346 &(stream_size as usize),
347 &stream_name,
348 stream_data,
349 )
350 }
351}
352
353#[repr(C)]
354#[derive(Deserialize, Debug, scroll::Pread)]
355pub struct ClrStruct {
356 cb: u32,
357 majorr_runtime_version: u16,
358 minor_runtime_version: u16,
359 meta_data_rva: u32,
360 meta_data_size: u32,
361 flags: u32,
362 entry_point_token_or_rva: u32,
363 resources_rva: u32,
364 resources_size: u32,
365 strong_name_signature_rva: u32,
366 strong_name_signature_size: u32,
367 code_manager_table_rva: u32,
368 code_manager_table_size: u32,
369 v_table_fixups_rva: u32,
370 v_table_fixups_size: u32,
371 export_address_table_jumps_rva: u32,
372 export_address_table_jumps_size: u32,
373 managed_native_header_rva: u32,
374 managed_native_header_size: u32,
375}
376
377#[derive(Debug, Serialize, PartialOrd, Ord, PartialEq, Eq)]
378pub enum ClrHeaderFlags {
379 IlOnly,
380 BitRequired32,
381 IlLibrary,
382 StrongNamesSigned,
383 NativeEntryPiont,
384 TrackDebugData,
385 Prefer32Bit,
386}
387
388impl ClrHeaderFlags {
389 pub fn new(value: usize) -> std::collections::BTreeSet<Self> {
390 let mut res = std::collections::BTreeSet::new();
391 if value & 1 != 0 {
392 res.insert(Self::IlOnly);
393 }
394 if value & 2 != 0 {
395 res.insert(Self::BitRequired32);
396 }
397 if value & 4 != 0 {
398 res.insert(Self::IlLibrary);
399 }
400 if value & 8 != 0 {
401 res.insert(Self::StrongNamesSigned);
402 }
403 if value & 0x10 != 0 {
404 res.insert(Self::NativeEntryPiont);
405 }
406 if value & 0x10000 != 0 {
407 res.insert(Self::TrackDebugData);
408 }
409 if value & 0x20000 != 0 {
410 res.insert(Self::Prefer32Bit);
411 }
412 res
413 }
414}
415
416#[derive(Debug, Serialize)]
417pub struct ClrData<'a> {
418 pub metadata: MetaData<'a>,
419 pub flags: std::collections::BTreeSet<ClrHeaderFlags>,
420 pub functions: Vec<lang::cil::function::Function>,
421}
422
423impl<'a> ClrData<'a> {
424 pub fn md_table(
425 &self,
426 name: &'static str,
427 ) -> Result<&stream::meta_data_tables::mdtables::MetaDataTable> {
428 self.metadata.md_table(name)
429 }
430
431 pub fn md_table_by_index(
432 &self,
433 index: &usize,
434 ) -> Result<&stream::meta_data_tables::mdtables::MetaDataTable> {
435 self.metadata.md_table_by_index(index)
436 }
437
438 pub fn resolve_coded_index<T>(
439 &self,
440 index: &dyn stream::meta_data_tables::mdtables::codedindex::CodedIndex,
441 ) -> Result<&T>
442 where
443 T: stream::meta_data_tables::mdtables::MDTableRowTrait + 'static,
444 {
445 let table = self.md_table(index.table())?;
446 let idx = index
448 .row_index()
449 .checked_sub(1)
450 .ok_or(Error::UndefinedMetaDataTableName(index.table()))?;
451 table.row(idx)
452 }
453
454 pub fn functions(&self) -> &Vec<lang::cil::function::Function> {
455 &self.functions
456 }
457
458 pub fn get_us(&self, rid: usize) -> Result<String> {
459 self.metadata.get_us(rid)
460 }
461
462 pub fn assembly(&self) -> Result<&stream::meta_data_tables::mdtables::Assembly> {
469 let table = self.md_table("Assembly")?;
470 if table.row_count() == 0 {
471 return Err(Error::UndefinedMetaDataTableName("Assembly"));
472 }
473 table.row::<stream::meta_data_tables::mdtables::Assembly>(0)
474 }
475}
476
477#[repr(C)]
478#[derive(Deserialize, Debug, Clone, scroll::Pread)]
479pub struct MetaDataStruct {
480 signature: u32,
481 major_version: u16,
482 minor_version: u16,
483 reserved: u32,
484 version_length: u32,
485}
486
487#[derive(Debug, Serialize)]
488pub struct MetaData<'a> {
489 #[serde(skip_serializing)]
490 _version: String,
491 flags: u16,
492 pub streams: std::collections::HashMap<String, stream::ClrStream<'a>>,
493}
494
495impl<'a> MetaData<'a> {
496 pub fn md_table(
497 &self,
498 name: &'static str,
499 ) -> Result<&stream::meta_data_tables::mdtables::MetaDataTable> {
500 for s in self.streams.values() {
501 if let stream::Stream::MetaDataTables(mt) = &s.stream {
502 match mt
503 .tables
504 .get(&stream::meta_data_tables::mdtables::table_name_2_index(
505 name,
506 )?) {
507 Some(s) => return Ok(s),
508 None => return Err(Error::UndefinedMetaDataTableName(name)),
509 }
510 }
511 }
512 Err(Error::UndefinedMetaDataTableName(name))
513 }
514
515 pub fn md_table_by_index(
516 &self,
517 index: &usize,
518 ) -> Result<&stream::meta_data_tables::mdtables::MetaDataTable> {
519 for s in self.streams.values() {
520 if let stream::Stream::MetaDataTables(mt) = &s.stream {
521 match mt.tables.get(index) {
522 Some(s) => return Ok(s),
523 None => return Err(Error::UndefinedMetaDataTableIndex(*index as u32)),
524 }
525 }
526 }
527 Err(Error::UndefinedMetaDataTableIndex(*index as u32))
528 }
529
530 pub fn get_us(&self, rid: usize) -> Result<String> {
531 for s in self.streams.values() {
532 if let stream::Stream::UserStringHeap(us) = &s.stream {
533 return us.get_us(rid);
534 }
535 }
536 Err(Error::UndefinedMetaDataTableName("US"))
537 }
538}
539
540#[cfg(test)]
541mod tests {
542 use super::*;
543
544 #[test]
545 fn clr_header_flags_decodes_bits() {
546 let flags = ClrHeaderFlags::new(0b0000_0001);
547 assert!(flags.contains(&ClrHeaderFlags::IlOnly));
548 assert!(!flags.contains(&ClrHeaderFlags::BitRequired32));
549 }
550
551 #[test]
552 fn clr_header_flags_decodes_all_low_bits() {
553 let flags = ClrHeaderFlags::new(0x1F); for v in [
555 ClrHeaderFlags::IlOnly,
556 ClrHeaderFlags::BitRequired32,
557 ClrHeaderFlags::IlLibrary,
558 ClrHeaderFlags::StrongNamesSigned,
559 ClrHeaderFlags::NativeEntryPiont,
560 ] {
561 assert!(flags.contains(&v), "missing flag: {v:?}");
562 }
563 }
564
565 #[test]
566 fn clr_header_flags_decodes_high_bits() {
567 let flags = ClrHeaderFlags::new(0x30000); assert!(flags.contains(&ClrHeaderFlags::TrackDebugData));
569 assert!(flags.contains(&ClrHeaderFlags::Prefer32Bit));
570 }
571
572 #[test]
573 fn clr_header_flags_zero_value_is_empty() {
574 let flags = ClrHeaderFlags::new(0);
575 assert!(flags.is_empty());
576 }
577}