1use super::{
2 binary::{
3 cli::{Header, Metadata, RVASize},
4 heap::Reader,
5 metadata, method,
6 },
7 resolution::{read, Resolution},
8};
9use object::{
10 endian::{LittleEndian, U32Bytes},
11 pe::{self, ImageDataDirectory},
12 read::{
13 pe::{PeFile32, PeFile64, SectionTable},
14 Error as ObjectReadError, FileKind,
15 },
16};
17use scroll::{Error as ScrollError, Pread};
18use thiserror::Error;
19use DLLError::*;
20
21#[derive(Debug)]
23pub struct DLL<'a> {
24 buffer: &'a [u8],
25 pub cli: Header,
27 sections: SectionTable<'a>,
28}
29
30#[derive(Debug, Error)]
34pub enum DLLError {
35 #[error("PE parsing: {0}")]
38 PERead(#[from] ObjectReadError),
39 #[error("CLI metadata: {0}")]
42 CLI(#[from] ScrollError),
43 #[error("Other parsing: {0}")]
46 Other(&'static str),
47}
48
49pub type Result<T> = std::result::Result<T, DLLError>;
50
51impl<'a> DLL<'a> {
52 pub fn parse(bytes: &'a [u8]) -> Result<DLL<'a>> {
57 let (sections, dir) = match FileKind::parse(bytes)? {
58 FileKind::Pe32 => {
59 let file = PeFile32::parse(bytes)?;
60 (
61 file.section_table(),
62 file.data_directory(pe::IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR),
63 )
64 }
65 FileKind::Pe64 => {
66 let file = PeFile64::parse(bytes)?;
67 (
68 file.section_table(),
69 file.data_directory(pe::IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR),
70 )
71 }
72 _ => return Err(Other("invalid object type, must be PE32 or PE64")),
73 };
74
75 let cli_b = dir
76 .ok_or(Other("missing CLI metadata data directory in PE image"))?
77 .data(bytes, §ions)?;
78 Ok(DLL {
79 buffer: bytes,
80 cli: cli_b.pread_with(0, scroll::LE)?,
81 sections,
82 })
83 }
84
85 pub fn at_rva(&self, rva: &RVASize) -> Result<&'a [u8]> {
86 let dir = ImageDataDirectory {
87 virtual_address: U32Bytes::new(LittleEndian, rva.rva),
88 size: U32Bytes::new(LittleEndian, rva.size),
89 };
90 dir.data(self.buffer, &self.sections).map_err(PERead)
91 }
92
93 pub(crate) fn raw_rva(&self, rva: u32) -> Result<&'a [u8]> {
94 self.sections
95 .pe_data_at(self.buffer, rva)
96 .ok_or(Other("bad stream offset"))
97 }
98
99 fn get_stream(&self, name: &'static str) -> Result<Option<&'a [u8]>> {
100 let meta = self.get_cli_metadata()?;
101 let Some(header) = meta.stream_headers.iter().find(|h| h.name == name) else {
102 return Ok(None);
103 };
104 let data = self.raw_rva(self.cli.metadata.rva + header.offset)?;
105 Ok(Some(&data[..header.size as usize]))
106 }
107
108 pub fn get_heap<T: Reader<'a>>(&self) -> Result<T> {
109 Ok(T::new(self.get_stream(T::NAME)?.unwrap_or(&[])))
112 }
113
114 pub fn get_cli_metadata(&self) -> Result<Metadata<'a>> {
115 self.at_rva(&self.cli.metadata)?.pread(0).map_err(CLI)
116 }
117
118 pub fn get_logical_metadata(&self) -> Result<metadata::header::Header> {
119 self.get_stream("#~")?
120 .ok_or(Other("unable to find metadata stream"))?
121 .pread(0)
122 .map_err(CLI)
123 }
124
125 #[allow(clippy::nonminimal_bool)]
126 pub fn get_method(&self, def: &metadata::table::MethodDef) -> Result<method::Method> {
127 let bytes = self.raw_rva(def.rva)?;
128 let mut offset = 0;
129 if !check_bitmask!(bytes[0], 0x2) {
131 offset = 4 - (def.rva as usize % 4);
132 }
133 bytes.pread(offset).map_err(CLI)
134 }
135
136 pub fn resolve(&self, opts: read::Options) -> Result<Resolution<'a>> {
138 read::read_impl(self, opts)
139 }
140}