// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file contains unit tests for PEImage. #include "testing/gtest/include/gtest/gtest.h" #include "base/win/pe_image.h" #include "base/win/windows_version.h" namespace base { namespace win { // Just counts the number of invocations. bool ExportsCallback(const PEImage &image, DWORD ordinal, DWORD hint, LPCSTR name, PVOID function, LPCSTR forward, PVOID cookie) { int* count = reinterpret_cast<int*>(cookie); (*count)++; return true; } // Just counts the number of invocations. bool ImportsCallback(const PEImage &image, LPCSTR module, DWORD ordinal, LPCSTR name, DWORD hint, PIMAGE_THUNK_DATA iat, PVOID cookie) { int* count = reinterpret_cast<int*>(cookie); (*count)++; return true; } // Just counts the number of invocations. bool SectionsCallback(const PEImage &image, PIMAGE_SECTION_HEADER header, PVOID section_start, DWORD section_size, PVOID cookie) { int* count = reinterpret_cast<int*>(cookie); (*count)++; return true; } // Just counts the number of invocations. bool RelocsCallback(const PEImage &image, WORD type, PVOID address, PVOID cookie) { int* count = reinterpret_cast<int*>(cookie); (*count)++; return true; } // Just counts the number of invocations. bool ImportChunksCallback(const PEImage &image, LPCSTR module, PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat, PVOID cookie) { int* count = reinterpret_cast<int*>(cookie); (*count)++; return true; } // Just counts the number of invocations. bool DelayImportChunksCallback(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) { int* count = reinterpret_cast<int*>(cookie); (*count)++; return true; } // Identifiers for the set of supported expectations. enum ExpectationSet { WIN_2K_SET, WIN_XP_SET, WIN_VISTA_SET, WIN_7_SET, WIN_8_SET, UNSUPPORTED_SET, }; // We'll be using some known values for the tests. enum Value { sections = 0, imports_dlls, delay_dlls, exports, imports, delay_imports, relocs }; ExpectationSet GetExpectationSet(DWORD os) { if (os == 50) return WIN_2K_SET; if (os == 51) return WIN_XP_SET; if (os == 60) return WIN_VISTA_SET; if (os == 61) return WIN_7_SET; if (os >= 62) return WIN_8_SET; return UNSUPPORTED_SET; } // Retrieves the expected value from advapi32.dll based on the OS. int GetExpectedValue(Value value, DWORD os) { const int xp_delay_dlls = 2; const int xp_exports = 675; const int xp_imports = 422; const int xp_delay_imports = 8; const int xp_relocs = 9180; const int vista_delay_dlls = 4; const int vista_exports = 799; const int vista_imports = 476; const int vista_delay_imports = 24; const int vista_relocs = 10188; const int w2k_delay_dlls = 0; const int w2k_exports = 566; const int w2k_imports = 357; const int w2k_delay_imports = 0; const int w2k_relocs = 7388; const int win7_delay_dlls = 7; const int win7_exports = 806; const int win7_imports = 568; const int win7_delay_imports = 71; int win7_relocs = 7812; int win7_sections = 4; const int win8_delay_dlls = 9; const int win8_exports = 806; const int win8_imports = 568; const int win8_delay_imports = 113; const int win8_relocs = 9478; int win8_sections = 4; int win8_import_dlls = 17; base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); // 32-bit process on a 32-bit system. if (os_info->architecture() == base::win::OSInfo::X86_ARCHITECTURE) { win8_sections = 5; win8_import_dlls = 19; // 64-bit process on a 64-bit system. } else if (os_info->wow64_status() == base::win::OSInfo::WOW64_DISABLED) { win7_sections = 6; win7_relocs = 2712; } // Contains the expected value, for each enumerated property (Value), and the // OS version: [Value][os_version] const int expected[][5] = { {4, 4, 4, win7_sections, win8_sections}, {3, 3, 3, 13, win8_import_dlls}, {w2k_delay_dlls, xp_delay_dlls, vista_delay_dlls, win7_delay_dlls, win8_delay_dlls}, {w2k_exports, xp_exports, vista_exports, win7_exports, win8_exports}, {w2k_imports, xp_imports, vista_imports, win7_imports, win8_imports}, {w2k_delay_imports, xp_delay_imports, vista_delay_imports, win7_delay_imports, win8_delay_imports}, {w2k_relocs, xp_relocs, vista_relocs, win7_relocs, win8_relocs} }; COMPILE_ASSERT(arraysize(expected[0]) == UNSUPPORTED_SET, expected_value_set_mismatch); if (value > relocs) return 0; ExpectationSet expected_set = GetExpectationSet(os); if (expected_set >= arraysize(expected)) { // This should never happen. Log a failure if it does. EXPECT_NE(UNSUPPORTED_SET, expected_set); expected_set = WIN_2K_SET; } return expected[value][expected_set]; } // TODO(jschuh): crbug.com/167707 Need to fix test on Win64 bots #if defined(OS_WIN) && defined(ARCH_CPU_X86_64) #define MAYBE_EnumeratesPE DISABLED_EnumeratesPE #else #define MAYBE_EnumeratesPE EnumeratesPE #endif // Tests that we are able to enumerate stuff from a PE file, and that // the actual number of items found is within the expected range. TEST(PEImageTest, MAYBE_EnumeratesPE) { HMODULE module = LoadLibrary(L"advapi32.dll"); ASSERT_TRUE(NULL != module); PEImage pe(module); int count = 0; EXPECT_TRUE(pe.VerifyMagic()); DWORD os = pe.GetNTHeaders()->OptionalHeader.MajorOperatingSystemVersion; os = os * 10 + pe.GetNTHeaders()->OptionalHeader.MinorOperatingSystemVersion; // Skip this test for unsupported OS versions. if (GetExpectationSet(os) == UNSUPPORTED_SET) return; pe.EnumSections(SectionsCallback, &count); EXPECT_EQ(GetExpectedValue(sections, os), count); count = 0; pe.EnumImportChunks(ImportChunksCallback, &count); EXPECT_EQ(GetExpectedValue(imports_dlls, os), count); count = 0; pe.EnumDelayImportChunks(DelayImportChunksCallback, &count); EXPECT_EQ(GetExpectedValue(delay_dlls, os), count); count = 0; pe.EnumExports(ExportsCallback, &count); EXPECT_GT(count, GetExpectedValue(exports, os) - 20); EXPECT_LT(count, GetExpectedValue(exports, os) + 100); count = 0; pe.EnumAllImports(ImportsCallback, &count); EXPECT_GT(count, GetExpectedValue(imports, os) - 20); EXPECT_LT(count, GetExpectedValue(imports, os) + 100); count = 0; pe.EnumAllDelayImports(ImportsCallback, &count); EXPECT_GT(count, GetExpectedValue(delay_imports, os) - 2); EXPECT_LT(count, GetExpectedValue(delay_imports, os) + 8); count = 0; pe.EnumRelocs(RelocsCallback, &count); EXPECT_GT(count, GetExpectedValue(relocs, os) - 150); EXPECT_LT(count, GetExpectedValue(relocs, os) + 1500); FreeLibrary(module); } // Tests that we can locate an specific exported symbol, by name and by ordinal. TEST(PEImageTest, RetrievesExports) { HMODULE module = LoadLibrary(L"advapi32.dll"); ASSERT_TRUE(NULL != module); PEImage pe(module); WORD ordinal; EXPECT_TRUE(pe.GetProcOrdinal("RegEnumKeyExW", &ordinal)); FARPROC address1 = pe.GetProcAddress("RegEnumKeyExW"); FARPROC address2 = pe.GetProcAddress(reinterpret_cast<char*>(ordinal)); EXPECT_TRUE(address1 != NULL); EXPECT_TRUE(address2 != NULL); EXPECT_TRUE(address1 == address2); FreeLibrary(module); } } // namespace win } // namespace base