1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use crate::error;
use scroll::{Pread, Pwrite, SizeWith};

use crate::pe::data_directories;
use crate::pe::options;
use crate::pe::section_table;
use crate::pe::utils;

#[derive(Debug, PartialEq, Copy, Clone, Default)]
pub struct DebugData<'a> {
    pub image_debug_directory: ImageDebugDirectory,
    pub codeview_pdb70_debug_info: Option<CodeviewPDB70DebugInfo<'a>>,
}

impl<'a> DebugData<'a> {
    pub fn parse(
        bytes: &'a [u8],
        dd: data_directories::DataDirectory,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
    ) -> error::Result<Self> {
        Self::parse_with_opts(
            bytes,
            dd,
            sections,
            file_alignment,
            &options::ParseOptions::default(),
        )
    }

    pub fn parse_with_opts(
        bytes: &'a [u8],
        dd: data_directories::DataDirectory,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
        opts: &options::ParseOptions,
    ) -> error::Result<Self> {
        let image_debug_directory =
            ImageDebugDirectory::parse_with_opts(bytes, dd, sections, file_alignment, opts)?;
        let codeview_pdb70_debug_info =
            CodeviewPDB70DebugInfo::parse_with_opts(bytes, &image_debug_directory, opts)?;

        Ok(DebugData {
            image_debug_directory,
            codeview_pdb70_debug_info,
        })
    }

    /// Return this executable's debugging GUID, suitable for matching against a PDB file.
    pub fn guid(&self) -> Option<[u8; 16]> {
        self.codeview_pdb70_debug_info.map(|pdb70| pdb70.signature)
    }
}

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680307(v=vs.85).aspx
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
pub struct ImageDebugDirectory {
    pub characteristics: u32,
    pub time_date_stamp: u32,
    pub major_version: u16,
    pub minor_version: u16,
    pub data_type: u32,
    pub size_of_data: u32,
    pub address_of_raw_data: u32,
    pub pointer_to_raw_data: u32,
}

pub const IMAGE_DEBUG_TYPE_UNKNOWN: u32 = 0;
pub const IMAGE_DEBUG_TYPE_COFF: u32 = 1;
pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2;
pub const IMAGE_DEBUG_TYPE_FPO: u32 = 3;
pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4;
pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5;
pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6;
pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9;

impl ImageDebugDirectory {
    #[allow(unused)]
    fn parse(
        bytes: &[u8],
        dd: data_directories::DataDirectory,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
    ) -> error::Result<Self> {
        Self::parse_with_opts(
            bytes,
            dd,
            sections,
            file_alignment,
            &options::ParseOptions::default(),
        )
    }

    fn parse_with_opts(
        bytes: &[u8],
        dd: data_directories::DataDirectory,
        sections: &[section_table::SectionTable],
        file_alignment: u32,
        opts: &options::ParseOptions,
    ) -> error::Result<Self> {
        let rva = dd.virtual_address as usize;
        let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
            error::Error::Malformed(format!(
                "Cannot map ImageDebugDirectory rva {:#x} into offset",
                rva
            ))
        })?;
        let idd: Self = bytes.pread_with(offset, scroll::LE)?;
        Ok(idd)
    }
}

pub const CODEVIEW_PDB70_MAGIC: u32 = 0x5344_5352;
pub const CODEVIEW_PDB20_MAGIC: u32 = 0x3031_424e;
pub const CODEVIEW_CV50_MAGIC: u32 = 0x3131_424e;
pub const CODEVIEW_CV41_MAGIC: u32 = 0x3930_424e;

// http://llvm.org/doxygen/CVDebugRecord_8h_source.html
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone, Default)]
pub struct CodeviewPDB70DebugInfo<'a> {
    pub codeview_signature: u32,
    pub signature: [u8; 16],
    pub age: u32,
    pub filename: &'a [u8],
}

impl<'a> CodeviewPDB70DebugInfo<'a> {
    pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
        Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
    }

    pub fn parse_with_opts(
        bytes: &'a [u8],
        idd: &ImageDebugDirectory,
        opts: &options::ParseOptions,
    ) -> error::Result<Option<Self>> {
        if idd.data_type != IMAGE_DEBUG_TYPE_CODEVIEW {
            // not a codeview debug directory
            // that's not an error, but it's not a CodeviewPDB70DebugInfo either
            return Ok(None);
        }

        // ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly
        let mut offset: usize = match opts.resolve_rva {
            true => idd.pointer_to_raw_data as usize,
            false => idd.address_of_raw_data as usize,
        };

        // calculate how long the eventual filename will be, which doubles as a check of the record size
        let filename_length = idd.size_of_data as isize - 24;
        if filename_length < 0 {
            // the record is too short to be plausible
            return Err(error::Error::Malformed(format!(
                "ImageDebugDirectory size of data seems wrong: {:?}",
                idd.size_of_data
            )));
        }
        let filename_length = filename_length as usize;

        // check the codeview signature
        let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
        if codeview_signature != CODEVIEW_PDB70_MAGIC {
            return Ok(None);
        }

        // read the rest
        let mut signature: [u8; 16] = [0; 16];
        signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?);
        let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
        if let Some(filename) = bytes.get(offset..offset + filename_length) {
            Ok(Some(CodeviewPDB70DebugInfo {
                codeview_signature,
                signature,
                age,
                filename,
            }))
        } else {
            Err(error::Error::Malformed(format!(
                "ImageDebugDirectory seems corrupted: {:?}",
                idd
            )))
        }
    }
}