TestPEExportSection.cpp (24771B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 // This test makes sure mozilla::nt::PEExportSection can parse the export 8 // section of a local process, and a remote process even though it's 9 // modified by an external code. 10 11 #include "mozilla/CmdLineAndEnvUtils.h" 12 #include "mozilla/NativeNt.h" 13 #include "nsWindowsDllInterceptor.h" 14 15 #include <stdio.h> 16 #include <windows.h> 17 18 #define EXPORT_FUNCTION_EQ(name, func) \ 19 (GetProcAddress(imageBase, name) == reinterpret_cast<void*>(func)) 20 21 #define VERIFY_EXPORT_FUNCTION(tables, name, expected, errorMessage) \ 22 do { \ 23 if (tables.GetProcAddress(name) != reinterpret_cast<void*>(expected)) { \ 24 printf("TEST-FAILED | TestPEExportSection | %s", errorMessage); \ 25 return kTestFail; \ 26 } \ 27 } while (0) 28 29 using namespace mozilla::nt; 30 using mozilla::interceptor::MMPolicyInProcess; 31 using mozilla::interceptor::MMPolicyOutOfProcess; 32 using LocalPEExportSection = PEExportSection<MMPolicyInProcess>; 33 using RemotePEExportSection = PEExportSection<MMPolicyOutOfProcess>; 34 35 constexpr DWORD kEventTimeoutinMs = 5000; 36 const wchar_t kProcessControlEventName[] = 37 L"TestPEExportSection.Process.Control.Event"; 38 39 enum TestResult : int { 40 kTestSuccess = 0, 41 kTestFail, 42 kTestSkip, 43 }; 44 45 // These strings start with the same keyword to make sure we don't do substring 46 // match. Moreover, kSecretFunctionInvalid is purposely longer than the 47 // combination of the other two strings and located in between the other two 48 // strings to effectively test binary search. 49 const char kSecretFunction[] = "Secret"; 50 const char kSecretFunctionInvalid[] = "Secret invalid long name"; 51 const char kSecretFunctionWithSuffix[] = "Secret2"; 52 53 const wchar_t* kNoModification = L"--NoModification"; 54 const wchar_t* kNoExport = L"--NoExport"; 55 const wchar_t* kModifyTableEntry = L"--ModifyTableEntry"; 56 const wchar_t* kModifyTable = L"--ModifyTable"; 57 const wchar_t* kModifyDirectoryEntry = L"--ModifyDirectoryEntry"; 58 const wchar_t* kExportByOrdinal = L"--ExportByOrdinal"; 59 60 // Use the global variable to pass the child process's error status to the 61 // parent process. We don't use a process's exit code to keep the test simple. 62 int gChildProcessStatus = 0; 63 64 // These functions are exported by linker or export section tampering at 65 // runtime. Each of function bodies needs to be different to avoid ICF. 66 extern "C" __declspec(dllexport) int Export1() { return 0; } 67 extern "C" __declspec(dllexport) int Export2() { return 1; } 68 int SecretFunction1() { return 100; } 69 int SecretFunction2() { return 101; } 70 71 // This class allocates a writable region downstream of the mapped image 72 // and prepares it as a valid export section. 73 class ExportDirectoryPatcher final { 74 static constexpr int kRegionAllocationTryLimit = 100; 75 static constexpr int kNumOfTableEntries = 2; 76 // VirtualAlloc sometimes fails if a desired base address is too small. 77 // Define a minimum desired base to reduce the number of allocation tries. 78 static constexpr uintptr_t kMinimumAllocationPoint = 0x8000000; 79 80 struct ExportDirectory { 81 IMAGE_EXPORT_DIRECTORY mDirectoryHeader; 82 DWORD mExportAddressTable[kNumOfTableEntries]; 83 DWORD mExportNameTable[kNumOfTableEntries]; 84 WORD mExportOrdinalTable[kNumOfTableEntries]; 85 char mNameBuffer1[sizeof(kSecretFunction)]; 86 char mNameBuffer2[sizeof(kSecretFunctionWithSuffix)]; 87 88 template <typename T> 89 static DWORD PtrToRVA(T aPtr, uintptr_t aBase) { 90 return reinterpret_cast<uintptr_t>(aPtr) - aBase; 91 } 92 93 explicit ExportDirectory(uintptr_t aImageBase) : mDirectoryHeader{} { 94 mDirectoryHeader.Base = 1; 95 mExportAddressTable[0] = PtrToRVA(SecretFunction1, aImageBase); 96 mExportAddressTable[1] = PtrToRVA(SecretFunction2, aImageBase); 97 mExportNameTable[0] = PtrToRVA(mNameBuffer1, aImageBase); 98 mExportNameTable[1] = PtrToRVA(mNameBuffer2, aImageBase); 99 mExportOrdinalTable[0] = 0; 100 mExportOrdinalTable[1] = 1; 101 strcpy(mNameBuffer1, kSecretFunction); 102 strcpy(mNameBuffer2, kSecretFunctionWithSuffix); 103 } 104 }; 105 106 uintptr_t mImageBase; 107 ExportDirectory* mNewExportDirectory; 108 109 DWORD PtrToRVA(const void* aPtr) const { 110 return reinterpret_cast<uintptr_t>(aPtr) - mImageBase; 111 } 112 113 public: 114 explicit ExportDirectoryPatcher(HMODULE aModule) 115 : mImageBase(PEHeaders::HModuleToBaseAddr<uintptr_t>(aModule)), 116 mNewExportDirectory(nullptr) { 117 SYSTEM_INFO si = {}; 118 ::GetSystemInfo(&si); 119 120 int numPagesRequired = ((sizeof(ExportDirectory) - 1) / si.dwPageSize) + 1; 121 122 uintptr_t desiredBase = mImageBase + si.dwAllocationGranularity; 123 desiredBase = std::max(desiredBase, kMinimumAllocationPoint); 124 125 for (int i = 0; i < kRegionAllocationTryLimit; ++i) { 126 void* allocated = 127 ::VirtualAlloc(reinterpret_cast<void*>(desiredBase), 128 numPagesRequired * si.dwPageSize, 129 MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 130 if (allocated) { 131 // Use the end of a allocated page as ExportDirectory in order to test 132 // the boundary between a commit page and a non-commited page. 133 allocated = reinterpret_cast<uint8_t*>(allocated) + 134 (numPagesRequired * si.dwPageSize) - 135 sizeof(ExportDirectory); 136 mNewExportDirectory = new (allocated) ExportDirectory(mImageBase); 137 return; 138 } 139 140 desiredBase += si.dwAllocationGranularity; 141 } 142 143 gChildProcessStatus = kTestSkip; 144 printf( 145 "TEST-SKIP | TestPEExportSection | " 146 "Giving up finding an allocatable space following the mapped image.\n"); 147 } 148 149 ~ExportDirectoryPatcher() { 150 // Intentionally leave mNewExportDirectory leaked to keep a patched data 151 // available until the process is terminated. 152 } 153 154 explicit operator bool() const { return !!mNewExportDirectory; } 155 156 void PopulateDirectory(IMAGE_EXPORT_DIRECTORY& aOutput) const { 157 aOutput.NumberOfFunctions = aOutput.NumberOfNames = kNumOfTableEntries; 158 aOutput.AddressOfFunctions = 159 PtrToRVA(mNewExportDirectory->mExportAddressTable); 160 aOutput.AddressOfNames = PtrToRVA(mNewExportDirectory->mExportNameTable); 161 aOutput.AddressOfNameOrdinals = 162 PtrToRVA(mNewExportDirectory->mExportOrdinalTable); 163 } 164 165 void PopulateDirectoryEntry(IMAGE_DATA_DIRECTORY& aOutput) const { 166 PopulateDirectory(mNewExportDirectory->mDirectoryHeader); 167 aOutput.VirtualAddress = PtrToRVA(&mNewExportDirectory->mDirectoryHeader); 168 aOutput.Size = sizeof(ExportDirectory); 169 } 170 }; 171 172 // This exports SecretFunction1 as "Export1" by replacing an entry of the 173 // export address table. 174 void ModifyExportAddressTableEntry() { 175 MMPolicyInProcess policy; 176 HMODULE imageBase = ::GetModuleHandleW(nullptr); 177 auto ourExe = LocalPEExportSection::Get(imageBase, policy); 178 179 auto addressTableEntry = 180 const_cast<DWORD*>(ourExe.FindExportAddressTableEntry("Export1")); 181 if (!addressTableEntry) { 182 gChildProcessStatus = kTestFail; 183 return; 184 } 185 186 mozilla::AutoVirtualProtect protection( 187 addressTableEntry, sizeof(*addressTableEntry), PAGE_READWRITE); 188 if (!protection) { 189 gChildProcessStatus = kTestFail; 190 return; 191 } 192 193 *addressTableEntry = reinterpret_cast<uintptr_t>(SecretFunction1) - 194 PEHeaders::HModuleToBaseAddr<uintptr_t>(imageBase); 195 196 if (!EXPORT_FUNCTION_EQ("Export1", SecretFunction1) || 197 !EXPORT_FUNCTION_EQ("Export2", Export2)) { 198 gChildProcessStatus = kTestFail; 199 } 200 } 201 202 // This switches the entire address table into one exporting SecretFunction1 203 // and SecretFunction2. 204 void ModifyExportAddressTable() { 205 MMPolicyInProcess policy; 206 HMODULE imageBase = ::GetModuleHandleW(nullptr); 207 auto ourExe = LocalPEExportSection::Get(imageBase, policy); 208 209 auto exportDirectory = ourExe.GetExportDirectory(); 210 if (!exportDirectory) { 211 gChildProcessStatus = kTestFail; 212 return; 213 } 214 215 mozilla::AutoVirtualProtect protection( 216 exportDirectory, sizeof(*exportDirectory), PAGE_READWRITE); 217 if (!protection) { 218 gChildProcessStatus = kTestFail; 219 return; 220 } 221 222 ExportDirectoryPatcher patcher(imageBase); 223 if (!patcher) { 224 return; 225 } 226 227 patcher.PopulateDirectory(*exportDirectory); 228 229 if (GetProcAddress(imageBase, "Export1") || 230 GetProcAddress(imageBase, "Export2") || 231 !EXPORT_FUNCTION_EQ(kSecretFunction, SecretFunction1) || 232 !EXPORT_FUNCTION_EQ(kSecretFunctionWithSuffix, SecretFunction2)) { 233 gChildProcessStatus = kTestFail; 234 } 235 } 236 237 // This hides all export functions by setting the table size to 0. 238 void HideExportSection() { 239 HMODULE imageBase = ::GetModuleHandleW(nullptr); 240 PEHeaders ourExe(imageBase); 241 242 auto sectionTable = 243 ourExe.GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_EXPORT); 244 245 mozilla::AutoVirtualProtect protection(sectionTable, sizeof(*sectionTable), 246 PAGE_READWRITE); 247 if (!protection) { 248 gChildProcessStatus = kTestFail; 249 return; 250 } 251 252 sectionTable->VirtualAddress = sectionTable->Size = 0; 253 254 if (GetProcAddress(imageBase, "Export1") || 255 GetProcAddress(imageBase, "Export2")) { 256 gChildProcessStatus = kTestFail; 257 } 258 } 259 260 // This makes the export directory entry point to a new export section 261 // which exports SecretFunction1 and SecretFunction2. 262 void ModifyExportDirectoryEntry() { 263 HMODULE imageBase = ::GetModuleHandleW(nullptr); 264 PEHeaders ourExe(imageBase); 265 266 auto sectionTable = 267 ourExe.GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_EXPORT); 268 269 mozilla::AutoVirtualProtect protection(sectionTable, sizeof(*sectionTable), 270 PAGE_READWRITE); 271 if (!protection) { 272 gChildProcessStatus = kTestFail; 273 return; 274 } 275 276 ExportDirectoryPatcher patcher(imageBase); 277 if (!patcher) { 278 return; 279 } 280 281 patcher.PopulateDirectoryEntry(*sectionTable); 282 283 if (GetProcAddress(imageBase, "Export1") || 284 GetProcAddress(imageBase, "Export2") || 285 !EXPORT_FUNCTION_EQ(kSecretFunction, SecretFunction1) || 286 !EXPORT_FUNCTION_EQ(kSecretFunctionWithSuffix, SecretFunction2)) { 287 gChildProcessStatus = kTestFail; 288 } 289 } 290 291 // This exports functions only by Ordinal by hiding the export name table. 292 void ExportByOrdinal() { 293 ModifyExportDirectoryEntry(); 294 if (gChildProcessStatus != kTestSuccess) { 295 return; 296 } 297 298 MMPolicyInProcess policy; 299 HMODULE imageBase = ::GetModuleHandleW(nullptr); 300 auto ourExe = LocalPEExportSection::Get(imageBase, policy); 301 302 auto exportDirectory = ourExe.GetExportDirectory(); 303 if (!exportDirectory) { 304 gChildProcessStatus = kTestFail; 305 return; 306 } 307 308 exportDirectory->NumberOfNames = 0; 309 310 if (GetProcAddress(imageBase, "Export1") || 311 GetProcAddress(imageBase, "Export2") || 312 GetProcAddress(imageBase, kSecretFunction) || 313 GetProcAddress(imageBase, kSecretFunctionWithSuffix) || 314 !EXPORT_FUNCTION_EQ(MAKEINTRESOURCE(1), SecretFunction1) || 315 !EXPORT_FUNCTION_EQ(MAKEINTRESOURCE(2), SecretFunction2)) { 316 gChildProcessStatus = kTestFail; 317 } 318 } 319 320 class ChildProcess final { 321 nsAutoHandle mChildProcess; 322 nsAutoHandle mChildMainThread; 323 324 public: 325 static int Main(const nsAutoHandle& aEvent, const wchar_t* aOption) { 326 if (wcscmp(aOption, kNoModification) == 0) { 327 ; 328 } else if (wcscmp(aOption, kNoExport) == 0) { 329 HideExportSection(); 330 } else if (wcscmp(aOption, kModifyTableEntry) == 0) { 331 ModifyExportAddressTableEntry(); 332 } else if (wcscmp(aOption, kModifyTable) == 0) { 333 ModifyExportAddressTable(); 334 } else if (wcscmp(aOption, kModifyDirectoryEntry) == 0) { 335 ModifyExportDirectoryEntry(); 336 } else if (wcscmp(aOption, kExportByOrdinal) == 0) { 337 ExportByOrdinal(); 338 } 339 340 // Letting the parent process know the child process is ready. 341 ::SetEvent(aEvent); 342 343 // The child process does not exit itself. It's force terminated by 344 // the parent process when all tests are done. 345 for (;;) { 346 ::Sleep(100); 347 } 348 } 349 350 ChildProcess(const wchar_t* aExecutable, const wchar_t* aOption, 351 const nsAutoHandle& aEvent, const nsAutoHandle& aJob) { 352 const wchar_t* childArgv[] = {aExecutable, aOption}; 353 auto cmdLine(mozilla::MakeCommandLine(std::size(childArgv), childArgv)); 354 355 STARTUPINFOW si = {sizeof(si)}; 356 PROCESS_INFORMATION pi; 357 BOOL ok = ::CreateProcessW(aExecutable, cmdLine.get(), nullptr, nullptr, 358 FALSE, 0, nullptr, nullptr, &si, &pi); 359 if (!ok) { 360 printf( 361 "TEST-FAILED | TestPEExportSection | " 362 "CreateProcessW falied - %08lx.\n", 363 GetLastError()); 364 return; 365 } 366 367 if (aJob && !::AssignProcessToJobObject(aJob, pi.hProcess)) { 368 printf( 369 "TEST-FAILED | TestPEExportSection | " 370 "AssignProcessToJobObject falied - %08lx.\n", 371 GetLastError()); 372 ::TerminateProcess(pi.hProcess, 1); 373 return; 374 } 375 376 // Wait until requested modification is done in the child process. 377 if (::WaitForSingleObject(aEvent, kEventTimeoutinMs) != WAIT_OBJECT_0) { 378 printf( 379 "TEST-FAILED | TestPEExportSection | " 380 "Child process was not ready in time.\n"); 381 return; 382 } 383 384 mChildProcess.own(pi.hProcess); 385 mChildMainThread.own(pi.hThread); 386 } 387 388 ~ChildProcess() { ::TerminateProcess(mChildProcess, 0); } 389 390 operator HANDLE() const { return mChildProcess; } 391 392 TestResult GetStatus() const { 393 TestResult status = kTestSuccess; 394 if (!::ReadProcessMemory(mChildProcess, &gChildProcessStatus, &status, 395 sizeof(status), nullptr)) { 396 status = kTestFail; 397 printf( 398 "TEST-FAILED | TestPEExportSection | " 399 "ReadProcessMemory failed - %08lx\n", 400 GetLastError()); 401 } 402 return status; 403 } 404 }; 405 406 template <typename MMPolicy> 407 TestResult BasicTest(const MMPolicy& aMMPolicy) { 408 const bool isAppHelpLoaded = ::GetModuleHandleW(L"apphelp.dll"); 409 410 // Use ntdll.dll because it does not have any forwarder RVA. 411 HMODULE ntdllImageBase = ::GetModuleHandleW(L"ntdll.dll"); 412 auto ntdllExports = PEExportSection<MMPolicy>::Get(ntdllImageBase, aMMPolicy); 413 414 auto exportDir = ntdllExports.GetExportDirectory(); 415 auto tableOfNames = 416 ntdllExports.template RVAToPtr<const PDWORD>(exportDir->AddressOfNames); 417 for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) { 418 const auto name = 419 ntdllExports.template RVAToPtr<const char*>(tableOfNames[i]); 420 421 if (isAppHelpLoaded && strcmp(name, "NtdllDefWindowProc_W") == 0) { 422 // In this case, GetProcAddress will return 423 // apphelp!DWM8AND16BitHook_DefWindowProcW. 424 continue; 425 } 426 427 auto funcEntry = ntdllExports.FindExportAddressTableEntry(name); 428 if (ntdllExports.template RVAToPtr<const void*>(*funcEntry) != 429 ::GetProcAddress(ntdllImageBase, name)) { 430 printf( 431 "TEST-FAILED | TestPEExportSection | " 432 "FindExportAddressTableEntry did not resolve ntdll!%s.\n", 433 name); 434 return kTestFail; 435 } 436 } 437 438 for (DWORD i = 0; i < 0x10000; i += 0x10) { 439 if (ntdllExports.GetProcAddress(MAKEINTRESOURCE(i)) != 440 ::GetProcAddress(ntdllImageBase, MAKEINTRESOURCE(i))) { 441 printf( 442 "TEST-FAILED | TestPEExportSection | " 443 "GetProcAddress did not resolve ntdll!Ordinal#%lu.\n", 444 i); 445 return kTestFail; 446 } 447 } 448 449 // Test a known forwarder RVA. 450 auto k32Exports = PEExportSection<MMPolicy>::Get( 451 ::GetModuleHandleW(L"kernel32.dll"), aMMPolicy); 452 if (k32Exports.FindExportAddressTableEntry("HeapAlloc")) { 453 printf( 454 "TEST-FAILED | TestPEExportSection | " 455 "kernel32!HeapAlloc should be forwarded to ntdll!RtlAllocateHeap.\n"); 456 return kTestFail; 457 } 458 459 // Test invalid names. 460 if (k32Exports.FindExportAddressTableEntry("Invalid name") || 461 k32Exports.FindExportAddressTableEntry("")) { 462 printf( 463 "TEST-FAILED | TestPEExportSection | " 464 "FindExportAddressTableEntry should return " 465 "nullptr for a non-existent name.\n"); 466 return kTestFail; 467 } 468 469 return kTestSuccess; 470 } 471 472 TestResult RunChildProcessTest( 473 const wchar_t* aExecutable, const wchar_t* aOption, 474 const nsAutoHandle& aEvent, const nsAutoHandle& aJob, 475 TestResult (*aTestCallback)(const RemotePEExportSection&)) { 476 ChildProcess childProcess(aExecutable, aOption, aEvent, aJob); 477 if (!childProcess) { 478 return kTestFail; 479 } 480 481 auto result = childProcess.GetStatus(); 482 if (result != kTestSuccess) { 483 return result; 484 } 485 486 MMPolicyOutOfProcess policy(childProcess); 487 488 // One time is enough to run BasicTest in the child process. 489 static TestResult oneTimeResult = BasicTest<MMPolicyOutOfProcess>(policy); 490 if (oneTimeResult != kTestSuccess) { 491 return oneTimeResult; 492 } 493 494 auto exportTableChild = 495 RemotePEExportSection::Get(::GetModuleHandleW(nullptr), policy); 496 return aTestCallback(exportTableChild); 497 } 498 499 mozilla::LauncherResult<nsReturnRef<HANDLE>> CreateJobToLimitProcessLifetime() { 500 uint64_t version; 501 PEHeaders ntdllHeaders(::GetModuleHandleW(L"ntdll.dll")); 502 if (!ntdllHeaders.GetVersionInfo(version)) { 503 printf( 504 "TEST-FAILED | TestPEExportSection | " 505 "Unable to obtain version information from ntdll.dll\n"); 506 return LAUNCHER_ERROR_FROM_LAST(); 507 } 508 509 constexpr uint64_t kWin8 = 0x60002ull << 32; 510 nsAutoHandle job; 511 512 if (version < kWin8) { 513 // Since a process can be associated only with a single job in Win7 or 514 // older and this test program is already assigned with a job by 515 // infrastructure, we cannot use a job. 516 return job.out(); 517 } 518 519 job.own(::CreateJobObject(nullptr, nullptr)); 520 if (!job) { 521 printf( 522 "TEST-FAILED | TestPEExportSection | " 523 "CreateJobObject falied - %08lx.\n", 524 GetLastError()); 525 return LAUNCHER_ERROR_FROM_LAST(); 526 } 527 528 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = {}; 529 jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 530 531 if (!::SetInformationJobObject(job, JobObjectExtendedLimitInformation, 532 &jobInfo, sizeof(jobInfo))) { 533 printf( 534 "TEST-FAILED | TestPEExportSection | " 535 "SetInformationJobObject falied - %08lx.\n", 536 GetLastError()); 537 return LAUNCHER_ERROR_FROM_LAST(); 538 } 539 540 return job.out(); 541 } 542 543 extern "C" int wmain(int argc, wchar_t* argv[]) { 544 nsAutoHandle controlEvent( 545 ::CreateEventW(nullptr, FALSE, FALSE, kProcessControlEventName)); 546 547 if (argc == 2) { 548 return ChildProcess::Main(controlEvent, argv[1]); 549 } 550 551 if (argc != 1) { 552 printf( 553 "TEST-FAILED | TestPEExportSection | " 554 "Invalid arguments.\n"); 555 return kTestFail; 556 } 557 558 MMPolicyInProcess policy; 559 if (BasicTest<MMPolicyInProcess>(policy)) { 560 return kTestFail; 561 } 562 563 auto exportTableSelf = 564 LocalPEExportSection::Get(::GetModuleHandleW(nullptr), policy); 565 if (!exportTableSelf) { 566 printf( 567 "TEST-FAILED | TestPEExportSection | " 568 "LocalPEExportSection::Get failed.\n"); 569 return kTestFail; 570 } 571 572 VERIFY_EXPORT_FUNCTION(exportTableSelf, "Export1", Export1, 573 "Local | Export1 was not exported.\n"); 574 VERIFY_EXPORT_FUNCTION(exportTableSelf, "Export2", Export2, 575 "Local | Export2 was not exported.\n"); 576 VERIFY_EXPORT_FUNCTION( 577 exportTableSelf, "Invalid name", 0, 578 "Local | GetProcAddress should return nullptr for an invalid name.\n"); 579 580 // We'll add the child process to a job so that, in the event of a failure in 581 // this parent process, the child process will be automatically terminated. 582 auto probablyJob = CreateJobToLimitProcessLifetime(); 583 if (probablyJob.isErr()) { 584 return kTestFail; 585 } 586 587 nsAutoHandle job(probablyJob.unwrap()); 588 589 auto result = RunChildProcessTest( 590 argv[0], kNoModification, controlEvent, job, 591 [](const RemotePEExportSection& aTables) { 592 VERIFY_EXPORT_FUNCTION(aTables, "Export1", Export1, 593 "NoModification | Export1 was not exported.\n"); 594 VERIFY_EXPORT_FUNCTION(aTables, "Export2", Export2, 595 "NoModification | Export2 was not exported.\n"); 596 return kTestSuccess; 597 }); 598 if (result == kTestFail) { 599 return result; 600 } 601 602 result = RunChildProcessTest( 603 argv[0], kNoExport, controlEvent, job, 604 [](const RemotePEExportSection& aTables) { 605 VERIFY_EXPORT_FUNCTION(aTables, "Export1", 0, 606 "NoExport | Export1 was exported.\n"); 607 VERIFY_EXPORT_FUNCTION(aTables, "Export2", 0, 608 "NoExport | Export2 was exported.\n"); 609 return kTestSuccess; 610 }); 611 if (result == kTestFail) { 612 return result; 613 } 614 615 result = RunChildProcessTest( 616 argv[0], kModifyTableEntry, controlEvent, job, 617 [](const RemotePEExportSection& aTables) { 618 VERIFY_EXPORT_FUNCTION( 619 aTables, "Export1", SecretFunction1, 620 "ModifyTableEntry | SecretFunction1 was not exported.\n"); 621 VERIFY_EXPORT_FUNCTION( 622 aTables, "Export2", Export2, 623 "ModifyTableEntry | Export2 was not exported.\n"); 624 return kTestSuccess; 625 }); 626 if (result == kTestFail) { 627 return result; 628 } 629 630 result = RunChildProcessTest( 631 argv[0], kModifyTable, controlEvent, job, 632 [](const RemotePEExportSection& aTables) { 633 VERIFY_EXPORT_FUNCTION(aTables, "Export1", 0, 634 "ModifyTable | Export1 was exported.\n"); 635 VERIFY_EXPORT_FUNCTION(aTables, "Export2", 0, 636 "ModifyTable | Export2 was exported.\n"); 637 VERIFY_EXPORT_FUNCTION( 638 aTables, kSecretFunction, SecretFunction1, 639 "ModifyTable | SecretFunction1 was not exported.\n"); 640 VERIFY_EXPORT_FUNCTION( 641 aTables, kSecretFunctionWithSuffix, SecretFunction2, 642 "ModifyTable | SecretFunction2 was not exported.\n"); 643 VERIFY_EXPORT_FUNCTION( 644 aTables, kSecretFunctionInvalid, 0, 645 "ModifyTable | kSecretFunctionInvalid was exported.\n"); 646 return kTestSuccess; 647 }); 648 if (result == kTestFail) { 649 return result; 650 } 651 652 result = RunChildProcessTest( 653 argv[0], kModifyDirectoryEntry, controlEvent, job, 654 [](const RemotePEExportSection& aTables) { 655 VERIFY_EXPORT_FUNCTION( 656 aTables, "Export1", 0, 657 "ModifyDirectoryEntry | Export1 was exported.\n"); 658 VERIFY_EXPORT_FUNCTION( 659 aTables, "Export2", 0, 660 "ModifyDirectoryEntry | Export2 was exported.\n"); 661 VERIFY_EXPORT_FUNCTION( 662 aTables, kSecretFunction, SecretFunction1, 663 "ModifyDirectoryEntry | SecretFunction1 was not exported.\n"); 664 VERIFY_EXPORT_FUNCTION( 665 aTables, kSecretFunctionWithSuffix, SecretFunction2, 666 "ModifyDirectoryEntry | SecretFunction2 was not exported.\n"); 667 VERIFY_EXPORT_FUNCTION( 668 aTables, kSecretFunctionInvalid, 0, 669 "ModifyDirectoryEntry | kSecretFunctionInvalid was exported.\n"); 670 return kTestSuccess; 671 }); 672 if (result == kTestFail) { 673 return result; 674 } 675 676 result = RunChildProcessTest( 677 argv[0], kExportByOrdinal, controlEvent, job, 678 [](const RemotePEExportSection& aTables) { 679 VERIFY_EXPORT_FUNCTION(aTables, "Export1", 0, 680 "ExportByOrdinal | Export1 was exported.\n"); 681 VERIFY_EXPORT_FUNCTION(aTables, "Export2", 0, 682 "ExportByOrdinal | Export2 was exported.\n"); 683 VERIFY_EXPORT_FUNCTION( 684 aTables, kSecretFunction, 0, 685 "ModifyDirectoryEntry | kSecretFunction was exported by name.\n"); 686 VERIFY_EXPORT_FUNCTION( 687 aTables, kSecretFunctionWithSuffix, 0, 688 "ModifyDirectoryEntry | " 689 "kSecretFunctionWithSuffix was exported by name.\n"); 690 VERIFY_EXPORT_FUNCTION( 691 aTables, MAKEINTRESOURCE(1), SecretFunction1, 692 "ModifyDirectoryEntry | " 693 "kSecretFunction was not exported by ordinal.\n"); 694 VERIFY_EXPORT_FUNCTION( 695 aTables, MAKEINTRESOURCE(2), SecretFunction2, 696 "ModifyDirectoryEntry | " 697 "kSecretFunctionWithSuffix was not exported by ordinal.\n"); 698 return kTestSuccess; 699 }); 700 if (result == kTestFail) { 701 return result; 702 } 703 704 return kTestSuccess; 705 }