/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "Banner.h" #include "Helper.hpp" #include "Windows.h" #include "Patcher.hpp" #include "Patches/Common.hpp" #include "Patches/Windows.hpp" #include "Patterns/Common.hpp" #include "Patterns/Windows.hpp" #include #include #include #include namespace po = boost::program_options; namespace ClientLauncher { template void do_module(const std::string& moduleName, const boost::filesystem::path& path) { namespace fs = boost::filesystem; fs::path const modulePath (path / std::string(&moduleName[0], 2) / std::string(&moduleName[2], 2)); fs::path const module(modulePath / moduleName); std::cout << "Patching module...\n"; FilePatcher patcher(module); std::cout << "patching Password\n"; // if (Authentication::ServerSignature::ClientValidateProof(x)) to if (true) patcher.Patch(PATCH::Password(), PATTERN::Password()); std::string const newModuleName(Helper::GetFileChecksum(patcher.GetBinary()) + ".auth"); fs::path const newModulePath (path / std::string(&newModuleName[0], 2) / std::string(&newModuleName[2], 2)); if (!fs::exists(newModulePath)) fs::create_directories(newModulePath); if (fs::exists(newModulePath / newModuleName)) fs::permissions(newModulePath / newModuleName, fs::add_perms | fs::others_write | fs::group_write | fs::owner_write); patcher.Finish(newModulePath / newModuleName); fs::permissions(newModulePath / newModuleName, fs::remove_perms | fs::others_write | fs::group_write | fs::owner_write); std::cout << "Patching module finished.\n"; } template void do_client_patches(Patcher* patcher) { std::cout << "Patching client memory...\n"; std::cout << "patching redirect RSA Modulus\n"; patcher->Patch(Patches::Common::Modulus(), Patterns::Common::Modulus()); std::cout << "Patching done.\n"; } bool LaunchAndInject(boost::filesystem::path binaryDir, bool patcherConsole, std::vector const& args) { HANDLE processHandle = INVALID_HANDLE_VALUE; try { #ifdef _M_IX86 char const* executable = "Wow.exe"; char const* dllName = "client_patcher_32.dll"; #else char const* executable = "Wow-64.exe"; char const* dllName = "client_patcher_64.dll"; #endif std::string gameInstallDir = binaryDir.string(); std::cout << "Injecting " << dllName << " into " << executable << " process..\n"; char commandLine[32768] = {}; strcat(commandLine, gameInstallDir.c_str()); strcat(commandLine, "\\"); strcat(commandLine, executable); for (std::string const& arg : args) { strcat(commandLine, " "); strcat(commandLine, arg.c_str()); } char selfName[MAX_PATH] = {}; GetModuleFileNameA(nullptr, selfName, sizeof(selfName)); std::string dllPath = (boost::filesystem::path(selfName).remove_filename() / dllName).string(); if (!boost::filesystem::exists(dllPath)) throw std::exception("Launcher::LaunchAndInject | ERROR: couldn't find client_patcher dll!"); STARTUPINFOA startupInfo{ sizeof(STARTUPINFOA) }; PROCESS_INFORMATION processInfo; if (!CreateProcessA(nullptr, commandLine, nullptr, nullptr, TRUE, CREATE_SUSPENDED, nullptr, gameInstallDir.c_str(), &startupInfo, &processInfo)) return false; processHandle = processInfo.hProcess; MemoryPatcher patcher(processInfo.hProcess); if (!patcher.IsSupportedBuild()) throw std::exception("Launcher::LaunchAndInject | ERROR: Client version different than 4.3.4.15595!"); #ifdef _M_IX86 do_client_patches(&patcher); #else do_client_patches(&patcher); #endif ResumeThread(processInfo.hThread); HMODULE hModule = GetModuleHandle("kernel32.dll"); if (!hModule) throw std::exception("Launcher::LaunchAndInject | ERROR: couldn't get kernel32.dll handle!"); FARPROC loadLibA = GetProcAddress(hModule, "LoadLibraryA"); if (!loadLibA) throw std::exception("Launcher::LaunchAndInject | ERROR: couldn't get LoadLibraryA ProcAddress"); LPVOID allocatedMem = VirtualAllocEx(processInfo.hProcess, nullptr, dllPath.length() + 1, (MEM_RESERVE | MEM_COMMIT), PAGE_READWRITE); if (!allocatedMem) { CloseHandle(processInfo.hProcess); throw std::exception("Launcher::LaunchAndInject | ERROR: VirtualAllocEx failed. Errorcode " + GetLastError()); } if (!WriteProcessMemory(processInfo.hProcess, allocatedMem, dllPath.c_str(), dllPath.length() + 1, nullptr)) { VirtualFreeEx(processInfo.hProcess, allocatedMem, 0, MEM_RELEASE); CloseHandle(processInfo.hProcess); throw std::exception("Launcher::LaunchAndInject | ERROR: WriteProcessMemory failed. Errorcode " + GetLastError()); } WaitForInputIdle(processInfo.hProcess, INFINITE); HANDLE hRemoteThread = CreateRemoteThread(processInfo.hProcess, nullptr, CREATE_SUSPENDED, (LPTHREAD_START_ROUTINE)loadLibA, allocatedMem, 0, nullptr); if (!hRemoteThread) { VirtualFreeEx(processInfo.hProcess, allocatedMem, 0, MEM_RELEASE); CloseHandle(processInfo.hProcess); throw std::exception("Launcher::LaunchAndInject | ERROR: CreateRemoteThread failed. Errorcode " + GetLastError()); } if (patcherConsole) { HRESULT hr = SetThreadDescription(hRemoteThread, L"console"); if (FAILED(hr)) { std::cout << "Launcher::LaunchAndInject | Warning: SetThreadDescription failed\n"; } } WaitForSingleObject(hRemoteThread, INFINITE); VirtualFreeEx(processInfo.hProcess, allocatedMem, 0, MEM_RELEASE); CloseHandle(hRemoteThread); CloseHandle(processInfo.hProcess); CloseHandle(processHandle); return true; } catch (const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; if (processHandle != INVALID_HANDLE_VALUE) TerminateProcess(processHandle, 1); return false; } return true; } std::pair> GetConsoleArguments(int argc, char** argv) { po::options_description all("Allowed options"); all.add_options() ("help,h", "print usage message") ("path", po::value()->default_value(boost::filesystem::current_path().string()), "Path to the Wow.exe") ("console", "Open patcher console") ; po::positional_options_description pos; pos.add("path", 1); po::variables_map vm; std::vector unrecognized; try { po::basic_parsed_options parsed = po::command_line_parser(argc, argv).options(all).positional(pos).allow_unregistered().run(); po::store(parsed, vm); unrecognized = po::collect_unrecognized(parsed.options, po::exclude_positional); po::notify(vm); } catch (std::exception& e) { std::cerr << e.what() << "\n"; } if (vm.count("help")) std::cout << all << "\n"; return std::make_pair(std::move(vm), std::move(unrecognized)); } } int main(int argc, char** argv) { using namespace ClientLauncher; try { Trinity::Banner::Show("client_launcher", [](char const* text) { std::cout << text << std::endl; }, nullptr); auto vm = GetConsoleArguments(argc, argv); // exit if help is enabled if (vm.first.count("help")) { std::cin.get(); return 0; } bool console = false; if (vm.first.count("console")) console = true; wchar_t* commonAppData(nullptr); SHGetKnownFolderPath(FOLDERID_ProgramData, 0, nullptr, &commonAppData); if (!Helper::VerifyRequirements(std::wstring(commonAppData) + std::wstring(L"/Blizzard Entertainment/Battle.net/Cache/"))) return 1; #ifdef _M_IX86 do_module("8f52906a2c85b416a595702251570f96d3522f39237603115f2f1ab24962043c.auth", std::wstring(commonAppData) + std::wstring(L"/Blizzard Entertainment/Battle.net/Cache/")); #else do_module("0a3afee2cade3a0e8b458c4b4660104cac7fc50e2ca9bef0d708942e77f15c1d.auth", std::wstring(commonAppData) + std::wstring(L"/Blizzard Entertainment/Battle.net/Cache/")); #endif boost::filesystem::path binary_path(vm.first["path"].as()); if (!LaunchAndInject(binary_path, console, vm.second)) return 1; return 0; } catch (std::exception const& ex) { std::cerr << "EX: " << ex.what() << std::endl; std::cerr << "An error occurred. Press ENTER to continue..."; std::cin.get(); return 1; } }