1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This file implements PEImage, a generic class to manipulate PE files.
6 // This file was adapted from GreenBorder's Code.
7
8 #include "base/win/pe_image.h"
9
10 namespace base {
11 namespace win {
12
13 #if defined(_WIN64) && !defined(NACL_WIN64)
14 // TODO(rvargas): Bug 27218. Make sure this is ok.
15 #error This code is not tested on x64. Please make sure all the base unit tests\
16 pass before doing any real work. The current unit tests don't test the\
17 differences between 32- and 64-bits implementations. Bugs may slip through.\
18 You need to improve the coverage before continuing.
19 #endif
20
21 // Structure to perform imports enumerations.
22 struct EnumAllImportsStorage {
23 PEImage::EnumImportsFunction callback;
24 PVOID cookie;
25 };
26
27 namespace {
28
29 // Compare two strings byte by byte on an unsigned basis.
30 // if s1 == s2, return 0
31 // if s1 < s2, return negative
32 // if s1 > s2, return positive
33 // Exception if inputs are invalid.
StrCmpByByte(LPCSTR s1,LPCSTR s2)34 int StrCmpByByte(LPCSTR s1, LPCSTR s2) {
35 while (*s1 != '\0' && *s1 == *s2) {
36 ++s1;
37 ++s2;
38 }
39
40 return (*reinterpret_cast<const unsigned char*>(s1) -
41 *reinterpret_cast<const unsigned char*>(s2));
42 }
43
44 } // namespace
45
46 // Callback used to enumerate imports. See EnumImportChunksFunction.
47 bool ProcessImportChunk(const PEImage &image, LPCSTR module,
48 PIMAGE_THUNK_DATA name_table,
49 PIMAGE_THUNK_DATA iat, PVOID cookie) {
50 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
51 cookie);
52
53 return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
54 storage.cookie);
55 }
56
57 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
58 bool ProcessDelayImportChunk(const PEImage &image,
59 PImgDelayDescr delay_descriptor,
60 LPCSTR module, PIMAGE_THUNK_DATA name_table,
61 PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat,
62 PIMAGE_THUNK_DATA unload_iat, PVOID cookie) {
63 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
64 cookie);
65
66 return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
67 module, name_table, iat, bound_iat,
68 unload_iat, storage.cookie);
69 }
70
71 void PEImage::set_module(HMODULE module) {
72 module_ = module;
73 }
74
75 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
76 return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
77 }
78
79 PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
80 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
81
82 return reinterpret_cast<PIMAGE_NT_HEADERS>(
83 reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
84 }
85
86 PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
87 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
88 PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
89
90 if (section < nt_headers->FileHeader.NumberOfSections)
91 return first_section + section;
92 else
93 return NULL;
94 }
95
96 WORD PEImage::GetNumSections() const {
97 return GetNTHeaders()->FileHeader.NumberOfSections;
98 }
99
100 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
101 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
102
103 return nt_headers->OptionalHeader.DataDirectory[directory].Size;
104 }
105
106 PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
107 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
108
109 return RVAToAddr(
110 nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
111 }
112
113 PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
114 PBYTE target = reinterpret_cast<PBYTE>(address);
115 PIMAGE_SECTION_HEADER section;
116
117 for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) {
118 // Don't use the virtual RVAToAddr.
119 PBYTE start = reinterpret_cast<PBYTE>(
120 PEImage::RVAToAddr(section->VirtualAddress));
121
122 DWORD size = section->Misc.VirtualSize;
123
124 if ((start <= target) && (start + size > target))
125 return section;
126 }
127
128 return NULL;
129 }
130
GetImageSectionHeaderByName(LPCSTR section_name) const131 PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
132 LPCSTR section_name) const {
133 if (NULL == section_name)
134 return NULL;
135
136 PIMAGE_SECTION_HEADER ret = NULL;
137 int num_sections = GetNumSections();
138
139 for (int i = 0; i < num_sections; i++) {
140 PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
141 if (0 == _strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
142 sizeof(section->Name))) {
143 ret = section;
144 break;
145 }
146 }
147
148 return ret;
149 }
150
GetExportEntry(LPCSTR name) const151 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
152 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
153
154 if (NULL == exports)
155 return NULL;
156
157 WORD ordinal = 0;
158 if (!GetProcOrdinal(name, &ordinal))
159 return NULL;
160
161 PDWORD functions = reinterpret_cast<PDWORD>(
162 RVAToAddr(exports->AddressOfFunctions));
163
164 return functions + ordinal - exports->Base;
165 }
166
GetProcAddress(LPCSTR function_name) const167 FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
168 PDWORD export_entry = GetExportEntry(function_name);
169 if (NULL == export_entry)
170 return NULL;
171
172 PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
173
174 PBYTE exports = reinterpret_cast<PBYTE>(
175 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
176 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
177
178 // Check for forwarded exports as a special case.
179 if (exports <= function && exports + size > function)
180 #pragma warning(push)
181 #pragma warning(disable: 4312)
182 // This cast generates a warning because it is 32 bit specific.
183 return reinterpret_cast<FARPROC>(0xFFFFFFFF);
184 #pragma warning(pop)
185
186 return reinterpret_cast<FARPROC>(function);
187 }
188
GetProcOrdinal(LPCSTR function_name,WORD * ordinal) const189 bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
190 if (NULL == ordinal)
191 return false;
192
193 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
194
195 if (NULL == exports)
196 return false;
197
198 if (IsOrdinal(function_name)) {
199 *ordinal = ToOrdinal(function_name);
200 } else {
201 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
202 PDWORD lower = names;
203 PDWORD upper = names + exports->NumberOfNames;
204 int cmp = -1;
205
206 // Binary Search for the name.
207 while (lower != upper) {
208 PDWORD middle = lower + (upper - lower) / 2;
209 LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
210
211 // This may be called by sandbox before MSVCRT dll loads, so can't use
212 // CRT function here.
213 cmp = StrCmpByByte(function_name, name);
214
215 if (cmp == 0) {
216 lower = middle;
217 break;
218 }
219
220 if (cmp > 0)
221 lower = middle + 1;
222 else
223 upper = middle;
224 }
225
226 if (cmp != 0)
227 return false;
228
229
230 PWORD ordinals = reinterpret_cast<PWORD>(
231 RVAToAddr(exports->AddressOfNameOrdinals));
232
233 *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
234 }
235
236 return true;
237 }
238
EnumSections(EnumSectionsFunction callback,PVOID cookie) const239 bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
240 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
241 UINT num_sections = nt_headers->FileHeader.NumberOfSections;
242 PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
243
244 for (UINT i = 0; i < num_sections; i++, section++) {
245 PVOID section_start = RVAToAddr(section->VirtualAddress);
246 DWORD size = section->Misc.VirtualSize;
247
248 if (!callback(*this, section, section_start, size, cookie))
249 return false;
250 }
251
252 return true;
253 }
254
EnumExports(EnumExportsFunction callback,PVOID cookie) const255 bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
256 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
257 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
258
259 // Check if there are any exports at all.
260 if (NULL == directory || 0 == size)
261 return true;
262
263 PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
264 directory);
265 UINT ordinal_base = exports->Base;
266 UINT num_funcs = exports->NumberOfFunctions;
267 UINT num_names = exports->NumberOfNames;
268 PDWORD functions = reinterpret_cast<PDWORD>(RVAToAddr(
269 exports->AddressOfFunctions));
270 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
271 PWORD ordinals = reinterpret_cast<PWORD>(RVAToAddr(
272 exports->AddressOfNameOrdinals));
273
274 for (UINT count = 0; count < num_funcs; count++) {
275 PVOID func = RVAToAddr(functions[count]);
276 if (NULL == func)
277 continue;
278
279 // Check for a name.
280 LPCSTR name = NULL;
281 UINT hint;
282 for (hint = 0; hint < num_names; hint++) {
283 if (ordinals[hint] == count) {
284 name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
285 break;
286 }
287 }
288
289 if (name == NULL)
290 hint = 0;
291
292 // Check for forwarded exports.
293 LPCSTR forward = NULL;
294 if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
295 reinterpret_cast<char*>(func) <= reinterpret_cast<char*>(directory) +
296 size) {
297 forward = reinterpret_cast<LPCSTR>(func);
298 func = 0;
299 }
300
301 if (!callback(*this, ordinal_base + count, hint, name, func, forward,
302 cookie))
303 return false;
304 }
305
306 return true;
307 }
308
EnumRelocs(EnumRelocsFunction callback,PVOID cookie) const309 bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
310 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
311 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
312 PIMAGE_BASE_RELOCATION base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
313 directory);
314
315 if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
316 return true;
317
318 while (base->SizeOfBlock) {
319 PWORD reloc = reinterpret_cast<PWORD>(base + 1);
320 UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
321 sizeof(WORD);
322
323 for (UINT i = 0; i < num_relocs; i++, reloc++) {
324 WORD type = *reloc >> 12;
325 PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
326
327 if (!callback(*this, type, address, cookie))
328 return false;
329 }
330
331 base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
332 reinterpret_cast<char*>(base) + base->SizeOfBlock);
333 }
334
335 return true;
336 }
337
EnumImportChunks(EnumImportChunksFunction callback,PVOID cookie) const338 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
339 PVOID cookie) const {
340 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
341 PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
342
343 if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
344 return true;
345
346 for (; import->FirstThunk; import++) {
347 LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
348 PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
349 RVAToAddr(import->OriginalFirstThunk));
350 PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
351 RVAToAddr(import->FirstThunk));
352
353 if (!callback(*this, module_name, name_table, iat, cookie))
354 return false;
355 }
356
357 return true;
358 }
359
EnumOneImportChunk(EnumImportsFunction callback,LPCSTR module_name,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PVOID cookie) const360 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
361 LPCSTR module_name,
362 PIMAGE_THUNK_DATA name_table,
363 PIMAGE_THUNK_DATA iat, PVOID cookie) const {
364 if (NULL == name_table)
365 return false;
366
367 for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
368 LPCSTR name = NULL;
369 WORD ordinal = 0;
370 WORD hint = 0;
371
372 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
373 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
374 } else {
375 PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
376 RVAToAddr(name_table->u1.ForwarderString));
377
378 hint = import->Hint;
379 name = reinterpret_cast<LPCSTR>(&import->Name);
380 }
381
382 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
383 return false;
384 }
385
386 return true;
387 }
388
EnumAllImports(EnumImportsFunction callback,PVOID cookie) const389 bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
390 EnumAllImportsStorage temp = { callback, cookie };
391 return EnumImportChunks(ProcessImportChunk, &temp);
392 }
393
EnumDelayImportChunks(EnumDelayImportChunksFunction callback,PVOID cookie) const394 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
395 PVOID cookie) const {
396 PVOID directory = GetImageDirectoryEntryAddr(
397 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
398 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
399 PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
400
401 if (directory == NULL || size == 0)
402 return true;
403
404 for (; delay_descriptor->rvaHmod; delay_descriptor++) {
405 PIMAGE_THUNK_DATA name_table;
406 PIMAGE_THUNK_DATA iat;
407 PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT
408 PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT
409 LPCSTR module_name;
410
411 // check if VC7-style imports, using RVAs instead of
412 // VC6-style addresses.
413 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
414
415 if (rvas) {
416 module_name = reinterpret_cast<LPCSTR>(
417 RVAToAddr(delay_descriptor->rvaDLLName));
418 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
419 RVAToAddr(delay_descriptor->rvaINT));
420 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
421 RVAToAddr(delay_descriptor->rvaIAT));
422 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
423 RVAToAddr(delay_descriptor->rvaBoundIAT));
424 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
425 RVAToAddr(delay_descriptor->rvaUnloadIAT));
426 } else {
427 #pragma warning(push)
428 #pragma warning(disable: 4312)
429 // These casts generate warnings because they are 32 bit specific.
430 module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
431 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
432 delay_descriptor->rvaINT);
433 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
434 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
435 delay_descriptor->rvaBoundIAT);
436 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
437 delay_descriptor->rvaUnloadIAT);
438 #pragma warning(pop)
439 }
440
441 if (!callback(*this, delay_descriptor, module_name, name_table, iat,
442 bound_iat, unload_iat, cookie))
443 return false;
444 }
445
446 return true;
447 }
448
EnumOneDelayImportChunk(EnumImportsFunction callback,PImgDelayDescr delay_descriptor,LPCSTR module_name,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PIMAGE_THUNK_DATA bound_iat,PIMAGE_THUNK_DATA unload_iat,PVOID cookie) const449 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
450 PImgDelayDescr delay_descriptor,
451 LPCSTR module_name,
452 PIMAGE_THUNK_DATA name_table,
453 PIMAGE_THUNK_DATA iat,
454 PIMAGE_THUNK_DATA bound_iat,
455 PIMAGE_THUNK_DATA unload_iat,
456 PVOID cookie) const {
457 UNREFERENCED_PARAMETER(bound_iat);
458 UNREFERENCED_PARAMETER(unload_iat);
459
460 for (; name_table->u1.Ordinal; name_table++, iat++) {
461 LPCSTR name = NULL;
462 WORD ordinal = 0;
463 WORD hint = 0;
464
465 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
466 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
467 } else {
468 PIMAGE_IMPORT_BY_NAME import;
469 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
470
471 if (rvas) {
472 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
473 RVAToAddr(name_table->u1.ForwarderString));
474 } else {
475 #pragma warning(push)
476 #pragma warning(disable: 4312)
477 // This cast generates a warning because it is 32 bit specific.
478 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
479 name_table->u1.ForwarderString);
480 #pragma warning(pop)
481 }
482
483 hint = import->Hint;
484 name = reinterpret_cast<LPCSTR>(&import->Name);
485 }
486
487 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
488 return false;
489 }
490
491 return true;
492 }
493
EnumAllDelayImports(EnumImportsFunction callback,PVOID cookie) const494 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
495 PVOID cookie) const {
496 EnumAllImportsStorage temp = { callback, cookie };
497 return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
498 }
499
VerifyMagic() const500 bool PEImage::VerifyMagic() const {
501 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
502
503 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
504 return false;
505
506 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
507
508 if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
509 return false;
510
511 if (nt_headers->FileHeader.SizeOfOptionalHeader !=
512 sizeof(IMAGE_OPTIONAL_HEADER))
513 return false;
514
515 if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
516 return false;
517
518 return true;
519 }
520
ImageRVAToOnDiskOffset(DWORD rva,DWORD * on_disk_offset) const521 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
522 LPVOID address = RVAToAddr(rva);
523 return ImageAddrToOnDiskOffset(address, on_disk_offset);
524 }
525
ImageAddrToOnDiskOffset(LPVOID address,DWORD * on_disk_offset) const526 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
527 DWORD *on_disk_offset) const {
528 if (NULL == address)
529 return false;
530
531 // Get the section that this address belongs to.
532 PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
533 if (NULL == section_header)
534 return false;
535
536 #pragma warning(push)
537 #pragma warning(disable: 4311)
538 // These casts generate warnings because they are 32 bit specific.
539 // Don't follow the virtual RVAToAddr, use the one on the base.
540 DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
541 reinterpret_cast<DWORD>(PEImage::RVAToAddr(
542 section_header->VirtualAddress));
543 #pragma warning(pop)
544
545 *on_disk_offset = section_header->PointerToRawData + offset_within_section;
546 return true;
547 }
548
RVAToAddr(DWORD rva) const549 PVOID PEImage::RVAToAddr(DWORD rva) const {
550 if (rva == 0)
551 return NULL;
552
553 return reinterpret_cast<char*>(module_) + rva;
554 }
555
RVAToAddr(DWORD rva) const556 PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
557 if (rva == 0)
558 return NULL;
559
560 PVOID in_memory = PEImage::RVAToAddr(rva);
561 DWORD disk_offset;
562
563 if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset))
564 return NULL;
565
566 return PEImage::RVAToAddr(disk_offset);
567 }
568
569 } // namespace win
570 } // namespace base
571