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.
18 // Structure to perform imports enumerations.
19 struct EnumAllImportsStorage {
20 PEImage::EnumImportsFunction callback;
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*>(
31 return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
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*>(
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) {
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;
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();
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
))
109 PIMAGE_SECTION_HEADER
PEImage::GetImageSectionHeaderByName(
110 LPCSTR section_name
) const {
111 if (NULL
== section_name
)
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
))) {
129 PDWORD
PEImage::GetExportEntry(LPCSTR name
) const {
130 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
136 if (!GetProcOrdinal(name
, &ordinal
))
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
)
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);
164 return reinterpret_cast<FARPROC
>(function
);
167 bool PEImage::GetProcOrdinal(LPCSTR function_name
, WORD
*ordinal
) const {
171 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
176 if (IsOrdinal(function_name
)) {
177 *ordinal
= ToOrdinal(function_name
);
179 PDWORD names
= reinterpret_cast<PDWORD
>(RVAToAddr(exports
->AddressOfNames
));
180 PDWORD lower
= names
;
181 PDWORD upper
= names
+ exports
->NumberOfNames
;
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
);
206 PWORD ordinals
= reinterpret_cast<PWORD
>(
207 RVAToAddr(exports
->AddressOfNameOrdinals
));
209 *ordinal
= ordinals
[lower
- names
] + static_cast<WORD
>(exports
->Base
);
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
))
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
)
239 PIMAGE_EXPORT_DIRECTORY exports
= reinterpret_cast<PIMAGE_EXPORT_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
]);
258 for (hint
= 0; hint
< num_names
; hint
++) {
259 if (ordinals
[hint
] == count
) {
260 name
= reinterpret_cast<LPCSTR
>(RVAToAddr(names
[hint
]));
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
) +
273 forward
= reinterpret_cast<LPCSTR
>(func
);
277 if (!callback(*this, ordinal_base
+ count
, hint
, name
, func
, forward
,
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
>(
291 if (directory
== NULL
|| size
< sizeof(IMAGE_BASE_RELOCATION
))
294 while (base
->SizeOfBlock
) {
295 PWORD reloc
= reinterpret_cast<PWORD
>(base
+ 1);
296 UINT num_relocs
= (base
->SizeOfBlock
- sizeof(IMAGE_BASE_RELOCATION
)) /
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
))
307 base
= reinterpret_cast<PIMAGE_BASE_RELOCATION
>(
308 reinterpret_cast<char*>(base
) + base
->SizeOfBlock
);
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
))
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
))
336 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback
,
338 PIMAGE_THUNK_DATA name_table
,
339 PIMAGE_THUNK_DATA iat
, PVOID cookie
) const {
340 if (NULL
== name_table
)
343 for (; name_table
&& name_table
->u1
.Ordinal
; name_table
++, iat
++) {
348 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
349 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
351 PIMAGE_IMPORT_BY_NAME import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
352 RVAToAddr(name_table
->u1
.ForwarderString
));
355 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
358 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
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)
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
387 // check if VC7-style imports, using RVAs instead of
388 // VC6-style addresses.
389 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
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
));
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
);
417 if (!callback(*this, delay_descriptor
, module_name
, name_table
, iat
,
418 bound_iat
, unload_iat
, cookie
))
425 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback
,
426 PImgDelayDescr delay_descriptor
,
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
++) {
441 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
442 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
444 PIMAGE_IMPORT_BY_NAME import
;
445 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
448 import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
449 RVAToAddr(name_table
->u1
.ForwarderString
));
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
);
460 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
463 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
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
)
482 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
484 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
)
487 if (nt_headers
->FileHeader
.SizeOfOptionalHeader
!=
488 sizeof(IMAGE_OPTIONAL_HEADER
))
491 if (nt_headers
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
)
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 {
507 // Get the section that this address belongs to.
508 PIMAGE_SECTION_HEADER section_header
= GetImageSectionFromAddr(address
);
509 if (NULL
== section_header
)
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
));
521 *on_disk_offset
= section_header
->PointerToRawData
+ offset_within_section
;
525 PVOID
PEImage::RVAToAddr(DWORD rva
) const {
529 return reinterpret_cast<char*>(module_
) + rva
;
532 PVOID
PEImageAsData::RVAToAddr(DWORD rva
) const {
536 PVOID in_memory
= PEImage::RVAToAddr(rva
);
539 if (!ImageAddrToOnDiskOffset(in_memory
, &dummy
))