Bug 1772588 [wpt PR 34302] - [wpt] Add test for block-in-inline offsetParent., a...
[gecko.git] / mozglue / tests / TestPEExportSection.cpp
blobcf160368d39454234916e77610daf514e4b6e479
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/. */
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.
11 #include "mozilla/CmdLineAndEnvUtils.h"
12 #include "mozilla/NativeNt.h"
13 #include "nsWindowsDllInterceptor.h"
15 #include <stdio.h>
16 #include <windows.h>
18 #define EXPORT_FUNCTION_EQ(name, func) \
19 (GetProcAddress(imageBase, name) == reinterpret_cast<void*>(func))
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)
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>;
35 constexpr DWORD kEventTimeoutinMs = 5000;
36 const wchar_t kProcessControlEventName[] =
37 L"TestPEExportSection.Process.Control.Event";
39 enum TestResult : int {
40 kTestSuccess = 0,
41 kTestFail,
42 kTestSkip,
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";
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";
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;
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; }
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;
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)];
88 template <typename T>
89 static DWORD PtrToRVA(T aPtr, uintptr_t aBase) {
90 return reinterpret_cast<uintptr_t>(aPtr) - aBase;
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);
106 uintptr_t mImageBase;
107 ExportDirectory* mNewExportDirectory;
109 DWORD PtrToRVA(const void* aPtr) const {
110 return reinterpret_cast<uintptr_t>(aPtr) - mImageBase;
113 public:
114 explicit ExportDirectoryPatcher(HMODULE aModule)
115 : mImageBase(PEHeaders::HModuleToBaseAddr<uintptr_t>(aModule)),
116 mNewExportDirectory(nullptr) {
117 SYSTEM_INFO si = {};
118 ::GetSystemInfo(&si);
120 int numPagesRequired = ((sizeof(ExportDirectory) - 1) / si.dwPageSize) + 1;
122 uintptr_t desiredBase = mImageBase + si.dwAllocationGranularity;
123 desiredBase = std::max(desiredBase, kMinimumAllocationPoint);
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;
140 desiredBase += si.dwAllocationGranularity;
143 gChildProcessStatus = kTestSkip;
144 printf(
145 "TEST-SKIP | TestPEExportSection | "
146 "Giving up finding an allocatable space following the mapped image.\n");
149 ~ExportDirectoryPatcher() {
150 // Intentionally leave mNewExportDirectory leaked to keep a patched data
151 // available until the process is terminated.
154 explicit operator bool() const { return !!mNewExportDirectory; }
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);
165 void PopulateDirectoryEntry(IMAGE_DATA_DIRECTORY& aOutput) const {
166 PopulateDirectory(mNewExportDirectory->mDirectoryHeader);
167 aOutput.VirtualAddress = PtrToRVA(&mNewExportDirectory->mDirectoryHeader);
168 aOutput.Size = sizeof(ExportDirectory);
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);
179 auto addressTableEntry =
180 const_cast<DWORD*>(ourExe.FindExportAddressTableEntry("Export1"));
181 if (!addressTableEntry) {
182 gChildProcessStatus = kTestFail;
183 return;
186 mozilla::AutoVirtualProtect protection(
187 addressTableEntry, sizeof(*addressTableEntry), PAGE_READWRITE);
188 if (!protection) {
189 gChildProcessStatus = kTestFail;
190 return;
193 *addressTableEntry = reinterpret_cast<uintptr_t>(SecretFunction1) -
194 PEHeaders::HModuleToBaseAddr<uintptr_t>(imageBase);
196 if (!EXPORT_FUNCTION_EQ("Export1", SecretFunction1) ||
197 !EXPORT_FUNCTION_EQ("Export2", Export2)) {
198 gChildProcessStatus = kTestFail;
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);
209 auto exportDirectory = ourExe.GetExportDirectory();
210 if (!exportDirectory) {
211 gChildProcessStatus = kTestFail;
212 return;
215 mozilla::AutoVirtualProtect protection(
216 exportDirectory, sizeof(*exportDirectory), PAGE_READWRITE);
217 if (!protection) {
218 gChildProcessStatus = kTestFail;
219 return;
222 ExportDirectoryPatcher patcher(imageBase);
223 if (!patcher) {
224 return;
227 patcher.PopulateDirectory(*exportDirectory);
229 if (GetProcAddress(imageBase, "Export1") ||
230 GetProcAddress(imageBase, "Export2") ||
231 !EXPORT_FUNCTION_EQ(kSecretFunction, SecretFunction1) ||
232 !EXPORT_FUNCTION_EQ(kSecretFunctionWithSuffix, SecretFunction2)) {
233 gChildProcessStatus = kTestFail;
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);
242 auto sectionTable =
243 ourExe.GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_EXPORT);
245 mozilla::AutoVirtualProtect protection(sectionTable, sizeof(*sectionTable),
246 PAGE_READWRITE);
247 if (!protection) {
248 gChildProcessStatus = kTestFail;
249 return;
252 sectionTable->VirtualAddress = sectionTable->Size = 0;
254 if (GetProcAddress(imageBase, "Export1") ||
255 GetProcAddress(imageBase, "Export2")) {
256 gChildProcessStatus = kTestFail;
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);
266 auto sectionTable =
267 ourExe.GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_EXPORT);
269 mozilla::AutoVirtualProtect protection(sectionTable, sizeof(*sectionTable),
270 PAGE_READWRITE);
271 if (!protection) {
272 gChildProcessStatus = kTestFail;
273 return;
276 ExportDirectoryPatcher patcher(imageBase);
277 if (!patcher) {
278 return;
281 patcher.PopulateDirectoryEntry(*sectionTable);
283 if (GetProcAddress(imageBase, "Export1") ||
284 GetProcAddress(imageBase, "Export2") ||
285 !EXPORT_FUNCTION_EQ(kSecretFunction, SecretFunction1) ||
286 !EXPORT_FUNCTION_EQ(kSecretFunctionWithSuffix, SecretFunction2)) {
287 gChildProcessStatus = kTestFail;
291 // This exports functions only by Ordinal by hiding the export name table.
292 void ExportByOrdinal() {
293 ModifyExportDirectoryEntry();
294 if (gChildProcessStatus != kTestSuccess) {
295 return;
298 MMPolicyInProcess policy;
299 HMODULE imageBase = ::GetModuleHandleW(nullptr);
300 auto ourExe = LocalPEExportSection::Get(imageBase, policy);
302 auto exportDirectory = ourExe.GetExportDirectory();
303 if (!exportDirectory) {
304 gChildProcessStatus = kTestFail;
305 return;
308 exportDirectory->NumberOfNames = 0;
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;
320 class ChildProcess final {
321 nsAutoHandle mChildProcess;
322 nsAutoHandle mChildMainThread;
324 public:
325 static int Main(const nsAutoHandle& aEvent, const wchar_t* aOption) {
326 if (wcscmp(aOption, kNoModification) == 0) {
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();
340 // Letting the parent process know the child process is ready.
341 ::SetEvent(aEvent);
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);
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(
354 mozilla::MakeCommandLine(mozilla::ArrayLength(childArgv), childArgv));
356 STARTUPINFOW si = {sizeof(si)};
357 PROCESS_INFORMATION pi;
358 BOOL ok = ::CreateProcessW(aExecutable, cmdLine.get(), nullptr, nullptr,
359 FALSE, 0, nullptr, nullptr, &si, &pi);
360 if (!ok) {
361 printf(
362 "TEST-FAILED | TestPEExportSection | "
363 "CreateProcessW falied - %08lx.\n",
364 GetLastError());
365 return;
368 if (aJob && !::AssignProcessToJobObject(aJob, pi.hProcess)) {
369 printf(
370 "TEST-FAILED | TestPEExportSection | "
371 "AssignProcessToJobObject falied - %08lx.\n",
372 GetLastError());
373 ::TerminateProcess(pi.hProcess, 1);
374 return;
377 // Wait until requested modification is done in the child process.
378 if (::WaitForSingleObject(aEvent, kEventTimeoutinMs) != WAIT_OBJECT_0) {
379 printf(
380 "TEST-FAILED | TestPEExportSection | "
381 "Child process was not ready in time.\n");
382 return;
385 mChildProcess.own(pi.hProcess);
386 mChildMainThread.own(pi.hThread);
389 ~ChildProcess() { ::TerminateProcess(mChildProcess, 0); }
391 operator HANDLE() const { return mChildProcess; }
393 TestResult GetStatus() const {
394 TestResult status = kTestSuccess;
395 if (!::ReadProcessMemory(mChildProcess, &gChildProcessStatus, &status,
396 sizeof(status), nullptr)) {
397 status = kTestFail;
398 printf(
399 "TEST-FAILED | TestPEExportSection | "
400 "ReadProcessMemory failed - %08lx\n",
401 GetLastError());
403 return status;
407 template <typename MMPolicy>
408 TestResult BasicTest(const MMPolicy& aMMPolicy) {
409 const bool isAppHelpLoaded = ::GetModuleHandleW(L"apphelp.dll");
411 // Use ntdll.dll because it does not have any forwarder RVA.
412 HMODULE ntdllImageBase = ::GetModuleHandleW(L"ntdll.dll");
413 auto ntdllExports = PEExportSection<MMPolicy>::Get(ntdllImageBase, aMMPolicy);
415 auto exportDir = ntdllExports.GetExportDirectory();
416 auto tableOfNames =
417 ntdllExports.template RVAToPtr<const PDWORD>(exportDir->AddressOfNames);
418 for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) {
419 const auto name =
420 ntdllExports.template RVAToPtr<const char*>(tableOfNames[i]);
422 if (isAppHelpLoaded && strcmp(name, "NtdllDefWindowProc_W") == 0) {
423 // In this case, GetProcAddress will return
424 // apphelp!DWM8AND16BitHook_DefWindowProcW.
425 continue;
428 auto funcEntry = ntdllExports.FindExportAddressTableEntry(name);
429 if (ntdllExports.template RVAToPtr<const void*>(*funcEntry) !=
430 ::GetProcAddress(ntdllImageBase, name)) {
431 printf(
432 "TEST-FAILED | TestPEExportSection | "
433 "FindExportAddressTableEntry did not resolve ntdll!%s.\n",
434 name);
435 return kTestFail;
439 for (DWORD i = 0; i < 0x10000; i += 0x10) {
440 if (ntdllExports.GetProcAddress(MAKEINTRESOURCE(i)) !=
441 ::GetProcAddress(ntdllImageBase, MAKEINTRESOURCE(i))) {
442 printf(
443 "TEST-FAILED | TestPEExportSection | "
444 "GetProcAddress did not resolve ntdll!Ordinal#%lu.\n",
446 return kTestFail;
450 // Test a known forwarder RVA.
451 auto k32Exports = PEExportSection<MMPolicy>::Get(
452 ::GetModuleHandleW(L"kernel32.dll"), aMMPolicy);
453 if (k32Exports.FindExportAddressTableEntry("HeapAlloc")) {
454 printf(
455 "TEST-FAILED | TestPEExportSection | "
456 "kernel32!HeapAlloc should be forwarded to ntdll!RtlAllocateHeap.\n");
457 return kTestFail;
460 // Test invalid names.
461 if (k32Exports.FindExportAddressTableEntry("Invalid name") ||
462 k32Exports.FindExportAddressTableEntry("")) {
463 printf(
464 "TEST-FAILED | TestPEExportSection | "
465 "FindExportAddressTableEntry should return "
466 "nullptr for a non-existent name.\n");
467 return kTestFail;
470 return kTestSuccess;
473 TestResult RunChildProcessTest(
474 const wchar_t* aExecutable, const wchar_t* aOption,
475 const nsAutoHandle& aEvent, const nsAutoHandle& aJob,
476 TestResult (*aTestCallback)(const RemotePEExportSection&)) {
477 ChildProcess childProcess(aExecutable, aOption, aEvent, aJob);
478 if (!childProcess) {
479 return kTestFail;
482 auto result = childProcess.GetStatus();
483 if (result != kTestSuccess) {
484 return result;
487 MMPolicyOutOfProcess policy(childProcess);
489 // One time is enough to run BasicTest in the child process.
490 static TestResult oneTimeResult = BasicTest<MMPolicyOutOfProcess>(policy);
491 if (oneTimeResult != kTestSuccess) {
492 return oneTimeResult;
495 auto exportTableChild =
496 RemotePEExportSection::Get(::GetModuleHandleW(nullptr), policy);
497 return aTestCallback(exportTableChild);
500 mozilla::LauncherResult<nsReturnRef<HANDLE>> CreateJobToLimitProcessLifetime() {
501 uint64_t version;
502 PEHeaders ntdllHeaders(::GetModuleHandleW(L"ntdll.dll"));
503 if (!ntdllHeaders.GetVersionInfo(version)) {
504 printf(
505 "TEST-FAILED | TestPEExportSection | "
506 "Unable to obtain version information from ntdll.dll\n");
507 return LAUNCHER_ERROR_FROM_LAST();
510 constexpr uint64_t kWin8 = 0x60002ull << 32;
511 nsAutoHandle job;
513 if (version < kWin8) {
514 // Since a process can be associated only with a single job in Win7 or
515 // older and this test program is already assigned with a job by
516 // infrastructure, we cannot use a job.
517 return job.out();
520 job.own(::CreateJobObject(nullptr, nullptr));
521 if (!job) {
522 printf(
523 "TEST-FAILED | TestPEExportSection | "
524 "CreateJobObject falied - %08lx.\n",
525 GetLastError());
526 return LAUNCHER_ERROR_FROM_LAST();
529 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = {};
530 jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
532 if (!::SetInformationJobObject(job, JobObjectExtendedLimitInformation,
533 &jobInfo, sizeof(jobInfo))) {
534 printf(
535 "TEST-FAILED | TestPEExportSection | "
536 "SetInformationJobObject falied - %08lx.\n",
537 GetLastError());
538 return LAUNCHER_ERROR_FROM_LAST();
541 return job.out();
544 extern "C" int wmain(int argc, wchar_t* argv[]) {
545 nsAutoHandle controlEvent(
546 ::CreateEventW(nullptr, FALSE, FALSE, kProcessControlEventName));
548 if (argc == 2) {
549 return ChildProcess::Main(controlEvent, argv[1]);
552 if (argc != 1) {
553 printf(
554 "TEST-FAILED | TestPEExportSection | "
555 "Invalid arguments.\n");
556 return kTestFail;
559 MMPolicyInProcess policy;
560 if (BasicTest<MMPolicyInProcess>(policy)) {
561 return kTestFail;
564 auto exportTableSelf =
565 LocalPEExportSection::Get(::GetModuleHandleW(nullptr), policy);
566 if (!exportTableSelf) {
567 printf(
568 "TEST-FAILED | TestPEExportSection | "
569 "LocalPEExportSection::Get failed.\n");
570 return kTestFail;
573 VERIFY_EXPORT_FUNCTION(exportTableSelf, "Export1", Export1,
574 "Local | Export1 was not exported.\n");
575 VERIFY_EXPORT_FUNCTION(exportTableSelf, "Export2", Export2,
576 "Local | Export2 was not exported.\n");
577 VERIFY_EXPORT_FUNCTION(
578 exportTableSelf, "Invalid name", 0,
579 "Local | GetProcAddress should return nullptr for an invalid name.\n");
581 // We'll add the child process to a job so that, in the event of a failure in
582 // this parent process, the child process will be automatically terminated.
583 auto probablyJob = CreateJobToLimitProcessLifetime();
584 if (probablyJob.isErr()) {
585 return kTestFail;
588 nsAutoHandle job(probablyJob.unwrap());
590 auto result = RunChildProcessTest(
591 argv[0], kNoModification, controlEvent, job,
592 [](const RemotePEExportSection& aTables) {
593 VERIFY_EXPORT_FUNCTION(aTables, "Export1", Export1,
594 "NoModification | Export1 was not exported.\n");
595 VERIFY_EXPORT_FUNCTION(aTables, "Export2", Export2,
596 "NoModification | Export2 was not exported.\n");
597 return kTestSuccess;
599 if (result == kTestFail) {
600 return result;
603 result = RunChildProcessTest(
604 argv[0], kNoExport, controlEvent, job,
605 [](const RemotePEExportSection& aTables) {
606 VERIFY_EXPORT_FUNCTION(aTables, "Export1", 0,
607 "NoExport | Export1 was exported.\n");
608 VERIFY_EXPORT_FUNCTION(aTables, "Export2", 0,
609 "NoExport | Export2 was exported.\n");
610 return kTestSuccess;
612 if (result == kTestFail) {
613 return result;
616 result = RunChildProcessTest(
617 argv[0], kModifyTableEntry, controlEvent, job,
618 [](const RemotePEExportSection& aTables) {
619 VERIFY_EXPORT_FUNCTION(
620 aTables, "Export1", SecretFunction1,
621 "ModifyTableEntry | SecretFunction1 was not exported.\n");
622 VERIFY_EXPORT_FUNCTION(
623 aTables, "Export2", Export2,
624 "ModifyTableEntry | Export2 was not exported.\n");
625 return kTestSuccess;
627 if (result == kTestFail) {
628 return result;
631 result = RunChildProcessTest(
632 argv[0], kModifyTable, controlEvent, job,
633 [](const RemotePEExportSection& aTables) {
634 VERIFY_EXPORT_FUNCTION(aTables, "Export1", 0,
635 "ModifyTable | Export1 was exported.\n");
636 VERIFY_EXPORT_FUNCTION(aTables, "Export2", 0,
637 "ModifyTable | Export2 was exported.\n");
638 VERIFY_EXPORT_FUNCTION(
639 aTables, kSecretFunction, SecretFunction1,
640 "ModifyTable | SecretFunction1 was not exported.\n");
641 VERIFY_EXPORT_FUNCTION(
642 aTables, kSecretFunctionWithSuffix, SecretFunction2,
643 "ModifyTable | SecretFunction2 was not exported.\n");
644 VERIFY_EXPORT_FUNCTION(
645 aTables, kSecretFunctionInvalid, 0,
646 "ModifyTable | kSecretFunctionInvalid was exported.\n");
647 return kTestSuccess;
649 if (result == kTestFail) {
650 return result;
653 result = RunChildProcessTest(
654 argv[0], kModifyDirectoryEntry, controlEvent, job,
655 [](const RemotePEExportSection& aTables) {
656 VERIFY_EXPORT_FUNCTION(
657 aTables, "Export1", 0,
658 "ModifyDirectoryEntry | Export1 was exported.\n");
659 VERIFY_EXPORT_FUNCTION(
660 aTables, "Export2", 0,
661 "ModifyDirectoryEntry | Export2 was exported.\n");
662 VERIFY_EXPORT_FUNCTION(
663 aTables, kSecretFunction, SecretFunction1,
664 "ModifyDirectoryEntry | SecretFunction1 was not exported.\n");
665 VERIFY_EXPORT_FUNCTION(
666 aTables, kSecretFunctionWithSuffix, SecretFunction2,
667 "ModifyDirectoryEntry | SecretFunction2 was not exported.\n");
668 VERIFY_EXPORT_FUNCTION(
669 aTables, kSecretFunctionInvalid, 0,
670 "ModifyDirectoryEntry | kSecretFunctionInvalid was exported.\n");
671 return kTestSuccess;
673 if (result == kTestFail) {
674 return result;
677 result = RunChildProcessTest(
678 argv[0], kExportByOrdinal, controlEvent, job,
679 [](const RemotePEExportSection& aTables) {
680 VERIFY_EXPORT_FUNCTION(aTables, "Export1", 0,
681 "ExportByOrdinal | Export1 was exported.\n");
682 VERIFY_EXPORT_FUNCTION(aTables, "Export2", 0,
683 "ExportByOrdinal | Export2 was exported.\n");
684 VERIFY_EXPORT_FUNCTION(
685 aTables, kSecretFunction, 0,
686 "ModifyDirectoryEntry | kSecretFunction was exported by name.\n");
687 VERIFY_EXPORT_FUNCTION(
688 aTables, kSecretFunctionWithSuffix, 0,
689 "ModifyDirectoryEntry | "
690 "kSecretFunctionWithSuffix was exported by name.\n");
691 VERIFY_EXPORT_FUNCTION(
692 aTables, MAKEINTRESOURCE(1), SecretFunction1,
693 "ModifyDirectoryEntry | "
694 "kSecretFunction was not exported by ordinal.\n");
695 VERIFY_EXPORT_FUNCTION(
696 aTables, MAKEINTRESOURCE(2), SecretFunction2,
697 "ModifyDirectoryEntry | "
698 "kSecretFunctionWithSuffix was not exported by ordinal.\n");
699 return kTestSuccess;
701 if (result == kTestFail) {
702 return result;
705 return kTestSuccess;