Last active
May 18, 2025 06:38
-
-
Save Aetopia/d41c5e164bb4c352b7233d617bae6019 to your computer and use it in GitHub Desktop.
An experimental function to inject multiple dynamic link libraries using QueueUserAPC() & CreateRemoteThread().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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