Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / mozglue / tests / TestPEExportSection.cpp
blob9b05aa8b8c1e6bdc7aa68cd09aa63c75141412fe
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(mozilla::MakeCommandLine(std::size(childArgv), childArgv));
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;
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;
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;
384 mChildProcess.own(pi.hProcess);
385 mChildMainThread.own(pi.hThread);
388 ~ChildProcess() { ::TerminateProcess(mChildProcess, 0); }
390 operator HANDLE() const { return mChildProcess; }
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());
402 return status;
406 template <typename MMPolicy>
407 TestResult BasicTest(const MMPolicy& aMMPolicy) {
408 const bool isAppHelpLoaded = ::GetModuleHandleW(L"apphelp.dll");
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);
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]);
421 if (isAppHelpLoaded && strcmp(name, "NtdllDefWindowProc_W") == 0) {
422 // In this case, GetProcAddress will return
423 // apphelp!DWM8AND16BitHook_DefWindowProcW.
424 continue;
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;
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",
445 return kTestFail;
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;
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;
469 return kTestSuccess;
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;
481 auto result = childProcess.GetStatus();
482 if (result != kTestSuccess) {
483 return result;
486 MMPolicyOutOfProcess policy(childProcess);
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;
494 auto exportTableChild =
495 RemotePEExportSection::Get(::GetModuleHandleW(nullptr), policy);
496 return aTestCallback(exportTableChild);
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();
509 constexpr uint64_t kWin8 = 0x60002ull << 32;
510 nsAutoHandle job;
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();
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();
528 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = {};
529 jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
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();
540 return job.out();
543 extern "C" int wmain(int argc, wchar_t* argv[]) {
544 nsAutoHandle controlEvent(
545 ::CreateEventW(nullptr, FALSE, FALSE, kProcessControlEventName));
547 if (argc == 2) {
548 return ChildProcess::Main(controlEvent, argv[1]);
551 if (argc != 1) {
552 printf(
553 "TEST-FAILED | TestPEExportSection | "
554 "Invalid arguments.\n");
555 return kTestFail;
558 MMPolicyInProcess policy;
559 if (BasicTest<MMPolicyInProcess>(policy)) {
560 return kTestFail;
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;
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");
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;
587 nsAutoHandle job(probablyJob.unwrap());
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;
598 if (result == kTestFail) {
599 return result;
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;
611 if (result == kTestFail) {
612 return result;
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;
626 if (result == kTestFail) {
627 return result;
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;
648 if (result == kTestFail) {
649 return result;
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;
672 if (result == kTestFail) {
673 return result;
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;
700 if (result == kTestFail) {
701 return result;
704 return kTestSuccess;