Skip to content

Instantly share code, notes, and snippets.

@Aetopia
Last active May 18, 2025 06:38
Show Gist options
  • Select an option

  • Save Aetopia/d41c5e164bb4c352b7233d617bae6019 to your computer and use it in GitHub Desktop.

Select an option

Save Aetopia/d41c5e164bb4c352b7233d617bae6019 to your computer and use it in GitHub Desktop.
An experimental function to inject multiple dynamic link libraries using QueueUserAPC() & CreateRemoteThread().
#include "Igneous.h"
HRESULT WINAPI Loader_Launch(Game *This, PDWORD Value, PCWSTR *Paths, DWORD Count)
{
// Launch the game.
HRESULT hResult = Game_Launch(This, Value);
// Only proceed if the game succesfully launched.
if (!hResult)
{
// Create a dummy thread on which we will perform dynamic link library injection.
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, *Value),
hThread =
CreateRemoteThread(hProcess, NULL, (SIZE_T){}, (PVOID)LoadLibraryW, NULL, CREATE_SUSPENDED, NULL);
// Create an array to cache addresses of written dynamic link library paths.
PVOID *pAddresses = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HANDLE) * Count);
WCHAR szPath[MAX_PATH] = {};
for (DWORD _ = {}; _ < Count; _++)
{
// Try to expand the specified path to be it's full path, LoadLibrary() only works with MAX_PATH.
GetFullPathNameW(Paths[_], MAX_PATH, szPath, NULL);
if (GetLastError())
{
hResult = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
goto _;
}
// Verify if the specified path is a file, has an extension & is actually loadable.
DWORD dwAttributes = GetFileAttributesW(szPath);
if (dwAttributes == INVALID_FILE_ATTRIBUTES || (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
!PathFindExtensionW(szPath))
{
hResult = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
goto _;
}
else if (!FreeLibrary(LoadLibraryExW(szPath, NULL, DONT_RESOLVE_DLL_REFERENCES)))
{
hResult = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
goto _;
}
// Try assigning an ACL to the file so it is loadable in the target process.
DWORD dwErrCode =
SetNamedSecurityInfoW(szPath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, Acl, NULL);
if (dwErrCode)
{
hResult = HRESULT_FROM_WIN32(dwErrCode);
goto _;
}
// Allocate & write the path to the dynamic link library to the target process.
pAddresses[_] = VirtualAllocEx(hProcess, NULL, sizeof(szPath), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(hProcess, pAddresses[_], szPath, sizeof(szPath), NULL);
// Schedule dynamic link library injection.
QueueUserAPC((PVOID)LoadLibraryW, hThread, (ULONG_PTR)pAddresses[_]);
}
// Flush the queue which will execute the scheduled APCs for dynamic link library injection.
ResumeThread(hThread);
WaitForSingleObject(hThread, INFINITE);
_:
// Discard the thread if something fails, if the thread is already terminated, this does nothing.
TerminateThread(hThread, EXIT_SUCCESS);
// Cleanup allocated resources.
for (DWORD _ = {}; _ < Count; _++)
VirtualFreeEx(hProcess, pAddresses[_], (SIZE_T){}, MEM_RELEASE);
HeapFree(GetProcessHeap(), (DWORD){}, pAddresses);
CloseHandle(hThread);
CloseHandle(hProcess);
}
return hResult;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment