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
;
23 // Compare two strings byte by byte on an unsigned basis.
24 // if s1 == s2, return 0
25 // if s1 < s2, return negative
26 // if s1 > s2, return positive
27 // Exception if inputs are invalid.
28 int StrCmpByByte(LPCSTR s1
, LPCSTR s2
) {
29 while (*s1
!= '\0' && *s1
== *s2
) {
34 return (*reinterpret_cast<const unsigned char*>(s1
) -
35 *reinterpret_cast<const unsigned char*>(s2
));
40 // Callback used to enumerate imports. See EnumImportChunksFunction.
41 bool ProcessImportChunk(const PEImage
&image
, LPCSTR module
,
42 PIMAGE_THUNK_DATA name_table
,
43 PIMAGE_THUNK_DATA iat
, PVOID cookie
) {
44 EnumAllImportsStorage
&storage
= *reinterpret_cast<EnumAllImportsStorage
*>(
47 return image
.EnumOneImportChunk(storage
.callback
, module
, name_table
, iat
,
51 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
52 bool ProcessDelayImportChunk(const PEImage
&image
,
53 PImgDelayDescr delay_descriptor
,
54 LPCSTR module
, PIMAGE_THUNK_DATA name_table
,
55 PIMAGE_THUNK_DATA iat
, PIMAGE_THUNK_DATA bound_iat
,
56 PIMAGE_THUNK_DATA unload_iat
, PVOID cookie
) {
57 EnumAllImportsStorage
&storage
= *reinterpret_cast<EnumAllImportsStorage
*>(
60 return image
.EnumOneDelayImportChunk(storage
.callback
, delay_descriptor
,
61 module
, name_table
, iat
, bound_iat
,
62 unload_iat
, storage
.cookie
);
65 void PEImage::set_module(HMODULE module
) {
69 PIMAGE_DOS_HEADER
PEImage::GetDosHeader() const {
70 return reinterpret_cast<PIMAGE_DOS_HEADER
>(module_
);
73 PIMAGE_NT_HEADERS
PEImage::GetNTHeaders() const {
74 PIMAGE_DOS_HEADER dos_header
= GetDosHeader();
76 return reinterpret_cast<PIMAGE_NT_HEADERS
>(
77 reinterpret_cast<char*>(dos_header
) + dos_header
->e_lfanew
);
80 PIMAGE_SECTION_HEADER
PEImage::GetSectionHeader(UINT section
) const {
81 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
82 PIMAGE_SECTION_HEADER first_section
= IMAGE_FIRST_SECTION(nt_headers
);
84 if (section
< nt_headers
->FileHeader
.NumberOfSections
)
85 return first_section
+ section
;
90 WORD
PEImage::GetNumSections() const {
91 return GetNTHeaders()->FileHeader
.NumberOfSections
;
94 DWORD
PEImage::GetImageDirectoryEntrySize(UINT directory
) const {
95 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
97 return nt_headers
->OptionalHeader
.DataDirectory
[directory
].Size
;
100 PVOID
PEImage::GetImageDirectoryEntryAddr(UINT directory
) const {
101 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
104 nt_headers
->OptionalHeader
.DataDirectory
[directory
].VirtualAddress
);
107 PIMAGE_SECTION_HEADER
PEImage::GetImageSectionFromAddr(PVOID address
) const {
108 PBYTE target
= reinterpret_cast<PBYTE
>(address
);
109 PIMAGE_SECTION_HEADER section
;
111 for (UINT i
= 0; NULL
!= (section
= GetSectionHeader(i
)); i
++) {
112 // Don't use the virtual RVAToAddr.
113 PBYTE start
= reinterpret_cast<PBYTE
>(
114 PEImage::RVAToAddr(section
->VirtualAddress
));
116 DWORD size
= section
->Misc
.VirtualSize
;
118 if ((start
<= target
) && (start
+ size
> target
))
125 PIMAGE_SECTION_HEADER
PEImage::GetImageSectionHeaderByName(
126 LPCSTR section_name
) const {
127 if (NULL
== section_name
)
130 PIMAGE_SECTION_HEADER ret
= NULL
;
131 int num_sections
= GetNumSections();
133 for (int i
= 0; i
< num_sections
; i
++) {
134 PIMAGE_SECTION_HEADER section
= GetSectionHeader(i
);
135 if (0 == _strnicmp(reinterpret_cast<LPCSTR
>(section
->Name
), section_name
,
136 sizeof(section
->Name
))) {
145 PDWORD
PEImage::GetExportEntry(LPCSTR name
) const {
146 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
152 if (!GetProcOrdinal(name
, &ordinal
))
155 PDWORD functions
= reinterpret_cast<PDWORD
>(
156 RVAToAddr(exports
->AddressOfFunctions
));
158 return functions
+ ordinal
- exports
->Base
;
161 FARPROC
PEImage::GetProcAddress(LPCSTR function_name
) const {
162 PDWORD export_entry
= GetExportEntry(function_name
);
163 if (NULL
== export_entry
)
166 PBYTE function
= reinterpret_cast<PBYTE
>(RVAToAddr(*export_entry
));
168 PBYTE exports
= reinterpret_cast<PBYTE
>(
169 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT
));
170 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT
);
172 // Check for forwarded exports as a special case.
173 if (exports
<= function
&& exports
+ size
> function
)
174 #pragma warning(push)
175 #pragma warning(disable: 4312)
176 // This cast generates a warning because it is 32 bit specific.
177 return reinterpret_cast<FARPROC
>(0xFFFFFFFF);
180 return reinterpret_cast<FARPROC
>(function
);
183 bool PEImage::GetProcOrdinal(LPCSTR function_name
, WORD
*ordinal
) const {
187 PIMAGE_EXPORT_DIRECTORY exports
= GetExportDirectory();
192 if (IsOrdinal(function_name
)) {
193 *ordinal
= ToOrdinal(function_name
);
195 PDWORD names
= reinterpret_cast<PDWORD
>(RVAToAddr(exports
->AddressOfNames
));
196 PDWORD lower
= names
;
197 PDWORD upper
= names
+ exports
->NumberOfNames
;
200 // Binary Search for the name.
201 while (lower
!= upper
) {
202 PDWORD middle
= lower
+ (upper
- lower
) / 2;
203 LPCSTR name
= reinterpret_cast<LPCSTR
>(RVAToAddr(*middle
));
205 // This may be called by sandbox before MSVCRT dll loads, so can't use
206 // CRT function here.
207 cmp
= StrCmpByByte(function_name
, name
);
224 PWORD ordinals
= reinterpret_cast<PWORD
>(
225 RVAToAddr(exports
->AddressOfNameOrdinals
));
227 *ordinal
= ordinals
[lower
- names
] + static_cast<WORD
>(exports
->Base
);
233 bool PEImage::EnumSections(EnumSectionsFunction callback
, PVOID cookie
) const {
234 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
235 UINT num_sections
= nt_headers
->FileHeader
.NumberOfSections
;
236 PIMAGE_SECTION_HEADER section
= GetSectionHeader(0);
238 for (UINT i
= 0; i
< num_sections
; i
++, section
++) {
239 PVOID section_start
= RVAToAddr(section
->VirtualAddress
);
240 DWORD size
= section
->Misc
.VirtualSize
;
242 if (!callback(*this, section
, section_start
, size
, cookie
))
249 bool PEImage::EnumExports(EnumExportsFunction callback
, PVOID cookie
) const {
250 PVOID directory
= GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT
);
251 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT
);
253 // Check if there are any exports at all.
254 if (NULL
== directory
|| 0 == size
)
257 PIMAGE_EXPORT_DIRECTORY exports
= reinterpret_cast<PIMAGE_EXPORT_DIRECTORY
>(
259 UINT ordinal_base
= exports
->Base
;
260 UINT num_funcs
= exports
->NumberOfFunctions
;
261 UINT num_names
= exports
->NumberOfNames
;
262 PDWORD functions
= reinterpret_cast<PDWORD
>(RVAToAddr(
263 exports
->AddressOfFunctions
));
264 PDWORD names
= reinterpret_cast<PDWORD
>(RVAToAddr(exports
->AddressOfNames
));
265 PWORD ordinals
= reinterpret_cast<PWORD
>(RVAToAddr(
266 exports
->AddressOfNameOrdinals
));
268 for (UINT count
= 0; count
< num_funcs
; count
++) {
269 PVOID func
= RVAToAddr(functions
[count
]);
276 for (hint
= 0; hint
< num_names
; hint
++) {
277 if (ordinals
[hint
] == count
) {
278 name
= reinterpret_cast<LPCSTR
>(RVAToAddr(names
[hint
]));
286 // Check for forwarded exports.
287 LPCSTR forward
= NULL
;
288 if (reinterpret_cast<char*>(func
) >= reinterpret_cast<char*>(directory
) &&
289 reinterpret_cast<char*>(func
) <= reinterpret_cast<char*>(directory
) +
291 forward
= reinterpret_cast<LPCSTR
>(func
);
295 if (!callback(*this, ordinal_base
+ count
, hint
, name
, func
, forward
,
303 bool PEImage::EnumRelocs(EnumRelocsFunction callback
, PVOID cookie
) const {
304 PVOID directory
= GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC
);
305 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC
);
306 PIMAGE_BASE_RELOCATION base
= reinterpret_cast<PIMAGE_BASE_RELOCATION
>(
312 while (size
>= sizeof(IMAGE_BASE_RELOCATION
) && base
->SizeOfBlock
&&
313 size
>= base
->SizeOfBlock
) {
314 PWORD reloc
= reinterpret_cast<PWORD
>(base
+ 1);
315 UINT num_relocs
= (base
->SizeOfBlock
- sizeof(IMAGE_BASE_RELOCATION
)) /
318 for (UINT i
= 0; i
< num_relocs
; i
++, reloc
++) {
319 WORD type
= *reloc
>> 12;
320 PVOID address
= RVAToAddr(base
->VirtualAddress
+ (*reloc
& 0x0FFF));
322 if (!callback(*this, type
, address
, cookie
))
326 size
-= base
->SizeOfBlock
;
327 base
= reinterpret_cast<PIMAGE_BASE_RELOCATION
>(
328 reinterpret_cast<char*>(base
) + base
->SizeOfBlock
);
334 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback
,
335 PVOID cookie
) const {
336 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT
);
337 PIMAGE_IMPORT_DESCRIPTOR import
= GetFirstImportChunk();
339 if (import
== NULL
|| size
< sizeof(IMAGE_IMPORT_DESCRIPTOR
))
342 for (; import
->FirstThunk
; import
++) {
343 LPCSTR module_name
= reinterpret_cast<LPCSTR
>(RVAToAddr(import
->Name
));
344 PIMAGE_THUNK_DATA name_table
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
345 RVAToAddr(import
->OriginalFirstThunk
));
346 PIMAGE_THUNK_DATA iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
347 RVAToAddr(import
->FirstThunk
));
349 if (!callback(*this, module_name
, name_table
, iat
, cookie
))
356 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback
,
358 PIMAGE_THUNK_DATA name_table
,
359 PIMAGE_THUNK_DATA iat
, PVOID cookie
) const {
360 if (NULL
== name_table
)
363 for (; name_table
&& name_table
->u1
.Ordinal
; name_table
++, iat
++) {
368 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
369 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
371 PIMAGE_IMPORT_BY_NAME import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
372 RVAToAddr(name_table
->u1
.ForwarderString
));
375 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
378 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
385 bool PEImage::EnumAllImports(EnumImportsFunction callback
, PVOID cookie
) const {
386 EnumAllImportsStorage temp
= { callback
, cookie
};
387 return EnumImportChunks(ProcessImportChunk
, &temp
);
390 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback
,
391 PVOID cookie
) const {
392 PVOID directory
= GetImageDirectoryEntryAddr(
393 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
);
394 DWORD size
= GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
);
395 PImgDelayDescr delay_descriptor
= reinterpret_cast<PImgDelayDescr
>(directory
);
397 if (directory
== NULL
|| size
== 0)
400 for (; delay_descriptor
->rvaHmod
; delay_descriptor
++) {
401 PIMAGE_THUNK_DATA name_table
;
402 PIMAGE_THUNK_DATA iat
;
403 PIMAGE_THUNK_DATA bound_iat
; // address of the optional bound IAT
404 PIMAGE_THUNK_DATA unload_iat
; // address of optional copy of original IAT
407 // check if VC7-style imports, using RVAs instead of
408 // VC6-style addresses.
409 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
412 module_name
= reinterpret_cast<LPCSTR
>(
413 RVAToAddr(delay_descriptor
->rvaDLLName
));
414 name_table
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
415 RVAToAddr(delay_descriptor
->rvaINT
));
416 iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
417 RVAToAddr(delay_descriptor
->rvaIAT
));
418 bound_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
419 RVAToAddr(delay_descriptor
->rvaBoundIAT
));
420 unload_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
421 RVAToAddr(delay_descriptor
->rvaUnloadIAT
));
423 #pragma warning(push)
424 #pragma warning(disable: 4312)
425 // These casts generate warnings because they are 32 bit specific.
426 module_name
= reinterpret_cast<LPCSTR
>(delay_descriptor
->rvaDLLName
);
427 name_table
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
428 delay_descriptor
->rvaINT
);
429 iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(delay_descriptor
->rvaIAT
);
430 bound_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
431 delay_descriptor
->rvaBoundIAT
);
432 unload_iat
= reinterpret_cast<PIMAGE_THUNK_DATA
>(
433 delay_descriptor
->rvaUnloadIAT
);
437 if (!callback(*this, delay_descriptor
, module_name
, name_table
, iat
,
438 bound_iat
, unload_iat
, cookie
))
445 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback
,
446 PImgDelayDescr delay_descriptor
,
448 PIMAGE_THUNK_DATA name_table
,
449 PIMAGE_THUNK_DATA iat
,
450 PIMAGE_THUNK_DATA bound_iat
,
451 PIMAGE_THUNK_DATA unload_iat
,
452 PVOID cookie
) const {
453 UNREFERENCED_PARAMETER(bound_iat
);
454 UNREFERENCED_PARAMETER(unload_iat
);
456 for (; name_table
->u1
.Ordinal
; name_table
++, iat
++) {
461 if (IMAGE_SNAP_BY_ORDINAL(name_table
->u1
.Ordinal
)) {
462 ordinal
= static_cast<WORD
>(IMAGE_ORDINAL32(name_table
->u1
.Ordinal
));
464 PIMAGE_IMPORT_BY_NAME import
;
465 bool rvas
= (delay_descriptor
->grAttrs
& dlattrRva
) != 0;
468 import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
469 RVAToAddr(name_table
->u1
.ForwarderString
));
471 #pragma warning(push)
472 #pragma warning(disable: 4312)
473 // This cast generates a warning because it is 32 bit specific.
474 import
= reinterpret_cast<PIMAGE_IMPORT_BY_NAME
>(
475 name_table
->u1
.ForwarderString
);
480 name
= reinterpret_cast<LPCSTR
>(&import
->Name
);
483 if (!callback(*this, module_name
, ordinal
, name
, hint
, iat
, cookie
))
490 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback
,
491 PVOID cookie
) const {
492 EnumAllImportsStorage temp
= { callback
, cookie
};
493 return EnumDelayImportChunks(ProcessDelayImportChunk
, &temp
);
496 bool PEImage::VerifyMagic() const {
497 PIMAGE_DOS_HEADER dos_header
= GetDosHeader();
499 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
)
502 PIMAGE_NT_HEADERS nt_headers
= GetNTHeaders();
504 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
)
507 if (nt_headers
->FileHeader
.SizeOfOptionalHeader
!=
508 sizeof(IMAGE_OPTIONAL_HEADER
))
511 if (nt_headers
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
)
517 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva
, DWORD
*on_disk_offset
) const {
518 LPVOID address
= RVAToAddr(rva
);
519 return ImageAddrToOnDiskOffset(address
, on_disk_offset
);
522 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address
,
523 DWORD
*on_disk_offset
) const {
527 // Get the section that this address belongs to.
528 PIMAGE_SECTION_HEADER section_header
= GetImageSectionFromAddr(address
);
529 if (NULL
== section_header
)
532 #pragma warning(push)
533 #pragma warning(disable: 4311)
534 // These casts generate warnings because they are 32 bit specific.
535 // Don't follow the virtual RVAToAddr, use the one on the base.
536 DWORD offset_within_section
= reinterpret_cast<DWORD
>(address
) -
537 reinterpret_cast<DWORD
>(PEImage::RVAToAddr(
538 section_header
->VirtualAddress
));
541 *on_disk_offset
= section_header
->PointerToRawData
+ offset_within_section
;
545 PVOID
PEImage::RVAToAddr(DWORD rva
) const {
549 return reinterpret_cast<char*>(module_
) + rva
;
552 PVOID
PEImageAsData::RVAToAddr(DWORD rva
) const {
556 PVOID in_memory
= PEImage::RVAToAddr(rva
);
559 if (!ImageAddrToOnDiskOffset(in_memory
, &disk_offset
))
562 return PEImage::RVAToAddr(disk_offset
);