The Enhanced Mitigation Experience Toolkit (EMET) is a software solution from Microsoft that aims at preventing the exploitation of – legacy – software. It is designed to “help prevent attackers from gaining access to computer systems [and] anticipates the most common techniques attackers might use to exploit vulnerabilities in computer systems, and helps protect by diverting, terminating, blocking, and invalidating those actions and techniques.” [1]
This blog post presents a previously undocumented disarming technique that can be used to completely disable EMETs mitigations without requiring large, complicated ROP chains or shellcodes and a way to handle EAF in EMET 5.5x. As a result it will be possible to use a default ROP chain and shellcode generated by Metasploit.
TL;DR: The pointer to the read-only struct that stores all of EMET’s mitigation settings is always retrieved from the writable process heap. Hijacking this pointer only requires read-write access to memory and could enable an adversary to prevent EMET’s anti-ROP mitigations from ever being performed. EAF can optionally be subverted with a single, additional VirtualProtect call.
Bypassing tactics
As EMET is probably one of the most well-known exploit mitigation solutions around, it should not come as a surprise that a lot has already been written about EMET or exploit mitigation bypasses in general [3-19]. In general, a few ways can be chosen to bypass EMET:
- Bypass individual mitigations by crafting a ROP chain and shellcode in such a way that all mitigation checks pass.
- Jump over hooks or use system calls.
- Use an implementation flaw to disable one or multiple mitigations.
Previous EMET disarming techniques
The third route will be taken in this blog post, as the number of new generic exploit mitigation bypasses is quite limited. Previous research performed by Offensive Security in 2014 gives an idea of what kind of implementation flaws – that could be used to disable EMETs mitigations – plagued it in the past.
EMET 4.1 [3, 4]
EMET relies on a DLL (EMET.dll) that is injected into every protected process to perform its mitigation checks. In EMET 4.1 all mitigation settings were stored as global variables in writable memory in the .data segment of EMET.dll. It should not come as a surprise that these settings could easily be manipulated. Offensive Security disabled the anti-ROP mitigation (ROP-P) switch at offset +0x0007e220 within EMET.dll to prevent all anti-ROP mitigations from ever being performed. Although other variables like the mitigation settings bitmap stored at offset +0x0007e21c could also be manipulated. Disabling EMET 4.1 using this implementation flaw would only require determining the base address of EMET.dll and zero-ing out either the ROP-P switch or the mitigation bitmask.
var emetBase:uint = ...; // ROP-P switch pe.writeDword(emetBase+0x7e220, 0x00000000); // Mitigation bitmask //pe.writeDword(emetBase+0x7e21c, 0x00000000);
EMET 5.0 [5]
In EMET 5.0 some things changed. The ROP-P switch would now be stored on the heap instead in a structure that Offensive Security named CONFIG_STRUCT. Moreover, the address of the struct would also be encoded using the EncodePointer API. Offensive Security noticed that the ROP-P switch was still located in writable memory, thus making a new disarming opportunity possible. They obtained the address of the writable CONFIG_STRUCT structure by calling DecodePointer from a ROP chain. Offsec’s technique can be implemented using a ROP chain that calls DecodePointer with the encoded pointer as argument and that zeroes out the CONFIG_STRUCT->ROP-P field.
var ntdllBase:uint = ...; var emetBase:uint = ...; var DecodePointer:uint = pe.getProcAddress(ntdllBase, "RtlDecodePointer"); var encodedPtr:uint = pe.readDword(emetBase + 0xaa84c); rop[i++] = DecodePointer; // Call DecodePointer with 'encodedPtr' as argument (CONFIG_STRUCT addr ends in eax) rop[i++] = add_esp_0c; // (add esp, 0x0c; ret) Return to 'pop_esi' further on the stack rop[i++] = encodedPtr; rop[i++] = 0x41414141; // Padding rop[i++] = 0x42424242; rop[i++] = 0x43434343; rop[i++] = pop_esi; // (pop esi; ret) Place ROP-P offset within CONFIG_STRUCT (+0x558) in esi rop[i++] = 0x558; rop[i++] = add_eax_esi; // (add eax, esi; ret) Add +0x558 to the CONFIG_STRUCT address to get the ROP-P swith addr rop[i++] = pop_edx; // (pop edx; ret) Place 0x00000000 in edx rop[i++] = 0x00000000; rop[i++] = mov_dword_eax_edx; // (mov [eax], edx; ret) Zero out the ROP-P switch
EMET 5.1 [6]
It took another release of EMET to fix this issue. Since EMET 5.1 all mitigation settings are stored in read-only memory. Offensive security used the unhooked version of the NtProtectVirtualMemory API to make the CONFIG_STRUCT structure writable again, even though this is more a limitation of exploit mitigation software in general than another disarming flaw.
EMET 5.2 and 5.5x
As far as the author of this blog could determine only two other disarming techniques for EMET were published since the series of publications from Offensive Security. One relying on functionality in EMET that could be used to remove all hooks [7] and one that hijacked the encoded CONFIG_STRUCT pointer that was stored in the writable .data segment of EMET.dll [8]. A review of Offsec’s Advanced Windows Exploitation (AWE) course indicates that Offensive Security applies a variant of their EMET 5.1 disarming technique to later versions of EMET [9].
EMET 5.52 Anti-ROP disarm
Since v5.0 EMET uses two sections of read-only heap memory to store two essential structures. The first one is the EMET_SETTINGS struct, which stores the mitigation settings that can be configured in the Application Configuration window of the EMET GUI.
The second read-only struct – named CONFIG_STRUCT by Offsec – stores a pointer to the EMET_SETTINGS struct, the addresses of all hooked APIs and the addresses of the hook handlers (each hooked API has one).
struct CONFIG_STRUCT { LPVOID lpEMET_SETTINGS; ... // List of API / Hook handler addresses };
In EMET 5.52 memory for the EMET_SETTINGS structure is allocated in a function at offset 0x0002139F within EMET.dll. In this function VirtualAlloc allocates 0x2000 bytes of memory for the EMET_SETTINGS struct. Subsequently, RtlAllocateHeap is used to allocate a block of memory of 0x24 bytes from the process heap for a struct that Offsec named EMETd. The EMETd struct stores the size of the EMET_SETTINGS or CONFIG_STRUCT struct, a pointer to one of these two structs and whether either of these structs is writable or not.
struct EMETd { LPCRITICAL_SECTION CriticalSection; // Reserved DWORD configSize; LPVOID lpConfigPtr; // EMET_SETTINGS / CONFIG_STRUCT ptr DWORD isWritable; };
Finally, the pointer to the EMETd struct is encoded using EncodePointer and the memory storing the EMET_SETTINGS struct is marked as PAGE_READONLY. In a different function EMET_SETTINGS is filled with the actual mitigation settings that are stored in the Registry.
// Code simplified for clarity int *init_emet_settings() { ... SIZE_T dwSize = 0x2000; EMET_SETTINGS *es = (EMET_SETTINGS *)VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_READWRITE); EMETd *emetd = (EMETd *)RtlAllocateHeap(*(HANDLE *)(__readfsdword(0x30u) + 0x18), HEAP_ZERO_MEMORY, 0x24); ... emetd->lpConfigPtr = EMET_SETTINGS; // *((DWORD *)emetd + 6) emetd->configSize = dwSize // *((DWORD *)emetd + 7) emetd->isWritable = 1; // *((DWORD *)emetd + 8) InitializeCriticalSectionAndSpinCount((LPCRITICAL_SECTION)emetd, 0x8000FA0u); dword_100F2B90 = EncodePointer(emetd); mark_config_readonly((LPCRITICAL_SECTION)emetd); return &dword_100F2B90; }
In a similar function (at offset 0x00025734 in EMET.dll) 0x1000 bytes of memory is allocated for the CONFIG_STRUCT struct.
// Code simplified for clarity int *init_config_struct() { ... SIZE_T dwSize = 0x1000; CONFIG_STRUCT *cs = (CONFIG_STRUCT *)VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_READWRITE); EMETd *emetd = (EMETd *)RtlAllocateHeap(*(HANDLE *)(__readfsdword(0x30u) + 0x18), HEAP_ZERO_MEMORY, 0x24); ... emetd->lpConfigPtr = CONFIG_STRUCT; // *((DWORD *)emetd + 6) emetd->configSize = dwSize; // *((DWORD *)emetd + 7) emetd->isWritable = 1; // *((DWORD *)emetd + 8) InitializeCriticalSectionAndSpinCount((LPCRITICAL_SECTION)emetd, 0x8000FA0); dword_100F2BC8 = EncodePointer(emetd); mark_config_readonly((LPCRITICAL_SECTION)emetd); return &dword_100F2BC8; }
In the end the address of the EMET_SETTINGS struct is only retrieved in one of the following two ways in EMET.dll:
1.
EMETd *emetd = (EMETd *)DecodePointer(EMET.dll+0x000f2bc8); CONFIG_STRUCT *cs = (CONFIG_STRUCT *)emetd->lpConfigPtr; EMET_SETTINGS *es = (EMET_SETTINGS *)cs->lpEMET_SETTINGS;
2.
EMETd *emetd = (EMETd *)DecodePointer(EMET.dll+0x000f2b90); EMET_SETTINGS *es = (EMET_SETTINGS *)emetd->lpConfigPtr;
Both the init_emet_settings and the init_config_struct function form the basis of a previously undocumented disarming condition, due to the fact that the two EMETd structs are stored on the process heap. As the process heap has a PAGE_READWRITE protection, any adversary could control the contents of the two EMETd structs. The two structs store a pointer to either the EMET_SETTINGS or the CONFIG_STRUCT struct, making it is possible to control the mitigation settings in one way or another.
Breaking at a DecodePointer(EMET.dll+0x000f2bc8) call in EMET.dll also demonstrates the fact that the EMETd struct is stored in writable heap memory.
Preventing anti-ROP checks
One place where this disarming flaw can be (ab)used is in the function at offset 0x00060d50 within EMET.dll that determines which mitigation checks have to be performed. It should not come as an surprise that the mitigation_handler function relies on data from the CONFIG_STRUCT and EMET_SETTINGS structs.
The function obtains the address of the CONFIG_STRUCT structure by first decoding the address of the second EMETd struct and looking up the CONFIG_STRUCT address stored in the EMETd->lpConfigPtr field. Subsequently, an if-statement checks whether CONFIG_STRUCT->lpEMET_SETTINGS is zero. If it is not zero, the pointer should store the address of the EMET_SETTINGS structure. Finally, EMET_SETTINGS is consulted to find out which mitigation checks have to be performed.
// Code simplified for clarity int mitigation_handler(int a1) { EMETd *emetd = (EMETd *)DecodePointer(dword_100F2BC8); CONFIG_STRUCT *cs = (CONFIG_STRUCT *)emetd->lpConfigPtr; /* * Make this check fail and no anti-ROP mitigation will be performed. * This can be done by making sure ((CONFIG_STRUCT *)EMETd->lpConfigPtr)->lpEMET_SETTINGS is zero */ if ( cs->lpEMET_SETTINGS ) { EMET_SETTINGS *es = (EMET_SETTINGS *)cs->lpEMET_SETTINGS; int mitigation_bitmask = dword_100CC0D8[3 * DecodePointer(*(DWORD *)(a1 + 0x30))]; if ( !(mitigation_bitmask & 0x100000) || es->MandatoryASLR != 1 || ... ) { ... if (...) { ... if ( mitigation_bitmask & 0x40 & es->StackPivot ) stackpivot_mitigation(...); if ( mitigation_bitmask & 0x10 & es->MemProt ) memprot_mitigation(...); if ( mitigation_bitmask & 0x100 & es->BannedFunctions ) bannedfunctions_mitigation(...); if ( *(BYTE *)(es + 0x44) ) { if ( mitigation_bitmask & 0x400 & es->Caller ) caller_mitigation(...); if ( mitigation_bitmask & 0x1000 & es->SimExecFlow ) simexecflow_mitigation(...); } if ( mitigation_bitmask & 0x4 & es->LoadLib ) loadlib_mitigation(...); if ( mitigation_bitmask & 0x4000 & es->ASR & !asr_mitigation(...) ) ... } ... if ( mitigation_bitmask & 0x10000 & es->EAF+ ) eafplus_rop_mitigation(...); ... } else { ... } } else { ... } return ...; }
As mentioned earlier, everything stored in one of the two EMETd structs, can be controlled from an exploit, if we make sure that ((CONFIG_STRUCT *)EMETd->lpConfigPtr)->lpEMET_SETTINGS is zero, then no single anti-ROP mitigation check will ever be performed.
This can be achieved by writing the address of an empty dword to EMETd->lpConfigPtr. If 0x00000000 is for example stored at the address 0x0c2c1200, then writing 0x0c2c1200 to EMETd->lpConfigPtr on the process heap, ensures that all anti-ROP mitigations will be skipped.
Exploitation
Locating the EMETd struct on the process heap is the trickiest part of the exploitation process. The location of the process heap cannot be guessed due to the randomized allocation of heap memory. Two routes can be taken to locate the EMETd struct:
- Invoke DecodePointer(EMET.dll+0x000f2bc8) using a ROP chain
- Perform a series of read operations
Simply calling DecodePointer(EMET.dll+0x000f2bc8) would require multiple ROP gadgets, therefore I will only focus on the route that relies on a series of read operations. This approach requires full read access to the process’ virtual address space, although nowadays most (browser) exploits will already have this ability to bypass ASLR.
Scanning the process heap
As seen in the figure below, the values 0x1000, 0x0 and 0x0 can be found on the process heap right after the EMET->lpConfigPtr field.
These values can function as markers when scanning the process heap. When the location of the EMETd->lpConfigPtr field has been determined, it can be replaced with the address of a location that stores zero. Although this all first requires knowing the location of the process heap.
Locating the process heap
The location of the process heap does not appear to be reliably guessable due to the randomized allocation of heap memory, a reference to the process heap can still be obtained from the PEB via _PEB->ProcessHeap.
Locating the PEB
Searching for references of the PEB reveals that, at least on 32 and 64 bit versions of Windows 7, the .data of ntdll.dll stores the addresses of the following fields from the PEB:
⦁ _PEB->TlsBitmapBits (PEB+0x44)
⦁ _PEB->TlsExpansionBitmapBits (PEB+0x154)
⦁ _PEB->FlsBitmapBits (PEB+0x21c)
Summary
Combining the steps mentioned above results in a disarming solution that only requires read-write access to memory, that does not touch EMET.dll and that only relies on writing a single dword to memory. To summarize, this flaw can be used to prevent most of EMETs mitigations in the following way:
- Determine the location of the PEB using addresses of several fields of the PEB that are stored in the .data segment of ntdll.dll
- Get the address of the process heap from _PEB->ProcessHeap
- Scan the process heap for the EMETd struct
- Overwrite the EMETd->lpConfigPtr field on the process heap with with the address of a location that stores zero.
After the final write action, it has been ensured that all anti-ROP mitigations will be skipped. How about the other mitigations?
Heap Spray Allocation
By default, EMET pre-allocates several regions of memory based on a specified list of addresses (like 0x0a0a0a0a and 0x0c0c0c0c) that might be used by off-the-shelf exploits that contain a heap spray. This mitigation does not form a barrier as a ROP chain and shellcode can be stored at other addresses on the heap.
EAF+
As just about all modern browsers should be properly ASLR’ed at this point in time, bypassing ASLR would require some kind an info leak. This could either require an additional out-of-bounds (OOB) read vulnerability or a vulnerability that can be used to create a read-write primitive to the virtual address space of the exploited process. The second way seems to have become the standard way. Creating this RW primitive can for example be achieved by corrupting the length field of an array object [2]. After this RW primitive has been created, the content of the process’ dlls can be parsed by the exploit in search of APIs like VirtualProtect and ROP gadgets. EAF+ is designed to complicate this type of memory read-outs [1]. EAF+ does not prevent the corruption of the length field of an array object, but should make it harder for an attacker to obtain useful information from loaded dlls. Of the four sub-mitigations of EAF+, the following two are most relevant:
- [Detection of] memory read access to export table pointers of KERNEL32, NTDLL and KERNELBASE originated from specific modules
- [Detection of] memory read accesses to the MZ/PE header of specific modules [1]
Unfortunately, EAF+ appears to be malfunctioning since EMET 5.5, therefore there is no need to bypass it. If it would have worked properly, it would have been a powerful mitigation. The author of this blog post is not aware of any previous publicly documented EAF+ bypasses that do not rely on hardcoding offsets.
Export Address Table Access Filtering (EAF)
The task of EAF is to disrupt shellcodes that parse the export table of certain modules in search of certain APIs [1]. In earlier versions of EMET, hardware breakpoints placed on the IMAGE_EXPORT_DIRECTORY.AddressOfFunctions field in kernel32.dll, kernelbase.dll and ntdll.dll would be used to detect these actions. In EMET 5.5x, EAF appears to use a guard page instead (even though this is not documented anywhere).
In EMET 5.5 and 5.51 this guard page is placed on the Export Directory of ntdll.dll.
Whereas in EMET 5.52 it can be found on the MZ/PE header of ntdll.dll.

Guard page on PE/MZ header of ntdll.dll under EMET 5.52
As no single anti-ROP mitigations will be performed at this point, VirtualProtect can be called from a standard ROP chain to change the protection of the guard page. This subverts the guard page and effectively prevents EAF from functioning, making it is possible to execute a standard Metasploit shellcode.
// NTDLL.dll MZ/PE header protection: PAGE_READONLY + PAGE_GUARD --> PAGE_READONLY rop[i++] = VirtualProtect; // Call VirtualProtect rop[i++] = ...; // Return address // VirtualProtect() arguments rop[i++] = ntdllBase // lpAddress rop[i++] = 0x1000; // dwSize rop[i++] = 0x2; // flNewProtect (PAGE_READONLY) rop[i++] = evAddr + 0x20; // lpflOldProtect
The choice was made to use an unmodified shellcode generated by Metasploit, this has as disadvantage that it will be caught by EAF unless the mitigation gets disabled. Alternatively, a shellcode may be used that extracts addresses of APIs from the Import Address Table (IAT) instead of the Export Address Table (EAT). Previous research indicates that EAF will not prevent the execution of this kind of shellcodes [15, 18], although this has not been tested by the author of this blog.
Reference list
[1] “EMET 5.52 User Guide” by Microsoft, https://www.microsoft.com/en-us/download/details.aspx?id=54265
[2] “ASLR Bypass Apocalypse in Recent Zero-Day Exploits” by Xiaobo Chen (FireEye), https://www.fireeye.com/blog/threat-research/2013/10/aslr-bypass-apocalypse-in-lately-zero-day-exploits.html
Previous EMET disarming flaws
[3] “Fun with Dr. Brown” by Spencer McIntyre (SecureState), http://www.slideshare.net/zeroSteiner/fun-with-dr-brown
[4] “Disarming Enhanced Mitigation Experience Toolkit (EMET)” by Offensive Security, https://www.offensive-security.com/vulndev/disarming-enhanced-mitigation-experience-toolkit-emet/
[5] “Disarming EMET v5.0” by Offensive Security, https://www.offensive-security.com/vulndev/disarming-emet-v5-0/
[6] “Disarming and Bypassing EMET 5.1” by Offensive Security, https://www.offensive-security.com/vulndev/disarming-and-bypassing-emet-5-1/
[7] “Using EMET to disable EMET” by Abdulellah Alsaheel (Mandiant) and Raghav Pande (FireEye), https://www.fireeye.com/blog/threat-research/2016/02/using_emet_to_disabl.html
[8] “Look Mom, I don’t use shellcode” by Moritz Jodeit (Blue Frost Security Research Lab),
https://labs.bluefrostsecurity.de/files/Look_Mom_I_Dont_Use_Shellcode-WP.pdf
[9] “Our pentester on tour: Advanced Windows Exploitation course review” by Wesley Neelen (DearBytes), https://www.dearbytes.com/blog/advanced-windows-exploitation-review/
Previous EMET mitigation bypasses
[10] “Defeating EMET 5.2 & 5.5” by Raghav Pande, https://casual-scrutiny.blogspot.com/2015/03/defeating-emet-52.html
[11] “Defeating EMET Protections (2)” by Raghav Pande, https://casual-scrutiny.blogspot.com/2015/03/defeating-emet-52-protections-2.html
[12] “WoW64 and So Can You – Bypassing EMET With a Single Instruction” by Darren Kemp and Mikhail Davidov (Duo Security), https://duo.com/assets/pdf/wow-64-and-so-can-you.pdf
[13] “Bypassing EMET 4.1” by Jared DeMott (Bromium), https://bromiumlabs.files.wordpress.com/2014/02/bypassing-emet-4-1.pdf
[14] “Mitigating Wow64 Exploit Attacks” by SurfRight, https://hitmanpro.wordpress.com/2015/11/10/mitigating-wow64-exploit-attacks/
[15] “Teaching Old Shellcode New Tricks” by Josh Pitts, https://recon.cx/2017/brussels/resources/slides/RECON-BRX-2017-Teaching_Old_Shellcode_New_Tricks.pdf
Generic (partial) ITW exploit mitigation bypasses
[16] “Deep Dive into ROP Payload Analysis” by Sudeep Singh, https://dl.packetstormsecurity.net/papers/general/rop-deepdive.pdf
[17] “How the Wolf attacked and outsmarted defenses with CVE-2015-3113” by SurfRight, https://hitmanpro.wordpress.com/2015/07/02/how-apt3-evaded-anti-exploits-with-cve-2015-3113/
[18] “CVE-2015-2545 ITW EMET Evasion” by Raghav Pande, https://casual-scrutiny.blogspot.com/2016/02/cve-2015-2545-itw-emet-evasion.html
[19] “Angler Exploit Kit evading EMET” by Raghav Pande and Amit Malik (FireEye), https://www.fireeye.com/blog/threat-research/2016/06/angler_exploit_kite.html
Appendix – Timeline
May 2016 | Disarming flaw reported to MSRC |
1 August 2016 | EMET 5.51 released (flaw unfixed) |
14 November 2016 | EMET 5.52 released (flaw unfixed) |
January 2017 | Last contact with MSRC (“This issue should be addressed in [the next] version whenever it is finally released”) |
Appendix – Pre-allocated heap pages
Specified address | Pre-allocated range |
0x04040404 | 0x04040000 – 0x04042000 |
0x05050505 | 0x05050000 – 0x05052000 |
0x06060606 | 0x06060000 – 0x06062000 |
0x07070707 | 0x07070000 – 0x07072000 |
0x08080808 | 0x08080000 – 0x08082000 |
0x09090909 | 0x09090000 – 0x09092000 |
0x0a040a04 | 0x0a040000 – 0x0a042000 |
0x0a0a0a0a | 0x0a0a0000 – 0x0a0a2000 |
0x0b0b0b0b | 0x0b0b0000 – 0x0b0b2000 |
0x0c0c0c0c | 0x0c0c0000 – 0x0c0c2000 |
0x0d0d0d0d | 0x0d0d0000 – 0x0d0d2000 |
0x0e0e0e0e | 0x0e0e0000 – 0x0e0e2000 |
0x14141414 | 0x14140000 – 0x14143000 |
0x20202020 | 0x20200000 – 0x20204000 |
Appendix – Hooked functions
kernel32!CreateFileA
kernel32!CreateFileMappingA
kernel32!CreateFileMappingWStub
kernel32!CreateFileWImplementation
kernel32!CreateProcessA
kernel32!CreateProcessInternalA
kernel32!CreateProcessInternalW
kernel32!CreateProcessW
kernel32!CreateRemoteThreadStub
kernel32!GetProcessDEPPolicy
kernel32!HeapCreateStub
kernel32!LoadLibraryA
kernel32!LoadLibraryExAStub
kernel32!LoadLibraryExWStub
kernel32!LoadLibraryW
kernel32!MapViewOfFileExStub
kernel32!MapViewOfFileStub
kernel32!SetProcessDEPPolicy
kernel32!VirtualAllocExStub
kernel32!VirtualAllocStub
kernel32!VirtualProtectExStub
kernel32!VirtualProtectStub
kernel32!WinExec
kernel32!WriteProcessMemoryStub
KERNELBASE!CreateFileMappingNumaW
KERNELBASE!CreateFileMappingW
KERNELBASE!CreateFileW
KERNELBASE!CreateRemoteThreadEx
KERNELBASE!CreateRemoteThreadEx
KERNELBASE!HeapCreate
KERNELBASE!LoadLibraryExA
KERNELBASE!LoadLibraryExW
KERNELBASE!MapViewOfFile
KERNELBASE!MapViewOfFileEx
KERNELBASE!VirtualAlloc
KERNELBASE!VirtualAllocEx
KERNELBASE!VirtualProtect
KERNELBASE!VirtualProtectEx
KERNELBASE!WriteProcessMemory
ntdll!LdrHotPatchRoutine
ntdll!LdrLoadDll
ntdll!NtCreateFile
ntdll!NtCreateUserProcess
ntdll!NtProtectVirtualMemory
ntdll!NtUnmapViewOfSection
ntdll!RtlAddVectoredExceptionHandler
ntdll!RtlCreateHeap
ntdll!ZwAllocateVirtualMemory
ntdll!ZwCreateProcess
ntdll!ZwCreateProcessEx
ntdll!ZwCreateSection
ntdll!ZwCreateThreadEx
ntdll!ZwMapViewOfSection
ntdll!ZwWriteVirtualMemory