1 // 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. 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/pe_image.h" 9 10 #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 17 // Structure to perform imports enumerations. 18 struct EnumAllImportsStorage { 19 PEImage::EnumImportsFunction callback; 20 PVOID cookie; 21 }; 22 23 // Callback used to enumerate imports. See EnumImportChunksFunction. 24 bool 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. 35 bool 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 48 void PEImage::set_module(HMODULE module) { 49 module_ = module; 50 } 51 52 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const { 53 return reinterpret_cast<PIMAGE_DOS_HEADER>(module_); 54 } 55 56 PIMAGE_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 63 PIMAGE_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 73 WORD PEImage::GetNumSections() const { 74 return GetNTHeaders()->FileHeader.NumberOfSections; 75 } 76 77 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const { 78 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); 79 80 return nt_headers->OptionalHeader.DataDirectory[directory].Size; 81 } 82 83 PVOID 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 90 PIMAGE_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 108 PIMAGE_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 128 PDWORD 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 144 FARPROC 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 166 bool 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 214 bool 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 230 bool 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 284 bool 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 313 bool 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 335 bool 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 364 bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const { 365 EnumAllImportsStorage temp = { callback, cookie }; 366 return EnumImportChunks(ProcessImportChunk, &temp); 367 } 368 369 bool 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 424 bool 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 469 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback, 470 PVOID cookie) const { 471 EnumAllImportsStorage temp = { callback, cookie }; 472 return EnumDelayImportChunks(ProcessDelayImportChunk, &temp); 473 } 474 475 bool 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 496 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const { 497 LPVOID address = RVAToAddr(rva); 498 return ImageAddrToOnDiskOffset(address, on_disk_offset); 499 } 500 501 bool 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 524 PVOID PEImage::RVAToAddr(DWORD rva) const { 525 if (rva == 0) 526 return NULL; 527 528 return reinterpret_cast<char*>(module_) + rva; 529 } 530 531 PVOID PEImageAsData::RVAToAddr(DWORD rva) const { 532 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 } 543