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