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.
4
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/win/pe_image.h"
9
10 namespace base {
11 namespace win {
12
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.")
20 #endif
21
22 // Structure to perform imports enumerations.
23 struct EnumAllImportsStorage {
24 PEImage::EnumImportsFunction callback;
25 PVOID cookie;
26 };
27
28 namespace {
29
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.
StrCmpByByte(LPCSTR s1,LPCSTR s2)35 int StrCmpByByte(LPCSTR s1, LPCSTR s2) {
36 while (*s1 != '\0' && *s1 == *s2) {
37 ++s1;
38 ++s2;
39 }
40
41 return (*reinterpret_cast<const unsigned char*>(s1) -
42 *reinterpret_cast<const unsigned char*>(s2));
43 }
44
45 } // namespace
46
47 // Callback used to enumerate imports. See EnumImportChunksFunction.
ProcessImportChunk(const PEImage & image,LPCSTR module,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PVOID cookie)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*>(
52 cookie);
53
54 return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
55 storage.cookie);
56 }
57
58 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
ProcessDelayImportChunk(const PEImage & image,PImgDelayDescr delay_descriptor,LPCSTR module,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PIMAGE_THUNK_DATA bound_iat,PIMAGE_THUNK_DATA unload_iat,PVOID cookie)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*>(
65 cookie);
66
67 return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
68 module, name_table, iat, bound_iat,
69 unload_iat, storage.cookie);
70 }
71
set_module(HMODULE module)72 void PEImage::set_module(HMODULE module) {
73 module_ = module;
74 }
75
GetDosHeader() const76 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
77 return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
78 }
79
GetNTHeaders() const80 PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
81 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
82
83 return reinterpret_cast<PIMAGE_NT_HEADERS>(
84 reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
85 }
86
GetSectionHeader(UINT section) const87 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);
90
91 if (section < nt_headers->FileHeader.NumberOfSections)
92 return first_section + section;
93 else
94 return NULL;
95 }
96
GetNumSections() const97 WORD PEImage::GetNumSections() const {
98 return GetNTHeaders()->FileHeader.NumberOfSections;
99 }
100
GetImageDirectoryEntrySize(UINT directory) const101 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
102 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
103
104 return nt_headers->OptionalHeader.DataDirectory[directory].Size;
105 }
106
GetImageDirectoryEntryAddr(UINT directory) const107 PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
108 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
109
110 return RVAToAddr(
111 nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
112 }
113
GetImageSectionFromAddr(PVOID address) const114 PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
115 PBYTE target = reinterpret_cast<PBYTE>(address);
116 PIMAGE_SECTION_HEADER section;
117
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));
122
123 DWORD size = section->Misc.VirtualSize;
124
125 if ((start <= target) && (start + size > target))
126 return section;
127 }
128
129 return NULL;
130 }
131
GetImageSectionHeaderByName(LPCSTR section_name) const132 PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
133 LPCSTR section_name) const {
134 if (NULL == section_name)
135 return NULL;
136
137 PIMAGE_SECTION_HEADER ret = NULL;
138 int num_sections = GetNumSections();
139
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))) {
144 ret = section;
145 break;
146 }
147 }
148
149 return ret;
150 }
151
GetExportEntry(LPCSTR name) const152 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
153 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
154
155 if (NULL == exports)
156 return NULL;
157
158 WORD ordinal = 0;
159 if (!GetProcOrdinal(name, &ordinal))
160 return NULL;
161
162 PDWORD functions = reinterpret_cast<PDWORD>(
163 RVAToAddr(exports->AddressOfFunctions));
164
165 return functions + ordinal - exports->Base;
166 }
167
GetProcAddress(LPCSTR function_name) const168 FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
169 PDWORD export_entry = GetExportEntry(function_name);
170 if (NULL == export_entry)
171 return NULL;
172
173 PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
174
175 PBYTE exports = reinterpret_cast<PBYTE>(
176 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
177 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
178
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);
185 #pragma warning(pop)
186
187 return reinterpret_cast<FARPROC>(function);
188 }
189
GetProcOrdinal(LPCSTR function_name,WORD * ordinal) const190 bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
191 if (NULL == ordinal)
192 return false;
193
194 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
195
196 if (NULL == exports)
197 return false;
198
199 if (IsOrdinal(function_name)) {
200 *ordinal = ToOrdinal(function_name);
201 } else {
202 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
203 PDWORD lower = names;
204 PDWORD upper = names + exports->NumberOfNames;
205 int cmp = -1;
206
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));
211
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);
215
216 if (cmp == 0) {
217 lower = middle;
218 break;
219 }
220
221 if (cmp > 0)
222 lower = middle + 1;
223 else
224 upper = middle;
225 }
226
227 if (cmp != 0)
228 return false;
229
230
231 PWORD ordinals = reinterpret_cast<PWORD>(
232 RVAToAddr(exports->AddressOfNameOrdinals));
233
234 *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
235 }
236
237 return true;
238 }
239
EnumSections(EnumSectionsFunction callback,PVOID cookie) const240 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);
244
245 for (UINT i = 0; i < num_sections; i++, section++) {
246 PVOID section_start = RVAToAddr(section->VirtualAddress);
247 DWORD size = section->Misc.VirtualSize;
248
249 if (!callback(*this, section, section_start, size, cookie))
250 return false;
251 }
252
253 return true;
254 }
255
EnumExports(EnumExportsFunction callback,PVOID cookie) const256 bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
257 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
258 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
259
260 // Check if there are any exports at all.
261 if (NULL == directory || 0 == size)
262 return true;
263
264 PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
265 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));
274
275 for (UINT count = 0; count < num_funcs; count++) {
276 PVOID func = RVAToAddr(functions[count]);
277 if (NULL == func)
278 continue;
279
280 // Check for a name.
281 LPCSTR name = NULL;
282 UINT hint;
283 for (hint = 0; hint < num_names; hint++) {
284 if (ordinals[hint] == count) {
285 name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
286 break;
287 }
288 }
289
290 if (name == NULL)
291 hint = 0;
292
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) +
297 size) {
298 forward = reinterpret_cast<LPCSTR>(func);
299 func = 0;
300 }
301
302 if (!callback(*this, ordinal_base + count, hint, name, func, forward,
303 cookie))
304 return false;
305 }
306
307 return true;
308 }
309
EnumRelocs(EnumRelocsFunction callback,PVOID cookie) const310 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>(
314 directory);
315
316 if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
317 return true;
318
319 while (base->SizeOfBlock) {
320 PWORD reloc = reinterpret_cast<PWORD>(base + 1);
321 UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
322 sizeof(WORD);
323
324 for (UINT i = 0; i < num_relocs; i++, reloc++) {
325 WORD type = *reloc >> 12;
326 PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
327
328 if (!callback(*this, type, address, cookie))
329 return false;
330 }
331
332 base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
333 reinterpret_cast<char*>(base) + base->SizeOfBlock);
334 }
335
336 return true;
337 }
338
EnumImportChunks(EnumImportChunksFunction callback,PVOID cookie) const339 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
340 PVOID cookie) const {
341 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
342 PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
343
344 if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
345 return true;
346
347 for (; import->FirstThunk; import++) {
348 LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
349 PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
350 RVAToAddr(import->OriginalFirstThunk));
351 PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
352 RVAToAddr(import->FirstThunk));
353
354 if (!callback(*this, module_name, name_table, iat, cookie))
355 return false;
356 }
357
358 return true;
359 }
360
EnumOneImportChunk(EnumImportsFunction callback,LPCSTR module_name,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PVOID cookie) const361 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
362 LPCSTR module_name,
363 PIMAGE_THUNK_DATA name_table,
364 PIMAGE_THUNK_DATA iat, PVOID cookie) const {
365 if (NULL == name_table)
366 return false;
367
368 for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
369 LPCSTR name = NULL;
370 WORD ordinal = 0;
371 WORD hint = 0;
372
373 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
374 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
375 } else {
376 PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
377 RVAToAddr(name_table->u1.ForwarderString));
378
379 hint = import->Hint;
380 name = reinterpret_cast<LPCSTR>(&import->Name);
381 }
382
383 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
384 return false;
385 }
386
387 return true;
388 }
389
EnumAllImports(EnumImportsFunction callback,PVOID cookie) const390 bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
391 EnumAllImportsStorage temp = { callback, cookie };
392 return EnumImportChunks(ProcessImportChunk, &temp);
393 }
394
EnumDelayImportChunks(EnumDelayImportChunksFunction callback,PVOID cookie) const395 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
396 PVOID cookie) const {
397 PVOID directory = GetImageDirectoryEntryAddr(
398 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
399 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
400 PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
401
402 if (directory == NULL || size == 0)
403 return true;
404
405 for (; delay_descriptor->rvaHmod; delay_descriptor++) {
406 PIMAGE_THUNK_DATA name_table;
407 PIMAGE_THUNK_DATA iat;
408 PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT
409 PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT
410 LPCSTR module_name;
411
412 // check if VC7-style imports, using RVAs instead of
413 // VC6-style addresses.
414 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
415
416 if (rvas) {
417 module_name = reinterpret_cast<LPCSTR>(
418 RVAToAddr(delay_descriptor->rvaDLLName));
419 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
420 RVAToAddr(delay_descriptor->rvaINT));
421 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
422 RVAToAddr(delay_descriptor->rvaIAT));
423 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
424 RVAToAddr(delay_descriptor->rvaBoundIAT));
425 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
426 RVAToAddr(delay_descriptor->rvaUnloadIAT));
427 } else {
428 #pragma warning(push)
429 #pragma warning(disable: 4312)
430 // These casts generate warnings because they are 32 bit specific.
431 module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
432 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
433 delay_descriptor->rvaINT);
434 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
435 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
436 delay_descriptor->rvaBoundIAT);
437 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
438 delay_descriptor->rvaUnloadIAT);
439 #pragma warning(pop)
440 }
441
442 if (!callback(*this, delay_descriptor, module_name, name_table, iat,
443 bound_iat, unload_iat, cookie))
444 return false;
445 }
446
447 return true;
448 }
449
EnumOneDelayImportChunk(EnumImportsFunction callback,PImgDelayDescr delay_descriptor,LPCSTR module_name,PIMAGE_THUNK_DATA name_table,PIMAGE_THUNK_DATA iat,PIMAGE_THUNK_DATA bound_iat,PIMAGE_THUNK_DATA unload_iat,PVOID cookie) const450 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
451 PImgDelayDescr delay_descriptor,
452 LPCSTR module_name,
453 PIMAGE_THUNK_DATA name_table,
454 PIMAGE_THUNK_DATA iat,
455 PIMAGE_THUNK_DATA bound_iat,
456 PIMAGE_THUNK_DATA unload_iat,
457 PVOID cookie) const {
458 UNREFERENCED_PARAMETER(bound_iat);
459 UNREFERENCED_PARAMETER(unload_iat);
460
461 for (; name_table->u1.Ordinal; name_table++, iat++) {
462 LPCSTR name = NULL;
463 WORD ordinal = 0;
464 WORD hint = 0;
465
466 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
467 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
468 } else {
469 PIMAGE_IMPORT_BY_NAME import;
470 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
471
472 if (rvas) {
473 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
474 RVAToAddr(name_table->u1.ForwarderString));
475 } else {
476 #pragma warning(push)
477 #pragma warning(disable: 4312)
478 // This cast generates a warning because it is 32 bit specific.
479 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
480 name_table->u1.ForwarderString);
481 #pragma warning(pop)
482 }
483
484 hint = import->Hint;
485 name = reinterpret_cast<LPCSTR>(&import->Name);
486 }
487
488 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
489 return false;
490 }
491
492 return true;
493 }
494
EnumAllDelayImports(EnumImportsFunction callback,PVOID cookie) const495 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
496 PVOID cookie) const {
497 EnumAllImportsStorage temp = { callback, cookie };
498 return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
499 }
500
VerifyMagic() const501 bool PEImage::VerifyMagic() const {
502 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
503
504 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
505 return false;
506
507 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
508
509 if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
510 return false;
511
512 if (nt_headers->FileHeader.SizeOfOptionalHeader !=
513 sizeof(IMAGE_OPTIONAL_HEADER))
514 return false;
515
516 if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
517 return false;
518
519 return true;
520 }
521
ImageRVAToOnDiskOffset(DWORD rva,DWORD * on_disk_offset) const522 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
523 LPVOID address = RVAToAddr(rva);
524 return ImageAddrToOnDiskOffset(address, on_disk_offset);
525 }
526
ImageAddrToOnDiskOffset(LPVOID address,DWORD * on_disk_offset) const527 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
528 DWORD *on_disk_offset) const {
529 if (NULL == address)
530 return false;
531
532 // Get the section that this address belongs to.
533 PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
534 if (NULL == section_header)
535 return false;
536
537 #pragma warning(push)
538 #pragma warning(disable: 4311)
539 // These casts generate warnings because they are 32 bit specific.
540 // Don't follow the virtual RVAToAddr, use the one on the base.
541 DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
542 reinterpret_cast<DWORD>(PEImage::RVAToAddr(
543 section_header->VirtualAddress));
544 #pragma warning(pop)
545
546 *on_disk_offset = section_header->PointerToRawData + offset_within_section;
547 return true;
548 }
549
RVAToAddr(DWORD rva) const550 PVOID PEImage::RVAToAddr(DWORD rva) const {
551 if (rva == 0)
552 return NULL;
553
554 return reinterpret_cast<char*>(module_) + rva;
555 }
556
RVAToAddr(DWORD rva) const557 PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
558 if (rva == 0)
559 return NULL;
560
561 PVOID in_memory = PEImage::RVAToAddr(rva);
562 DWORD disk_offset;
563
564 if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset))
565 return NULL;
566
567 return PEImage::RVAToAddr(disk_offset);
568 }
569
570 } // namespace win
571 } // namespace base
572