{"record":{"id":"53c9d58a71c276ba","repo":"ramensoftware/windhawk","slug":"ntqueueapcthread-not-found","errorCode":null,"errorMessage":"NtQueueApcThread not found","messagePattern":"NtQueueApcThread not found","errorType":"exception","errorClass":"std::runtime_error","httpStatus":null,"severity":"error","filePath":"src/windhawk/engine/dll_inject.cpp","lineNumber":619,"sourceCode":"        // If you try to queue an APC from a 32 bit process to a 64 bit\n        // process and you use a 32 bit address, you'll get this status code:\n        // [...] STATUS_INVALID_HANDLE\"\n        // https://repnz.github.io/posts/apc/wow64-user-apc/\n        NtQueueApcThread64(hThread, pfnAPC, data, nullptr, nullptr);\n        return;\n    }\n#endif  // _WIN64\n\n    using NtQueueApcThread_t = NTSTATUS(NTAPI*)(\n        _In_ HANDLE ThreadHandle, _In_ PPS_APC_ROUTINE ApcRoutine,\n        _In_opt_ PVOID ApcArgument1, _In_opt_ PVOID ApcArgument2,\n        _In_opt_ PVOID ApcArgument3);\n\n    GET_PROC_ADDRESS_ONCE(NtQueueApcThread_t, pNtQueueApcThread, L\"ntdll.dll\",\n                          \"NtQueueApcThread\");\n\n    if (!pNtQueueApcThread) {\n        throw std::runtime_error(\"NtQueueApcThread not found\");\n    }\n\n#ifdef _WIN64\n    if (targetProcessArch == IMAGE_FILE_MACHINE_I386) {\n        // x64 native to WOW64, encode address.\n        pfnAPC = (PPS_APC_ROUTINE)EncodeWow64ApcRoutine((ULONG64)pfnAPC);\n    }\n#endif  // _WIN64\n\n    THROW_IF_NTSTATUS_FAILED(\n        pNtQueueApcThread(hThread, pfnAPC, data, nullptr, nullptr));\n}\n\nUSHORT GetProcessArch(HANDLE hProcess) {\n    using GetProcessInformation_t = BOOL(WINAPI*)(\n        HANDLE hProcess, PROCESS_INFORMATION_CLASS ProcessInformationClass,\n        LPVOID ProcessInformation, DWORD ProcessInformationSize);\n","sourceCodeStart":601,"sourceCodeEnd":637,"githubUrl":"https://github.com/ramensoftware/windhawk/blob/61d99ed8e182e1af1b60109612b6763ad1b4b74e/src/windhawk/engine/dll_inject.cpp#L601-L637","documentation":"MyQueueUserAPC queues a cross-process APC to inject a DLL, but it needs the undocumented ntdll export NtQueueApcThread. It resolves the function once via GET_PROC_ADDRESS_ONCE; if ntdll does not export it, the code throws runtime_error instead of proceeding. Without this API the APC-based injection path cannot work.","triggerScenarios":"DllInject selecting the APC injection method on a system where GetProcAddress(GetModuleHandle(L\"ntdll.dll\"), \"NtQueueApcThread\") returns NULL — essentially only on very old (pre-Windows XP) or heavily locked-down/modified systems where the export is missing.","commonSituations":"Running the injector on an unsupported or stripped-down Windows variant; security/hardening products that filter ntdll exports; mismatched injection method chosen for the target OS.","solutions":["Verify the host is a supported Windows version (XP or later); NtQueueApcThread has existed since XP, so a missing export indicates an unsupported environment.","Fall back to a different injection method (e.g. CreateRemoteThread) when the APC path is unavailable.","Check that no DLL-injection/anti-cheat software is unhooking or hiding ntdll exports.","Report the Windows version and environment to the vendor if it occurs on a normal modern Windows install."],"exampleFix":"// before\nGET_PROC_ADDRESS_ONCE(NtQueueApcThread_t, pNtQueueApcThread, L\"ntdll.dll\", \"NtQueueApcThread\");\nif (!pNtQueueApcThread) {\n    throw std::runtime_error(\"NtQueueApcThread not found\");\n}\n// after\nGET_PROC_ADDRESS_ONCE(NtQueueApcThread_t, pNtQueueApcThread, L\"ntdll.dll\", \"NtQueueApcThread\");\nif (!pNtQueueApcThread) {\n    // fall back to a supported injection path instead of failing\n    return InjectViaCreateRemoteThread(...);\n}","handlingStrategy":"try-catch","validationCode":"auto hNtdll = GetModuleHandleW(L\"ntdll.dll\");\nbool canUseApcInjection =\n    hNtdll && GetProcAddress(hNtdll, \"NtQueueApcThread\") != nullptr;","typeGuard":"bool HasNtQueueApcThread() {\n    auto h = GetModuleHandleW(L\"ntdll.dll\");\n    return h && GetProcAddress(h, \"NtQueueApcThread\") != nullptr;\n}","tryCatchPattern":"try {\n    DllInject(..., /*method=*/InjectionMethod::Apc, ...);\n} catch (const std::runtime_error& e) {\n    if (std::string_view(e.what()) == \"NtQueueApcThread not found\") {\n        DllInject(..., /*method=*/InjectionMethod::CreateRemoteThread, ...);\n    }\n}","preventionTips":["Check NtQueueApcThread availability before choosing the APC injection method.","Restrict injection to supported Windows versions (XP+).","Keep a fallback injection method implemented in your code path.","Avoid environments where security software strips ntdll exports."],"tags":["windows","ntdll","dll-injection","apc"],"backgroundTag":"missing-dependency","analyzedSha":"61d99ed8e182e1af1b60109612b6763ad1b4b74e","analyzedAt":"2026-09-12T14:02:41.115Z","contentChangedAt":"2026-09-12T14:02:41.115Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}