Skip to main content

object/read/pe/
section.rs

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