1 // Copyright (c) 2019, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "pe_util.h"
31 
32 #include <windows.h>
33 #include <winnt.h>
34 #include <atlbase.h>
35 #include <ImageHlp.h>
36 
37 #include <functional>
38 
39 #include "common/windows/string_utils-inl.h"
40 #include "common/windows/guid_string.h"
41 
42 namespace {
43 
44 /*
45  * Not defined in WinNT.h for some reason. Definitions taken from:
46  * http://uninformed.org/index.cgi?v=4&a=1&p=13
47  *
48  */
49 typedef unsigned char UBYTE;
50 
51 #if !defined(_WIN64)
52 #define UNW_FLAG_EHANDLER  0x01
53 #define UNW_FLAG_UHANDLER  0x02
54 #define UNW_FLAG_CHAININFO 0x04
55 #endif
56 
57 union UnwindCode {
58   struct {
59     UBYTE offset_in_prolog;
60     UBYTE unwind_operation_code : 4;
61     UBYTE operation_info : 4;
62   };
63   USHORT frame_offset;
64 };
65 
66 enum UnwindOperationCodes {
67   UWOP_PUSH_NONVOL = 0, /* info == register number */
68   UWOP_ALLOC_LARGE,     /* no info, alloc size in next 2 slots */
69   UWOP_ALLOC_SMALL,     /* info == size of allocation / 8 - 1 */
70   UWOP_SET_FPREG,       /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
71   UWOP_SAVE_NONVOL,     /* info == register number, offset in next slot */
72   UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
73   // XXX: these are missing from MSDN!
74   // See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm
75   UWOP_SAVE_XMM,
76   UWOP_SAVE_XMM_FAR,
77   UWOP_SAVE_XMM128,     /* info == XMM reg number, offset in next slot */
78   UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
79   UWOP_PUSH_MACHFRAME   /* info == 0: no error-code, 1: error-code */
80 };
81 
82 // See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
83 // Note: some fields removed as we don't use them.
84 struct UnwindInfo {
85   UBYTE version : 3;
86   UBYTE flags : 5;
87   UBYTE size_of_prolog;
88   UBYTE count_of_codes;
89   UBYTE frame_register : 4;
90   UBYTE frame_offset : 4;
91   UnwindCode unwind_code[1];
92 };
93 
94 struct CV_INFO_PDB70 {
95   ULONG cv_signature;
96   GUID signature;
97   ULONG age;
98   CHAR pdb_filename[ANYSIZE_ARRAY];
99 };
100 
101 #define CV_SIGNATURE_RSDS 'SDSR'
102 
103 // A helper class to scope a PLOADED_IMAGE.
104 class AutoImage {
105 public:
AutoImage(PLOADED_IMAGE img)106   explicit AutoImage(PLOADED_IMAGE img) : img_(img) {}
~AutoImage()107   ~AutoImage() {
108     if (img_)
109       ImageUnload(img_);
110   }
111 
operator PLOADED_IMAGE()112   operator PLOADED_IMAGE() { return img_; }
operator ->()113   PLOADED_IMAGE operator->() { return img_; }
114 
115 private:
116   PLOADED_IMAGE img_;
117 };
118 }  // namespace
119 
120 namespace google_breakpad {
121 
122 using std::unique_ptr;
123 using google_breakpad::GUIDString;
124 
ReadModuleInfo(const wstring & pe_file,PDBModuleInfo * info)125 bool ReadModuleInfo(const wstring & pe_file, PDBModuleInfo * info) {
126   // Convert wchar to native charset because ImageLoad only takes
127   // a PSTR as input.
128   string img_file;
129   if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
130     fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
131         pe_file.c_str());
132     return false;
133   }
134 
135   AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
136   if (!img) {
137     fprintf(stderr, "Failed to load %s\n", img_file.c_str());
138     return false;
139   }
140 
141   info->cpu = FileHeaderMachineToCpuString(
142       img->FileHeader->FileHeader.Machine);
143 
144   PIMAGE_OPTIONAL_HEADER64 optional_header =
145       &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
146 
147   // Search debug directories for a guid signature & age
148   DWORD debug_rva = optional_header->
149     DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
150   DWORD debug_size = optional_header->
151     DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
152   PIMAGE_DEBUG_DIRECTORY debug_directories =
153     static_cast<PIMAGE_DEBUG_DIRECTORY>(
154       ImageRvaToVa(img->FileHeader,
155         img->MappedAddress,
156         debug_rva,
157         &img->LastRvaSection));
158 
159   for (DWORD i = 0; i < debug_size / sizeof(*debug_directories); i++) {
160     if (debug_directories[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW ||
161         debug_directories[i].SizeOfData < sizeof(CV_INFO_PDB70)) {
162       continue;
163     }
164 
165     struct CV_INFO_PDB70* cv_info = static_cast<CV_INFO_PDB70*>(ImageRvaToVa(
166         img->FileHeader,
167         img->MappedAddress,
168         debug_directories[i].AddressOfRawData,
169         &img->LastRvaSection));
170     if (cv_info->cv_signature != CV_SIGNATURE_RSDS) {
171       continue;
172     }
173 
174     info->debug_identifier = GenerateDebugIdentifier(cv_info->age,
175         cv_info->signature);
176 
177     // This code assumes that the pdb_filename is stored as ASCII without
178     // multibyte characters, but it's not clear if that's true.
179     size_t debug_file_length = strnlen_s(cv_info->pdb_filename, MAX_PATH);
180     if (debug_file_length < 0 || debug_file_length >= MAX_PATH) {
181       fprintf(stderr, "PE debug directory is corrupt.\n");
182       return false;
183     }
184     std::string debug_file(cv_info->pdb_filename, debug_file_length);
185     if (!WindowsStringUtils::safe_mbstowcs(debug_file, &info->debug_file)) {
186       fprintf(stderr, "PDB filename '%s' contains unrecognized characters.\n",
187           debug_file.c_str());
188       return false;
189     }
190     info->debug_file = WindowsStringUtils::GetBaseName(info->debug_file);
191 
192     return true;
193   }
194 
195   fprintf(stderr, "Image is missing debug information.\n");
196   return false;
197 }
198 
ReadPEInfo(const wstring & pe_file,PEModuleInfo * info)199 bool ReadPEInfo(const wstring & pe_file, PEModuleInfo * info) {
200   // Convert wchar to native charset because ImageLoad only takes
201   // a PSTR as input.
202   string img_file;
203   if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
204     fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
205         pe_file.c_str());
206     return false;
207   }
208 
209   AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
210   if (!img) {
211     fprintf(stderr, "Failed to open PE file: %S\n", pe_file.c_str());
212     return false;
213   }
214 
215   info->code_file = WindowsStringUtils::GetBaseName(pe_file);
216 
217   // The date and time that the file was created by the linker.
218   DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp;
219   // The size of the file in bytes, including all headers.
220   DWORD SizeOfImage = 0;
221   PIMAGE_OPTIONAL_HEADER64 opt =
222     &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader;
223   if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
224     // 64-bit PE file.
225     SizeOfImage = opt->SizeOfImage;
226   }
227   else {
228     // 32-bit PE file.
229     SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage;
230   }
231   wchar_t code_identifier[32];
232   swprintf(code_identifier,
233     sizeof(code_identifier) / sizeof(code_identifier[0]),
234     L"%08X%X", TimeDateStamp, SizeOfImage);
235   info->code_identifier = code_identifier;
236 
237   return true;
238 }
239 
PrintPEFrameData(const wstring & pe_file,FILE * out_file)240 bool PrintPEFrameData(const wstring & pe_file, FILE * out_file)
241 {
242   // Convert wchar to native charset because ImageLoad only takes
243   // a PSTR as input.
244   string img_file;
245   if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
246     fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
247         pe_file.c_str());
248     return false;
249   }
250 
251   AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
252   if (!img) {
253     fprintf(stderr, "Failed to load %s\n", img_file.c_str());
254     return false;
255   }
256   PIMAGE_OPTIONAL_HEADER64 optional_header =
257     &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
258   if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
259     fprintf(stderr, "Not a PE32+ image\n");
260     return false;
261   }
262 
263   // Read Exception Directory
264   DWORD exception_rva = optional_header->
265     DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
266   DWORD exception_size = optional_header->
267     DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
268   PIMAGE_RUNTIME_FUNCTION_ENTRY funcs =
269     static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
270       ImageRvaToVa(img->FileHeader,
271         img->MappedAddress,
272         exception_rva,
273         &img->LastRvaSection));
274   for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) {
275     DWORD unwind_rva = funcs[i].UnwindInfoAddress;
276     // handle chaining
277     while (unwind_rva & 0x1) {
278       unwind_rva ^= 0x1;
279       PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
280         static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
281           ImageRvaToVa(img->FileHeader,
282             img->MappedAddress,
283             unwind_rva,
284             &img->LastRvaSection));
285       unwind_rva = chained_func->UnwindInfoAddress;
286     }
287 
288     UnwindInfo *unwind_info = static_cast<UnwindInfo *>(
289       ImageRvaToVa(img->FileHeader,
290         img->MappedAddress,
291         unwind_rva,
292         &img->LastRvaSection));
293 
294     DWORD stack_size = 8;  // minimal stack size is 8 for RIP
295     DWORD rip_offset = 8;
296     do {
297       for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) {
298         UnwindCode *unwind_code = &unwind_info->unwind_code[c];
299         switch (unwind_code->unwind_operation_code) {
300         case UWOP_PUSH_NONVOL: {
301           stack_size += 8;
302           break;
303         }
304         case UWOP_ALLOC_LARGE: {
305           if (unwind_code->operation_info == 0) {
306             c++;
307             if (c < unwind_info->count_of_codes)
308               stack_size += (unwind_code + 1)->frame_offset * 8;
309           }
310           else {
311             c += 2;
312             if (c < unwind_info->count_of_codes)
313               stack_size += (unwind_code + 1)->frame_offset |
314               ((unwind_code + 2)->frame_offset << 16);
315           }
316           break;
317         }
318         case UWOP_ALLOC_SMALL: {
319           stack_size += unwind_code->operation_info * 8 + 8;
320           break;
321         }
322         case UWOP_SET_FPREG:
323         case UWOP_SAVE_XMM:
324         case UWOP_SAVE_XMM_FAR:
325           break;
326         case UWOP_SAVE_NONVOL:
327         case UWOP_SAVE_XMM128: {
328           c++;  // skip slot with offset
329           break;
330         }
331         case UWOP_SAVE_NONVOL_FAR:
332         case UWOP_SAVE_XMM128_FAR: {
333           c += 2;  // skip 2 slots with offset
334           break;
335         }
336         case UWOP_PUSH_MACHFRAME: {
337           if (unwind_code->operation_info) {
338             stack_size += 88;
339           }
340           else {
341             stack_size += 80;
342           }
343           rip_offset += 80;
344           break;
345         }
346         }
347       }
348       if (unwind_info->flags & UNW_FLAG_CHAININFO) {
349         PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
350           reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
351           (unwind_info->unwind_code +
352             ((unwind_info->count_of_codes + 1) & ~1)));
353 
354         unwind_info = static_cast<UnwindInfo *>(
355           ImageRvaToVa(img->FileHeader,
356             img->MappedAddress,
357             chained_func->UnwindInfoAddress,
358             &img->LastRvaSection));
359       }
360       else {
361         unwind_info = NULL;
362       }
363     } while (unwind_info);
364     fprintf(out_file, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n",
365       funcs[i].BeginAddress,
366       funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset);
367     fprintf(out_file, "STACK CFI %lx .cfa: $rsp %lu +\n",
368       funcs[i].BeginAddress, stack_size);
369   }
370 
371   return true;
372 }
373 
GenerateDebugIdentifier(DWORD age,GUID signature)374 wstring GenerateDebugIdentifier(DWORD age, GUID signature)
375 {
376   // Use the same format that the MS symbol server uses in filesystem
377   // hierarchies.
378   wchar_t age_string[9];
379   swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]),
380     L"%x", age);
381 
382   // remove when VC++7.1 is no longer supported
383   age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0';
384 
385   wstring debug_identifier = GUIDString::GUIDToSymbolServerWString(&signature);
386   debug_identifier.append(age_string);
387 
388   return debug_identifier;
389 }
390 
GenerateDebugIdentifier(DWORD age,DWORD signature)391 wstring GenerateDebugIdentifier(DWORD age, DWORD signature)
392 {
393   // Use the same format that the MS symbol server uses in filesystem
394   // hierarchies.
395   wchar_t identifier_string[17];
396   swprintf(identifier_string,
397     sizeof(identifier_string) / sizeof(identifier_string[0]),
398     L"%08X%x", signature, age);
399 
400   // remove when VC++7.1 is no longer supported
401   identifier_string[sizeof(identifier_string) /
402     sizeof(identifier_string[0]) - 1] = L'\0';
403 
404   return wstring(identifier_string);
405 }
406 
407 }  // namespace google_breakpad
408