1use core::marker::PhantomData;
2use core::{cmp, iter, slice, str};
3
4use crate::endian::LittleEndian as LE;
5use crate::pe;
6use crate::pe::ImageSectionHeader;
7use crate::read::{
8 self, CompressedData, CompressedFileRange, ObjectSection, ObjectSegment, ReadError, ReadRef,
9 Relocation, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags,
10};
11
12use super::{ImageNtHeaders, PeFile, SectionTable};
13
14/// An iterator for the loadable sections in a [`PeFile32`](super::PeFile32).
15pub type PeSegmentIterator32<'data, 'file, R = &'data [u8]> =
16 PeSegmentIterator<'data, 'file, pe::ImageNtHeaders32, R>;
17/// An iterator for the loadable sections in a [`PeFile64`](super::PeFile64).
18pub type PeSegmentIterator64<'data, 'file, R = &'data [u8]> =
19 PeSegmentIterator<'data, 'file, pe::ImageNtHeaders64, R>;
20
21/// An iterator for the loadable sections in a [`PeFile`].
22#[derive(Debug)]
23pub struct PeSegmentIterator<'data, 'file, Pe, R = &'data [u8]>
24where
25 Pe: ImageNtHeaders,
26 R: ReadRef<'data>,
27{
28 pub(super) file: &'file PeFile<'data, Pe, R>,
29 pub(super) iter: slice::Iter<'data, pe::ImageSectionHeader>,
30}
31
32impl<'data, 'file, Pe, R> Iterator for PeSegmentIterator<'data, 'file, Pe, R>
33where
34 Pe: ImageNtHeaders,
35 R: ReadRef<'data>,
36{
37 type Item = PeSegment<'data, 'file, Pe, R>;
38
39 fn next(&mut self) -> Option<Self::Item> {
40 self.iter.next().map(|section: &ImageSectionHeader| PeSegment {
41 file: self.file,
42 section,
43 })
44 }
45}
46
47/// A loadable section in a [`PeFile32`](super::PeFile32).
48pub type PeSegment32<'data, 'file, R = &'data [u8]> =
49 PeSegment<'data, 'file, pe::ImageNtHeaders32, R>;
50/// A loadable section in a [`PeFile64`](super::PeFile64).
51pub type PeSegment64<'data, 'file, R = &'data [u8]> =
52 PeSegment<'data, 'file, pe::ImageNtHeaders64, R>;
53
54/// A loadable section in a [`PeFile`].
55///
56/// Most functionality is provided by the [`ObjectSegment`] trait implementation.
57#[derive(Debug)]
58pub struct PeSegment<'data, 'file, Pe, R = &'data [u8]>
59where
60 Pe: ImageNtHeaders,
61 R: ReadRef<'data>,
62{
63 file: &'file PeFile<'data, Pe, R>,
64 section: &'data pe::ImageSectionHeader,
65}
66
67impl<'data, 'file, Pe, R> read::private::Sealed for PeSegment<'data, 'file, Pe, R>
68where
69 Pe: ImageNtHeaders,
70 R: ReadRef<'data>,
71{
72}
73
74impl<'data, 'file, Pe, R> ObjectSegment<'data> for PeSegment<'data, 'file, Pe, R>
75where
76 Pe: ImageNtHeaders,
77 R: ReadRef<'data>,
78{
79 #[inline]
80 fn address(&self) -> u64 {
81 u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base)
82 }
83
84 #[inline]
85 fn size(&self) -> u64 {
86 u64::from(self.section.virtual_size.get(LE))
87 }
88
89 #[inline]
90 fn align(&self) -> u64 {
91 self.file.section_alignment()
92 }
93
94 #[inline]
95 fn file_range(&self) -> (u64, u64) {
96 let (offset, size) = self.section.pe_file_range();
97 (u64::from(offset), u64::from(size))
98 }
99
100 fn data(&self) -> Result<&'data [u8]> {
101 self.section.pe_data(self.file.data)
102 }
103
104 fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
105 Ok(read::util::data_range(
106 self.data()?,
107 self.address(),
108 address,
109 size,
110 ))
111 }
112
113 #[inline]
114 fn name_bytes(&self) -> Result<Option<&[u8]>> {
115 self.section
116 .name(self.file.common.symbols.strings())
117 .map(Some)
118 }
119
120 #[inline]
121 fn name(&self) -> Result<Option<&str>> {
122 let name = self.section.name(self.file.common.symbols.strings())?;
123 Ok(Some(
124 str::from_utf8(name)
125 .ok()
126 .read_error("Non UTF-8 PE section name")?,
127 ))
128 }
129
130 #[inline]
131 fn flags(&self) -> SegmentFlags {
132 let characteristics = self.section.characteristics.get(LE);
133 SegmentFlags::Coff { characteristics }
134 }
135}
136
137/// An iterator for the sections in a [`PeFile32`](super::PeFile32).
138pub type PeSectionIterator32<'data, 'file, R = &'data [u8]> =
139 PeSectionIterator<'data, 'file, pe::ImageNtHeaders32, R>;
140/// An iterator for the sections in a [`PeFile64`](super::PeFile64).
141pub type PeSectionIterator64<'data, 'file, R = &'data [u8]> =
142 PeSectionIterator<'data, 'file, pe::ImageNtHeaders64, R>;
143
144/// An iterator for the sections in a [`PeFile`].
145#[derive(Debug)]
146pub struct PeSectionIterator<'data, 'file, Pe, R = &'data [u8]>
147where
148 Pe: ImageNtHeaders,
149 R: ReadRef<'data>,
150{
151 pub(super) file: &'file PeFile<'data, Pe, R>,
152 pub(super) iter: iter::Enumerate<slice::Iter<'data, pe::ImageSectionHeader>>,
153}
154
155impl<'data, 'file, Pe, R> Iterator for PeSectionIterator<'data, 'file, Pe, R>
156where
157 Pe: ImageNtHeaders,
158 R: ReadRef<'data>,
159{
160 type Item = PeSection<'data, 'file, Pe, R>;
161
162 fn next(&mut self) -> Option<Self::Item> {
163 self.iter.next().map(|(index: usize, section: &ImageSectionHeader)| PeSection {
164 file: self.file,
165 index: SectionIndex(index + 1),
166 section,
167 })
168 }
169}
170
171/// A section in a [`PeFile32`](super::PeFile32).
172pub type PeSection32<'data, 'file, R = &'data [u8]> =
173 PeSection<'data, 'file, pe::ImageNtHeaders32, R>;
174/// A section in a [`PeFile64`](super::PeFile64).
175pub type PeSection64<'data, 'file, R = &'data [u8]> =
176 PeSection<'data, 'file, pe::ImageNtHeaders64, R>;
177
178/// A section in a [`PeFile`].
179///
180/// Most functionality is provided by the [`ObjectSection`] trait implementation.
181#[derive(Debug)]
182pub struct PeSection<'data, 'file, Pe, R = &'data [u8]>
183where
184 Pe: ImageNtHeaders,
185 R: ReadRef<'data>,
186{
187 pub(super) file: &'file PeFile<'data, Pe, R>,
188 pub(super) index: SectionIndex,
189 pub(super) section: &'data pe::ImageSectionHeader,
190}
191
192impl<'data, 'file, Pe, R> read::private::Sealed for PeSection<'data, 'file, Pe, R>
193where
194 Pe: ImageNtHeaders,
195 R: ReadRef<'data>,
196{
197}
198
199impl<'data, 'file, Pe, R> ObjectSection<'data> for PeSection<'data, 'file, Pe, R>
200where
201 Pe: ImageNtHeaders,
202 R: ReadRef<'data>,
203{
204 type RelocationIterator = PeRelocationIterator<'data, 'file, R>;
205
206 #[inline]
207 fn index(&self) -> SectionIndex {
208 self.index
209 }
210
211 #[inline]
212 fn address(&self) -> u64 {
213 u64::from(self.section.virtual_address.get(LE)).wrapping_add(self.file.common.image_base)
214 }
215
216 #[inline]
217 fn size(&self) -> u64 {
218 u64::from(self.section.virtual_size.get(LE))
219 }
220
221 #[inline]
222 fn align(&self) -> u64 {
223 self.file.section_alignment()
224 }
225
226 #[inline]
227 fn file_range(&self) -> Option<(u64, u64)> {
228 let (offset, size) = self.section.pe_file_range();
229 if size == 0 {
230 None
231 } else {
232 Some((u64::from(offset), u64::from(size)))
233 }
234 }
235
236 fn data(&self) -> Result<&'data [u8]> {
237 self.section.pe_data(self.file.data)
238 }
239
240 fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
241 Ok(read::util::data_range(
242 self.data()?,
243 self.address(),
244 address,
245 size,
246 ))
247 }
248
249 #[inline]
250 fn compressed_file_range(&self) -> Result<CompressedFileRange> {
251 Ok(CompressedFileRange::none(self.file_range()))
252 }
253
254 #[inline]
255 fn compressed_data(&self) -> Result<CompressedData<'data>> {
256 self.data().map(CompressedData::none)
257 }
258
259 #[inline]
260 fn name_bytes(&self) -> Result<&[u8]> {
261 self.section.name(self.file.common.symbols.strings())
262 }
263
264 #[inline]
265 fn name(&self) -> Result<&str> {
266 let name = self.name_bytes()?;
267 str::from_utf8(name)
268 .ok()
269 .read_error("Non UTF-8 PE section name")
270 }
271
272 #[inline]
273 fn segment_name_bytes(&self) -> Result<Option<&[u8]>> {
274 Ok(None)
275 }
276
277 #[inline]
278 fn segment_name(&self) -> Result<Option<&str>> {
279 Ok(None)
280 }
281
282 #[inline]
283 fn kind(&self) -> SectionKind {
284 self.section.kind()
285 }
286
287 fn relocations(&self) -> PeRelocationIterator<'data, 'file, R> {
288 PeRelocationIterator(PhantomData)
289 }
290
291 fn flags(&self) -> SectionFlags {
292 SectionFlags::Coff {
293 characteristics: self.section.characteristics.get(LE),
294 }
295 }
296}
297
298impl<'data> SectionTable<'data> {
299 /// Return the file offset of the given virtual address, and the size up
300 /// to the end of the section containing it.
301 ///
302 /// Returns `None` if no section contains the address.
303 pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> {
304 self.iter().find_map(|section| section.pe_file_range_at(va))
305 }
306
307 /// Return the data starting at the given virtual address, up to the end of the
308 /// section containing it.
309 ///
310 /// Ignores sections with invalid data.
311 ///
312 /// Returns `None` if no section contains the address.
313 pub fn pe_data_at<R: ReadRef<'data>>(&self, data: R, va: u32) -> Option<&'data [u8]> {
314 self.iter().find_map(|section| section.pe_data_at(data, va))
315 }
316
317 /// Return the data of the section that contains the given virtual address in a PE file.
318 ///
319 /// Also returns the virtual address of that section.
320 ///
321 /// Ignores sections with invalid data.
322 pub fn pe_data_containing<R: ReadRef<'data>>(
323 &self,
324 data: R,
325 va: u32,
326 ) -> Option<(&'data [u8], u32)> {
327 self.iter()
328 .find_map(|section| section.pe_data_containing(data, va))
329 }
330
331 /// Return the section that contains a given virtual address.
332 pub fn section_containing(&self, va: u32) -> Option<&'data ImageSectionHeader> {
333 self.iter().find(|section| section.contains_rva(va))
334 }
335}
336
337impl pe::ImageSectionHeader {
338 /// Return the offset and size of the section in a PE file.
339 ///
340 /// The size of the range will be the minimum of the file size and virtual size.
341 pub fn pe_file_range(&self) -> (u32, u32) {
342 // Pointer and size will be zero for uninitialized data; we don't need to validate this.
343 let offset = self.pointer_to_raw_data.get(LE);
344 let size = cmp::min(self.virtual_size.get(LE), self.size_of_raw_data.get(LE));
345 (offset, size)
346 }
347
348 /// Return the file offset of the given virtual address, and the remaining size up
349 /// to the end of the section.
350 ///
351 /// Returns `None` if the section does not contain the address.
352 pub fn pe_file_range_at(&self, va: u32) -> Option<(u32, u32)> {
353 let section_va = self.virtual_address.get(LE);
354 let offset = va.checked_sub(section_va)?;
355 let (section_offset, section_size) = self.pe_file_range();
356 // Address must be within section (and not at its end).
357 if offset < section_size {
358 Some((section_offset.checked_add(offset)?, section_size - offset))
359 } else {
360 None
361 }
362 }
363
364 /// Return the virtual address and size of the section.
365 pub fn pe_address_range(&self) -> (u32, u32) {
366 (self.virtual_address.get(LE), self.virtual_size.get(LE))
367 }
368
369 /// Return the section data in a PE file.
370 ///
371 /// The length of the data will be the minimum of the file size and virtual size.
372 pub fn pe_data<'data, R: ReadRef<'data>>(&self, data: R) -> Result<&'data [u8]> {
373 let (offset, size) = self.pe_file_range();
374 data.read_bytes_at(offset.into(), size.into())
375 .read_error("Invalid PE section offset or size")
376 }
377
378 /// Return the data starting at the given virtual address, up to the end of the
379 /// section.
380 ///
381 /// Ignores sections with invalid data.
382 ///
383 /// Returns `None` if the section does not contain the address.
384 pub fn pe_data_at<'data, R: ReadRef<'data>>(&self, data: R, va: u32) -> Option<&'data [u8]> {
385 let (offset, size) = self.pe_file_range_at(va)?;
386 data.read_bytes_at(offset.into(), size.into()).ok()
387 }
388
389 /// Tests whether a given RVA is part of this section
390 pub fn contains_rva(&self, va: u32) -> bool {
391 let section_va = self.virtual_address.get(LE);
392 match va.checked_sub(section_va) {
393 None => false,
394 Some(offset) => {
395 // Address must be within section (and not at its end).
396 offset < self.virtual_size.get(LE)
397 }
398 }
399 }
400
401 /// Return the section data if it contains the given virtual address.
402 ///
403 /// Also returns the virtual address of that section.
404 ///
405 /// Ignores sections with invalid data.
406 pub fn pe_data_containing<'data, R: ReadRef<'data>>(
407 &self,
408 data: R,
409 va: u32,
410 ) -> Option<(&'data [u8], u32)> {
411 let section_va = self.virtual_address.get(LE);
412 let offset = va.checked_sub(section_va)?;
413 let (section_offset, section_size) = self.pe_file_range();
414 // Address must be within section (and not at its end).
415 if offset < section_size {
416 let section_data = data
417 .read_bytes_at(section_offset.into(), section_size.into())
418 .ok()?;
419 Some((section_data, section_va))
420 } else {
421 None
422 }
423 }
424}
425
426/// An iterator for the relocations in an [`PeSection`].
427///
428/// This is a stub that doesn't implement any functionality.
429#[derive(Debug)]
430pub struct PeRelocationIterator<'data, 'file, R = &'data [u8]>(
431 PhantomData<(&'data (), &'file (), R)>,
432);
433
434impl<'data, 'file, R> Iterator for PeRelocationIterator<'data, 'file, R> {
435 type Item = (u64, Relocation);
436
437 fn next(&mut self) -> Option<Self::Item> {
438 None
439 }
440}
441