diff options
-rw-r--r-- | src/tools/connection_patcher/Helper.cpp | 139 | ||||
-rw-r--r-- | src/tools/connection_patcher/Helper.hpp | 11 | ||||
-rw-r--r-- | src/tools/connection_patcher/Patcher.cpp | 75 | ||||
-rw-r--r-- | src/tools/connection_patcher/Patcher.hpp | 29 | ||||
-rw-r--r-- | src/tools/connection_patcher/Patches/Common.hpp | 1 | ||||
-rw-r--r-- | src/tools/connection_patcher/Patterns/Common.hpp | 2 | ||||
-rw-r--r-- | src/tools/connection_patcher/Program.cpp | 175 |
7 files changed, 254 insertions, 178 deletions
diff --git a/src/tools/connection_patcher/Helper.cpp b/src/tools/connection_patcher/Helper.cpp index 6407560f093..5489b54a338 100644 --- a/src/tools/connection_patcher/Helper.cpp +++ b/src/tools/connection_patcher/Helper.cpp @@ -18,15 +18,97 @@ #include "Helper.hpp" -#include <SHA256.h> -#include <Util.h> - -#include <stdexcept> - namespace Connection_Patcher { namespace Helper { + // adapted from http://stackoverflow.com/questions/8593608/how-can-i-copy-a-directory-using-boost-filesystem + void CopyDir(boost::filesystem::path const & source, boost::filesystem::path const & destination) + { + namespace fs = boost::filesystem; + if (!fs::exists(source) || !fs::is_directory(source)) + throw std::invalid_argument("Source directory " + source.string() + " does not exist or is not a directory."); + + if (fs::exists(destination)) + throw std::invalid_argument("Destination directory " + destination.string() + " already exists."); + + if (!fs::create_directory(destination)) + throw std::runtime_error("Unable to create destination directory" + destination.string()); + + for (fs::directory_iterator file(source); file != fs::directory_iterator(); ++file) + { + fs::path current(file->path()); + if (fs::is_directory(current)) + CopyDir(current, destination / current.filename()); + else + fs::copy_file(current, destination / current.filename()); + } + } + + // adapted from http://stackoverflow.com/questions/21422094/boostasio-download-image-file-from-server + void DownloadFile(const std::string& serverName, int port, const std::string& getCommand, std::ostream& out) + { + using namespace boost::asio; + using boost::asio::ip::tcp; + + io_service io_service; + + // Get a list of endpoints corresponding to the server name. + tcp::resolver resolver(io_service); + tcp::resolver::query query(serverName, std::to_string(port)); + tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); + tcp::resolver::iterator end; + + // Try each endpoint until we successfully establish a connection. + tcp::socket socket(io_service); + boost::system::error_code error = boost::asio::error::host_not_found; + while (error && endpoint_iterator != end) + { + socket.close(); + socket.connect(*endpoint_iterator++, error); + } + + boost::asio::streambuf request; + std::ostream request_stream(&request); + + request_stream << "GET " << getCommand << " HTTP/1.0\r\n"; + request_stream << "Host: " << serverName << ':' << port << "\r\n"; + request_stream << "Accept: */*\r\n"; + request_stream << "Connection: close\r\n\r\n"; + + // Send the request. + boost::asio::write(socket, request); + + // Read the response status line. + boost::asio::streambuf response; + boost::asio::read_until(socket, response, "\r\n"); + + // Check that response is OK. + std::istream response_stream(&response); + std::string http_version; + response_stream >> http_version; + unsigned int status_code; + response_stream >> status_code; + std::string status_message; + std::getline(response_stream, status_message); + + // Read the response headers, which are terminated by a blank line. + boost::asio::read_until(socket, response, "\r\n\r\n"); + + // Process the response headers. + std::string header; + while (std::getline(response_stream, header) && header != "\r") + { } + + // Write whatever content we already have to output. + if (response.size() > 0) + out << &response; + + // Read until EOF, writing data to output as we go. + while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error)) + out << &response; + } + Constants::BinaryTypes GetBinaryType(std::vector<unsigned char> const& data) { // Check MS-DOS magic @@ -41,9 +123,7 @@ namespace Connection_Patcher return Constants::BinaryTypes(*reinterpret_cast<uint16_t const*>(data.data() + peOffset + 4)); } else - { return Constants::BinaryTypes(*reinterpret_cast<uint32_t const*>(data.data())); - } } std::string GetFileChecksum(std::vector<unsigned char> const& data) @@ -54,5 +134,50 @@ namespace Connection_Patcher return ByteArrayToHexStr(h.GetDigest(), h.GetLength()); } + + std::set<size_t> SearchOffset(std::vector<unsigned char> const& binary, std::vector<unsigned char> const& pattern) + { + std::set<size_t> offsets; + for (size_t i = 0; (i + pattern.size()) < binary.size(); i++) + { + size_t matches = 0; + + for (size_t j = 0; j < pattern.size(); j++) + { + if (pattern[j] == 0) + { + matches++; + continue; + } + + if (binary[i + j] != pattern[j]) + break; + + matches++; + } + + if (matches == pattern.size()) + { + offsets.insert(i); + i += matches; + } + } + + return offsets.empty() ? throw std::runtime_error("unable to find pattern") : offsets; + } + + uint32_t GetBuildNumber(std::vector<unsigned char> const& binary) + { + std::set<size_t> offsets = SearchOffset(binary, Patterns::Common::BinaryVersion()); + + if (!offsets.empty()) + { + size_t const verOffset = (*offsets.begin()); + std::string ver(&binary[verOffset + 16], &binary[verOffset + 21]); + + return std::stoi(ver); + } + return 0; + } } } diff --git a/src/tools/connection_patcher/Helper.hpp b/src/tools/connection_patcher/Helper.hpp index 4458617708f..e0c4e8dd2de 100644 --- a/src/tools/connection_patcher/Helper.hpp +++ b/src/tools/connection_patcher/Helper.hpp @@ -20,16 +20,27 @@ #define CONNECTION_PATCHER_HELPER_HPP #include "Constants/BinaryTypes.hpp" +#include "Patterns/Common.hpp" +#include <boost/filesystem.hpp> +#include <boost/asio.hpp> #include <vector> +#include <set> #include <string> +#include <stdexcept> +#include <SHA256.h> +#include <Util.h> namespace Connection_Patcher { namespace Helper { + void CopyDir(boost::filesystem::path const & source, boost::filesystem::path const & destination); + void DownloadFile(const std::string& serverName, int port, const std::string& getCommand, std::ostream& out); Constants::BinaryTypes GetBinaryType(std::vector<unsigned char> const& data); std::string GetFileChecksum(std::vector<unsigned char> const& data); + std::set<size_t> SearchOffset(std::vector<unsigned char> const& binary, std::vector<unsigned char> const& pattern); + uint32_t GetBuildNumber(std::vector<unsigned char> const& binary); } } diff --git a/src/tools/connection_patcher/Patcher.cpp b/src/tools/connection_patcher/Patcher.cpp index ba7db70ec12..58adbbdd220 100644 --- a/src/tools/connection_patcher/Patcher.cpp +++ b/src/tools/connection_patcher/Patcher.cpp @@ -17,36 +17,32 @@ */ #include "Patcher.hpp" -#include "Helper.hpp" -#include <boost/filesystem.hpp> - -#include <fstream> -#include <iostream> -#include <iterator> -#include <set> -#include <stdexcept> - -namespace +namespace Connection_Patcher { - std::vector<unsigned char> read_file(boost::filesystem::path const& path) + Patcher::Patcher(boost::filesystem::path file) + : filePath(file) + { + ReadFile(); + binaryType = Helper::GetBinaryType(binary); + } + + void Patcher::ReadFile() { - std::ifstream ifs(path.string(), std::ifstream::binary); + std::ifstream ifs(filePath.string(), std::ifstream::binary); if (!ifs) - throw std::runtime_error("could not open " + path.string()); + throw std::runtime_error("could not open " + filePath.string()); - std::vector<unsigned char> binary; + binary.clear(); ifs >> std::noskipws; ifs.seekg(0, std::ios_base::end); binary.reserve(ifs.tellg()); ifs.seekg(0, std::ios_base::beg); std::copy(std::istream_iterator<unsigned char>(ifs), std::istream_iterator<unsigned char>(), std::back_inserter(binary)); - - return binary; } - void write_file(boost::filesystem::path const& path, std::vector<unsigned char> const& data) + void Patcher::WriteFile(boost::filesystem::path const& path) { std::ofstream ofs(path.string(), std::ofstream::binary); if (!ofs) @@ -54,47 +50,8 @@ namespace ofs << std::noskipws; - std::copy(data.begin(), data.end(), std::ostream_iterator<unsigned char>(ofs)); - } - - std::set<size_t> SearchOffset(std::vector<unsigned char> const& binary, std::vector<unsigned char> const& pattern) - { - std::set<size_t> offsets; - for (size_t i = 0; (i + pattern.size()) < binary.size(); i++) - { - size_t matches = 0; - - for (size_t j = 0; j < pattern.size(); j++) - { - if (pattern[j] == 0) - { - matches++; - continue; - } - - if (binary[i + j] != pattern[j]) - break; - - matches++; - } - - if (matches == pattern.size()) - { - offsets.insert(i); - i += matches; - } - } - - return offsets.empty() ? throw std::runtime_error("unable to find pattern") : offsets; + std::copy(binary.begin(), binary.end(), std::ostream_iterator<unsigned char>(ofs)); } -} - -namespace Connection_Patcher -{ - Patcher::Patcher(boost::filesystem::path file) - : binary(read_file(file)) - , Type(Helper::GetBinaryType(binary)) - {} void Patcher::Patch(std::vector<unsigned char> const& bytes, std::vector<unsigned char> const& pattern) { @@ -104,7 +61,7 @@ namespace Connection_Patcher if (pattern.empty()) return; - for (size_t const offset : SearchOffset(binary, pattern)) + for (size_t const offset : Helper::SearchOffset(binary, pattern)) { std::cout << "Found offset " << offset << std::endl; @@ -119,6 +76,6 @@ namespace Connection_Patcher if (boost::filesystem::exists(out)) boost::filesystem::remove(out); - write_file(out, binary); + WriteFile(out); } } diff --git a/src/tools/connection_patcher/Patcher.hpp b/src/tools/connection_patcher/Patcher.hpp index 7bf29b30ca7..3ce0fdc94b0 100644 --- a/src/tools/connection_patcher/Patcher.hpp +++ b/src/tools/connection_patcher/Patcher.hpp @@ -19,24 +19,33 @@ #ifndef CONNECTION_PATCHER_PATCHER_HPP #define CONNECTION_PATCHER_PATCHER_HPP -#include "Constants/BinaryTypes.hpp" +#include "Helper.hpp" -#include <boost/filesystem.hpp> - -#include <vector> -#include <string> +#include <fstream> +#include <iostream> +#include <iterator> namespace Connection_Patcher { - struct Patcher + class Patcher { + public: + Patcher(boost::filesystem::path file); + + void Patch(std::vector<unsigned char> const& bytes, std::vector<unsigned char> const& pattern); + void Finish(boost::filesystem::path out); + Constants::BinaryTypes GetType() const { return binaryType; } + std::vector<unsigned char> const& GetBinary() const { return binary; } + + private: + void ReadFile(); + void WriteFile(boost::filesystem::path const& path); + + boost::filesystem::path filePath; std::vector<unsigned char> binary; - Constants::BinaryTypes Type; + Constants::BinaryTypes binaryType; - Patcher (boost::filesystem::path file); - void Patch(std::vector<unsigned char> const& bytes, std::vector<unsigned char> const& pattern); - void Finish (boost::filesystem::path out); }; } diff --git a/src/tools/connection_patcher/Patches/Common.hpp b/src/tools/connection_patcher/Patches/Common.hpp index 32e84b1503f..7b721830a26 100644 --- a/src/tools/connection_patcher/Patches/Common.hpp +++ b/src/tools/connection_patcher/Patches/Common.hpp @@ -50,6 +50,7 @@ namespace Connection_Patcher 0xF0, 0xB8, 0x32, 0xCB, 0x5B, 0x66, 0xCE, 0x51, 0x54, 0xB4, 0xC3, 0xD3, 0xD4, 0xDC, 0xB3, 0xEE }; } + static const std::string VersionsFile() { return "trinity6.github.io/%s/%s/build/versi"; }; }; } } diff --git a/src/tools/connection_patcher/Patterns/Common.hpp b/src/tools/connection_patcher/Patterns/Common.hpp index b5ff0343c65..8943e4fd1b1 100644 --- a/src/tools/connection_patcher/Patterns/Common.hpp +++ b/src/tools/connection_patcher/Patterns/Common.hpp @@ -29,6 +29,8 @@ namespace Connection_Patcher { static const std::vector<unsigned char> Portal() { return { '.', 'l', 'o', 'g', 'o', 'n', '.', 'b', 'a', 't', 't', 'l', 'e', '.', 'n', 'e', 't', 0x00 }; } static const std::vector<unsigned char> Modulus() { return { 0x91, 0xD5, 0x9B, 0xB7, 0xD4, 0xE1, 0x83, 0xA5 }; } + static const std::vector<unsigned char> BinaryVersion() { return{ 0x3C, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3E }; } + static const std::vector<unsigned char> VersionsFile() { return { '%', 's', '.', 'p', 'a', 't', 'c', 'h', '.', 'b', 'a', 't', 't', 'l', 'e', '.', 'n', 'e', 't', ':', '1', '1', '1', '9', '/', '%', 's', '/', 'v', 'e', 'r', 's', 'i', 'o', 'n', 's' }; } }; } } diff --git a/src/tools/connection_patcher/Program.cpp b/src/tools/connection_patcher/Program.cpp index 400bafbf6ba..7321871b5ba 100644 --- a/src/tools/connection_patcher/Program.cpp +++ b/src/tools/connection_patcher/Program.cpp @@ -28,23 +28,17 @@ #include <CompilerDefs.h> #include <boost/algorithm/string/replace.hpp> -#include <boost/filesystem.hpp> - -#include <iostream> -#include <fstream> -#include <boost/asio.hpp> +#include <boost/program_options.hpp> #if PLATFORM == PLATFORM_WINDOWS #include <Shlobj.h> #endif -using namespace boost::asio; -using boost::asio::ip::tcp; +namespace po = boost::program_options; namespace Connection_Patcher { - void copyDir(boost::filesystem::path const & source, boost::filesystem::path const & destination); - void GetFile(const std::string& serverName, int port, const std::string& getCommand, std::ostream& out); + po::variables_map GetConsoleArguments(int argc, char** argv); namespace { @@ -61,7 +55,7 @@ namespace Connection_Patcher // if (Authentication::ServerSignature::ClientValidateProof(x)) to if (true) patcher.Patch(PATCH::Password(), PATTERN::Password()); - std::string const moduleName(Helper::GetFileChecksum(patcher.binary) + ".auth"); + std::string const moduleName(Helper::GetFileChecksum(patcher.GetBinary()) + ".auth"); fs::path const modulePath (path / std::string(&moduleName[0], 2) / std::string(&moduleName[2], 2)); @@ -91,7 +85,7 @@ namespace Connection_Patcher boost::filesystem::create_directories(modulePath); std::ofstream outFile(module.string(), std::ofstream::out | std::ofstream::binary); - GetFile("xx.depot.battle.net", 1119, "/" + moduleName, outFile); + Helper::DownloadFile("xx.depot.battle.net", 1119, "/" + moduleName, outFile); outFile.close(); std::cout << "Done.\n"; } @@ -100,7 +94,7 @@ namespace Connection_Patcher } template<typename PATCH, typename PATTERN> - void do_patches(Patcher* patcher, boost::filesystem::path output) + void do_patches(Patcher* patcher, boost::filesystem::path output, bool patchVersionPath, uint32_t buildNumber) { std::cout << "patching Portal\n"; // '.logon.battle.net' -> '' to allow for set portal 'host' @@ -121,100 +115,55 @@ namespace Connection_Patcher // Creep::Instance::LoadModule() to allow for unsigned auth module patcher->Patch(PATCH::Signature(), PATTERN::Signature()); + if (patchVersionPath) + { + std::cout << "patching Versions\n"; + // sever the connection to blizzard's versions file to stop it from updating and replace with custom version + // hardcode %s.patch.battle.net:1119/%s/versions to trinity6.github.io/%s/%s/build/versi + std::string verPatch(Patches::Common::VersionsFile()); + std::string buildPattern = "build"; + + boost::algorithm::replace_all(verPatch, buildPattern, std::to_string(buildNumber)); + std::vector<unsigned char> verVec(verPatch.begin(), verPatch.end()); + patcher->Patch(verVec, Patterns::Common::VersionsFile()); + } + patcher->Finish(output); std::cout << "Patching done.\n"; } } - // adapted from http://stackoverflow.com/questions/8593608/how-can-i-copy-a-directory-using-boost-filesystem - void copyDir(boost::filesystem::path const & source, boost::filesystem::path const & destination) + po::variables_map GetConsoleArguments(int argc, char** argv) { - namespace fs = boost::filesystem; - if (!fs::exists(source) || !fs::is_directory(source)) - throw std::invalid_argument("Source directory " + source.string() + " does not exist or is not a directory."); - - if (fs::exists(destination)) - throw std::invalid_argument("Destination directory " + destination.string() + " already exists."); - - if (!fs::create_directory(destination)) - throw std::runtime_error("Unable to create destination directory" + destination.string()); - - for (fs::directory_iterator file(source); file != fs::directory_iterator(); ++file) + po::options_description all("Allowed options"); + all.add_options() + ("help,h", "print usage message") + ("path", po::value<std::string>()->required(), "Path to the Wow.exe") + ("extra,e", po::value<uint32_t>()->implicit_value(0), "Enable patching of versions file download path. Version can be specified explicitly.") + ; + + po::positional_options_description pos; + pos.add("path", 1); + + po::variables_map vm; + try { - fs::path current(file->path()); - if (fs::is_directory(current)) - copyDir(current, destination / current.filename()); - else - fs::copy_file(current, destination / current.filename()); + po::store(po::command_line_parser(argc, argv).options(all).positional(pos).run(), vm); + po::notify(vm); } - } - - // adapted from http://stackoverflow.com/questions/21422094/boostasio-download-image-file-from-server - void GetFile(const std::string& serverName, int port, const std::string& getCommand, std::ostream& out) - { - boost::asio::io_service io_service; - - // Get a list of endpoints corresponding to the server name. - tcp::resolver resolver(io_service); - tcp::resolver::query query(serverName, std::to_string(port)); - tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - tcp::resolver::iterator end; - - // Try each endpoint until we successfully establish a connection. - tcp::socket socket(io_service); - boost::system::error_code error = boost::asio::error::host_not_found; - while (error && endpoint_iterator != end) + catch (std::exception& e) { - socket.close(); - socket.connect(*endpoint_iterator++, error); + std::cerr << e.what() << "\n"; } - boost::asio::streambuf request; - std::ostream request_stream(&request); - - request_stream << "GET " << getCommand << " HTTP/1.0\r\n"; - request_stream << "Host: " << serverName << ':' << port << "\r\n"; - request_stream << "Accept: */*\r\n"; - request_stream << "Connection: close\r\n\r\n"; - - // Send the request. - boost::asio::write(socket, request); - - // Read the response status line. - boost::asio::streambuf response; - boost::asio::read_until(socket, response, "\r\n"); - - // Check that response is OK. - std::istream response_stream(&response); - std::string http_version; - response_stream >> http_version; - unsigned int status_code; - response_stream >> status_code; - std::string status_message; - std::getline(response_stream, status_message); - - - // Read the response headers, which are terminated by a blank line. - boost::asio::read_until(socket, response, "\r\n\r\n"); - - // Process the response headers. - std::string header; - while (std::getline(response_stream, header) && header != "\r") - { - } + if (vm.count("help")) + std::cout << all << "\n"; - // Write whatever content we already have to output. - if (response.size() > 0) - { - out << &response; - } + if (!vm.count("path")) + throw std::invalid_argument("Wrong number of arguments: Missing client file."); - // Read until EOF, writing data to output as we go. - while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error)) - { - out << &response; - } + return vm; } } @@ -224,11 +173,19 @@ int main(int argc, char** argv) try { - if (argc != 2) - throw std::invalid_argument("Wrong number of arguments: Missing client file."); + bool patchVersionPath = false; + int wowBuild = 0; - std::string const binary_path(argv[1]); + auto vm = GetConsoleArguments(argc, argv); + // exit if help is enabled + if (vm.count("help")) + { + std::cin.get(); + return 0; + } + + std::string const binary_path(std::move(vm["path"].as<std::string>())); std::string renamed_binary_path(binary_path); wchar_t* commonAppData(nullptr); @@ -236,18 +193,32 @@ int main(int argc, char** argv) SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &commonAppData); #endif - std::cout << "Creating patched binaries for "; + std::cout << "Creating patched binary..." << std::endl; Patcher patcher(binary_path); - switch (patcher.Type) + if (vm.count("extra")) + { + patchVersionPath = true; + wowBuild = vm["extra"].as<uint32_t>(); + + if (wowBuild == 0) + wowBuild = Helper::GetBuildNumber(patcher.GetBinary()); + + if (wowBuild == 0 || wowBuild < 10000 || wowBuild > 65535) // Build number has to be exactly 5 characters long + throw std::runtime_error("Could not retrieve build number or it was out of range. Build: " + std::to_string(wowBuild)); + + std::cout << "Determined build number: " << std::to_string(wowBuild) << std::endl; + } + + switch (patcher.GetType()) { case Constants::BinaryTypes::Pe32: std::cout << "Win32 client...\n"; boost::algorithm::replace_all(renamed_binary_path, ".exe", "_Patched.exe"); do_patches<Patches::Windows::x86, Patterns::Windows::x86> - (&patcher, renamed_binary_path); + (&patcher, renamed_binary_path, patchVersionPath, wowBuild); do_module<Patches::Windows::x86, Patterns::Windows::x86> ( "8f52906a2c85b416a595702251570f96d3522f39237603115f2f1ab24962043c.auth" @@ -260,7 +231,7 @@ int main(int argc, char** argv) boost::algorithm::replace_all(renamed_binary_path, ".exe", "_Patched.exe"); do_patches<Patches::Windows::x64, Patterns::Windows::x64> - (&patcher, renamed_binary_path); + (&patcher, renamed_binary_path, patchVersionPath, wowBuild); do_module<Patches::Windows::x64, Patterns::Windows::x64> ( "0a3afee2cade3a0e8b458c4b4660104cac7fc50e2ca9bef0d708942e77f15c1d.auth" @@ -272,12 +243,12 @@ int main(int argc, char** argv) std::cout << "Mac client...\n"; boost::algorithm::replace_all (renamed_binary_path, ".app", " Patched.app"); - copyDir ( boost::filesystem::path(binary_path).parent_path()/*MacOS*/.parent_path()/*Contents*/.parent_path() + Helper::CopyDir(boost::filesystem::path(binary_path).parent_path()/*MacOS*/.parent_path()/*Contents*/.parent_path() , boost::filesystem::path(renamed_binary_path).parent_path()/*MacOS*/.parent_path()/*Contents*/.parent_path() ); do_patches<Patches::Mac::x64, Patterns::Mac::x64> - (&patcher, renamed_binary_path); + (&patcher, renamed_binary_path, patchVersionPath, wowBuild); { namespace fs = boost::filesystem; @@ -291,7 +262,7 @@ int main(int argc, char** argv) break; default: - throw std::runtime_error("Type: " + std::to_string(static_cast<uint32_t>(patcher.Type)) + " not supported!"); + throw std::runtime_error("Type: " + std::to_string(static_cast<uint32_t>(patcher.GetType())) + " not supported!"); } std::cout << "Successfully created your patched binaries.\n"; |