Files
OpenSlinky/Slinky/src/Injector.cpp
T
2026-10-14 22:51:32 -05:00

211 lines
7.8 KiB
C++

#include "Injector.hpp"
#include "ProcessUtil.hpp"
#include "ConsoleUI.hpp"
#include "Resource.h"
#include <cstdio>
#include <vector>
namespace Slinky {
bool Injector::ExtractBundledDll(std::wstring& outDllPath, std::wstring& outLogPath) {
wchar_t tempPath[MAX_PATH] = { 0 };
if (!GetTempPathW(MAX_PATH, tempPath)) {
ConsoleUI::PrintError("Finding temp folder", GetLastError());
return false;
}
wchar_t slinkyTempDir[MAX_PATH] = { 0 };
swprintf_s(slinkyTempDir, L"%lsslinky-%lu", tempPath, GetCurrentProcessId());
if (!CreateDirectoryW(slinkyTempDir, nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) {
ConsoleUI::PrintError("Creating temp folder", GetLastError());
return false;
}
outDllPath = std::wstring(slinkyTempDir) + L"\\SlinkyNative.dll";
outLogPath = std::wstring(slinkyTempDir) + L"\\slinky-native.log";
DeleteFileW(outLogPath.c_str());
HRSRC hRes = FindResourceW(nullptr, MAKEINTRESOURCEW(IDR_BUNDLED_DLL), (LPCWSTR)10);
if (hRes) {
DWORD resSize = SizeofResource(nullptr, hRes);
HGLOBAL hLoad = LoadResource(nullptr, hRes);
if (hLoad && resSize > 0) {
const void* pData = LockResource(hLoad);
if (pData) {
HANDLE hFile = CreateFileW(outDllPath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE) {
DWORD written = 0;
WriteFile(hFile, pData, resSize, &written, nullptr);
CloseHandle(hFile);
return true;
}
}
}
}
wchar_t exePath[MAX_PATH] = { 0 };
if (GetModuleFileNameW(nullptr, exePath, MAX_PATH) > 0) {
wchar_t* lastSlash = wcsrchr(exePath, L'\\');
if (lastSlash) {
*lastSlash = L'\0';
std::wstring localDll = std::wstring(exePath) + L"\\SlinkyNative.dll";
if (GetFileAttributesW(localDll.c_str()) != INVALID_FILE_ATTRIBUTES) {
if (CopyFileW(localDll.c_str(), outDllPath.c_str(), FALSE)) {
return true;
}
outDllPath = localDll;
outLogPath = std::wstring(exePath) + L"\\slinky-native.log";
DeleteFileW(outLogPath.c_str());
return true;
}
}
}
ConsoleUI::SetStatusText("Could not extract bundled DLL.");
ConsoleUI::PrintError("Reading bundled DLL", ERROR_FILE_NOT_FOUND);
return false;
}
bool Injector::InjectDll(DWORD pid, const std::wstring& dllPath) {
if (ProcessUtil::IsModuleLoaded(pid, L"SlinkyNative.dll")) {
ConsoleUI::SetStatusText("Unloading previous instance...");
if (!ProcessUtil::UnloadModule(pid, L"SlinkyNative.dll")) {
ConsoleUI::SetStatusText("Could not unload previous instance.");
return false;
}
Sleep(300);
}
ConsoleUI::SetStatusText("Opening game process");
HANDLE hProcess = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
FALSE, pid);
if (!hProcess) {
ConsoleUI::PrintError("OpenProcess", GetLastError());
return false;
}
ConsoleUI::SetStatusText("Allocating DLL path in game");
SIZE_T pathSizeBytes = (dllPath.length() + 1) * sizeof(wchar_t);
LPVOID pRemotePath = VirtualAllocEx(hProcess, nullptr, pathSizeBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!pRemotePath) {
ConsoleUI::PrintError("Allocating DLL path in game", GetLastError());
CloseHandle(hProcess);
return false;
}
ConsoleUI::SetStatusText("Copying DLL path into game");
if (!WriteProcessMemory(hProcess, pRemotePath, dllPath.c_str(), pathSizeBytes, nullptr)) {
ConsoleUI::PrintError("Copying DLL path into game", GetLastError());
VirtualFreeEx(hProcess, pRemotePath, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
ConsoleUI::SetStatusText("Finding Windows DLL loader");
HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll");
LPTHREAD_START_ROUTINE pLoadLibraryW = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryW");
if (!pLoadLibraryW) {
ConsoleUI::PrintError("Finding Windows DLL loader", GetLastError());
VirtualFreeEx(hProcess, pRemotePath, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
ConsoleUI::SetStatusText("Starting DLL loader in game");
HANDLE hThread = CreateRemoteThread(hProcess, nullptr, 0, pLoadLibraryW, pRemotePath, 0, nullptr);
if (!hThread) {
ConsoleUI::PrintError("Starting DLL loader in game", GetLastError());
VirtualFreeEx(hProcess, pRemotePath, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
ConsoleUI::SetStatusText("Reading DLL loader result");
DWORD waitRes = WaitForSingleObject(hThread, 30000);
if (waitRes != WAIT_OBJECT_0) {
ConsoleUI::SetStatusText("Game did not finish loading the DLL.");
CloseHandle(hThread);
VirtualFreeEx(hProcess, pRemotePath, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
DWORD dwExitCode = 0;
GetExitCodeThread(hThread, &dwExitCode);
CloseHandle(hThread);
VirtualFreeEx(hProcess, pRemotePath, 0, MEM_RELEASE);
CloseHandle(hProcess);
if (dwExitCode == 0) {
ConsoleUI::SetStatusText("LoadLibraryW failed in game process.");
return false;
}
if (!ProcessUtil::WaitForModuleLoad(pid, L"SlinkyNative.dll", 5000)) {
ConsoleUI::SetStatusText("DLL not in game after load attempt.");
return false;
}
ConsoleUI::SetStatusText("Injected successfully");
return true;
}
bool Injector::MonitorStartup(DWORD pid, const std::wstring& logPath) {
HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
ConsoleUI::SetStatusText("Still loading...");
DWORD startTime = GetTickCount();
const DWORD timeoutMs = 30000;
while (GetTickCount() - startTime < timeoutMs) {
if (hProcess && WaitForSingleObject(hProcess, 0) == WAIT_OBJECT_0) {
ConsoleUI::SetStatusText("Game process closed before startup finished.");
CloseHandle(hProcess);
return false;
}
FILE* fp = nullptr;
if (_wfopen_s(&fp, logPath.c_str(), L"r, ccs=UTF-8") == 0 && fp) {
wchar_t line[1024];
while (fgetws(line, 1024, fp)) {
if (wcsstr(line, L"FINAL STATUS: RUNNING")) {
ConsoleUI::SetStatusText("Press Right Shift in Minecraft.");
fclose(fp);
if (hProcess) CloseHandle(hProcess);
return true;
} else if (wcsstr(line, L"FINAL STATUS: RELOADED")) {
ConsoleUI::SetStatusText("Slinky reloaded. Press Right Shift.");
fclose(fp);
if (hProcess) CloseHandle(hProcess);
return true;
} else if (wcsstr(line, L"FINAL STATUS: ALREADY_RUNNING")) {
ConsoleUI::SetStatusText("Already running. Unload from menu first.");
fclose(fp);
if (hProcess) CloseHandle(hProcess);
return false;
} else if (wcsstr(line, L"FINAL STATUS: FAILED")) {
ConsoleUI::SetStatusText("Slinky could not start.");
fclose(fp);
if (hProcess) CloseHandle(hProcess);
return false;
}
}
fclose(fp);
}
Sleep(150);
}
if (hProcess) CloseHandle(hProcess);
ConsoleUI::SetStatusText("DLL loaded but Slinky never confirmed startup.");
return false;
}
} // namespace Slinky