Provide image baselines for struct-image-07-t.svg
[chromium-blink-merge.git] / base / pe_image.cc
blobf2fb14086e2a643172a3de9380e8a2ee3a8bfe9b
1 // Copyright (c) 2006-2008 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.
5 // This file implements PEImage, a generic class to manipulate PE files.
6 // This file was adapted from GreenBorder's Code.
8 #include "base/pe_image.h"
10 #if defined(_WIN64) && !defined(NACL_WIN64)
11 // TODO(rvargas): Bug 27218. Make sure this is ok.
12 #error This code is not tested on x64. Please make sure all the base unit tests\
13 pass before doing any real work. The current unit tests don't test the\
14 differences between 32- and 64-bits implementations. Bugs may slip through.\
15 You need to improve the coverage before continuing.
16 #endif
18 // Structure to perform imports enumerations.
19 struct EnumAllImportsStorage {
20 PEImage::EnumImportsFunction callback;
21 PVOID cookie;
24 // Callback used to enumerate imports. See EnumImportChunksFunction.
25 bool ProcessImportChunk(const PEImage &image, LPCSTR module,
26 PIMAGE_THUNK_DATA name_table,
27 PIMAGE_THUNK_DATA iat, PVOID cookie) {
28 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
29 cookie);
31 return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
32 storage.cookie);
35 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
36 bool ProcessDelayImportChunk(const PEImage &image,
37 PImgDelayDescr delay_descriptor,
38 LPCSTR module, PIMAGE_THUNK_DATA name_table,
39 PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat,
40 PIMAGE_THUNK_DATA unload_iat, PVOID cookie) {
41 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
42 cookie);
44 return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
45 module, name_table, iat, bound_iat,
46 unload_iat, storage.cookie);
49 void PEImage::set_module(HMODULE module) {
50 module_ = module;
53 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
54 return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
57 PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
58 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
60 return reinterpret_cast<PIMAGE_NT_HEADERS>(
61 reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
64 PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
65 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
66 PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
68 if (section < nt_headers->FileHeader.NumberOfSections)
69 return first_section + section;
70 else
71 return NULL;
74 WORD PEImage::GetNumSections() const {
75 return GetNTHeaders()->FileHeader.NumberOfSections;
78 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
79 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
81 return nt_headers->OptionalHeader.DataDirectory[directory].Size;
84 PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
85 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
87 return RVAToAddr(
88 nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
91 PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
92 PBYTE target = reinterpret_cast<PBYTE>(address);
93 PIMAGE_SECTION_HEADER section;
95 for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) {
96 // Don't use the virtual RVAToAddr.
97 PBYTE start = reinterpret_cast<PBYTE>(
98 PEImage::RVAToAddr(section->VirtualAddress));
100 DWORD size = section->Misc.VirtualSize;
102 if ((start <= target) && (start + size > target))
103 return section;
106 return NULL;
109 PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
110 LPCSTR section_name) const {
111 if (NULL == section_name)
112 return NULL;
114 PIMAGE_SECTION_HEADER ret = NULL;
115 int num_sections = GetNumSections();
117 for (int i = 0; i < num_sections; i++) {
118 PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
119 if (0 == _strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
120 sizeof(section->Name))) {
121 ret = section;
122 break;
126 return ret;
129 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
130 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
132 if (NULL == exports)
133 return NULL;
135 WORD ordinal = 0;
136 if (!GetProcOrdinal(name, &ordinal))
137 return NULL;
139 PDWORD functions = reinterpret_cast<PDWORD>(
140 RVAToAddr(exports->AddressOfFunctions));
142 return functions + ordinal - exports->Base;
145 FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
146 PDWORD export_entry = GetExportEntry(function_name);
147 if (NULL == export_entry)
148 return NULL;
150 PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
152 PBYTE exports = reinterpret_cast<PBYTE>(
153 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
154 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
156 // Check for forwarded exports as a special case.
157 if (exports <= function && exports + size > function)
158 #pragma warning(push)
159 #pragma warning(disable: 4312)
160 // This cast generates a warning because it is 32 bit specific.
161 return reinterpret_cast<FARPROC>(0xFFFFFFFF);
162 #pragma warning(pop)
164 return reinterpret_cast<FARPROC>(function);
167 bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
168 if (NULL == ordinal)
169 return false;
171 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
173 if (NULL == exports)
174 return false;
176 if (IsOrdinal(function_name)) {
177 *ordinal = ToOrdinal(function_name);
178 } else {
179 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
180 PDWORD lower = names;
181 PDWORD upper = names + exports->NumberOfNames;
182 int cmp = -1;
184 // Binary Search for the name.
185 while (lower != upper) {
186 PDWORD middle = lower + (upper - lower) / 2;
187 LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
189 cmp = strcmp(function_name, name);
191 if (cmp == 0) {
192 lower = middle;
193 break;
196 if (cmp > 0)
197 lower = middle + 1;
198 else
199 upper = middle;
202 if (cmp != 0)
203 return false;
206 PWORD ordinals = reinterpret_cast<PWORD>(
207 RVAToAddr(exports->AddressOfNameOrdinals));
209 *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
212 return true;
215 bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
216 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
217 UINT num_sections = nt_headers->FileHeader.NumberOfSections;
218 PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
220 for (UINT i = 0; i < num_sections; i++, section++) {
221 PVOID section_start = RVAToAddr(section->VirtualAddress);
222 DWORD size = section->Misc.VirtualSize;
224 if (!callback(*this, section, section_start, size, cookie))
225 return false;
228 return true;
231 bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
232 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
233 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
235 // Check if there are any exports at all.
236 if (NULL == directory || 0 == size)
237 return true;
239 PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
240 directory);
241 UINT ordinal_base = exports->Base;
242 UINT num_funcs = exports->NumberOfFunctions;
243 UINT num_names = exports->NumberOfNames;
244 PDWORD functions = reinterpret_cast<PDWORD>(RVAToAddr(
245 exports->AddressOfFunctions));
246 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
247 PWORD ordinals = reinterpret_cast<PWORD>(RVAToAddr(
248 exports->AddressOfNameOrdinals));
250 for (UINT count = 0; count < num_funcs; count++) {
251 PVOID func = RVAToAddr(functions[count]);
252 if (NULL == func)
253 continue;
255 // Check for a name.
256 LPCSTR name = NULL;
257 UINT hint;
258 for (hint = 0; hint < num_names; hint++) {
259 if (ordinals[hint] == count) {
260 name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
261 break;
265 if (name == NULL)
266 hint = 0;
268 // Check for forwarded exports.
269 LPCSTR forward = NULL;
270 if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
271 reinterpret_cast<char*>(func) <= reinterpret_cast<char*>(directory) +
272 size) {
273 forward = reinterpret_cast<LPCSTR>(func);
274 func = 0;
277 if (!callback(*this, ordinal_base + count, hint, name, func, forward,
278 cookie))
279 return false;
282 return true;
285 bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
286 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
287 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
288 PIMAGE_BASE_RELOCATION base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
289 directory);
291 if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
292 return true;
294 while (base->SizeOfBlock) {
295 PWORD reloc = reinterpret_cast<PWORD>(base + 1);
296 UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
297 sizeof(WORD);
299 for (UINT i = 0; i < num_relocs; i++, reloc++) {
300 WORD type = *reloc >> 12;
301 PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
303 if (!callback(*this, type, address, cookie))
304 return false;
307 base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
308 reinterpret_cast<char*>(base) + base->SizeOfBlock);
311 return true;
314 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
315 PVOID cookie) const {
316 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
317 PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
319 if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
320 return true;
322 for (; import->FirstThunk; import++) {
323 LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
324 PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
325 RVAToAddr(import->OriginalFirstThunk));
326 PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
327 RVAToAddr(import->FirstThunk));
329 if (!callback(*this, module_name, name_table, iat, cookie))
330 return false;
333 return true;
336 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
337 LPCSTR module_name,
338 PIMAGE_THUNK_DATA name_table,
339 PIMAGE_THUNK_DATA iat, PVOID cookie) const {
340 if (NULL == name_table)
341 return false;
343 for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
344 LPCSTR name = NULL;
345 WORD ordinal = 0;
346 WORD hint = 0;
348 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
349 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
350 } else {
351 PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
352 RVAToAddr(name_table->u1.ForwarderString));
354 hint = import->Hint;
355 name = reinterpret_cast<LPCSTR>(&import->Name);
358 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
359 return false;
362 return true;
365 bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
366 EnumAllImportsStorage temp = { callback, cookie };
367 return EnumImportChunks(ProcessImportChunk, &temp);
370 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
371 PVOID cookie) const {
372 PVOID directory = GetImageDirectoryEntryAddr(
373 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
374 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
375 PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
377 if (directory == NULL || size == 0)
378 return true;
380 for (; delay_descriptor->rvaHmod; delay_descriptor++) {
381 PIMAGE_THUNK_DATA name_table;
382 PIMAGE_THUNK_DATA iat;
383 PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT
384 PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT
385 LPCSTR module_name;
387 // check if VC7-style imports, using RVAs instead of
388 // VC6-style addresses.
389 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
391 if (rvas) {
392 module_name = reinterpret_cast<LPCSTR>(
393 RVAToAddr(delay_descriptor->rvaDLLName));
394 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
395 RVAToAddr(delay_descriptor->rvaINT));
396 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
397 RVAToAddr(delay_descriptor->rvaIAT));
398 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
399 RVAToAddr(delay_descriptor->rvaBoundIAT));
400 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
401 RVAToAddr(delay_descriptor->rvaUnloadIAT));
402 } else {
403 #pragma warning(push)
404 #pragma warning(disable: 4312)
405 // These casts generate warnings because they are 32 bit specific.
406 module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
407 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
408 delay_descriptor->rvaINT);
409 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
410 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
411 delay_descriptor->rvaBoundIAT);
412 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
413 delay_descriptor->rvaUnloadIAT);
414 #pragma warning(pop)
417 if (!callback(*this, delay_descriptor, module_name, name_table, iat,
418 bound_iat, unload_iat, cookie))
419 return false;
422 return true;
425 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
426 PImgDelayDescr delay_descriptor,
427 LPCSTR module_name,
428 PIMAGE_THUNK_DATA name_table,
429 PIMAGE_THUNK_DATA iat,
430 PIMAGE_THUNK_DATA bound_iat,
431 PIMAGE_THUNK_DATA unload_iat,
432 PVOID cookie) const {
433 UNREFERENCED_PARAMETER(bound_iat);
434 UNREFERENCED_PARAMETER(unload_iat);
436 for (; name_table->u1.Ordinal; name_table++, iat++) {
437 LPCSTR name = NULL;
438 WORD ordinal = 0;
439 WORD hint = 0;
441 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
442 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
443 } else {
444 PIMAGE_IMPORT_BY_NAME import;
445 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
447 if (rvas) {
448 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
449 RVAToAddr(name_table->u1.ForwarderString));
450 } else {
451 #pragma warning(push)
452 #pragma warning(disable: 4312)
453 // This cast generates a warning because it is 32 bit specific.
454 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
455 name_table->u1.ForwarderString);
456 #pragma warning(pop)
459 hint = import->Hint;
460 name = reinterpret_cast<LPCSTR>(&import->Name);
463 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
464 return false;
467 return true;
470 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
471 PVOID cookie) const {
472 EnumAllImportsStorage temp = { callback, cookie };
473 return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
476 bool PEImage::VerifyMagic() const {
477 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
479 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
480 return false;
482 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
484 if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
485 return false;
487 if (nt_headers->FileHeader.SizeOfOptionalHeader !=
488 sizeof(IMAGE_OPTIONAL_HEADER))
489 return false;
491 if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
492 return false;
494 return true;
497 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
498 LPVOID address = RVAToAddr(rva);
499 return ImageAddrToOnDiskOffset(address, on_disk_offset);
502 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
503 DWORD *on_disk_offset) const {
504 if (NULL == address)
505 return false;
507 // Get the section that this address belongs to.
508 PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
509 if (NULL == section_header)
510 return false;
512 #pragma warning(push)
513 #pragma warning(disable: 4311)
514 // These casts generate warnings because they are 32 bit specific.
515 // Don't follow the virtual RVAToAddr, use the one on the base.
516 DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
517 reinterpret_cast<DWORD>(PEImage::RVAToAddr(
518 section_header->VirtualAddress));
519 #pragma warning(pop)
521 *on_disk_offset = section_header->PointerToRawData + offset_within_section;
522 return true;
525 PVOID PEImage::RVAToAddr(DWORD rva) const {
526 if (rva == 0)
527 return NULL;
529 return reinterpret_cast<char*>(module_) + rva;
532 PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
533 if (rva == 0)
534 return NULL;
536 PVOID in_memory = PEImage::RVAToAddr(rva);
537 DWORD dummy;
539 if (!ImageAddrToOnDiskOffset(in_memory, &dummy))
540 return NULL;
542 return in_memory;