diff --git a/CMakeLists.txt b/CMakeLists.txt index e3299b9..5454527 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) if(MSVC) add_compile_options(/W3 /utf-8 /MP) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif() add_subdirectory(Slinky) diff --git a/README.md b/README.md index 84b509e..23df5c6 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ --- +

+ https://discord.gg/AEwatvnvSf +

+

jak hack @@ -17,6 +21,12 @@ --- +

+ proof +

+ +--- + ## How to build ### Prerequisites diff --git a/Slinky/include/ProcessUtil.hpp b/Slinky/include/ProcessUtil.hpp index 8bbb173..33e82e1 100644 --- a/Slinky/include/ProcessUtil.hpp +++ b/Slinky/include/ProcessUtil.hpp @@ -18,6 +18,7 @@ public: static std::vector FindMinecraftWindows(); static bool IsModuleLoaded(DWORD pid, const wchar_t* moduleName); static bool WaitForModuleLoad(DWORD pid, const wchar_t* moduleName, DWORD timeoutMs); + static bool UnloadModule(DWORD pid, const wchar_t* moduleName); }; } // namespace Slinky diff --git a/Slinky/src/Injector.cpp b/Slinky/src/Injector.cpp index 4171a76..6231f45 100644 --- a/Slinky/src/Injector.cpp +++ b/Slinky/src/Injector.cpp @@ -70,8 +70,12 @@ bool Injector::ExtractBundledDll(std::wstring& outDllPath, std::wstring& outLogP bool Injector::InjectDll(DWORD pid, const std::wstring& dllPath) { if (ProcessUtil::IsModuleLoaded(pid, L"SlinkyNative.dll")) { - ConsoleUI::SetStatusText("DLL already loaded."); - return true; + 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"); @@ -138,6 +142,11 @@ bool Injector::InjectDll(DWORD pid, const std::wstring& dllPath) { 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; diff --git a/Slinky/src/ProcessUtil.cpp b/Slinky/src/ProcessUtil.cpp index f9a387a..03fe3aa 100644 --- a/Slinky/src/ProcessUtil.cpp +++ b/Slinky/src/ProcessUtil.cpp @@ -97,4 +97,55 @@ bool ProcessUtil::WaitForModuleLoad(DWORD pid, const wchar_t* moduleName, DWORD return false; } +bool ProcessUtil::UnloadModule(DWORD pid, const wchar_t* moduleName) { + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); + if (hSnap == INVALID_HANDLE_VALUE) return false; + + MODULEENTRY32W me; + me.dwSize = sizeof(me); + + HMODULE hTargetModule = nullptr; + if (Module32FirstW(hSnap, &me)) { + do { + if (_wcsicmp(me.szModule, moduleName) == 0 || + wcsstr(me.szExePath, moduleName) != nullptr) + { + hTargetModule = me.hModule; + break; + } + } while (Module32NextW(hSnap, &me)); + } + CloseHandle(hSnap); + + if (!hTargetModule) return true; + + HANDLE hProcess = OpenProcess( + PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION, + FALSE, pid); + if (!hProcess) return false; + + HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll"); + LPTHREAD_START_ROUTINE pFreeLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "FreeLibrary"); + if (!pFreeLibrary) { + CloseHandle(hProcess); + return false; + } + + HANDLE hThread = CreateRemoteThread(hProcess, nullptr, 0, pFreeLibrary, (LPVOID)hTargetModule, 0, nullptr); + if (hThread) { + WaitForSingleObject(hThread, 5000); + CloseHandle(hThread); + } + CloseHandle(hProcess); + + DWORD elapsed = 0; + while (elapsed < 3000) { + if (!IsModuleLoaded(pid, moduleName)) return true; + Sleep(50); + elapsed += 50; + } + + return !IsModuleLoaded(pid, moduleName); +} + } // namespace Slinky diff --git a/SlinkyNative/src/DllMain.cpp b/SlinkyNative/src/DllMain.cpp index f7f59b6..3d15794 100644 --- a/SlinkyNative/src/DllMain.cpp +++ b/SlinkyNative/src/DllMain.cpp @@ -6,11 +6,7 @@ static HMODULE g_hModule = nullptr; -DWORD WINAPI SlinkyNativeWorkerThread(LPVOID lpParam) { - HMODULE hModule = (HMODULE)lpParam; - - SlinkyNative::Logger::Initialize(hModule); - +static DWORD RunWorker(HMODULE hModule) { if (!SlinkyNative::JvmManager::Initialize()) { SlinkyNative::Logger::Log(L"FINAL STATUS: FAILED"); return 1; @@ -27,6 +23,22 @@ DWORD WINAPI SlinkyNativeWorkerThread(LPVOID lpParam) { return (status == SlinkyNative::InjectionStatus::Success || status == SlinkyNative::InjectionStatus::AlreadyRunning) ? 0 : 1; } +DWORD WINAPI SlinkyNativeWorkerThread(LPVOID lpParam) { + HMODULE hModule = (HMODULE)lpParam; + SlinkyNative::Logger::Initialize(hModule); + + DWORD ret = 1; + __try { + ret = RunWorker(hModule); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + SlinkyNative::Logger::Log(L"SlinkyNativeWorkerThread crashed with exception 0x%08X", GetExceptionCode()); + SlinkyNative::Logger::Log(L"FINAL STATUS: FAILED"); + ret = 1; + } + return ret; +} + BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: diff --git a/SlinkyNative/src/PayloadLoader.cpp b/SlinkyNative/src/PayloadLoader.cpp index a10aec6..d8de642 100644 --- a/SlinkyNative/src/PayloadLoader.cpp +++ b/SlinkyNative/src/PayloadLoader.cpp @@ -355,11 +355,7 @@ bool PayloadLoader::DefinePayloadClasses(JNIEnv* env, jobject classLoader, const env->ReleaseStringUTFChars(nameStr, nameChars); if (entryName.rfind("dev/slinky/", 0) == 0 && entryName.size() > 6 && entryName.substr(entryName.size() - 6) == ".class") { - // Convert "dev/slinky/Foo.class" -> "dev.slinky.Foo" - std::string className = entryName.substr(0, entryName.size() - 6); - for (char& c : className) { - if (c == '/') c = '.'; - } + std::string internalName = entryName.substr(0, entryName.size() - 6); // Read bytes jobject inputStream = env->CallObjectMethod(jarObj, jarGetInputStream, entry); @@ -375,7 +371,11 @@ bool PayloadLoader::DefinePayloadClasses(JNIEnv* env, jobject classLoader, const jsize byteLen = env->GetArrayLength(classBytesArr); jbyte* rawBytes = env->GetByteArrayElements(classBytesArr, nullptr); if (rawBytes) { - jclass definedClass = env->DefineClass(nullptr, classLoader, rawBytes, byteLen); + jclass definedClass = env->DefineClass(internalName.c_str(), classLoader, rawBytes, byteLen); + if (!definedClass) { + env->ExceptionClear(); + definedClass = env->DefineClass(nullptr, classLoader, rawBytes, byteLen); + } if (definedClass) { definedCount++; env->DeleteLocalRef(definedClass); diff --git a/assets/proof.png b/assets/proof.png new file mode 100644 index 0000000..8c9acc8 Binary files /dev/null and b/assets/proof.png differ