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 // TODO(jschuh): crbug.com/167707 Make sure this code works on 64-bit.
15 // Structure to perform imports enumerations.
16 struct EnumAllImportsStorage
{
17 PEImage::EnumImportsFunction callback
;
24 const DWORD kPdbInfoSignature
= 'SDSR';
26 // Compare two strings byte by byte on an unsigned basis.
27 // if s1 == s2, return 0
28 // if s1 < s2, return negative
29 // if s1 > s2, return positive
30 // Exception if inputs are invalid.
31 int StrCmpByByte(LPCSTR s1
, LPCSTR s2
) {
32 while (*s1
!= '\0' && *s1
== *s2
) {
37 return (*reinterpret_cast<const unsigned char*>(s1
) -
38 *reinterpret_cast<const unsigned char*>(s2
));
49 // Callback used to enumerate imports. See EnumImportChunksFunction.
50 bool ProcessImportChunk(const PEImage
&image
, LPCSTR module
,
51 PIMAGE_THUNK_DATA name_table
,
52 PIMAGE_THUNK_DATA iat
, PVOID cookie
) {
53 EnumAllImportsStorage
&storage
= *reinterpret_cast<EnumAllImportsStorage
*>(
56 return image
.EnumOneImportChunk(storage
.callback
, module
, name_table
, iat
,
60 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
61 bool ProcessDelayImportChunk(const PEImage
&image
,
62 PImgDelayDescr delay_descriptor
,
63 LPCSTR module
, PIMAGE_THUNK_DATA name_table
,
64 PIMAGE_THUNK_DATA iat
, PIMAGE_THUNK_DATA bound_iat
,
65 PIMAGE_THUNK_DATA unload_iat
, PVOID cookie
) {
66 EnumAllImportsStorage
&storage
= *reinterpret_cast<EnumAllImportsStorage
*>(
69 return image
.EnumOneDelayImportChunk(storage
.callback
, delay_descriptor
,
70 module
, name_table
, iat
, bound_iat
,
71 unload_iat
, storage
.cookie
);
74 void PEImage::set_module(HMODULE module
) {
78 PIMAGE_DOS_HEADER
PEImage::GetDosHeader() const {
79 return reinterpret_cast<PIMAGE_DOS_HEADER
>(module_
);
82 PIMAGE_NT_HEADERS
PEImage::GetNTHeaders() const {
83 PIMAGE_DOS_HEADER dos_header
= GetDosHeader();
85 return reinterpret_cast<PIMAGE_NT_HEADERS
>(
86 reinterpret_cast<char*>(dos_header
) + dos_header
->e_lfanew
);
89 PIMAGE_SECTION_HEADER
PEImage::GetSectionHeader(UINT section
) const {
90 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
91 PIMAGE_SECTION_HEADER first_section
= IMAGE_FIRST_SECTION(nt_headers
);
93 if (section
< nt_headers
->FileHeader
.NumberOfSections
)
94 return first_section
+ section
;
99 WORD
PEImage::GetNumSections() const {
100 return GetNTHeaders()->FileHeader
.NumberOfSections
;
103 DWORD
PEImage::GetImageDirectoryEntrySize(UINT directory
) const {
104 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
106 return nt_headers
->OptionalHeader
.DataDirectory
[directory
].Size
;
109 PVOID
PEImage::GetImageDirectoryEntryAddr(UINT directory
) const {
110 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
113 nt_headers
->OptionalHeader
.DataDirectory
[directory
].VirtualAddress
);
116 PIMAGE_SECTION_HEADER
PEImage::GetImageSectionFromAddr(PVOID address
) const {
117 PBYTE target
= reinterpret_cast<PBYTE
>(address
);
118 PIMAGE_SECTION_HEADER section
;
120 for (UINT i
= 0; NULL
!= (section
= GetSectionHeader(i
)); i
++) {
121 // Don't use the virtual RVAToAddr.
122 PBYTE start
= reinterpret_cast<PBYTE
>(
123 PEImage::RVAToAddr(section
->VirtualAddress
));
125 DWORD size
= section
->Misc
.VirtualSize
;
127 if ((start
<= target
) && (start
+ size
> target
))
134 PIMAGE_SECTION_HEADER
PEImage::GetImageSectionHeaderByName(
135 LPCSTR section_name
) const {
136 if (NULL
== section_name
)
139 PIMAGE_SECTION_HEADER ret
= NULL
;
140 int num_sections
= GetNumSections();
142 for (int i
= 0; i
< num_sections
; i
++) {
143 PIMAGE_SECTION_HEADER section
= GetSectionHeader(i
);
144 if (0 == _strnicmp(reinterpret_cast<LPCSTR
>(section
->Name
), section_name
,
145 sizeof(section
->Name
))) {
154 bool PEImage::GetDebugId(LPGUID guid
, LPDWORD age
) const {
155 if (NULL
== guid
|| NULL
== age
) {
159 DWORD debug_directory_size
=
160 GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG
);
161 PIMAGE_DEBUG_DIRECTORY debug_directory
=
162 reinterpret_cast<PIMAGE_DEBUG_DIRECTORY
>(
163 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG
));
165 size_t directory_count
=
166 debug_directory_size
/ sizeof(IMAGE_DEBUG_DIRECTORY
);
168 for (size_t index
= 0; index
< directory_count
; ++index
) {
169 if (debug_directory
[index
].Type
== IMAGE_DEBUG_TYPE_CODEVIEW
) {
170 PdbInfo
* pdb_info
= reinterpret_cast<PdbInfo
*>(
171 RVAToAddr(debug_directory
[index
].AddressOfRawData
));
172 if (pdb_info
->Signature
!= kPdbInfoSignature
) {
173 // Unsupported PdbInfo signature
176 *guid
= pdb_info
->Guid
;
177 *age
= pdb_info
->Age
;
184 PDWORD
PEImage::GetExportEntry(LPCSTR name
) const {
185 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
191 if (!GetProcOrdinal(name
, &ordinal
))
194 PDWORD functions
= reinterpret_cast<PDWORD
>(
195 RVAToAddr(exports
->AddressOfFunctions
));
197 return functions
+ ordinal
- exports
->Base
;
200 FARPROC
PEImage::GetProcAddress(LPCSTR function_name
) const {
201 PDWORD export_entry
= GetExportEntry(function_name
);
202 if (NULL
== export_entry
)
205 PBYTE function
= reinterpret_cast<PBYTE
>(RVAToAddr(*export_entry
));
207 PBYTE exports
= reinterpret_cast<PBYTE
>(
208 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT
));
209 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT
);
211 // Check for forwarded exports as a special case.
212 if (exports
<= function
&& exports
+ size
> function
)
213 #pragma warning(push)
214 #pragma warning(disable: 4312)
215 // This cast generates a warning because it is 32 bit specific.
216 return reinterpret_cast<FARPROC
>(0xFFFFFFFF);
219 return reinterpret_cast<FARPROC
>(function
);
222 bool PEImage::GetProcOrdinal(LPCSTR function_name
, WORD
*ordinal
) const {
226 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
231 if (IsOrdinal(function_name
)) {
232 *ordinal
= ToOrdinal(function_name
);
234 PDWORD names
= reinterpret_cast<PDWORD
>(RVAToAddr(exports
->AddressOfNames
));
235 PDWORD lower
= names
;
236 PDWORD upper
= names
+ exports
->NumberOfNames
;
239 // Binary Search for the name.
240 while (lower
!= upper
) {
241 PDWORD middle
= lower
+ (upper
- lower
) / 2;
242 LPCSTR name
= reinterpret_cast<LPCSTR
>(RVAToAddr(*middle
));
244 // This may be called by sandbox before MSVCRT dll loads, so can't use
245 // CRT function here.
246 cmp
= StrCmpByByte(function_name
, name
);
263 PWORD ordinals
= reinterpret_cast<PWORD
>(
264 RVAToAddr(exports
->AddressOfNameOrdinals
));
266 *ordinal
= ordinals
[lower
- names
] + static_cast<WORD
>(exports
->Base
);
272 bool PEImage::EnumSections(EnumSectionsFunction callback
, PVOID cookie
) const {
273 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
274 UINT num_sections
= nt_headers
->FileHeader
.NumberOfSections
;
275 PIMAGE_SECTION_HEADER section
= GetSectionHeader(0);
277 for (UINT i
= 0; i
< num_sections
; i
++, section
++) {
278 PVOID section_start
= RVAToAddr(section
->VirtualAddress
);
279 DWORD size
= section
->Misc
.VirtualSize
;
281 if (!callback(*this, section
, section_start
, size
, cookie
))
288 bool PEImage::EnumExports(EnumExportsFunction callback
, PVOID cookie
) const {
289 PVOID directory
= GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT
);
290 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT
);
292 // Check if there are any exports at all.
293 if (NULL
== directory
|| 0 == size
)
296 PIMAGE_EXPORT_DIRECTORY exports
= reinterpret_cast<PIMAGE_EXPORT_DIRECTORY
>(
298 UINT ordinal_base
= exports
->Base
;
299 UINT num_funcs
= exports
->NumberOfFunctions
;
300 UINT num_names
= exports
->NumberOfNames
;
301 PDWORD functions
= reinterpret_cast<PDWORD
>(RVAToAddr(
302 exports
->AddressOfFunctions
));
303 PDWORD names
= reinterpret_cast<PDWORD
>(RVAToAddr(exports
->AddressOfNames
));
304 PWORD ordinals
= reinterpret_cast<PWORD
>(RVAToAddr(
305 exports
->AddressOfNameOrdinals
));
307 for (UINT count
= 0; count
< num_funcs
; count
++) {
308 PVOID func
= RVAToAddr(functions
[count
]);
315 for (hint
= 0; hint
< num_names
; hint
++) {
316 if (ordinals
[hint
] == count
) {
317 name
= reinterpret_cast<LPCSTR
>(RVAToAddr(names
[hint
]));
325 // Check for forwarded exports.
326 LPCSTR forward
= NULL
;
327 if (reinterpret_cast<char*>(func
) >= reinterpret_cast<char*>(directory
) &&
328 reinterpret_cast<char*>(func
) <= reinterpret_cast<char*>(directory
) +
330 forward
= reinterpret_cast<LPCSTR
>(func
);
334 if (!callback(*this, ordinal_base
+ count
, hint
, name
, func
, forward
,
342 bool PEImage::EnumRelocs(EnumRelocsFunction callback
, PVOID cookie
) const {
343 PVOID directory
= GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC
);
344 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC
);
345 PIMAGE_BASE_RELOCATION base
= reinterpret_cast<PIMAGE_BASE_RELOCATION
>(
351 while (size
>= sizeof(IMAGE_BASE_RELOCATION
) && base
->SizeOfBlock
&&
352 size
>= base
->SizeOfBlock
) {
353 PWORD reloc
= reinterpret_cast<PWORD
>(base
+ 1);
354 UINT num_relocs
= (base
->SizeOfBlock
- sizeof(IMAGE_BASE_RELOCATION
)) /
357 for (UINT i
= 0; i
< num_relocs
; i
++, reloc
++) {
358 WORD type
= *reloc
>> 12;
359 PVOID address
= RVAToAddr(base
->VirtualAddress
+ (*reloc
& 0x0FFF));
361 if (!callback(*this, type
, address
, cookie
))
365 size
-= base
->SizeOfBlock
;
366 base
= reinterpret_cast<PIMAGE_BASE_RELOCATION
>(
367 reinterpret_cast<char*>(base
) + base
->SizeOfBlock
);
373 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback
,
374 PVOID cookie
) const {
375 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT
);
376 PIMAGE_IMPORT_DESCRIPTOR import
= GetFirstImportChunk();
378 if (import
== NULL
|| size
< sizeof(IMAGE_IMPORT_DESCRIPTOR
))
381 for (; import
->FirstThunk
; import
++) {
382 LPCSTR module_name
= reinterpret_cast<LPCSTR
>(RVAToAddr(import
->Name
));
383 PIMAGE_THUNK_DATA name_table
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
384 RVAToAddr(import
->OriginalFirstThunk
));
385 PIMAGE_THUNK_DATA iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
386 RVAToAddr(import
->FirstThunk
));
388 if (!callback(*this, module_name
, name_table
, iat
, cookie
))
395 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback
,
397 PIMAGE_THUNK_DATA name_table
,
398 PIMAGE_THUNK_DATA iat
, PVOID cookie
) const {
399 if (NULL
== name_table
)
402 for (; name_table
&& name_table
->u1
.Ordinal
; name_table
++, iat
++) {
407 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
408 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
410 PIMAGE_IMPORT_BY_NAME import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
411 RVAToAddr(name_table
->u1
.ForwarderString
));
414 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
417 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
424 bool PEImage::EnumAllImports(EnumImportsFunction callback
, PVOID cookie
) const {
425 EnumAllImportsStorage temp
= { callback
, cookie
};
426 return EnumImportChunks(ProcessImportChunk
, &temp
);
429 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback
,
430 PVOID cookie
) const {
431 PVOID directory
= GetImageDirectoryEntryAddr(
432 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
);
433 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
);
434 PImgDelayDescr delay_descriptor
= reinterpret_cast<PImgDelayDescr
>(directory
);
436 if (directory
== NULL
|| size
== 0)
439 for (; delay_descriptor
->rvaHmod
; delay_descriptor
++) {
440 PIMAGE_THUNK_DATA name_table
;
441 PIMAGE_THUNK_DATA iat
;
442 PIMAGE_THUNK_DATA bound_iat
; // address of the optional bound IAT
443 PIMAGE_THUNK_DATA unload_iat
; // address of optional copy of original IAT
446 // check if VC7-style imports, using RVAs instead of
447 // VC6-style addresses.
448 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
451 module_name
= reinterpret_cast<LPCSTR
>(
452 RVAToAddr(delay_descriptor
->rvaDLLName
));
453 name_table
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
454 RVAToAddr(delay_descriptor
->rvaINT
));
455 iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
456 RVAToAddr(delay_descriptor
->rvaIAT
));
457 bound_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
458 RVAToAddr(delay_descriptor
->rvaBoundIAT
));
459 unload_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
460 RVAToAddr(delay_descriptor
->rvaUnloadIAT
));
462 #pragma warning(push)
463 #pragma warning(disable: 4312)
464 // These casts generate warnings because they are 32 bit specific.
465 module_name
= reinterpret_cast<LPCSTR
>(delay_descriptor
->rvaDLLName
);
466 name_table
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
467 delay_descriptor
->rvaINT
);
468 iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(delay_descriptor
->rvaIAT
);
469 bound_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
470 delay_descriptor
->rvaBoundIAT
);
471 unload_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
472 delay_descriptor
->rvaUnloadIAT
);
476 if (!callback(*this, delay_descriptor
, module_name
, name_table
, iat
,
477 bound_iat
, unload_iat
, cookie
))
484 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback
,
485 PImgDelayDescr delay_descriptor
,
487 PIMAGE_THUNK_DATA name_table
,
488 PIMAGE_THUNK_DATA iat
,
489 PIMAGE_THUNK_DATA bound_iat
,
490 PIMAGE_THUNK_DATA unload_iat
,
491 PVOID cookie
) const {
492 UNREFERENCED_PARAMETER(bound_iat
);
493 UNREFERENCED_PARAMETER(unload_iat
);
495 for (; name_table
->u1
.Ordinal
; name_table
++, iat
++) {
500 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
501 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
503 PIMAGE_IMPORT_BY_NAME import
;
504 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
507 import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
508 RVAToAddr(name_table
->u1
.ForwarderString
));
510 #pragma warning(push)
511 #pragma warning(disable: 4312)
512 // This cast generates a warning because it is 32 bit specific.
513 import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
514 name_table
->u1
.ForwarderString
);
519 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
522 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
529 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback
,
530 PVOID cookie
) const {
531 EnumAllImportsStorage temp
= { callback
, cookie
};
532 return EnumDelayImportChunks(ProcessDelayImportChunk
, &temp
);
535 bool PEImage::VerifyMagic() const {
536 PIMAGE_DOS_HEADER dos_header
= GetDosHeader();
538 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
)
541 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
543 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
)
546 if (nt_headers
->FileHeader
.SizeOfOptionalHeader
!=
547 sizeof(IMAGE_OPTIONAL_HEADER
))
550 if (nt_headers
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
)
556 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva
, DWORD
*on_disk_offset
) const {
557 LPVOID address
= RVAToAddr(rva
);
558 return ImageAddrToOnDiskOffset(address
, on_disk_offset
);
561 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address
,
562 DWORD
*on_disk_offset
) const {
566 // Get the section that this address belongs to.
567 PIMAGE_SECTION_HEADER section_header
= GetImageSectionFromAddr(address
);
568 if (NULL
== section_header
)
571 // Don't follow the virtual RVAToAddr, use the one on the base.
572 DWORD offset_within_section
=
573 static_cast<DWORD
>(reinterpret_cast<uintptr_t>(address
)) -
574 static_cast<DWORD
>(reinterpret_cast<uintptr_t>(
575 PEImage::RVAToAddr(section_header
->VirtualAddress
)));
577 *on_disk_offset
= section_header
->PointerToRawData
+ offset_within_section
;
581 PVOID
PEImage::RVAToAddr(DWORD rva
) const {
585 return reinterpret_cast<char*>(module_
) + rva
;
588 PVOID
PEImageAsData::RVAToAddr(DWORD rva
) const {
592 PVOID in_memory
= PEImage::RVAToAddr(rva
);
595 if (!ImageAddrToOnDiskOffset(in_memory
, &disk_offset
))
598 return PEImage::RVAToAddr(disk_offset
);