blob: 864b675ae8d4bcd5a403bc4aa85dac2b04cd9c56 [file] [log] [blame]
license.botf003cfe2008-08-24 09:55:55 +09001// 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.
initial.commit3f4a7322008-07-27 06:49:38 +09004
5// This file implements PEImage, a generic class to manipulate PE files.
6// This file was adapted from GreenBorder's Code.
7
8#include "base/pe_image.h"
9
maruel@google.coma855b0f2008-07-30 22:02:03 +090010#ifdef _WIN64
11#error This code is not tested on x64. Please make sure all the base unit tests\
12 pass before doing any real work. The current unit tests don't test the\
13 differences between 32- and 64-bits implementations. Bugs may slip through.\
14 You need to improve the coverage before continuing.
15#endif
16
initial.commit3f4a7322008-07-27 06:49:38 +090017// Structure to perform imports enumerations.
18struct EnumAllImportsStorage {
19 PEImage::EnumImportsFunction callback;
20 PVOID cookie;
21};
22
23// Callback used to enumerate imports. See EnumImportChunksFunction.
24bool ProcessImportChunk(const PEImage &image, LPCSTR module,
25 PIMAGE_THUNK_DATA name_table,
26 PIMAGE_THUNK_DATA iat, PVOID cookie) {
27 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
28 cookie);
29
30 return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
31 storage.cookie);
32}
33
34// Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
35bool ProcessDelayImportChunk(const PEImage &image,
36 PImgDelayDescr delay_descriptor,
37 LPCSTR module, PIMAGE_THUNK_DATA name_table,
38 PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat,
39 PIMAGE_THUNK_DATA unload_iat, PVOID cookie) {
40 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
41 cookie);
42
43 return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
44 module, name_table, iat, bound_iat,
45 unload_iat, storage.cookie);
46}
47
48void PEImage::set_module(HMODULE module) {
49 module_ = module;
50}
51
52PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
53 return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
54}
55
56PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
57 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
58
59 return reinterpret_cast<PIMAGE_NT_HEADERS>(
60 reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
61}
62
63PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
64 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
65 PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
66
67 if (section < nt_headers->FileHeader.NumberOfSections)
68 return first_section + section;
69 else
70 return NULL;
71}
72
73WORD PEImage::GetNumSections() const {
74 return GetNTHeaders()->FileHeader.NumberOfSections;
75}
76
77DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
78 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
79
80 return nt_headers->OptionalHeader.DataDirectory[directory].Size;
81}
82
83PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
84 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
85
86 return RVAToAddr(
87 nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
88}
89
90PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
91 PBYTE target = reinterpret_cast<PBYTE>(address);
92 PIMAGE_SECTION_HEADER section;
93
94 for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) {
95 // Don't use the virtual RVAToAddr.
96 PBYTE start = reinterpret_cast<PBYTE>(
97 PEImage::RVAToAddr(section->VirtualAddress));
98
99 DWORD size = section->Misc.VirtualSize;
100
101 if ((start <= target) && (start + size > target))
102 return section;
103 }
104
105 return NULL;
106}
107
108PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
109 LPCSTR section_name) const {
110 if (NULL == section_name)
111 return NULL;
112
113 PIMAGE_SECTION_HEADER ret = NULL;
114 int num_sections = GetNumSections();
115
116 for (int i = 0; i < num_sections; i++) {
117 PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
118 if (0 == _strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
119 sizeof(section->Name))) {
120 ret = section;
121 break;
122 }
123 }
124
125 return ret;
126}
127
128PDWORD PEImage::GetExportEntry(LPCSTR name) const {
129 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
130
131 if (NULL == exports)
132 return NULL;
133
134 WORD ordinal = 0;
135 if (!GetProcOrdinal(name, &ordinal))
136 return NULL;
137
138 PDWORD functions = reinterpret_cast<PDWORD>(
139 RVAToAddr(exports->AddressOfFunctions));
140
141 return functions + ordinal - exports->Base;
142}
143
144FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
145 PDWORD export_entry = GetExportEntry(function_name);
146 if (NULL == export_entry)
147 return NULL;
148
149 PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
150
151 PBYTE exports = reinterpret_cast<PBYTE>(
152 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
153 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
154
155 // Check for forwarded exports as a special case.
156 if (exports <= function && exports + size > function)
157#pragma warning(push)
158#pragma warning(disable: 4312)
159 // This cast generates a warning because it is 32 bit specific.
160 return reinterpret_cast<FARPROC>(0xFFFFFFFF);
161#pragma warning(pop)
162
163 return reinterpret_cast<FARPROC>(function);
164}
165
166bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
167 if (NULL == ordinal)
168 return false;
169
170 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
171
172 if (NULL == exports)
173 return false;
174
175 if (IsOrdinal(function_name)) {
176 *ordinal = ToOrdinal(function_name);
177 } else {
178 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
179 PDWORD lower = names;
180 PDWORD upper = names + exports->NumberOfNames;
181 int cmp = -1;
182
183 // Binary Search for the name.
184 while (lower != upper) {
185 PDWORD middle = lower + (upper - lower) / 2;
186 LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
187
188 cmp = strcmp(function_name, name);
189
190 if (cmp == 0) {
191 lower = middle;
192 break;
193 }
194
195 if (cmp > 0)
196 lower = middle + 1;
197 else
198 upper = middle;
199 }
200
201 if (cmp != 0)
202 return false;
203
204
205 PWORD ordinals = reinterpret_cast<PWORD>(
206 RVAToAddr(exports->AddressOfNameOrdinals));
207
208 *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
209 }
210
211 return true;
212}
213
214bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
215 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
216 UINT num_sections = nt_headers->FileHeader.NumberOfSections;
217 PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
218
219 for (UINT i = 0; i < num_sections; i++, section++) {
220 PVOID section_start = RVAToAddr(section->VirtualAddress);
221 DWORD size = section->Misc.VirtualSize;
222
223 if (!callback(*this, section, section_start, size, cookie))
224 return false;
225 }
226
227 return true;
228}
229
230bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
231 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
232 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
233
234 // Check if there are any exports at all.
235 if (NULL == directory || 0 == size)
236 return true;
237
238 PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
239 directory);
240 UINT ordinal_base = exports->Base;
241 UINT num_funcs = exports->NumberOfFunctions;
242 UINT num_names = exports->NumberOfNames;
243 PDWORD functions = reinterpret_cast<PDWORD>(RVAToAddr(
244 exports->AddressOfFunctions));
245 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
246 PWORD ordinals = reinterpret_cast<PWORD>(RVAToAddr(
247 exports->AddressOfNameOrdinals));
248
249 for (UINT count = 0; count < num_funcs; count++) {
250 PVOID func = RVAToAddr(functions[count]);
251 if (NULL == func)
252 continue;
253
254 // Check for a name.
255 LPCSTR name = NULL;
256 UINT hint;
257 for (hint = 0; hint < num_names; hint++) {
258 if (ordinals[hint] == count) {
259 name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
260 break;
261 }
262 }
263
264 if (name == NULL)
265 hint = 0;
266
267 // Check for forwarded exports.
268 LPCSTR forward = NULL;
269 if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
270 reinterpret_cast<char*>(func) <= reinterpret_cast<char*>(directory) +
271 size) {
272 forward = reinterpret_cast<LPCSTR>(func);
273 func = 0;
274 }
275
276 if (!callback(*this, ordinal_base + count, hint, name, func, forward,
277 cookie))
278 return false;
279 }
280
281 return true;
282}
283
284bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
285 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
286 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
287 PIMAGE_BASE_RELOCATION base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
288 directory);
289
290 if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
291 return true;
292
293 while (base->SizeOfBlock) {
294 PWORD reloc = reinterpret_cast<PWORD>(base + 1);
295 UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
296 sizeof(WORD);
297
298 for (UINT i = 0; i < num_relocs; i++, reloc++) {
299 WORD type = *reloc >> 12;
300 PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
301
302 if (!callback(*this, type, address, cookie))
303 return false;
304 }
305
306 base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
307 reinterpret_cast<char*>(base) + base->SizeOfBlock);
308 }
309
310 return true;
311}
312
313bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
314 PVOID cookie) const {
315 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
316 PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
317
318 if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
319 return true;
320
321 for (; import->FirstThunk; import++) {
322 LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
323 PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
324 RVAToAddr(import->OriginalFirstThunk));
325 PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
326 RVAToAddr(import->FirstThunk));
327
328 if (!callback(*this, module_name, name_table, iat, cookie))
329 return false;
330 }
331
332 return true;
333}
334
335bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
336 LPCSTR module_name,
337 PIMAGE_THUNK_DATA name_table,
338 PIMAGE_THUNK_DATA iat, PVOID cookie) const {
339 if (NULL == name_table)
340 return false;
341
342 for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
343 LPCSTR name = NULL;
344 WORD ordinal = 0;
345 WORD hint = 0;
346
347 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
348 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
349 } else {
350 PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
351 RVAToAddr(name_table->u1.ForwarderString));
352
353 hint = import->Hint;
354 name = reinterpret_cast<LPCSTR>(&import->Name);
355 }
356
357 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
358 return false;
359 }
360
361 return true;
362}
363
364bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
365 EnumAllImportsStorage temp = { callback, cookie };
366 return EnumImportChunks(ProcessImportChunk, &temp);
367}
368
369bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
370 PVOID cookie) const {
371 PVOID directory = GetImageDirectoryEntryAddr(
372 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
373 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
374 PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
375
376 if (directory == NULL || size == 0)
377 return true;
378
379 for (; delay_descriptor->rvaHmod; delay_descriptor++) {
380 PIMAGE_THUNK_DATA name_table;
381 PIMAGE_THUNK_DATA iat;
382 PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT
383 PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT
384 LPCSTR module_name;
385
386 // check if VC7-style imports, using RVAs instead of
387 // VC6-style addresses.
388 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
389
390 if (rvas) {
391 module_name = reinterpret_cast<LPCSTR>(
392 RVAToAddr(delay_descriptor->rvaDLLName));
393 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
394 RVAToAddr(delay_descriptor->rvaINT));
395 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
396 RVAToAddr(delay_descriptor->rvaIAT));
397 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
398 RVAToAddr(delay_descriptor->rvaBoundIAT));
399 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
400 RVAToAddr(delay_descriptor->rvaUnloadIAT));
401 } else {
402#pragma warning(push)
403#pragma warning(disable: 4312)
404 // These casts generate warnings because they are 32 bit specific.
405 module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
406 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
407 delay_descriptor->rvaINT);
408 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
409 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
410 delay_descriptor->rvaBoundIAT);
411 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
412 delay_descriptor->rvaUnloadIAT);
413#pragma warning(pop)
414 }
415
416 if (!callback(*this, delay_descriptor, module_name, name_table, iat,
417 bound_iat, unload_iat, cookie))
418 return false;
419 }
420
421 return true;
422}
423
424bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
425 PImgDelayDescr delay_descriptor,
426 LPCSTR module_name,
427 PIMAGE_THUNK_DATA name_table,
428 PIMAGE_THUNK_DATA iat,
429 PIMAGE_THUNK_DATA bound_iat,
430 PIMAGE_THUNK_DATA unload_iat,
431 PVOID cookie) const {
432 UNREFERENCED_PARAMETER(bound_iat);
433 UNREFERENCED_PARAMETER(unload_iat);
434
435 for (; name_table->u1.Ordinal; name_table++, iat++) {
436 LPCSTR name = NULL;
437 WORD ordinal = 0;
438 WORD hint = 0;
439
440 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
441 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
442 } else {
443 PIMAGE_IMPORT_BY_NAME import;
444 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
445
446 if (rvas) {
447 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
448 RVAToAddr(name_table->u1.ForwarderString));
449 } else {
450#pragma warning(push)
451#pragma warning(disable: 4312)
452 // This cast generates a warning because it is 32 bit specific.
453 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
454 name_table->u1.ForwarderString);
455#pragma warning(pop)
456 }
457
458 hint = import->Hint;
459 name = reinterpret_cast<LPCSTR>(&import->Name);
460 }
461
462 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
463 return false;
464 }
465
466 return true;
467}
468
469bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
470 PVOID cookie) const {
471 EnumAllImportsStorage temp = { callback, cookie };
472 return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
473}
474
475bool PEImage::VerifyMagic() const {
476 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
477
478 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
479 return false;
480
481 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
482
483 if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
484 return false;
485
486 if (nt_headers->FileHeader.SizeOfOptionalHeader !=
487 sizeof(IMAGE_OPTIONAL_HEADER))
488 return false;
489
490 if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
491 return false;
492
493 return true;
494}
495
496bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
497 LPVOID address = RVAToAddr(rva);
498 return ImageAddrToOnDiskOffset(address, on_disk_offset);
499}
500
501bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
502 DWORD *on_disk_offset) const {
503 if (NULL == address)
504 return false;
505
506 // Get the section that this address belongs to.
507 PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
508 if (NULL == section_header)
509 return false;
510
511#pragma warning(push)
512#pragma warning(disable: 4311)
513 // These casts generate warnings because they are 32 bit specific.
514 // Don't follow the virtual RVAToAddr, use the one on the base.
515 DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
516 reinterpret_cast<DWORD>(PEImage::RVAToAddr(
517 section_header->VirtualAddress));
518#pragma warning(pop)
519
520 *on_disk_offset = section_header->PointerToRawData + offset_within_section;
521 return true;
522}
523
maruel@google.coma855b0f2008-07-30 22:02:03 +0900524PVOID PEImage::RVAToAddr(DWORD_PTR rva) const {
initial.commit3f4a7322008-07-27 06:49:38 +0900525 if (rva == 0)
526 return NULL;
527
528 return reinterpret_cast<char*>(module_) + rva;
529}
530
maruel@google.coma855b0f2008-07-30 22:02:03 +0900531PVOID PEImageAsData::RVAToAddr(DWORD_PTR rva) const {
initial.commit3f4a7322008-07-27 06:49:38 +0900532 if (rva == 0)
533 return NULL;
534
535 PVOID in_memory = PEImage::RVAToAddr(rva);
536 DWORD dummy;
537
538 if (!ImageAddrToOnDiskOffset(in_memory, &dummy))
539 return NULL;
540
541 return in_memory;
542}
license.botf003cfe2008-08-24 09:55:55 +0900543