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.
5 // This file implements PEImage, a generic class to manipulate PE files.
6 // This file was adapted from GreenBorder's Code.
8 #include "base/win/pe_image.h"
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.
21 // Structure to perform imports enumerations.
22 struct EnumAllImportsStorage {
23 PEImage::EnumImportsFunction callback;
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.
34 int StrCmpByByte(LPCSTR s1, LPCSTR s2) {
35 while (*s1 != '\0' && *s1 == *s2) {
40 return (*reinterpret_cast<const unsigned char*>(s1) -
41 *reinterpret_cast<const unsigned char*>(s2));
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*>(
53 return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
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*>(
66 return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
67 module, name_table, iat, bound_iat,
68 unload_iat, storage.cookie);
71 void PEImage::set_module(HMODULE module) {
75 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
76 return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
79 PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
80 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
82 return reinterpret_cast<PIMAGE_NT_HEADERS>(
83 reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
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);
90 if (section < nt_headers->FileHeader.NumberOfSections)
91 return first_section + section;
96 WORD PEImage::GetNumSections() const {
97 return GetNTHeaders()->FileHeader.NumberOfSections;
100 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
101 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
103 return nt_headers->OptionalHeader.DataDirectory[directory].Size;
106 PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
107 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
110 nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
113 PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
114 PBYTE target = reinterpret_cast<PBYTE>(address);
115 PIMAGE_SECTION_HEADER section;
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
));
122 DWORD size
= section
->Misc
.VirtualSize
;
124 if ((start
<= target
) && (start
+ size
> target
))
131 PIMAGE_SECTION_HEADER
PEImage::GetImageSectionHeaderByName(
132 LPCSTR section_name
) const {
133 if (NULL
== section_name
)
136 PIMAGE_SECTION_HEADER ret
= NULL
;
137 int num_sections
= GetNumSections();
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
))) {
151 PDWORD
PEImage::GetExportEntry(LPCSTR name
) const {
152 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
158 if (!GetProcOrdinal(name
, &ordinal
))
161 PDWORD functions
= reinterpret_cast<PDWORD
>(
162 RVAToAddr(exports
->AddressOfFunctions
));
164 return functions
+ ordinal
- exports
->Base
;
167 FARPROC
PEImage::GetProcAddress(LPCSTR function_name
) const {
168 PDWORD export_entry
= GetExportEntry(function_name
);
169 if (NULL
== export_entry
)
172 PBYTE function
= reinterpret_cast<PBYTE
>(RVAToAddr(*export_entry
));
174 PBYTE exports
= reinterpret_cast<PBYTE
>(
175 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT
));
176 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT
);
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);
186 return reinterpret_cast<FARPROC
>(function
);
189 bool PEImage::GetProcOrdinal(LPCSTR function_name
, WORD
*ordinal
) const {
193 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
198 if (IsOrdinal(function_name
)) {
199 *ordinal
= ToOrdinal(function_name
);
201 PDWORD names
= reinterpret_cast<PDWORD
>(RVAToAddr(exports
->AddressOfNames
));
202 PDWORD lower
= names
;
203 PDWORD upper
= names
+ exports
->NumberOfNames
;
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
));
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
);
230 PWORD ordinals
= reinterpret_cast<PWORD
>(
231 RVAToAddr(exports
->AddressOfNameOrdinals
));
233 *ordinal
= ordinals
[lower
- names
] + static_cast<WORD
>(exports
->Base
);
239 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);
244 for (UINT i
= 0; i
< num_sections
; i
++, section
++) {
245 PVOID section_start
= RVAToAddr(section
->VirtualAddress
);
246 DWORD size
= section
->Misc
.VirtualSize
;
248 if (!callback(*this, section
, section_start
, size
, cookie
))
255 bool PEImage::EnumExports(EnumExportsFunction callback
, PVOID cookie
) const {
256 PVOID directory
= GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT
);
257 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT
);
259 // Check if there are any exports at all.
260 if (NULL
== directory
|| 0 == size
)
263 PIMAGE_EXPORT_DIRECTORY exports
= reinterpret_cast<PIMAGE_EXPORT_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
));
274 for (UINT count
= 0; count
< num_funcs
; count
++) {
275 PVOID func
= RVAToAddr(functions
[count
]);
282 for (hint
= 0; hint
< num_names
; hint
++) {
283 if (ordinals
[hint
] == count
) {
284 name
= reinterpret_cast<LPCSTR
>(RVAToAddr(names
[hint
]));
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
) +
297 forward
= reinterpret_cast<LPCSTR
>(func
);
301 if (!callback(*this, ordinal_base
+ count
, hint
, name
, func
, forward
,
309 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
>(
315 if (directory
== NULL
|| size
< sizeof(IMAGE_BASE_RELOCATION
))
318 while (base
->SizeOfBlock
) {
319 PWORD reloc
= reinterpret_cast<PWORD
>(base
+ 1);
320 UINT num_relocs
= (base
->SizeOfBlock
- sizeof(IMAGE_BASE_RELOCATION
)) /
323 for (UINT i
= 0; i
< num_relocs
; i
++, reloc
++) {
324 WORD type
= *reloc
>> 12;
325 PVOID address
= RVAToAddr(base
->VirtualAddress
+ (*reloc
& 0x0FFF));
327 if (!callback(*this, type
, address
, cookie
))
331 base
= reinterpret_cast<PIMAGE_BASE_RELOCATION
>(
332 reinterpret_cast<char*>(base
) + base
->SizeOfBlock
);
338 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback
,
339 PVOID cookie
) const {
340 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT
);
341 PIMAGE_IMPORT_DESCRIPTOR import
= GetFirstImportChunk();
343 if (import
== NULL
|| size
< sizeof(IMAGE_IMPORT_DESCRIPTOR
))
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
));
353 if (!callback(*this, module_name
, name_table
, iat
, cookie
))
360 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback
,
362 PIMAGE_THUNK_DATA name_table
,
363 PIMAGE_THUNK_DATA iat
, PVOID cookie
) const {
364 if (NULL
== name_table
)
367 for (; name_table
&& name_table
->u1
.Ordinal
; name_table
++, iat
++) {
372 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
373 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
375 PIMAGE_IMPORT_BY_NAME import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
376 RVAToAddr(name_table
->u1
.ForwarderString
));
379 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
382 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
389 bool PEImage::EnumAllImports(EnumImportsFunction callback
, PVOID cookie
) const {
390 EnumAllImportsStorage temp
= { callback
, cookie
};
391 return EnumImportChunks(ProcessImportChunk
, &temp
);
394 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
);
401 if (directory
== NULL
|| size
== 0)
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
411 // check if VC7-style imports, using RVAs instead of
412 // VC6-style addresses.
413 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
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
));
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
);
441 if (!callback(*this, delay_descriptor
, module_name
, name_table
, iat
,
442 bound_iat
, unload_iat
, cookie
))
449 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback
,
450 PImgDelayDescr delay_descriptor
,
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
);
460 for (; name_table
->u1
.Ordinal
; name_table
++, iat
++) {
465 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
466 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
468 PIMAGE_IMPORT_BY_NAME import
;
469 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
472 import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
473 RVAToAddr(name_table
->u1
.ForwarderString
));
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
);
484 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
487 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
494 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback
,
495 PVOID cookie
) const {
496 EnumAllImportsStorage temp
= { callback
, cookie
};
497 return EnumDelayImportChunks(ProcessDelayImportChunk
, &temp
);
500 bool PEImage::VerifyMagic() const {
501 PIMAGE_DOS_HEADER dos_header
= GetDosHeader();
503 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
)
506 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
508 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
)
511 if (nt_headers
->FileHeader
.SizeOfOptionalHeader
!=
512 sizeof(IMAGE_OPTIONAL_HEADER
))
515 if (nt_headers
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
)
521 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva
, DWORD
*on_disk_offset
) const {
522 LPVOID address
= RVAToAddr(rva
);
523 return ImageAddrToOnDiskOffset(address
, on_disk_offset
);
526 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address
,
527 DWORD
*on_disk_offset
) const {
531 // Get the section that this address belongs to.
532 PIMAGE_SECTION_HEADER section_header
= GetImageSectionFromAddr(address
);
533 if (NULL
== section_header
)
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
));
545 *on_disk_offset
= section_header
->PointerToRawData
+ offset_within_section
;
549 PVOID
PEImage::RVAToAddr(DWORD rva
) const {
553 return reinterpret_cast<char*>(module_
) + rva
;
556 PVOID
PEImageAsData::RVAToAddr(DWORD rva
) const {
560 PVOID in_memory
= PEImage::RVAToAddr(rva
);
563 if (!ImageAddrToOnDiskOffset(in_memory
, &disk_offset
))
566 return PEImage::RVAToAddr(disk_offset
);