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(jschuh): crbug.com/167707 Make sure this is ok.
15 #pragma message ("Warning: \
16 This code is not tested on x64. Please make sure all the base unit tests\
17 pass before doing any real work. The current unit tests don't test the\
18 differences between 32- and 64-bits implementations. Bugs may slip through.\
19 You need to improve the coverage before continuing.")
22 // Structure to perform imports enumerations.
23 struct EnumAllImportsStorage
{
24 PEImage::EnumImportsFunction callback
;
30 // Compare two strings byte by byte on an unsigned basis.
31 // if s1 == s2, return 0
32 // if s1 < s2, return negative
33 // if s1 > s2, return positive
34 // Exception if inputs are invalid.
35 int StrCmpByByte(LPCSTR s1
, LPCSTR s2
) {
36 while (*s1
!= '\0' && *s1
== *s2
) {
41 return (*reinterpret_cast<const unsigned char*>(s1
) -
42 *reinterpret_cast<const unsigned char*>(s2
));
47 // Callback used to enumerate imports. See EnumImportChunksFunction.
48 bool ProcessImportChunk(const PEImage
&image
, LPCSTR module
,
49 PIMAGE_THUNK_DATA name_table
,
50 PIMAGE_THUNK_DATA iat
, PVOID cookie
) {
51 EnumAllImportsStorage
&storage
= *reinterpret_cast<EnumAllImportsStorage
*>(
54 return image
.EnumOneImportChunk(storage
.callback
, module
, name_table
, iat
,
58 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
59 bool ProcessDelayImportChunk(const PEImage
&image
,
60 PImgDelayDescr delay_descriptor
,
61 LPCSTR module
, PIMAGE_THUNK_DATA name_table
,
62 PIMAGE_THUNK_DATA iat
, PIMAGE_THUNK_DATA bound_iat
,
63 PIMAGE_THUNK_DATA unload_iat
, PVOID cookie
) {
64 EnumAllImportsStorage
&storage
= *reinterpret_cast<EnumAllImportsStorage
*>(
67 return image
.EnumOneDelayImportChunk(storage
.callback
, delay_descriptor
,
68 module
, name_table
, iat
, bound_iat
,
69 unload_iat
, storage
.cookie
);
72 void PEImage::set_module(HMODULE module
) {
76 PIMAGE_DOS_HEADER
PEImage::GetDosHeader() const {
77 return reinterpret_cast<PIMAGE_DOS_HEADER
>(module_
);
80 PIMAGE_NT_HEADERS
PEImage::GetNTHeaders() const {
81 PIMAGE_DOS_HEADER dos_header
= GetDosHeader();
83 return reinterpret_cast<PIMAGE_NT_HEADERS
>(
84 reinterpret_cast<char*>(dos_header
) + dos_header
->e_lfanew
);
87 PIMAGE_SECTION_HEADER
PEImage::GetSectionHeader(UINT section
) const {
88 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
89 PIMAGE_SECTION_HEADER first_section
= IMAGE_FIRST_SECTION(nt_headers
);
91 if (section
< nt_headers
->FileHeader
.NumberOfSections
)
92 return first_section
+ section
;
97 WORD
PEImage::GetNumSections() const {
98 return GetNTHeaders()->FileHeader
.NumberOfSections
;
101 DWORD
PEImage::GetImageDirectoryEntrySize(UINT directory
) const {
102 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
104 return nt_headers
->OptionalHeader
.DataDirectory
[directory
].Size
;
107 PVOID
PEImage::GetImageDirectoryEntryAddr(UINT directory
) const {
108 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
111 nt_headers
->OptionalHeader
.DataDirectory
[directory
].VirtualAddress
);
114 PIMAGE_SECTION_HEADER
PEImage::GetImageSectionFromAddr(PVOID address
) const {
115 PBYTE target
= reinterpret_cast<PBYTE
>(address
);
116 PIMAGE_SECTION_HEADER section
;
118 for (UINT i
= 0; NULL
!= (section
= GetSectionHeader(i
)); i
++) {
119 // Don't use the virtual RVAToAddr.
120 PBYTE start
= reinterpret_cast<PBYTE
>(
121 PEImage::RVAToAddr(section
->VirtualAddress
));
123 DWORD size
= section
->Misc
.VirtualSize
;
125 if ((start
<= target
) && (start
+ size
> target
))
132 PIMAGE_SECTION_HEADER
PEImage::GetImageSectionHeaderByName(
133 LPCSTR section_name
) const {
134 if (NULL
== section_name
)
137 PIMAGE_SECTION_HEADER ret
= NULL
;
138 int num_sections
= GetNumSections();
140 for (int i
= 0; i
< num_sections
; i
++) {
141 PIMAGE_SECTION_HEADER section
= GetSectionHeader(i
);
142 if (0 == _strnicmp(reinterpret_cast<LPCSTR
>(section
->Name
), section_name
,
143 sizeof(section
->Name
))) {
152 PDWORD
PEImage::GetExportEntry(LPCSTR name
) const {
153 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
159 if (!GetProcOrdinal(name
, &ordinal
))
162 PDWORD functions
= reinterpret_cast<PDWORD
>(
163 RVAToAddr(exports
->AddressOfFunctions
));
165 return functions
+ ordinal
- exports
->Base
;
168 FARPROC
PEImage::GetProcAddress(LPCSTR function_name
) const {
169 PDWORD export_entry
= GetExportEntry(function_name
);
170 if (NULL
== export_entry
)
173 PBYTE function
= reinterpret_cast<PBYTE
>(RVAToAddr(*export_entry
));
175 PBYTE exports
= reinterpret_cast<PBYTE
>(
176 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT
));
177 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT
);
179 // Check for forwarded exports as a special case.
180 if (exports
<= function
&& exports
+ size
> function
)
181 #pragma warning(push)
182 #pragma warning(disable: 4312)
183 // This cast generates a warning because it is 32 bit specific.
184 return reinterpret_cast<FARPROC
>(0xFFFFFFFF);
187 return reinterpret_cast<FARPROC
>(function
);
190 bool PEImage::GetProcOrdinal(LPCSTR function_name
, WORD
*ordinal
) const {
194 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
199 if (IsOrdinal(function_name
)) {
200 *ordinal
= ToOrdinal(function_name
);
202 PDWORD names
= reinterpret_cast<PDWORD
>(RVAToAddr(exports
->AddressOfNames
));
203 PDWORD lower
= names
;
204 PDWORD upper
= names
+ exports
->NumberOfNames
;
207 // Binary Search for the name.
208 while (lower
!= upper
) {
209 PDWORD middle
= lower
+ (upper
- lower
) / 2;
210 LPCSTR name
= reinterpret_cast<LPCSTR
>(RVAToAddr(*middle
));
212 // This may be called by sandbox before MSVCRT dll loads, so can't use
213 // CRT function here.
214 cmp
= StrCmpByByte(function_name
, name
);
231 PWORD ordinals
= reinterpret_cast<PWORD
>(
232 RVAToAddr(exports
->AddressOfNameOrdinals
));
234 *ordinal
= ordinals
[lower
- names
] + static_cast<WORD
>(exports
->Base
);
240 bool PEImage::EnumSections(EnumSectionsFunction callback
, PVOID cookie
) const {
241 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
242 UINT num_sections
= nt_headers
->FileHeader
.NumberOfSections
;
243 PIMAGE_SECTION_HEADER section
= GetSectionHeader(0);
245 for (UINT i
= 0; i
< num_sections
; i
++, section
++) {
246 PVOID section_start
= RVAToAddr(section
->VirtualAddress
);
247 DWORD size
= section
->Misc
.VirtualSize
;
249 if (!callback(*this, section
, section_start
, size
, cookie
))
256 bool PEImage::EnumExports(EnumExportsFunction callback
, PVOID cookie
) const {
257 PVOID directory
= GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT
);
258 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT
);
260 // Check if there are any exports at all.
261 if (NULL
== directory
|| 0 == size
)
264 PIMAGE_EXPORT_DIRECTORY exports
= reinterpret_cast<PIMAGE_EXPORT_DIRECTORY
>(
266 UINT ordinal_base
= exports
->Base
;
267 UINT num_funcs
= exports
->NumberOfFunctions
;
268 UINT num_names
= exports
->NumberOfNames
;
269 PDWORD functions
= reinterpret_cast<PDWORD
>(RVAToAddr(
270 exports
->AddressOfFunctions
));
271 PDWORD names
= reinterpret_cast<PDWORD
>(RVAToAddr(exports
->AddressOfNames
));
272 PWORD ordinals
= reinterpret_cast<PWORD
>(RVAToAddr(
273 exports
->AddressOfNameOrdinals
));
275 for (UINT count
= 0; count
< num_funcs
; count
++) {
276 PVOID func
= RVAToAddr(functions
[count
]);
283 for (hint
= 0; hint
< num_names
; hint
++) {
284 if (ordinals
[hint
] == count
) {
285 name
= reinterpret_cast<LPCSTR
>(RVAToAddr(names
[hint
]));
293 // Check for forwarded exports.
294 LPCSTR forward
= NULL
;
295 if (reinterpret_cast<char*>(func
) >= reinterpret_cast<char*>(directory
) &&
296 reinterpret_cast<char*>(func
) <= reinterpret_cast<char*>(directory
) +
298 forward
= reinterpret_cast<LPCSTR
>(func
);
302 if (!callback(*this, ordinal_base
+ count
, hint
, name
, func
, forward
,
310 bool PEImage::EnumRelocs(EnumRelocsFunction callback
, PVOID cookie
) const {
311 PVOID directory
= GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC
);
312 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC
);
313 PIMAGE_BASE_RELOCATION base
= reinterpret_cast<PIMAGE_BASE_RELOCATION
>(
319 while (size
>= sizeof(IMAGE_BASE_RELOCATION
) && base
->SizeOfBlock
&&
320 size
>= base
->SizeOfBlock
) {
321 PWORD reloc
= reinterpret_cast<PWORD
>(base
+ 1);
322 UINT num_relocs
= (base
->SizeOfBlock
- sizeof(IMAGE_BASE_RELOCATION
)) /
325 for (UINT i
= 0; i
< num_relocs
; i
++, reloc
++) {
326 WORD type
= *reloc
>> 12;
327 PVOID address
= RVAToAddr(base
->VirtualAddress
+ (*reloc
& 0x0FFF));
329 if (!callback(*this, type
, address
, cookie
))
333 size
-= base
->SizeOfBlock
;
334 base
= reinterpret_cast<PIMAGE_BASE_RELOCATION
>(
335 reinterpret_cast<char*>(base
) + base
->SizeOfBlock
);
341 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback
,
342 PVOID cookie
) const {
343 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT
);
344 PIMAGE_IMPORT_DESCRIPTOR import
= GetFirstImportChunk();
346 if (import
== NULL
|| size
< sizeof(IMAGE_IMPORT_DESCRIPTOR
))
349 for (; import
->FirstThunk
; import
++) {
350 LPCSTR module_name
= reinterpret_cast<LPCSTR
>(RVAToAddr(import
->Name
));
351 PIMAGE_THUNK_DATA name_table
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
352 RVAToAddr(import
->OriginalFirstThunk
));
353 PIMAGE_THUNK_DATA iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
354 RVAToAddr(import
->FirstThunk
));
356 if (!callback(*this, module_name
, name_table
, iat
, cookie
))
363 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback
,
365 PIMAGE_THUNK_DATA name_table
,
366 PIMAGE_THUNK_DATA iat
, PVOID cookie
) const {
367 if (NULL
== name_table
)
370 for (; name_table
&& name_table
->u1
.Ordinal
; name_table
++, iat
++) {
375 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
376 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
378 PIMAGE_IMPORT_BY_NAME import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
379 RVAToAddr(name_table
->u1
.ForwarderString
));
382 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
385 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
392 bool PEImage::EnumAllImports(EnumImportsFunction callback
, PVOID cookie
) const {
393 EnumAllImportsStorage temp
= { callback
, cookie
};
394 return EnumImportChunks(ProcessImportChunk
, &temp
);
397 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback
,
398 PVOID cookie
) const {
399 PVOID directory
= GetImageDirectoryEntryAddr(
400 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
);
401 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
);
402 PImgDelayDescr delay_descriptor
= reinterpret_cast<PImgDelayDescr
>(directory
);
404 if (directory
== NULL
|| size
== 0)
407 for (; delay_descriptor
->rvaHmod
; delay_descriptor
++) {
408 PIMAGE_THUNK_DATA name_table
;
409 PIMAGE_THUNK_DATA iat
;
410 PIMAGE_THUNK_DATA bound_iat
; // address of the optional bound IAT
411 PIMAGE_THUNK_DATA unload_iat
; // address of optional copy of original IAT
414 // check if VC7-style imports, using RVAs instead of
415 // VC6-style addresses.
416 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
419 module_name
= reinterpret_cast<LPCSTR
>(
420 RVAToAddr(delay_descriptor
->rvaDLLName
));
421 name_table
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
422 RVAToAddr(delay_descriptor
->rvaINT
));
423 iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
424 RVAToAddr(delay_descriptor
->rvaIAT
));
425 bound_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
426 RVAToAddr(delay_descriptor
->rvaBoundIAT
));
427 unload_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
428 RVAToAddr(delay_descriptor
->rvaUnloadIAT
));
430 #pragma warning(push)
431 #pragma warning(disable: 4312)
432 // These casts generate warnings because they are 32 bit specific.
433 module_name
= reinterpret_cast<LPCSTR
>(delay_descriptor
->rvaDLLName
);
434 name_table
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
435 delay_descriptor
->rvaINT
);
436 iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(delay_descriptor
->rvaIAT
);
437 bound_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
438 delay_descriptor
->rvaBoundIAT
);
439 unload_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
440 delay_descriptor
->rvaUnloadIAT
);
444 if (!callback(*this, delay_descriptor
, module_name
, name_table
, iat
,
445 bound_iat
, unload_iat
, cookie
))
452 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback
,
453 PImgDelayDescr delay_descriptor
,
455 PIMAGE_THUNK_DATA name_table
,
456 PIMAGE_THUNK_DATA iat
,
457 PIMAGE_THUNK_DATA bound_iat
,
458 PIMAGE_THUNK_DATA unload_iat
,
459 PVOID cookie
) const {
460 UNREFERENCED_PARAMETER(bound_iat
);
461 UNREFERENCED_PARAMETER(unload_iat
);
463 for (; name_table
->u1
.Ordinal
; name_table
++, iat
++) {
468 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
469 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
471 PIMAGE_IMPORT_BY_NAME import
;
472 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
475 import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
476 RVAToAddr(name_table
->u1
.ForwarderString
));
478 #pragma warning(push)
479 #pragma warning(disable: 4312)
480 // This cast generates a warning because it is 32 bit specific.
481 import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
482 name_table
->u1
.ForwarderString
);
487 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
490 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
497 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback
,
498 PVOID cookie
) const {
499 EnumAllImportsStorage temp
= { callback
, cookie
};
500 return EnumDelayImportChunks(ProcessDelayImportChunk
, &temp
);
503 bool PEImage::VerifyMagic() const {
504 PIMAGE_DOS_HEADER dos_header
= GetDosHeader();
506 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
)
509 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
511 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
)
514 if (nt_headers
->FileHeader
.SizeOfOptionalHeader
!=
515 sizeof(IMAGE_OPTIONAL_HEADER
))
518 if (nt_headers
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
)
524 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva
, DWORD
*on_disk_offset
) const {
525 LPVOID address
= RVAToAddr(rva
);
526 return ImageAddrToOnDiskOffset(address
, on_disk_offset
);
529 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address
,
530 DWORD
*on_disk_offset
) const {
534 // Get the section that this address belongs to.
535 PIMAGE_SECTION_HEADER section_header
= GetImageSectionFromAddr(address
);
536 if (NULL
== section_header
)
539 #pragma warning(push)
540 #pragma warning(disable: 4311)
541 // These casts generate warnings because they are 32 bit specific.
542 // Don't follow the virtual RVAToAddr, use the one on the base.
543 DWORD offset_within_section
= reinterpret_cast<DWORD
>(address
) -
544 reinterpret_cast<DWORD
>(PEImage::RVAToAddr(
545 section_header
->VirtualAddress
));
548 *on_disk_offset
= section_header
->PointerToRawData
+ offset_within_section
;
552 PVOID
PEImage::RVAToAddr(DWORD rva
) const {
556 return reinterpret_cast<char*>(module_
) + rva
;
559 PVOID
PEImageAsData::RVAToAddr(DWORD rva
) const {
563 PVOID in_memory
= PEImage::RVAToAddr(rva
);
566 if (!ImageAddrToOnDiskOffset(in_memory
, &disk_offset
))
569 return PEImage::RVAToAddr(disk_offset
);