diff --git a/.gitmodules b/.gitmodules index 6490abe..aa85270 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,9 @@ [submodule "deps/winreg"] path = deps/winreg url = https://github.com/GiovanniDicanio/WinReg.git +[submodule "deps/picoproto"] + path = deps/picoproto + url = https://github.com/petewarden/picoproto.git +[submodule "deps/xxhash"] + path = deps/xxhash + url = https://github.com/stbrumme/xxhash.git diff --git a/README.md b/README.md index b9415bb..7218be1 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,25 @@ -"**They tried to bury us, they didn't know we were seeds**" - ![code](https://raw.githubusercontent.com/project-bo4/shield-development/master/assets/readme_header.jpg) ## SHIELD A very experimental modification platform for Call of Duty®: Black Ops 4 run by community, aiming at improving both functionality and performance of original game. -This repository is designated as development period repo. final product will be set in a separate repo and is not guaranteed to carry over everything featured here. + +## DEMONWARE +As of May11, 2023 we merged 'demonware' branch into 'master'. it includes a built-in demonware server emulator which allows player to start game without connection to official online servers. -## LIMITATIONS -This repo only contains client-side detours and is not included with server code. As of right now, server code is planned to be private to let us maintain slight control over it and prevent and ban violatars those who cant even obey basic rules and tend to ruin experience of other users and take fun away from them. +## INSTRUCTION +-- you should have publisher files required for play online under LPC folder of your game installation. if its not the case then start original game through battlenet launcher once to get those downloaded. -If you looking forward experimenting and in-dependent development from project-bo4 servers check out [demonware](https://github.com/project-bo4/shield-development/tree/demonware) branch including built-in bo4 dw server emulator. +1- clone repository with its sub-modules and use generate.bat to make visual studio solution then compile project and copy ``d3d11.dll`` into your bo4 folder. + +2- start BlackOps4.exe ## NOTES -- Base SDK(well kinda...) used by this project is developed by [Maurice Heumann](https://github.com/momo5502); Thanks to the guy 🫡 - -- To compile: clone repository using gitbash/powershell terminal; start generate.bat and it will grab all of required sub-modules and make visual studio solution which can be used to compile the client. +- Base SDK(well kinda...) used by this project is developed by [Maurice Heumann](https://github.com/momo5502); Thanks to the guy. - There are some 3rd-party project/tools that have influenced and helped the project in particular ways; If you belive there is something originated from you and want to be credited please contact any of our social media accounts. -- This Project is created purely for educational purposes. its free and open-sourced under gnu license. developers are not responsible or liable for misuse of this product. +- This Project is created purely for educational purposes. its free and open-sourced under gnu license. developers are not responsible or liable for misuse of this product. \ No newline at end of file diff --git a/deps/picoproto b/deps/picoproto new file mode 160000 index 0000000..116b45b --- /dev/null +++ b/deps/picoproto @@ -0,0 +1 @@ +Subproject commit 116b45be9c1b1ab7b44b620cf00209d226d3e776 diff --git a/deps/premake/picoproto.lua b/deps/premake/picoproto.lua new file mode 100644 index 0000000..0eeda3e --- /dev/null +++ b/deps/premake/picoproto.lua @@ -0,0 +1,31 @@ +picoproto = { + source = path.join(dependencies.basePath, "picoproto"), +} + +function picoproto.import() + links { "picoproto" } + picoproto.includes() +end + +function picoproto.includes() + includedirs { + picoproto.source + } +end + +function picoproto.project() + project "picoproto" + language "C" + + picoproto.includes() + + files { + path.join(picoproto.source, "picoproto.cc"), + path.join(picoproto.source, "picoproto.h"), + } + + warnings "Off" + kind "StaticLib" +end + +table.insert(dependencies, picoproto) diff --git a/deps/premake/xxhash.lua b/deps/premake/xxhash.lua new file mode 100644 index 0000000..d0c754a --- /dev/null +++ b/deps/premake/xxhash.lua @@ -0,0 +1,19 @@ +xxhash = { + source = path.join(dependencies.basePath, "xxhash"), +} + +function xxhash.import() + xxhash.includes() +end + +function xxhash.includes() + includedirs { + xxhash.source + } +end + +function xxhash.project() + disablewarnings { "4244" } +end + +table.insert(dependencies, xxhash) diff --git a/deps/xxhash b/deps/xxhash new file mode 160000 index 0000000..c2866db --- /dev/null +++ b/deps/xxhash @@ -0,0 +1 @@ +Subproject commit c2866db364b6ea3a11933e62235ddc166ba18565 diff --git a/source/proxy-dll/component/demonware.cpp b/source/proxy-dll/component/demonware.cpp index 04f890c..d08a664 100644 --- a/source/proxy-dll/component/demonware.cpp +++ b/source/proxy-dll/component/demonware.cpp @@ -1,30 +1,137 @@ #include #include "loader/component_loader.hpp" -#include +#include +#include +#include + +#include "demonware/servers/lobby_server.hpp" +#include "demonware/servers/auth3_server.hpp" +#include "demonware/servers/stun_server.hpp" +#include "demonware/servers/umbrella_server.hpp" +#include "demonware/server_registry.hpp" + +#define TCP_BLOCKING true +#define UDP_BLOCKING false namespace demonware { - const char* blocked_hosts[] = + const char* blocked_hosts[] = { + "us.cdn.blizzard.com", + "us.actual.battle.net", "eu.cdn.blizzard.com", + "eu.actual.battle.net", "level3.blizzard.com", "blzddist1-a.akamaihd.net", - "level3.ssl.blizzard.com", - "eu.actual.battle.net" + "level3.ssl.blizzard.com" }; namespace { + std::atomic_bool exit_server{ false }; + std::thread server_thread{}; + utils::concurrency::container> blocking_sockets{}; + utils::concurrency::container> socket_map{}; + server_registry tcp_servers{}; + server_registry udp_servers{}; std::unordered_map original_imports{}; - namespace network + tcp_server* find_server(const SOCKET socket) + { + return socket_map.access([&](const std::unordered_map& map) -> tcp_server* + { + const auto entry = map.find(socket); + if (entry == map.end()) + { + return nullptr; + } + + return entry->second; + }); + } + + bool socket_link(const SOCKET socket, const uint32_t address) + { + auto* server = tcp_servers.find(address); + if (!server) + { + return false; + } + + socket_map.access([&](std::unordered_map& map) + { + map[socket] = server; + }); + + return true; + } + + void socket_unlink(const SOCKET socket) + { + socket_map.access([&](std::unordered_map& map) + { + const auto entry = map.find(socket); + if (entry != map.end()) + { + map.erase(entry); + } + }); + } + + bool is_socket_blocking(const SOCKET socket, const bool def) + { + return blocking_sockets.access([&](std::unordered_map& map) + { + const auto entry = map.find(socket); + if (entry == map.end()) + { + return def; + } + + return entry->second; + }); + } + + void remove_blocking_socket(const SOCKET socket) + { + blocking_sockets.access([&](std::unordered_map& map) + { + const auto entry = map.find(socket); + if (entry != map.end()) + { + map.erase(entry); + } + }); + } + + void add_blocking_socket(const SOCKET socket, const bool block) + { + blocking_sockets.access([&](std::unordered_map& map) + { + map[socket] = block; + }); + } + + void server_main() + { + exit_server = false; + + while (!exit_server) + { + tcp_servers.frame(); + udp_servers.frame(); + std::this_thread::sleep_for(50ms); + } + } + + namespace io { int getaddrinfo_stub(const char* name, const char* service, const addrinfo* hints, addrinfo** res) { -#ifdef DEBUG - logger::write(logger::LOG_TYPE_DEBUG, "[ NETWORK ]: [getaddrinfo]: \"%s\" \"%s\"", name, service); +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[ network ]: [getaddrinfo]: \"%s\" \"%s\"", name, service); #endif for (auto i = 0; i < ARRAYSIZE(blocked_hosts); ++i) @@ -35,22 +142,299 @@ namespace demonware } } - return WSAHOST_NOT_FOUND; - /* TODO: RE-ROUTE DW HOSTS TO CUSTOM DW SERVER */ + base_server* server = tcp_servers.find(name); + if (!server) + { + server = udp_servers.find(name); + } - return getaddrinfo(name, service, hints, res); + if (!server) + { + return getaddrinfo(name, service, hints, res); + } + + const auto address = utils::memory::get_allocator()->allocate(); + const auto ai = utils::memory::get_allocator()->allocate(); + + auto in_addr = reinterpret_cast(address); + in_addr->sin_addr.s_addr = server->get_address(); + in_addr->sin_family = AF_INET; + + ai->ai_family = AF_INET; + ai->ai_socktype = SOCK_STREAM; + ai->ai_addr = address; + ai->ai_addrlen = sizeof(sockaddr); + ai->ai_next = nullptr; + ai->ai_flags = 0; + ai->ai_protocol = 0; + ai->ai_canonname = const_cast(name); + + *res = ai; + + return 0; + } + + void freeaddrinfo_stub(addrinfo* ai) + { + if (!utils::memory::get_allocator()->find(ai)) + { + return freeaddrinfo(ai); + } + + utils::memory::get_allocator()->free(ai->ai_addr); + utils::memory::get_allocator()->free(ai); + } + + int getpeername_stub(const SOCKET s, sockaddr* addr, socklen_t* addrlen) + { + auto* server = find_server(s); + + if (server) + { + auto in_addr = reinterpret_cast(addr); + in_addr->sin_addr.s_addr = server->get_address(); + in_addr->sin_family = AF_INET; + *addrlen = sizeof(sockaddr); + + return 0; + } + + return getpeername(s, addr, addrlen); + } + + int getsockname_stub(const SOCKET s, sockaddr* addr, socklen_t* addrlen) + { + auto* server = find_server(s); + + if (server) + { + auto in_addr = reinterpret_cast(addr); + in_addr->sin_addr.s_addr = server->get_address(); + in_addr->sin_family = AF_INET; + *addrlen = sizeof(sockaddr); + + return 0; + } + + return getsockname(s, addr, addrlen); } hostent* gethostbyname_stub(const char* name) { -#ifdef DEBUG - logger::write(logger::LOG_TYPE_DEBUG, "[ NETWORK ]: [gethostbyname]: \"%s\"", name); +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[ network ]: [gethostbyname]: \"%s\"", name); #endif + base_server* server = tcp_servers.find(name); + if (!server) + { + server = udp_servers.find(name); + } + + if (!server) + { #pragma warning(push) #pragma warning(disable: 4996) - return gethostbyname(name); + return gethostbyname(name); #pragma warning(pop) + } + + static thread_local in_addr address{}; + address.s_addr = server->get_address(); + + static thread_local in_addr* addr_list[2]{}; + addr_list[0] = &address; + addr_list[1] = nullptr; + + static thread_local hostent host{}; + host.h_name = const_cast(name); + host.h_aliases = nullptr; + host.h_addrtype = AF_INET; + host.h_length = sizeof(in_addr); + host.h_addr_list = reinterpret_cast(addr_list); + + return &host; + } + + int connect_stub(const SOCKET s, const struct sockaddr* addr, const int len) + { + if (len == sizeof(sockaddr_in)) + { + const auto* in_addr = reinterpret_cast(addr); + if (socket_link(s, in_addr->sin_addr.s_addr)) return 0; + } + + return connect(s, addr, len); + } + + int closesocket_stub(const SOCKET s) + { + remove_blocking_socket(s); + socket_unlink(s); + + return closesocket(s); + } + + int send_stub(const SOCKET s, const char* buf, const int len, const int flags) + { + auto* server = find_server(s); + + if (server) + { + server->handle_input(buf, len); + return len; + } + + return send(s, buf, len, flags); + } + + int recv_stub(const SOCKET s, char* buf, const int len, const int flags) + { + auto* server = find_server(s); + + if (server) + { + if (server->pending_data()) + { + return static_cast(server->handle_output(buf, len)); + } + else + { + WSASetLastError(WSAEWOULDBLOCK); + return -1; + } + } + + return recv(s, buf, len, flags); + } + + int sendto_stub(const SOCKET s, const char* buf, const int len, const int flags, const sockaddr* to, + const int tolen) + { + const auto* in_addr = reinterpret_cast(to); + auto* server = udp_servers.find(in_addr->sin_addr.s_addr); + + if (server) + { + server->handle_input(buf, len, { s, to, tolen }); + return len; + } + + return sendto(s, buf, len, flags, to, tolen); + } + + int recvfrom_stub(const SOCKET s, char* buf, const int len, const int flags, struct sockaddr* from, + int* fromlen) + { + // Not supported yet + if (is_socket_blocking(s, UDP_BLOCKING)) + { + return recvfrom(s, buf, len, flags, from, fromlen); + } + + size_t result = 0; + udp_servers.for_each([&](udp_server& server) + { + if (server.pending_data(s)) + { + result = server.handle_output( + s, buf, static_cast(len), from, fromlen); + } + }); + + if (result) + { + return static_cast(result); + } + + return recvfrom(s, buf, len, flags, from, fromlen); + } + + int select_stub(const int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, + struct timeval* timeout) + { + if (exit_server) + { + return select(nfds, readfds, writefds, exceptfds, timeout); + } + + auto result = 0; + std::vector read_sockets; + std::vector write_sockets; + + socket_map.access([&](std::unordered_map& sockets) + { + for (auto& s : sockets) + { + if (readfds) + { + if (FD_ISSET(s.first, readfds)) + { + if (s.second->pending_data()) + { + read_sockets.push_back(s.first); + FD_CLR(s.first, readfds); + } + } + } + + if (writefds) + { + if (FD_ISSET(s.first, writefds)) + { + write_sockets.push_back(s.first); + FD_CLR(s.first, writefds); + } + } + + if (exceptfds) + { + if (FD_ISSET(s.first, exceptfds)) + { + FD_CLR(s.first, exceptfds); + } + } + } + }); + + if ((!readfds || readfds->fd_count == 0) && (!writefds || writefds->fd_count == 0)) + { + timeout->tv_sec = 0; + timeout->tv_usec = 0; + } + + result = select(nfds, readfds, writefds, exceptfds, timeout); + if (result < 0) result = 0; + + for (const auto& socket : read_sockets) + { + if (readfds) + { + FD_SET(socket, readfds); + result++; + } + } + + for (const auto& socket : write_sockets) + { + if (writefds) + { + FD_SET(socket, writefds); + result++; + } + } + + return result; + } + + int ioctlsocket_stub(const SOCKET s, const long cmd, u_long* argp) + { + if (static_cast(cmd) == (FIONBIO)) + { + add_blocking_socket(s, *argp == 0); + } + + return ioctlsocket(s, cmd, argp); } } @@ -69,6 +453,15 @@ namespace demonware original_imports[result->first] = result->second; } + + void check_lpc_files() + { + if (!utils::io::file_exists("LPC/.manifest") || !utils::io::file_exists("LPC/core_ffotd_tu23_639_cf92ecf4a75d3f79.ff") || !utils::io::file_exists("LPC/core_playlists_tu23_639_cf92ecf4a75d3f79.ff")) + { + MessageBoxA(nullptr, "some required LPC files seems to be missing. You need to get and place them manually since this emulator doesnt host and provide those files; read instructions in github documentation for more info.", + "LPC Files Missing", MB_ICONERROR); + } + } } class component final : public component_interface @@ -76,17 +469,39 @@ namespace demonware public: component() { - /* PLACE_HOLDER */ + udp_servers.create("ops4-stun.us.demonware.net"); + udp_servers.create("ops4-stun.eu.demonware.net"); + udp_servers.create("ops4-stun.jp.demonware.net"); + udp_servers.create("ops4-stun.au.demonware.net"); + + tcp_servers.create("ops4-pc-auth3.prod.demonware.net"); + tcp_servers.create("ops4-pc-lobby.prod.demonware.net"); + tcp_servers.create("prod.umbrella.demonware.net"); } void pre_start() override { - register_hook("gethostbyname", network::gethostbyname_stub); - register_hook("getaddrinfo", network::getaddrinfo_stub); + register_hook("send", io::send_stub); + register_hook("recv", io::recv_stub); + register_hook("sendto", io::sendto_stub); + register_hook("recvfrom", io::recvfrom_stub); + register_hook("connect", io::connect_stub); + register_hook("select", io::select_stub); + register_hook("closesocket", io::closesocket_stub); + register_hook("ioctlsocket", io::ioctlsocket_stub); + register_hook("gethostbyname", io::gethostbyname_stub); + register_hook("getaddrinfo", io::getaddrinfo_stub); + register_hook("freeaddrinfo", io::freeaddrinfo_stub); + register_hook("getpeername", io::getpeername_stub); + register_hook("getsockname", io::getsockname_stub); + + check_lpc_files(); } void post_unpack() override { + server_thread = utils::thread::create_named_thread("Demonware", server_main); + utils::hook::set(0x144508469_g, 0x0); // CURLOPT_SSL_VERIFYPEER utils::hook::set(0x144508455_g, 0xAF); // CURLOPT_SSL_VERIFYHOST utils::hook::set(0x144B28D98_g, 0x0); // HTTPS -> HTTP @@ -94,19 +509,16 @@ namespace demonware utils::hook::copy_string(0x144A27C70_g, "http://prod.umbrella.demonware.net"); utils::hook::copy_string(0x144A2BAA0_g, "http://prod.uno.demonware.net/v1.0"); utils::hook::copy_string(0x144A29CB0_g, "http://%s:%d/auth/"); - - - /************************************************************************************************************* - ** TODO : in order to record match, while playing (as host?) game live-streams netcode to the content server - ** continuously troughout the play time. planning to patch it so it streams in memory before uploading - ** full demo at end of match to improve network performance - ** - ** - *************************************************************************************************************/ } void pre_destroy() override { + exit_server = true; + if (server_thread.joinable()) + { + server_thread.join(); + } + for (const auto& import : original_imports) { utils::hook::set(import.first, import.second); diff --git a/source/proxy-dll/component/exception.cpp b/source/proxy-dll/component/exception.cpp index 9878e3c..42f7ceb 100644 --- a/source/proxy-dll/component/exception.cpp +++ b/source/proxy-dll/component/exception.cpp @@ -72,7 +72,7 @@ namespace exception utils::thread::suspend_other_threads(); show_mouse_cursor(); - MessageBoxA(nullptr, error_str.data(), "Project-bo4 ERROR", MB_ICONERROR); + MessageBoxA(nullptr, error_str.data(), "Project-BO4 ERROR", MB_ICONERROR); TerminateProcess(GetCurrentProcess(), exception_data.code); } @@ -84,7 +84,7 @@ namespace exception ++recovery_data.recovery_counts; game::Com_Error(game::ERR_DROP, "Fatal error (0x%08X) at 0x%p (0x%p).\nA minidump has been written.\n\n" - "BOIII has tried to recover your game, but it might not run stable anymore.\n\n" + "Project-BO4 has tried to recover your game, but it might not run stable anymore.\n\n" "Make sure to update your graphics card drivers and install operating system updates!\n" "Closing or restarting Steam might also help.", exception_data.code, exception_data.address, @@ -129,7 +129,7 @@ namespace exception info.append("\r\n"); }; - line("Project-bo4 Crash Dump"); + line("Project-BO4 Crash Dump"); line(""); line(game::version_string); //line("Version: "s + VERSION); diff --git a/source/proxy-dll/component/experimental.cpp b/source/proxy-dll/component/experimental.cpp new file mode 100644 index 0000000..8a7f3f7 --- /dev/null +++ b/source/proxy-dll/component/experimental.cpp @@ -0,0 +1,31 @@ +#include +#include +#include "loader/component_loader.hpp" + +namespace experimental +{ + namespace + { + utils::hook::detour liveinventory_getitemquantity_hook; + int liveinventory_getitemquantity_stub(int controller_index, const int item_id) + { + return 1; // TODO: Higher quanity for zombie consumable loot + + return liveinventory_getitemquantity_hook.invoke(controller_index, item_id); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + liveinventory_getitemquantity_hook.create(0x1437F6ED0_g, liveinventory_getitemquantity_stub); + + utils::hook::set(0x142DD0E10_g, 0xC301B0); // Live_Qos_Finished + utils::hook::set(0x1438C2C70_g, 0xC301B0); // Live_Contracts? related to bdUNK125 + } + }; +} + +REGISTER_COMPONENT(experimental::component) \ No newline at end of file diff --git a/source/proxy-dll/component/logger.cpp b/source/proxy-dll/component/logger.cpp index 532ec1a..93b0aa1 100644 --- a/source/proxy-dll/component/logger.cpp +++ b/source/proxy-dll/component/logger.cpp @@ -8,23 +8,20 @@ namespace logger { - std::string get_type_str(const int type) + const char* LogTypeNames[] = { - switch (type) - { - case 1: - return "INFO"; - case 2: - return "WARN"; - case 3: - return "ERROR"; - default: - return "DEBUG"; - } - } + "DEBUG", + "INFO", + "WARN", + "ERROR" + }; void write(const int type, std::string str) { +#ifndef _DEBUG + if (type == LOG_TYPE_DEBUG) return; +#endif // _DEBUG + #ifdef OUTPUT_DEBUG_API OutputDebugStringA(str.c_str()); #endif // OUTPUT_DEBUG_API @@ -38,7 +35,7 @@ namespace logger stream << "" << std::put_time(t, "%Y-%m-%d %H:%M:%S") << "\t"; #endif // PREPEND_TIMESTAMP - stream << "[ " << get_type_str(type) << " ] " << str << std::endl; + stream << "[ " << LogTypeNames[type] << " ] " << str << std::endl; } void write(const int type, const char* fmt, ...) @@ -64,6 +61,10 @@ namespace logger public: void pre_start() override { +#ifdef REMOVE_PREVIOUS_LOG + utils::io::remove_file("project-bo4.log"); +#endif // REMOVE_PREVIOUS_LOG + write(LOG_TYPE_INFO, "======================================================================================================="); write(LOG_TYPE_INFO, " Project-BO4 Initializing ... %s[0x%llX]", utils::nt::library{}.get_name().c_str(), utils::nt::library{}.get_ptr()); write(LOG_TYPE_INFO, "======================================================================================================="); @@ -75,4 +76,4 @@ namespace logger } }; } -REGISTER_COMPONENT(logger::component) +REGISTER_COMPONENT(logger::component) \ No newline at end of file diff --git a/source/proxy-dll/component/platform.cpp b/source/proxy-dll/component/platform.cpp index 776b0f4..81bae6d 100644 --- a/source/proxy-dll/component/platform.cpp +++ b/source/proxy-dll/component/platform.cpp @@ -1,22 +1,53 @@ #include +#include "platform.hpp" #include "loader/component_loader.hpp" -#include "utils/hook.hpp" +#include +#include +#include +#include +#include #include "WinReg.hpp" +#include "definitions/t8_engine.hpp" namespace platform { + uint64_t bnet_get_userid() + { + static uint64_t userid = 0; + if (!userid) + { + uint32_t default_xuid = utils::cryptography::xxh32::compute(utils::identity::get_sys_username()); + userid = utils::json_config::ReadUnsignedInteger("identity", "xuid", default_xuid); + } + + return userid; + } + + const char* bnet_get_username() + { + static std::string username{}; + if (username.empty()) + { + std::string default_name = utils::identity::get_sys_username(); + username = utils::json_config::ReadString("identity", "name", default_name); + } + + return username.data(); + } + + std::string get_userdata_directory() + { + return std::format("players/bnet-{}", bnet_get_userid()); + } + namespace { - //utils::hook::detour BattleNet_API_RequestAppTicket_Hook; - //bool BattleNet_API_RequestAppTicket_stub(char* sessionToken, char* accountToken) - //{ - // /* PLACE_HOLDER */ - //} - utils::hook::detour PC_TextChat_Print_Hook; void PC_TextChat_Print_Stub(const char* text) { +#ifdef DEBUG logger::write(logger::LOG_TYPE_DEBUG, "PC_TextChat_Print(%s)", text); +#endif } void check_platform_registry() @@ -51,16 +82,19 @@ namespace platform utils::hook::set(0x142307B40_g, 0xC3); // patch#2 Annoying function crashing game; related to BattleNet (TODO : Needs Further Investigation) utils::hook::set(0x143D08290_g, 0x90C301B0); // patch#3 BattleNet_IsModeAvailable? (patch to mov al,1 retn) - utils::hook::nop(0x1437DA454_g, 13); // begin cross-auth even without platform being initialized - utils::hook::set(0x1444E34C0_g, 0xC301B0); // Checks extended_data and extra_data in json object [bdAuthPC::processPlatformData] + utils::hook::nop(0x1437DA454_g, 13); // begin cross-auth even without platform being initialized [LiveConnect_BeginCrossAuthPlatform] + utils::hook::set(0x1444D2D60_g, 0xC301B0); // Auth3 Response RSA signature check [bdAuth::validateResponseSignature] + utils::hook::set(0x1444E34C0_g, 0xC301B0); // Auth3 Response platform extended data check [bdAuthPC::processPlatformData] - //PC_TextChat_Print_Hook.create(0x000000000_g, PC_TextChat_Print_Stub); // Disable useless system messages passed into chat box - //BattleNet_API_RequestAppTicket_Hook.create(0x000000000_g, BattleNet_API_RequestAppTicket_stub); // Implement custom encryption token - } - - int priority() override - { - return 9996; + utils::hook::nop(0x1438994E9_g, 22); // get live name even without platform being initialized [Live_UserSignedIn] + utils::hook::nop(0x1438C3476_g, 22); // get live xuid even without platform being initialized [LiveUser_UserGetXuid] + + utils::hook::jump(0x142325C70_g, bnet_get_username); // detour battlenet username + utils::hook::jump(0x142325CA0_g, bnet_get_userid); // detour battlenet userid + + //PC_TextChat_Print_Hook.create(0x1422D4A20_g, PC_TextChat_Print_Stub); // Disable useless system messages passed into chat box + + logger::write(logger::LOG_TYPE_INFO, "[ PLATFORM ]: BattleTag: '%s', BattleID: '%llu'", bnet_get_username(), bnet_get_userid()); } }; } diff --git a/source/proxy-dll/component/platform.hpp b/source/proxy-dll/component/platform.hpp index 19f45df..ea487fa 100644 --- a/source/proxy-dll/component/platform.hpp +++ b/source/proxy-dll/component/platform.hpp @@ -2,5 +2,7 @@ namespace platform { - /* PLACE_HOLDER */ + uint64_t bnet_get_userid(); + const char* bnet_get_username(); + std::string get_userdata_directory(); } \ No newline at end of file diff --git a/source/proxy-dll/component/scheduler.cpp b/source/proxy-dll/component/scheduler.cpp index a833116..f1435e7 100644 --- a/source/proxy-dll/component/scheduler.cpp +++ b/source/proxy-dll/component/scheduler.cpp @@ -166,8 +166,8 @@ namespace scheduler void post_unpack() override { r_end_frame_hook.create(0x14361E260_g, r_end_frame_stub); // R_EndFrame - main_frame_hook.create(0x14288BAE0_g, main_frame_stub); // Com_Frame - g_run_frame_hook.create(0x142D08FC0_g, server_frame_stub); // G_RunFrame + //main_frame_hook.create(0x14288BAE0_g, main_frame_stub); // Com_Frame + //g_run_frame_hook.create(0x142D08FC0_g, server_frame_stub); // G_RunFrame } void pre_destroy() override diff --git a/source/proxy-dll/definitions/discovery.cpp b/source/proxy-dll/definitions/discovery.cpp index dacbb96..edf480b 100644 --- a/source/proxy-dll/definitions/discovery.cpp +++ b/source/proxy-dll/definitions/discovery.cpp @@ -125,7 +125,7 @@ namespace discovery if (symbols_list.find("com_get_build_version") != symbols_list.end()) { - const char* build_version = utils::hook::invoke(symbols_list["com_get_build_version"]); // Com_GetBuildVersion() + const char* build_version = utils::hook::invoke(symbols_list["com_get_build_version"]); logger::write(logger::LOG_TYPE_DEBUG, "Address-List Discovery Results for BlackOps4 %s", build_version); } @@ -163,5 +163,5 @@ namespace discovery } }; } -REGISTER_COMPONENT(discovery::component) +//REGISTER_COMPONENT(discovery::component) diff --git a/source/proxy-dll/demonware/bit_buffer.cpp b/source/proxy-dll/demonware/bit_buffer.cpp new file mode 100644 index 0000000..2b65be2 --- /dev/null +++ b/source/proxy-dll/demonware/bit_buffer.cpp @@ -0,0 +1,182 @@ +#include +#include "bit_buffer.hpp" + +namespace demonware +{ + bool bit_buffer::read_bytes(const unsigned int bytes, unsigned char* output) + { + return this->read(bytes * 8, output); + } + + bool bit_buffer::read_bool(bool* output) + { + if (!this->read_data_type(1)) + { + return false; + } + + return this->read(1, output); + } + + bool bit_buffer::read_uint32(unsigned int* output) + { + if (!this->read_data_type(8)) + { + return false; + } + + return this->read(32, output); + } + + bool bit_buffer::read_data_type(const char expected) + { + char data_type = 0; + + if (!this->use_data_types_) return true; + if (this->read(5, &data_type)) + { + return (data_type == expected); + } + + return false; + } + + bool bit_buffer::write_bytes(const unsigned int bytes, const char* data) + { + return this->write_bytes(bytes, reinterpret_cast(data)); + } + + bool bit_buffer::write_bytes(const unsigned int bytes, const unsigned char* data) + { + return this->write(bytes * 8, data); + } + + bool bit_buffer::write_bool(bool data) + { + if (this->write_data_type(1)) + { + return this->write(1, &data); + } + + return false; + } + + bool bit_buffer::write_int32(int data) + { + if (this->write_data_type(7)) + { + return this->write(32, &data); + } + + return false; + } + + bool bit_buffer::write_uint32(unsigned int data) + { + if (this->write_data_type(8)) + { + return this->write(32, &data); + } + + return false; + } + + bool bit_buffer::write_data_type(char data) + { + if (!this->use_data_types_) + { + return true; + } + + return this->write(5, &data); + } + + bool bit_buffer::read(unsigned int bits, void* output) + { + if (bits == 0) return false; + if ((this->current_bit_ + bits) > (this->buffer_.size() * 8)) return false; + + int cur_byte = this->current_bit_ >> 3; + auto cur_out = 0; + + const char* bytes = this->buffer_.data(); + const auto output_bytes = static_cast(output); + + while (bits > 0) + { + const int min_bit = (bits < 8) ? bits : 8; + const auto this_byte = bytes[cur_byte++] & 0xFF; + const int remain = this->current_bit_ & 7; + + if ((min_bit + remain) <= 8) + { + output_bytes[cur_out] = BYTE((0xFF >> (8 - min_bit)) & (this_byte >> remain)); + } + else + { + output_bytes[cur_out] = BYTE( + (0xFF >> (8 - min_bit)) & (bytes[cur_byte] << (8 - remain)) | (this_byte >> remain)); + } + + cur_out++; + this->current_bit_ += min_bit; + bits -= min_bit; + } + + return true; + } + + bool bit_buffer::write(const unsigned int bits, const void* data) + { + if (bits == 0) return false; + this->buffer_.resize(this->buffer_.size() + (bits >> 3) + 1); + + int bit = bits; + const auto bytes = const_cast(this->buffer_.data()); + const auto* input_bytes = static_cast(data); + + while (bit > 0) + { + const int bit_pos = this->current_bit_ & 7; + auto rem_bit = 8 - bit_pos; + const auto this_write = (bit < rem_bit) ? bit : rem_bit; + + const BYTE mask = ((0xFF >> rem_bit) | (0xFF << (bit_pos + this_write))); + const int byte_pos = this->current_bit_ >> 3; + + const BYTE temp_byte = (mask & bytes[byte_pos]); + const BYTE this_bit = ((bits - bit) & 7); + const auto this_byte = (bits - bit) >> 3; + + auto this_data = input_bytes[this_byte]; + + const auto next_byte = (((bits - 1) >> 3) > this_byte) ? input_bytes[this_byte + 1] : 0; + + this_data = BYTE((next_byte << (8 - this_bit)) | (this_data >> this_bit)); + + const BYTE out_byte = (~mask & (this_data << bit_pos) | temp_byte); + bytes[byte_pos] = out_byte; + + this->current_bit_ += this_write; + bit -= this_write; + } + + return true; + } + + void bit_buffer::set_use_data_types(const bool use_data_types) + { + this->use_data_types_ = use_data_types; + } + + unsigned int bit_buffer::size() const + { + return this->current_bit_ / 8 + (this->current_bit_ % 8 ? 1 : 0); + } + + std::string& bit_buffer::get_buffer() + { + this->buffer_.resize(this->size()); + return this->buffer_; + } +} diff --git a/source/proxy-dll/demonware/bit_buffer.hpp b/source/proxy-dll/demonware/bit_buffer.hpp new file mode 100644 index 0000000..f2fd5c0 --- /dev/null +++ b/source/proxy-dll/demonware/bit_buffer.hpp @@ -0,0 +1,40 @@ +#pragma once + +namespace demonware +{ + class bit_buffer final + { + public: + bit_buffer() = default; + + explicit bit_buffer(std::string buffer) : buffer_(std::move(buffer)) + { + } + + bool read_bytes(unsigned int bytes, unsigned char* output); + bool read_bool(bool* output); + bool read_uint32(unsigned int* output); + bool read_data_type(char expected); + + bool write_bytes(unsigned int bytes, const char* data); + bool write_bytes(unsigned int bytes, const unsigned char* data); + bool write_bool(bool data); + bool write_int32(int data); + bool write_uint32(unsigned int data); + bool write_data_type(char data); + + bool read(unsigned int bits, void* output); + bool write(unsigned int bits, const void* data); + + void set_use_data_types(bool use_data_types); + + unsigned int size() const; + + std::string& get_buffer(); + + private: + std::string buffer_{}; + unsigned int current_bit_ = 0; + bool use_data_types_ = true; + }; +} diff --git a/source/proxy-dll/demonware/byte_buffer.cpp b/source/proxy-dll/demonware/byte_buffer.cpp new file mode 100644 index 0000000..ee1dbee --- /dev/null +++ b/source/proxy-dll/demonware/byte_buffer.cpp @@ -0,0 +1,365 @@ +#include +#include "byte_buffer.hpp" + +namespace demonware +{ + bool byte_buffer::read_bool(bool* output) + { + if (!this->read_data_type(1)) return false; + return this->read(1, output); + } + + bool byte_buffer::read_byte(char* output) + { + if (!this->read_data_type(2)) return false; + return this->read(1, output); + } + + bool byte_buffer::read_ubyte(unsigned char* output) + { + if (!this->read_data_type(3)) return false; + return this->read(1, output); + } + + bool byte_buffer::read_int16(short* output) + { + if (!this->read_data_type(5)) return false; + return this->read(2, output); + } + + bool byte_buffer::read_uint16(unsigned short* output) + { + if (!this->read_data_type(6)) return false; + return this->read(2, output); + } + + bool byte_buffer::read_int32(int* output) + { + if (!this->read_data_type(7)) return false; + return this->read(4, output); + } + + bool byte_buffer::read_uint32(unsigned int* output) + { + if (!this->read_data_type(8)) return false; + return this->read(4, output); + } + + bool byte_buffer::read_int64(__int64* output) + { + if (!this->read_data_type(9)) return false; + return this->read(8, output); + } + + bool byte_buffer::read_uint64(unsigned __int64* output) + { + if (!this->read_data_type(10)) return false; + return this->read(8, output); + } + + bool byte_buffer::read_float(float* output) + { + if (!this->read_data_type(13)) return false; + return this->read(4, output); + } + + bool byte_buffer::read_string(std::string* output) + { + char* out_data; + if (this->read_string(&out_data)) + { + output->clear(); + output->append(out_data); + return true; + } + + return false; + } + + bool byte_buffer::read_string(char** output) + { + if (!this->read_data_type(16)) return false; + + *output = const_cast(this->buffer_.data()) + this->current_byte_; + this->current_byte_ += strlen(*output) + 1; + + return true; + } + + bool byte_buffer::read_string(char* output, const int length) + { + if (!this->read_data_type(16)) return false; + + strcpy_s(output, length, const_cast(this->buffer_.data()) + this->current_byte_); + this->current_byte_ += strlen(output) + 1; + + return true; + } + + bool byte_buffer::read_blob(std::string* output) + { + char* out_data; + int length; + if (this->read_blob(&out_data, &length)) + { + output->clear(); + output->append(out_data, length); + return true; + } + + return false; + } + + bool byte_buffer::read_blob(char** output, int* length) + { + if (!this->read_data_type(0x13)) + { + return false; + } + + unsigned int size; + this->read_uint32(&size); + + *output = const_cast(this->buffer_.data()) + this->current_byte_; + *length = static_cast(size); + + this->current_byte_ += size; + + return true; + } + + bool byte_buffer::read_struct(std::string* output) + { + char* out_data; + int length; + if (this->read_struct(&out_data, &length)) + { + output->clear(); + output->append(out_data, length); + return true; + } + + return false; + } + + bool byte_buffer::read_struct(char** output, int* length) + { + if (!this->read_data_type(0x17)) + { + return false; + } + + unsigned int size; + this->read_uint32(&size); + + *output = const_cast(this->buffer_.data()) + this->current_byte_; + *length = static_cast(size); + + this->current_byte_ += size; + + return true; + } + + bool byte_buffer::read_data_type(const char expected) + { + if (!this->use_data_types_) return true; + + char type; + this->read(1, &type); + return type == expected; + } + + bool byte_buffer::read_array_header(const unsigned char expected, unsigned int* element_count, + unsigned int* element_size) + { + if (element_count) *element_count = 0; + if (element_size) *element_size = 0; + + if (!this->read_data_type(expected + 100)) return false; + + uint32_t array_size, el_count; + if (!this->read_uint32(&array_size)) return false; + + this->set_use_data_types(false); + this->read_uint32(&el_count); + this->set_use_data_types(true); + + if (element_count) *element_count = el_count; + if (element_size) *element_size = array_size / el_count; + + return true; + } + + bool byte_buffer::write_bool(bool data) + { + this->write_data_type(1); + return this->write(1, &data); + } + + bool byte_buffer::write_byte(char data) + { + this->write_data_type(2); + return this->write(1, &data); + } + + bool byte_buffer::write_ubyte(unsigned char data) + { + this->write_data_type(3); + return this->write(1, &data); + } + + bool byte_buffer::write_int16(short data) + { + this->write_data_type(5); + return this->write(2, &data); + } + + bool byte_buffer::write_uint16(unsigned short data) + { + this->write_data_type(6); + return this->write(2, &data); + } + + bool byte_buffer::write_int32(int data) + { + this->write_data_type(7); + return this->write(4, &data); + } + + bool byte_buffer::write_uint32(unsigned int data) + { + this->write_data_type(8); + return this->write(4, &data); + } + + bool byte_buffer::write_int64(__int64 data) + { + this->write_data_type(9); + return this->write(8, &data); + } + + bool byte_buffer::write_uint64(unsigned __int64 data) + { + this->write_data_type(10); + return this->write(8, &data); + } + + bool byte_buffer::write_data_type(char data) + { + if (!this->use_data_types_) return true; + return this->write(1, &data); + } + + bool byte_buffer::write_float(float data) + { + this->write_data_type(13); + return this->write(4, &data); + } + + bool byte_buffer::write_string(const std::string& data) + { + return this->write_string(data.data()); + } + + bool byte_buffer::write_string(const char* data) + { + this->write_data_type(16); + return this->write(static_cast(strlen(data)) + 1, data); + } + + bool byte_buffer::write_blob(const std::string& data) + { + return this->write_blob(data.data(), INT(data.size())); + } + + bool byte_buffer::write_blob(const char* data, const int length) + { + this->write_data_type(0x13); + this->write_uint32(length); + + return this->write(length, data); + } + + bool byte_buffer::write_struct(const std::string& data) + { + return this->write_struct(data.data(), INT(data.size())); + } + + bool byte_buffer::write_struct(const char* data, const int length) + { + this->write_data_type(0x17); + this->write_uint32(length); + + return this->write(length, data); + } + + bool byte_buffer::write_array_header(const unsigned char type, const unsigned int element_count, + const unsigned int element_size) + { + const auto using_types = this->is_using_data_types(); + this->set_use_data_types(false); + + auto result = this->write_ubyte(type + 100); + + this->set_use_data_types(true); + result &= this->write_uint32(element_count * element_size); + this->set_use_data_types(false); + + result &= this->write_uint32(element_count); + + this->set_use_data_types(using_types); + return result; + } + + bool byte_buffer::read(const int bytes, void* output) + { + if (bytes + this->current_byte_ > this->buffer_.size()) return false; + + std::memmove(output, this->buffer_.data() + this->current_byte_, bytes); + this->current_byte_ += bytes; + + return true; + } + + bool byte_buffer::write(const int bytes, const void* data) + { + this->buffer_.append(static_cast(data), bytes); + this->current_byte_ += bytes; + return true; + } + + bool byte_buffer::write(const std::string& data) + { + return this->write(static_cast(data.size()), data.data()); + } + + void byte_buffer::set_use_data_types(const bool use_data_types) + { + this->use_data_types_ = use_data_types; + } + + size_t byte_buffer::size() const + { + return this->buffer_.size(); + } + + bool byte_buffer::is_using_data_types() const + { + return use_data_types_; + } + + std::string& byte_buffer::get_buffer() + { + return this->buffer_; + } + + std::string byte_buffer::get_remaining() + { + return std::string(this->buffer_.begin() + this->current_byte_, this->buffer_.end()); + } + + bool byte_buffer::has_more_data() const + { + return this->buffer_.size() > this->current_byte_; + } +} diff --git a/source/proxy-dll/demonware/byte_buffer.hpp b/source/proxy-dll/demonware/byte_buffer.hpp new file mode 100644 index 0000000..17299af --- /dev/null +++ b/source/proxy-dll/demonware/byte_buffer.hpp @@ -0,0 +1,75 @@ +#pragma once + +namespace demonware +{ + class byte_buffer final + { + public: + byte_buffer() = default; + + explicit byte_buffer(std::string buffer) : buffer_(std::move(buffer)) + { + } + + bool read_bool(bool* output); + bool read_byte(char* output); + bool read_ubyte(unsigned char* output); + bool read_int16(short* output); + bool read_uint16(unsigned short* output); + bool read_int32(int* output); + bool read_uint32(unsigned int* output); + bool read_int64(__int64* output); + bool read_uint64(unsigned __int64* output); + bool read_float(float* output); + bool read_string(char** output); + bool read_string(char* output, int length); + bool read_string(std::string* output); + bool read_blob(char** output, int* length); + bool read_blob(std::string* output); + bool read_struct(char** output, int* length); + bool read_struct(std::string* output); + bool read_data_type(char expected); + + bool read_array_header(unsigned char expected, unsigned int* element_count, + unsigned int* element_size = nullptr); + + bool write_bool(bool data); + bool write_byte(char data); + bool write_ubyte(unsigned char data); + bool write_int16(short data); + bool write_uint16(unsigned short data); + bool write_int32(int data); + bool write_uint32(unsigned int data); + bool write_int64(__int64 data); + bool write_uint64(unsigned __int64 data); + bool write_data_type(char data); + bool write_float(float data); + bool write_string(const char* data); + bool write_string(const std::string& data); + bool write_blob(const char* data, int length); + bool write_blob(const std::string& data); + bool write_struct(const char* data, int length); + bool write_struct(const std::string& data); + + bool write_array_header(unsigned char type, unsigned int element_count, unsigned int element_size); + + bool read(int bytes, void* output); + bool write(int bytes, const void* data); + bool write(const std::string& data); + + void set_use_data_types(bool use_data_types); + size_t size() const; + + bool is_using_data_types() const; + + std::string& get_buffer(); + std::string get_remaining(); + + bool has_more_data() const; + + private: + std::string buffer_; + size_t current_byte_ = 0; + bool use_data_types_ = true; + }; +} diff --git a/source/proxy-dll/demonware/data_types.hpp b/source/proxy-dll/demonware/data_types.hpp new file mode 100644 index 0000000..2e19229 --- /dev/null +++ b/source/proxy-dll/demonware/data_types.hpp @@ -0,0 +1,189 @@ +#pragma once + +#include "byte_buffer.hpp" + +namespace demonware +{ + class bdTaskResult + { + public: + virtual ~bdTaskResult() = default; + + virtual void serialize(byte_buffer*) + { + } + + virtual void deserialize(byte_buffer*) + { + } + }; + + class bdTimeStamp final : public bdTaskResult + { + public: + uint32_t unix_time; + + void serialize(byte_buffer* buffer) override + { + buffer->write_uint32(this->unix_time); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_uint32(&this->unix_time); + } + }; + + class bdDMLInfo : public bdTaskResult + { + public: + std::string country_code; // Char [3] + std::string country; // Char [65] + std::string region; // Char [65] + std::string city; // Char [129] + float latitude; + float longitude; + uint32_t asn; // Autonomous System Number. + std::string timezone; + + void serialize(byte_buffer* buffer) override + { + buffer->write_string(this->country_code); + buffer->write_string(this->country); + buffer->write_string(this->region); + buffer->write_string(this->city); + buffer->write_float(this->latitude); + buffer->write_float(this->longitude); + buffer->write_uint32(this->asn); + buffer->write_string(this->timezone); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_string(&this->country_code); + buffer->read_string(&this->country); + buffer->read_string(&this->region); + buffer->read_string(&this->city); + buffer->read_float(&this->latitude); + buffer->read_float(&this->longitude); + buffer->read_uint32(&this->asn); + buffer->read_string(&this->timezone); + } + }; + + class bdDMLHierarchicalInfo final : public bdDMLInfo + { + public: + uint32_t m_tier0; + uint32_t m_tier1; + uint32_t m_tier2; + uint32_t m_tier3; + uint32_t m_confidence; + + void serialize(byte_buffer* buffer) override + { + bdDMLInfo::serialize(buffer); + + buffer->write_uint32(this->m_tier0); + buffer->write_uint32(this->m_tier1); + buffer->write_uint32(this->m_tier2); + buffer->write_uint32(this->m_tier3); + buffer->write_uint32(this->m_confidence); + } + + void deserialize(byte_buffer* buffer) override + { + bdDMLInfo::deserialize(buffer); + + buffer->read_uint32(&this->m_tier0); + buffer->read_uint32(&this->m_tier1); + buffer->read_uint32(&this->m_tier2); + buffer->read_uint32(&this->m_tier3); + buffer->read_uint32(&this->m_confidence); + } + }; + + class bdStructedDataBuffer final : public bdTaskResult + { + public: + std::string structed_data_protobuffer; + + void serialize(byte_buffer* buffer) override + { + buffer->write_struct(this->structed_data_protobuffer); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_struct(&this->structed_data_protobuffer); + } + }; + + class bdMarketplaceInventory final : public bdTaskResult + { + public: + uint64_t m_userID; + std::string m_accountType; + uint32_t m_itemId; + uint32_t m_itemQuantity; + uint32_t m_itemXp; + std::string m_itemData; + uint32_t m_expireDateTime; + int64_t m_expiryDuration; + uint16_t m_collisionField; + uint32_t m_modDateTime; + uint32_t m_customSourceType; + + void serialize(byte_buffer* buffer) override + { + buffer->write_uint64(this->m_userID); + buffer->write_string(this->m_accountType); + buffer->write_uint32(this->m_itemId); + buffer->write_uint32(this->m_itemQuantity); + buffer->write_uint32(this->m_itemXp); + buffer->write_blob(this->m_itemData); + buffer->write_uint32(this->m_expireDateTime); + buffer->write_int64(this->m_expiryDuration); + buffer->write_uint16(this->m_collisionField); + buffer->write_uint32(this->m_modDateTime); + buffer->write_uint32(this->m_customSourceType); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_uint64(&this->m_userID); + buffer->read_string(&this->m_accountType); + buffer->read_uint32(&this->m_itemId); + buffer->read_uint32(&this->m_itemQuantity); + buffer->read_uint32(&this->m_itemXp); + buffer->read_blob(&this->m_itemData); + buffer->read_uint32(&this->m_expireDateTime); + buffer->read_int64(&this->m_expiryDuration); + buffer->read_uint16(&this->m_collisionField); + buffer->read_uint32(&this->m_modDateTime); + buffer->read_uint32(&this->m_customSourceType); + } + }; + + class bdPublicProfileInfo final : public bdTaskResult + { + public: + uint64_t m_entityID; + int32_t m_VERSION; + std::string m_ddl; + + void serialize(byte_buffer* buffer) override + { + buffer->write_uint64(this->m_entityID); + buffer->write_int32(this->m_VERSION); + buffer->write_blob(this->m_ddl); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_uint64(&this->m_entityID); + buffer->read_int32(&this->m_VERSION); + buffer->read_blob(&this->m_ddl); + } + }; +} diff --git a/source/proxy-dll/demonware/keys.cpp b/source/proxy-dll/demonware/keys.cpp new file mode 100644 index 0000000..3f420c4 --- /dev/null +++ b/source/proxy-dll/demonware/keys.cpp @@ -0,0 +1,141 @@ +#include +#include "keys.hpp" + +#include +#include +#include + +#include "resource.hpp" +#include + + +namespace demonware +{ + struct data_t + { + char m_session_key[24]; + char m_response[8]; + char m_hmac_key[20]; + char m_enc_key[16]; + char m_dec_key[16]; + } data{}; + + std::string packet_buffer; + + void calculate_hmacs(const char* dataxx, const unsigned int data_size, + const char* key, const unsigned int key_size, + char* dest, const unsigned int dest_size) + { + char buffer[512]; + unsigned int pos = 0; + unsigned int out_offset = 0; + char count = 1; + std::string result; + + // buffer add key + std::memcpy(&buffer[pos], key, key_size); + pos += key_size; + + // buffer add count + buffer[pos] = count; + pos++; + + // calculate hmac + result = utils::cryptography::hmac_sha1::compute(std::string(buffer, pos), std::string(dataxx, data_size)); + + // save output + std::memcpy(dest, result.data(), std::min(20u, (dest_size - out_offset))); + out_offset = 20; + + // second loop + while (true) + { + // if we filled the output buffer, exit + if (out_offset >= dest_size) + break; + + // buffer add last result + pos = 0; + std::memcpy(&buffer[pos], result.data(), 20); + pos += 20; + + // buffer add key + std::memcpy(&buffer[pos], key, key_size); + pos += key_size; + + // buffer add count + count++; + buffer[pos] = count; + pos++; + + // calculate hmac + result = utils::cryptography::hmac_sha1::compute(std::string(buffer, pos), std::string(dataxx, data_size)); + + // save output + std::memcpy(dest + out_offset, result.data(), std::min(20u, (dest_size - out_offset))); + out_offset += 20; + } + } + + void derive_keys_iw8() + { + std::string BD_AUTH_TRAFFIC_SIGNING_KEY = utils::nt::load_resource(DW_AUTH_TRAFFIC_SIGNING_KEY); + + const auto packet_hash = utils::cryptography::sha1::compute(packet_buffer); + + char out_1[24]; + calculate_hmacs(data.m_session_key, 24, BD_AUTH_TRAFFIC_SIGNING_KEY.data(), 294, out_1, 24); + + auto data_3 = utils::cryptography::hmac_sha1::compute(std::string(out_1, 24), packet_hash); + + char out_2[16]; + calculate_hmacs(data_3.data(), 20, "CLIENTCHAL", 10, out_2, 16); + + char out_3[72]; + calculate_hmacs(data_3.data(), 20, "BDDATA", 6, out_3, 72); + + + std::memcpy(data.m_response, &out_2[8], 8); + std::memcpy(data.m_hmac_key, &out_3[20], 20); + std::memcpy(data.m_dec_key, &out_3[40], 16); + std::memcpy(data.m_enc_key, &out_3[56], 16); + +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[DW] Response id: %s", utils::string::dump_hex(std::string(&out_2[8], 8)).data()); + logger::write(logger::LOG_TYPE_DEBUG, "[DW] Hash verify: %s", utils::string::dump_hex(std::string(&out_3[20], 20)).data()); + logger::write(logger::LOG_TYPE_DEBUG, "[DW] AES dec key: %s", utils::string::dump_hex(std::string(&out_3[40], 16)).data()); + logger::write(logger::LOG_TYPE_DEBUG, "[DW] AES enc key: %s", utils::string::dump_hex(std::string(&out_3[56], 16)).data()); + logger::write(logger::LOG_TYPE_DEBUG, "[DW] Bravo 6, going dark."); +#endif + } + + void queue_packet_to_hash(const std::string& packet) + { + packet_buffer.append(packet); + } + + void set_session_key(const std::string& key) + { + std::memcpy(data.m_session_key, key.data(), 24); + } + + std::string get_decrypt_key() + { + return std::string(data.m_dec_key, 16); + } + + std::string get_encrypt_key() + { + return std::string(data.m_enc_key, 16); + } + + std::string get_hmac_key() + { + return std::string(data.m_hmac_key, 20); + } + + std::string get_response_id() + { + return std::string(data.m_response, 8); + } +} diff --git a/source/proxy-dll/demonware/keys.hpp b/source/proxy-dll/demonware/keys.hpp new file mode 100644 index 0000000..9cecbcf --- /dev/null +++ b/source/proxy-dll/demonware/keys.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace demonware +{ + void derive_keys_iw8(); + void queue_packet_to_hash(const std::string& packet); + void set_session_key(const std::string& key); + std::string get_decrypt_key(); + std::string get_encrypt_key(); + std::string get_hmac_key(); + std::string get_response_id(); +} diff --git a/source/proxy-dll/demonware/objects.cpp b/source/proxy-dll/demonware/objects.cpp new file mode 100644 index 0000000..838cdea --- /dev/null +++ b/source/proxy-dll/demonware/objects.cpp @@ -0,0 +1,627 @@ +#include +#include +#include +#include +#include +#include "protobuf_helper.hpp" + +#include "objects.hpp" + +#include "resource.hpp" +#include + +#define PUBLISHER_OBJECTS_ENUMERATE_LPC_DIR + +namespace demonware +{ + std::string HexStringToBinaryString(const std::string& hex_str) + { + std::string data{}; + + for (unsigned int i = 0; i < hex_str.length(); i += 2) { + std::string byteString = hex_str.substr(i, 2); + char byte = (char)strtol(byteString.c_str(), NULL, 16); + data.push_back(byte); + } + + return data; + } + + std::string get_publisher_file_checksum(std::string file) + { + std::string file_data; + if (!utils::io::read_file(file, &file_data)) return ""; + + std::string checksum_md5 = utils::cryptography::md5::compute(file_data); + + return utils::cryptography::base64::encode(checksum_md5); + } + + std::vector get_publisher_objects_list(const std::string& category) + { + std::vector result; + +#ifdef PUBLISHER_OBJECTS_ENUMERATE_LPC_DIR + std::vector files = utils::io::list_files("LPC"); + + for (std::string file : files) + { + if (!utils::string::ends_with(file, ".ff")) continue; + + int64_t timestamp = static_cast(time(nullptr)); + result.push_back({ "treyarch", utils::io::file_name(file), get_publisher_file_checksum(file), utils::io::file_size(file), timestamp, timestamp, "" }); + } +#else // PUBLISHER_OBJECTS_ENUMERATE_CSV_LIST + const auto objects_list_csv = utils::nt::load_resource(DW_PUBLISHER_OBJECTS_LIST); + std::vector items = utils::string::split(objects_list_csv, "\r\n"); // WTF!? + + for (std::string item : items) + { + std::string checksum = utils::cryptography::base64::encode(HexStringToBinaryString(utils::string::split(item, ',')[2])); + std::string name = utils::string::split(item, ',')[0]; + uint64_t length = std::stoull(utils::string::split(item, ',')[1]); + + int64_t timestamp = static_cast(time(nullptr)); + result.push_back({ "treyarch", name, checksum, length, timestamp, timestamp, "" }); + } +#endif // PUBLISHER_OBJECTS_ENUMERATE_LPC_DIR + + return result; + } + + std::string generate_publisher_objects_list_json(const std::string& category) + { + rapidjson::StringBuffer json_buffer{}; + rapidjson::PrettyWriter json_writer(json_buffer); + + json_writer.StartObject(); + + json_writer.Key("next"); + json_writer.String("TODO"); + + json_writer.Key("nextPageToken"); + json_writer.Null(); + + json_writer.Key("objects"); + json_writer.StartArray(); + + std::vector objects = get_publisher_objects_list(category); + + for (objectMetadata object : objects) + { + json_writer.StartObject(); + + json_writer.Key("owner"); + json_writer.String(object.owner); + + json_writer.Key("expiresOn"); + json_writer.Uint(0); + + json_writer.Key("name"); + json_writer.String(object.name); + + json_writer.Key("checksum"); + json_writer.String(object.checksum); + + json_writer.Key("acl"); + json_writer.String("public"); + + json_writer.Key("objectID"); + json_writer.Uint(0); + json_writer.Key("contentID"); + json_writer.Null(); + json_writer.Key("objectVersion"); + json_writer.String(""); + json_writer.Key("contentVersion"); + json_writer.Null(); + + json_writer.Key("contentLength"); + json_writer.Uint64(object.contentLength); + + json_writer.Key("context"); + json_writer.String("t8-bnet"); + + json_writer.Key("category"); + json_writer.Null(); + + json_writer.Key("created"); + json_writer.Uint64(object.created); + + json_writer.Key("modified"); + json_writer.Uint64(object.modified); + + json_writer.Key("extraData"); + json_writer.Null(); + json_writer.Key("extraDataSize"); + json_writer.Null(); + json_writer.Key("summaryContentLength"); + json_writer.Null(); + json_writer.Key("summaryChecksum"); + json_writer.Null(); + json_writer.Key("hasSummary"); + json_writer.Bool(false); + + json_writer.EndObject(); + } + + json_writer.EndArray(); + + json_writer.EndObject(); + + return json_buffer.GetString(); + } + + std::string get_user_file_path(const std::string& file) + { + return std::format("{}/{}", platform::get_userdata_directory(), file); + } + + std::string get_user_file_checksum(std::string file_path) + { + std::string file_data; + if (!utils::io::read_file(file_path, &file_data)) return ""; + + return std::to_string(utils::cryptography::xxh32::compute(file_data)); + } + + std::string get_user_file_content(std::string file_path) + { + std::string file_data; + if (!utils::io::read_file(file_path, &file_data)) return ""; + + return utils::cryptography::base64::encode(file_data); + } + + std::string deliver_user_objects_vectorized_json(std::vector requested_items) + { + rapidjson::StringBuffer json_buffer{}; + rapidjson::PrettyWriter json_writer(json_buffer); + + json_writer.StartObject(); + + json_writer.Key("objects"); + json_writer.StartArray(); + + for (size_t i = 0; i < requested_items.size(); i++) + { + if (requested_items[i].contentLength == 0 || requested_items[i].contentURL.empty()) + { + continue; + } + + //std::string file_path = get_user_file_path(requested_items[i].owner, requested_items[i].name); + + json_writer.StartObject(); + + json_writer.Key("metadata"); + json_writer.StartObject(); + + json_writer.Key("owner"); + json_writer.String(requested_items[i].owner.data()); + + json_writer.Key("expiresOn"); + json_writer.Uint(0); + + json_writer.Key("name"); + json_writer.String(requested_items[i].name.data()); + + json_writer.Key("checksum"); + json_writer.String(requested_items[i].checksum.data()); + + json_writer.Key("acl"); + json_writer.String("public"); + + json_writer.Key("objectID"); + json_writer.Uint(0); + json_writer.Key("contentID"); + json_writer.Null(); + json_writer.Key("objectVersion"); + json_writer.String(""); + json_writer.Key("contentVersion"); + json_writer.Null(); + + json_writer.Key("contentLength"); + json_writer.Uint64(requested_items[i].contentLength); + + json_writer.Key("context"); + json_writer.String("t8-bnet"); + + json_writer.Key("category"); + json_writer.Null(); + + json_writer.Key("created"); + json_writer.Uint64(requested_items[i].created); + + json_writer.Key("modified"); + json_writer.Uint64(requested_items[i].modified); + + json_writer.Key("extraData"); + json_writer.Null(); + json_writer.Key("extraDataSize"); + json_writer.Null(); + json_writer.Key("summaryContentLength"); + json_writer.Null(); + json_writer.Key("summaryChecksum"); + json_writer.Null(); + json_writer.Key("hasSummary"); + json_writer.Bool(false); + + json_writer.EndObject(); + + json_writer.Key("content"); + json_writer.String(requested_items[i].contentURL.data()); + + json_writer.Key("requestIndex"); + json_writer.Uint64(i); + + json_writer.EndObject(); + } + + json_writer.EndArray(); + + json_writer.Key("errors"); + json_writer.StartArray(); + for (size_t i = 0; i < requested_items.size(); i++) + { + if (requested_items[i].contentLength == 0 || requested_items[i].contentURL.empty()) + { + json_writer.StartObject(); + + json_writer.Key("requestIndex"); + json_writer.Uint64(i); + json_writer.Key("owner"); + json_writer.String(requested_items[i].owner.data()); + json_writer.Key("name"); + json_writer.String(requested_items[i].name.data()); + json_writer.Key("error"); + json_writer.String("Error:ClientError:NotFound"); + + json_writer.EndObject(); + } + } + json_writer.EndArray(); + + json_writer.EndObject(); + + return json_buffer.GetString(); + } + + std::string deliver_user_objects_vectorized_json(std::vector requested_items) + { + std::vector files_metadata_list; + + for (objectID file : requested_items) + { + std::string file_path = get_user_file_path(file.name); + int64_t timestamp = static_cast(time(nullptr)); + files_metadata_list.push_back({ file.owner, file.name, get_user_file_checksum(file_path), utils::io::file_size(file_path), timestamp, timestamp, get_user_file_content(file_path) }); + } + + return deliver_user_objects_vectorized_json(files_metadata_list); + } + + std::string generate_user_objects_list_json() + { + rapidjson::StringBuffer json_buffer{}; + rapidjson::PrettyWriter json_writer(json_buffer); + + json_writer.StartObject(); + + json_writer.Key("nextPageToken"); + json_writer.Null(); + + json_writer.Key("next"); + json_writer.String("TODO"); + + json_writer.Key("objects"); + json_writer.StartArray(); + + std::string userdata_directory = platform::get_userdata_directory(); + + if (utils::io::directory_exists(userdata_directory)) + { + std::vector user_objects = utils::io::list_files(userdata_directory); + + for (std::string object : user_objects) + { + json_writer.StartObject(); + + json_writer.Key("metadata"); + json_writer.StartObject(); + + json_writer.Key("owner"); + json_writer.String(std::format("bnet-{}", platform::bnet_get_userid())); + + json_writer.Key("expiresOn"); + json_writer.Uint(0); + + json_writer.Key("name"); + json_writer.String(utils::io::file_name(object)); + + json_writer.Key("checksum"); + json_writer.String(get_user_file_checksum(object)); + + json_writer.Key("acl"); + json_writer.String("public"); + + json_writer.Key("objectID"); + json_writer.Uint(0); + json_writer.Key("contentID"); + json_writer.Null(); + json_writer.Key("objectVersion"); + json_writer.String(""); + json_writer.Key("contentVersion"); + json_writer.Null(); + + json_writer.Key("contentLength"); + json_writer.Uint64(utils::io::file_size(object)); + + json_writer.Key("context"); + json_writer.String("t8-bnet"); + + json_writer.Key("category"); + json_writer.Null(); + + json_writer.Key("created"); + json_writer.Uint64(static_cast(time(nullptr))); + + json_writer.Key("modified"); + json_writer.Uint64(static_cast(time(nullptr))); + + json_writer.Key("extraData"); + json_writer.Null(); + json_writer.Key("extraDataSize"); + json_writer.Null(); + json_writer.Key("summaryContentLength"); + json_writer.Null(); + json_writer.Key("summaryChecksum"); + json_writer.Null(); + json_writer.Key("hasSummary"); + json_writer.Bool(false); + + json_writer.EndObject(); + + json_writer.Key("tags"); + json_writer.StartArray(); + json_writer.EndArray(); + + json_writer.Key("statistics"); + json_writer.StartArray(); + json_writer.EndArray(); + + json_writer.EndObject(); + } + } + json_writer.EndArray(); + + json_writer.EndObject(); + + return json_buffer.GetString(); + } + + std::string generate_user_objects_count_json() + { + std::string userdata_directory = platform::get_userdata_directory(); + + int files_count = 0; + if (utils::io::directory_exists(userdata_directory)) + { + files_count = static_cast(utils::io::list_files(userdata_directory).size()); + } + + + rapidjson::StringBuffer json_buffer{}; + rapidjson::PrettyWriter json_writer(json_buffer); + + json_writer.StartObject(); + + json_writer.Key("total"); + json_writer.Uint(files_count); + + json_writer.Key("noCategory"); + json_writer.Uint(files_count); + + json_writer.Key("categories"); + json_writer.StartObject(); + json_writer.EndObject(); + + json_writer.EndObject(); + + return json_buffer.GetString(); + } + + std::string construct_file_upload_result_json(const std::string& uploaded_file) + { + std::string file_path = get_user_file_path(uploaded_file); + + rapidjson::StringBuffer json_buffer{}; + rapidjson::PrettyWriter json_writer(json_buffer); + + json_writer.StartObject(); + + json_writer.Key("metadata"); + json_writer.StartObject(); + + json_writer.Key("owner"); + json_writer.String(std::format("bnet-{}", platform::bnet_get_userid())); + + json_writer.Key("expiresOn"); + json_writer.Uint(0); + + json_writer.Key("name"); + json_writer.String(uploaded_file); + + json_writer.Key("checksum"); + json_writer.String(get_user_file_checksum(file_path)); + + json_writer.Key("acl"); + json_writer.String("public"); + + json_writer.Key("objectID"); + json_writer.Uint(0); + json_writer.Key("contentID"); + json_writer.Null(); + json_writer.Key("objectVersion"); + json_writer.String(""); + json_writer.Key("contentVersion"); + json_writer.Null(); + + json_writer.Key("contentLength"); + json_writer.Uint64(utils::io::file_size(file_path)); + + json_writer.Key("context"); + json_writer.String("t8-bnet"); + + json_writer.Key("category"); + json_writer.Null(); + + json_writer.Key("created"); + json_writer.Uint64(static_cast(time(nullptr))); + + json_writer.Key("modified"); + json_writer.Uint64(static_cast(time(nullptr))); + + json_writer.Key("extraData"); + json_writer.Null(); + json_writer.Key("extraDataSize"); + json_writer.Null(); + json_writer.Key("summaryContentLength"); + json_writer.Null(); + json_writer.Key("summaryChecksum"); + json_writer.Null(); + json_writer.Key("hasSummary"); + json_writer.Bool(false); + + json_writer.EndObject(); + + json_writer.EndObject(); + + return json_buffer.GetString(); + } + + std::string construct_vectorized_upload_list_json(std::vector uploaded_files) + { + rapidjson::StringBuffer json_buffer{}; + rapidjson::PrettyWriter json_writer(json_buffer); + + json_writer.StartObject(); + + json_writer.Key("objects"); + json_writer.StartArray(); + + for (size_t i = 0; i < uploaded_files.size(); i++) + { + //std::string file_path = get_user_file_path(requested_items[i].owner, requested_items[i].name); + + json_writer.StartObject(); + + json_writer.Key("metadata"); + json_writer.StartObject(); + + json_writer.Key("owner"); + json_writer.String(uploaded_files[i].owner.data()); + + json_writer.Key("expiresOn"); + json_writer.Uint(0); + + json_writer.Key("name"); + json_writer.String(uploaded_files[i].name.data()); + + json_writer.Key("checksum"); + json_writer.String(uploaded_files[i].checksum.data()); + + json_writer.Key("acl"); + json_writer.String("public"); + + json_writer.Key("objectID"); + json_writer.Uint(0); + json_writer.Key("contentID"); + json_writer.Null(); + json_writer.Key("objectVersion"); + json_writer.String(""); + json_writer.Key("contentVersion"); + json_writer.Null(); + + json_writer.Key("contentLength"); + json_writer.Uint64(uploaded_files[i].contentLength); + + json_writer.Key("context"); + json_writer.String("t8-bnet"); + + json_writer.Key("category"); + json_writer.Null(); + + json_writer.Key("created"); + json_writer.Uint64(uploaded_files[i].created); + + json_writer.Key("modified"); + json_writer.Uint64(uploaded_files[i].modified); + + json_writer.Key("extraData"); + json_writer.Null(); + json_writer.Key("extraDataSize"); + json_writer.Null(); + json_writer.Key("summaryContentLength"); + json_writer.Null(); + json_writer.Key("summaryChecksum"); + json_writer.Null(); + json_writer.Key("hasSummary"); + json_writer.Bool(false); + + json_writer.EndObject(); + + json_writer.Key("requestIndex"); + json_writer.Uint64(i); + + json_writer.EndObject(); + } + + json_writer.EndArray(); + + json_writer.Key("errors"); + json_writer.StartArray(); + json_writer.EndArray(); + + json_writer.Key("validationTokens"); + json_writer.StartArray(); + json_writer.EndArray(); + + json_writer.EndObject(); + + return json_buffer.GetString(); + } + + std::string construct_vectorized_upload_list_json(std::vector uploaded_files) + { + std::vector files_metadata_list; + + for (std::string file : uploaded_files) + { + std::string file_path = get_user_file_path(file); + int64_t timestamp = static_cast(time(nullptr)); + files_metadata_list.push_back({ std::format("bnet-{}", platform::bnet_get_userid()), file, get_user_file_checksum(file_path), utils::io::file_size(file_path), timestamp, timestamp, get_user_file_content(file_path) }); + } + + return construct_vectorized_upload_list_json(files_metadata_list); + } + + std::string serialize_objectstore_structed_buffer(std::string payload) + { + bdProtobufHelper header_1st; + header_1st.writeString(1, "Content-Length", 16); + header_1st.writeString(2, utils::string::va("%u", payload.length()), 8); + + bdProtobufHelper header_2nd; + header_2nd.writeString(1, "Authorization", 16); + header_2nd.writeString(2, "Bearer project-bo4", 2048); + + + bdProtobufHelper buffer; + buffer.writeString(1, header_1st.buffer.data(), static_cast(header_1st.buffer.length())); + buffer.writeString(1, header_2nd.buffer.data(), static_cast(header_2nd.buffer.length())); + buffer.writeUInt64(2, 200); // Status Code; Anything NON-2XX is Treated as Error + buffer.writeString(3, payload.data(), static_cast(payload.length())); + + return buffer.buffer; + } +} diff --git a/source/proxy-dll/demonware/objects.hpp b/source/proxy-dll/demonware/objects.hpp new file mode 100644 index 0000000..78f3a72 --- /dev/null +++ b/source/proxy-dll/demonware/objects.hpp @@ -0,0 +1,38 @@ +#pragma once + +namespace demonware +{ + struct objectID + { + std::string owner; + std::string name; + }; + + struct objectMetadata + { + std::string owner; + std::string name; + std::string checksum; + uint64_t contentLength; + int64_t created; + int64_t modified; + std::string contentURL; + }; + + std::string get_user_file_path(const std::string& file); + + std::string generate_publisher_objects_list_json(const std::string& category); + + std::string construct_file_upload_result_json(const std::string& uploaded_file); + + std::string generate_user_objects_list_json(); + std::string generate_user_objects_count_json(); + + std::string deliver_user_objects_vectorized_json(std::vector requested_items); + std::string deliver_user_objects_vectorized_json(std::vector requested_items); + + std::string construct_vectorized_upload_list_json(std::vector uploaded_files); + std::string construct_vectorized_upload_list_json(std::vector uploaded_files); + + std::string serialize_objectstore_structed_buffer(std::string payload); +} diff --git a/source/proxy-dll/demonware/protobuf_helper.cpp b/source/proxy-dll/demonware/protobuf_helper.cpp new file mode 100644 index 0000000..7f18d35 --- /dev/null +++ b/source/proxy-dll/demonware/protobuf_helper.cpp @@ -0,0 +1,110 @@ +#include +#include "protobuf_helper.hpp" +#include "ida_defs.h" + +bdProtobufHelper::bdProtobufHelper() {} +bdProtobufHelper::~bdProtobufHelper() {} + +/* Yes we unlocked next level of copy-pasta; someone bring the trophy :) */ + +bool bdProtobufHelper::encodeVarInt(uint64_t value) +{ + __int64 v2; // rbx + unsigned __int64 v4; // rax + char v5; // cl + char valuea[16]; // [rsp+20h] [rbp-28h] BYREF + + v2 = 0i64; + if (value) + { + do + { + if ((unsigned int)v2 >= 0xA) + break; + v4 = value; + v5 = value | 0x80; + value >>= 7; + valuea[v2] = v5; + v2 = (unsigned int)(v2 + 1); + } while (v4 >= 0x80); + valuea[(unsigned int)(v2 - 1)] &= 0x7Fu; + } + else + { + valuea[0] = 0; + LODWORD(v2) = 1; + } + //return bdStructSerializationOutputStream::write(stream, valuea, v2) == (_DWORD)v2; + this->buffer.append(std::string(valuea, v2)); + this->length += v2; + + return true; +} + +bool bdProtobufHelper::encodeVarInt(int64_t value) +{ + unsigned __int64 v2; // rax + + v2 = 2 * value; + if (value < 0) + v2 = ~v2; + return bdProtobufHelper::encodeVarInt(v2); +} + +bool bdProtobufHelper::encodeString(const char* value, uint32_t len) +{ + bool result; // al + + result = bdProtobufHelper::encodeVarInt(static_cast(len)); + if (result) + { + this->buffer.append(std::string(value, len)); + this->length += len; + } + //result = bdStructSerializationOutputStream::write(stream, value, len) == len; + + return result; +} + +bool bdProtobufHelper::encodeTag(uint32_t tagId, eWireType wireType) +{ + unsigned __int64 tag = (int)wireType | (8i64 * tagId); + return bdProtobufHelper::encodeVarInt(tag); +} + +bool bdProtobufHelper::writeInt64(uint32_t tag, int64_t value) +{ + return bdProtobufHelper::encodeTag(tag, WIRETYPE_VARINT) && bdProtobufHelper::encodeVarInt(value); +} + +bool bdProtobufHelper::writeUInt64(uint32_t tag, uint64_t value) +{ + return bdProtobufHelper::encodeTag(tag, WIRETYPE_VARINT) && bdProtobufHelper::encodeVarInt(value); +} + +bool bdProtobufHelper::writeInt32(uint32_t tag, int32_t value) +{ + return bdProtobufHelper::encodeTag(tag, WIRETYPE_VARINT) && bdProtobufHelper::encodeVarInt(static_cast(value)); +} + +bool bdProtobufHelper::writeUInt32(uint32_t tag, uint32_t value) +{ + return bdProtobufHelper::encodeTag(tag, WIRETYPE_VARINT) && bdProtobufHelper::encodeVarInt(static_cast(value)); +} + +bool bdProtobufHelper::writeString(uint32_t tag, const char* value, uint32_t size) +{ + unsigned int v5; // edi + const void* v8; // rax + + v5 = size; + v8 = memchr(value, 0, size); + if (v8) + v5 = (_DWORD)v8 - (_DWORD)value; + return bdProtobufHelper::encodeTag(tag, WIRETYPE_STRING) && bdProtobufHelper::encodeString(value, v5); +} + +bool bdProtobufHelper::writeBlob(uint32_t tag, void* buffer, uint32_t size) +{ + return bdProtobufHelper::encodeTag(tag, WIRETYPE_STRING) && bdProtobufHelper::encodeString(reinterpret_cast(buffer), size); +} \ No newline at end of file diff --git a/source/proxy-dll/demonware/protobuf_helper.hpp b/source/proxy-dll/demonware/protobuf_helper.hpp new file mode 100644 index 0000000..b49ceae --- /dev/null +++ b/source/proxy-dll/demonware/protobuf_helper.hpp @@ -0,0 +1,38 @@ +#include +#include + +enum eWireType +{ + WIRETYPE_VARINT = 0, + WIRETYPE_64BIT = 1, + WIRETYPE_STRING = 2, + WIRETYPE_32BIT = 5, + WIRETYPE_INVALID = -1 +}; + +class bdProtobufHelper +{ +public: + std::string buffer{}; + uint32_t length{}; + + bdProtobufHelper(); + ~bdProtobufHelper(); + + bool write(const void* value, uint32_t byteCount); + bool writeString(uint32_t tag, const char* value, uint32_t size); + bool writeBlob(uint32_t tag, void* buffer, uint32_t size); + bool writeInt64(uint32_t tag, int64_t value); + bool writeUInt64(uint32_t tag, uint64_t value); + bool writeInt32(uint32_t tag, int32_t value); + bool writeUInt32(uint32_t tag, uint32_t value); + + +private: + bool encodeTag(uint32_t tagId, eWireType wireType); + bool encodeVarInt(int64_t value); + bool encodeVarInt(uint64_t value); + bool encodeString(const char* value, uint32_t len); + + +}; \ No newline at end of file diff --git a/source/proxy-dll/demonware/reply.cpp b/source/proxy-dll/demonware/reply.cpp new file mode 100644 index 0000000..f067095 --- /dev/null +++ b/source/proxy-dll/demonware/reply.cpp @@ -0,0 +1,87 @@ +#include +#include "keys.hpp" +#include "reply.hpp" +#include "servers/service_server.hpp" + +#include + +namespace demonware +{ + std::string unencrypted_reply::data() + { + byte_buffer result; + result.set_use_data_types(false); + + result.write_int32(static_cast(this->buffer_.size()) + 2); + result.write_bool(false); + result.write_ubyte(this->type()); + result.write(this->buffer_); + + return result.get_buffer(); + } + + std::string encrypted_reply::data() + { + byte_buffer result; + result.set_use_data_types(false); + + byte_buffer enc_buffer; + enc_buffer.set_use_data_types(false); + + enc_buffer.write_uint32(static_cast(this->buffer_.size())); // service data size CHECKTHIS!! + enc_buffer.write_ubyte(this->type()); // TASK_REPLY type + enc_buffer.write(this->buffer_); // service data + + auto aligned_data = enc_buffer.get_buffer(); + auto size = aligned_data.size(); + size = ~15 & (size + 15); // 16 byte align + aligned_data.resize(size); + + // seed + std::string seed("\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED", 16); + + // encrypt + const auto enc_data = utils::cryptography::aes::encrypt(aligned_data, seed, demonware::get_encrypt_key()); + + // header : encrypted service data : hash + static auto msg_count = 0; + msg_count++; + + byte_buffer response; + response.set_use_data_types(false); + + response.write_int32(30 + static_cast(enc_data.size())); + response.write_ubyte(static_cast(0xAB)); + response.write_ubyte(static_cast(0x85)); + response.write_int32(msg_count); + response.write(16, seed.data()); + response.write(enc_data); + + // hash entire packet and append end + auto hash_data = utils::cryptography::hmac_sha1::compute(response.get_buffer(), demonware::get_hmac_key()); + hash_data.resize(8); + response.write(8, hash_data.data()); + + return response.get_buffer(); + } + + void remote_reply::send(bit_buffer* buffer, const bool encrypted) + { + std::unique_ptr reply; + + if (encrypted) reply = std::make_unique(this->type_, buffer); + else reply = std::make_unique(this->type_, buffer); + + this->server_->send_reply(reply.get()); + } + + void remote_reply::send(byte_buffer* buffer, const bool encrypted) + { + std::unique_ptr reply; + + if (encrypted) reply = std::make_unique(this->type_, buffer); + else reply = std::make_unique(this->type_, buffer); + + this->server_->send_reply(reply.get()); + } +} diff --git a/source/proxy-dll/demonware/reply.hpp b/source/proxy-dll/demonware/reply.hpp new file mode 100644 index 0000000..fe9d59b --- /dev/null +++ b/source/proxy-dll/demonware/reply.hpp @@ -0,0 +1,198 @@ +#pragma once + +#include "bit_buffer.hpp" +#include "byte_buffer.hpp" +#include "data_types.hpp" + +namespace demonware +{ + class reply + { + public: + reply() = default; + + reply(reply&&) = delete; + reply(const reply&) = delete; + reply& operator=(reply&&) = delete; + reply& operator=(const reply&) = delete; + + virtual ~reply() = default; + virtual std::string data() = 0; + }; + + class raw_reply : public reply + { + protected: + std::string buffer_; + + public: + raw_reply() = default; + + explicit raw_reply(std::string data) : buffer_(std::move(data)) + { + } + + std::string data() override + { + return this->buffer_; + } + }; + + class typed_reply : public raw_reply + { + public: + typed_reply(const uint8_t _type) : type_(_type) + { + } + + protected: + uint8_t type() const { return this->type_; } + + private: + uint8_t type_; + }; + + class encrypted_reply final : public typed_reply + { + public: + encrypted_reply(const uint8_t type, bit_buffer* bbuffer) : typed_reply(type) + { + this->buffer_.append(bbuffer->get_buffer()); + } + + encrypted_reply(const uint8_t type, byte_buffer* bbuffer) : typed_reply(type) + { + this->buffer_.append(bbuffer->get_buffer()); + } + + std::string data() override; + }; + + class unencrypted_reply final : public typed_reply + { + public: + unencrypted_reply(const uint8_t _type, bit_buffer* bbuffer) : typed_reply(_type) + { + this->buffer_.append(bbuffer->get_buffer()); + } + + unencrypted_reply(const uint8_t _type, byte_buffer* bbuffer) : typed_reply(_type) + { + this->buffer_.append(bbuffer->get_buffer()); + } + + std::string data() override; + }; + + class service_server; + + class remote_reply final + { + public: + remote_reply(service_server* server, uint8_t _type) : type_(_type), server_(server) + { + } + + void send(bit_buffer* buffer, bool encrypted); + void send(byte_buffer* buffer, bool encrypted); + + uint8_t type() const { return this->type_; } + + private: + uint8_t type_; + service_server* server_; + }; + + class service_reply final + { + public: + service_reply(service_server* _server, const uint8_t _type, const uint32_t _error, const bool _structed) + : type_(_type), error_(_error), structed_(_structed), reply_(_server, 1) + { + } + + uint64_t send() + { + static uint64_t id = 0x0000000000000000; + const auto transaction_id = ++id; + + byte_buffer buffer; + buffer.write_uint64(transaction_id); + buffer.write_uint32(this->error_); + buffer.write_ubyte(this->type_); + + if (!this->error_) + { + if (!structed_) + { + buffer.write_uint32(uint32_t(this->objects_.size())); + if (!this->objects_.empty()) + { + buffer.write_uint32(uint32_t(this->objects_.size())); + + for (auto& object : this->objects_) + { + object->serialize(&buffer); + } + + this->objects_.clear(); + } + } + else + { + if (!this->objects_.empty()) + { + this->objects_[0]->serialize(&buffer); + this->objects_.clear(); + } + } + } + else + { + buffer.write_uint64(transaction_id); + } + + this->reply_.send(&buffer, true); + return transaction_id; + } + + void add(const std::shared_ptr& object) + { + this->objects_.push_back(object); + } + + void add(bdTaskResult* object) + { + this->add(std::shared_ptr(object)); + } + + private: + uint8_t type_; + uint32_t error_; + bool structed_; + remote_reply reply_; + std::vector> objects_; + }; + + class structure_reply final + { + public: + structure_reply(service_server* _server, const uint8_t _type) + : reply_(_server, _type, 0, 1) + { + } + + uint64_t send(const std::string& buffer) + { + auto result = new bdStructedDataBuffer; + result->structed_data_protobuffer = buffer; + + this->reply_.add(result); + + return this->reply_.send(); + } + + private: + service_reply reply_; + }; +} diff --git a/source/proxy-dll/demonware/server_registry.hpp b/source/proxy-dll/demonware/server_registry.hpp new file mode 100644 index 0000000..725e24c --- /dev/null +++ b/source/proxy-dll/demonware/server_registry.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "servers/base_server.hpp" +#include + +namespace demonware +{ + template + class server_registry + { + static_assert(std::is_base_of::value, "Invalid server registry type"); + + public: + template + void create(Args&&... args) + { + static_assert(std::is_base_of::value, "Invalid server type"); + + auto server = std::make_unique(std::forward(args)...); + const auto address = server->get_address(); + servers_[address] = std::move(server); + } + + void for_each(const std::function& callback) const + { + for (auto& server : servers_) + { + callback(*server.second); + } + } + + T* find(const std::string& name) + { + const auto address = utils::cryptography::jenkins_one_at_a_time::compute(name); + return find(address); + } + + T* find(const uint32_t address) + { + const auto it = servers_.find(address); + if (it == servers_.end()) + { + return nullptr; + } + + return it->second.get(); + } + + void frame() + { + for (auto& server : servers_) + { + server.second->frame(); + } + } + + private: + std::unordered_map> servers_; + }; +} diff --git a/source/proxy-dll/demonware/servers/auth3_server.cpp b/source/proxy-dll/demonware/servers/auth3_server.cpp new file mode 100644 index 0000000..12d62ab --- /dev/null +++ b/source/proxy-dll/demonware/servers/auth3_server.cpp @@ -0,0 +1,185 @@ +#include + +#include "auth3_server.hpp" +#include "../keys.hpp" + +#include +#include + +namespace demonware +{ + namespace + { +#pragma pack(push, 1) + struct auth_ticket + { + unsigned int m_magicNumber; + char m_type; + unsigned int m_titleID; + unsigned int m_timeIssued; + unsigned int m_timeExpires; + unsigned __int64 m_licenseID; + unsigned __int64 m_userID; + char m_username[64]; + char m_sessionKey[24]; + char m_usingHashMagicNumber[3]; + char m_hash[4]; + }; +#pragma pack(pop) + } + + void auth3_server::send_reply(reply* data) + { + if (!data) return; + this->send(data->data()); + } + + void auth3_server::handle(const std::string& packet) + { + if (packet.starts_with("POST /auth/")) + { +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[DW]: [auth]: user requested authentication."); +#endif + return; + } + + unsigned int title_id = 0; + unsigned int iv_seed = 0; + std::string identity{}; + std::string session_token{}; + std::string account_token{}; + + rapidjson::Document j; + j.Parse(packet.data(), packet.size()); + + if (j.HasMember("title_id") && j["title_id"].IsString()) + { + title_id = std::stoul(j["title_id"].GetString()); + } + + if (j.HasMember("iv_seed") && j["iv_seed"].IsString()) + { + iv_seed = std::stoul(j["iv_seed"].GetString()); + } + + if (j.HasMember("identity") && j["identity"].IsString()) + { + identity = j["identity"].GetString(); + } + + if (j.HasMember("extra_data") && j["extra_data"].IsString()) + { + rapidjson::Document extra_data; + auto& ed = j["extra_data"]; + extra_data.Parse(ed.GetString(), ed.GetStringLength()); + + if (extra_data.HasMember("session_token") && extra_data["session_token"].IsString()) + { + auto& token_field = extra_data["session_token"]; + std::string token_b64(token_field.GetString(), token_field.GetStringLength()); + session_token = token_b64; + } + if (extra_data.HasMember("account_token") && extra_data["account_token"].IsString()) + { + auto& token_field = extra_data["account_token"]; + std::string token_b64(token_field.GetString(), token_field.GetStringLength()); + account_token = utils::cryptography::base64::decode(token_b64); + } + } + + std::string session_key( + "\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37", 24); + + // client_ticket + auth_ticket ticket{}; + std::memset(&ticket, 0x0, sizeof ticket); + ticket.m_magicNumber = 0xEFBDADDE; + ticket.m_type = 0; + ticket.m_titleID = title_id; + ticket.m_timeIssued = static_cast(time(nullptr)); + ticket.m_timeExpires = ticket.m_timeIssued + 30000; + ticket.m_licenseID = 0; + ticket.m_userID = 0; + strncpy_s(ticket.m_username, sizeof(ticket.m_username), session_token.data(), session_token.length()); + std::memcpy(ticket.m_sessionKey, session_key.data(), 24); + + const auto client_ticket_b64 = utils::cryptography::base64::encode(reinterpret_cast(&ticket), sizeof(ticket)); + + // server_ticket + uint8_t server_ticket[128]; + std::memset(&server_ticket, 0, sizeof server_ticket); + std::memcpy(server_ticket, session_key.data(), 24); + const auto server_ticket_b64 = utils::cryptography::base64::encode(server_ticket, 128); + + demonware::set_session_key(session_key); + + // header time + char date[64]; + const auto now = time(nullptr); + tm gmtm{}; + gmtime_s(&gmtm, &now); + strftime(date, 64, "%a, %d %b %G %T", &gmtm); + + rapidjson::Document extra; + extra.SetObject(); + + std::string username = std::string(ticket.m_username, sizeof(ticket.m_username)).data(); + extra.AddMember("username", username, extra.GetAllocator()); + extra.AddMember("time_to_live", 9999, extra.GetAllocator()); + + const auto lul = utils::cryptography::base64::encode("lul"); + extra.AddMember("extended_data", lul, extra.GetAllocator()); + + rapidjson::StringBuffer extra_buffer{}; + rapidjson::Writer> + extra_writer(extra_buffer); + extra.Accept(extra_writer); + + std::string extra_data{}; + extra_data.append(extra_buffer.GetString(), extra_buffer.GetLength()); + // json content + rapidjson::Document doc; + doc.SetObject(); + + doc.AddMember("auth_task", "85", doc.GetAllocator()); + doc.AddMember("code", "700", doc.GetAllocator()); + + auto seed = std::to_string(iv_seed); + doc.AddMember("iv_seed", seed, doc.GetAllocator()); + doc.AddMember("client_ticket", client_ticket_b64, doc.GetAllocator()); + doc.AddMember("server_ticket", server_ticket_b64, doc.GetAllocator()); + doc.AddMember("client_id", "treyarch-cod-t8-bnet", doc.GetAllocator()); + doc.AddMember("account_type", "bnet", doc.GetAllocator()); + doc.AddMember("crossplay_enabled", false, doc.GetAllocator()); + doc.AddMember("loginqueue_eanbled", false, doc.GetAllocator()); + doc.AddMember("identity", identity, doc.GetAllocator()); + doc.AddMember("extra_data", extra_data, doc.GetAllocator()); + doc.AddMember("service_level", "paid", doc.GetAllocator()); + + rapidjson::Value value{}; + doc.AddMember("lsg_endpoint", value, doc.GetAllocator()); + + rapidjson::StringBuffer buffer{}; + rapidjson::Writer> + writer(buffer); + doc.Accept(writer); + + // http stuff + std::string result; + result.append("HTTP/1.1 200 OK\r\n"); + result.append("Server: TornadoServer/4.5.3\r\n"); + result.append("Content-Type: application/json\r\n"); + result.append(utils::string::va("Date: %s GMT\r\n", date)); + result.append(utils::string::va("Content-Length: %d\r\n\r\n", buffer.GetLength())); + result.append(buffer.GetString(), buffer.GetLength()); + + raw_reply reply(result); + + this->send_reply(&reply); + +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[DW]: [auth]: user successfully authenticated."); +#endif + } +} \ No newline at end of file diff --git a/source/proxy-dll/demonware/servers/auth3_server.hpp b/source/proxy-dll/demonware/servers/auth3_server.hpp new file mode 100644 index 0000000..e58e73e --- /dev/null +++ b/source/proxy-dll/demonware/servers/auth3_server.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "tcp_server.hpp" +#include "../reply.hpp" + +namespace demonware +{ + class auth3_server : public tcp_server + { + public: + using tcp_server::tcp_server; + + private: + void send_reply(reply* data); + void handle(const std::string& packet) override; + }; +} diff --git a/source/proxy-dll/demonware/servers/base_server.cpp b/source/proxy-dll/demonware/servers/base_server.cpp new file mode 100644 index 0000000..727a36a --- /dev/null +++ b/source/proxy-dll/demonware/servers/base_server.cpp @@ -0,0 +1,22 @@ +#include +#include "base_server.hpp" + +#include + +namespace demonware +{ + base_server::base_server(std::string name): name_(std::move(name)) + { + this->address_ = utils::cryptography::jenkins_one_at_a_time::compute(this->name_); + } + + const std::string& base_server::get_name() const + { + return this->name_; + } + + uint32_t base_server::get_address() const + { + return this->address_; + } +} diff --git a/source/proxy-dll/demonware/servers/base_server.hpp b/source/proxy-dll/demonware/servers/base_server.hpp new file mode 100644 index 0000000..1c3e5aa --- /dev/null +++ b/source/proxy-dll/demonware/servers/base_server.hpp @@ -0,0 +1,30 @@ +#pragma once + +namespace demonware +{ + class base_server + { + public: + using stream_queue = std::queue; + using data_queue = std::queue; + + base_server(std::string name); + + base_server(base_server&&) = delete; + base_server(const base_server&) = delete; + base_server& operator=(base_server&&) = delete; + base_server& operator=(const base_server&) = delete; + + virtual ~base_server() = default; + + const std::string& get_name() const; + + uint32_t get_address() const; + + virtual void frame() = 0; + + private: + std::string name_; + std::uint32_t address_ = 0; + }; +} diff --git a/source/proxy-dll/demonware/servers/lobby_server.cpp b/source/proxy-dll/demonware/servers/lobby_server.cpp new file mode 100644 index 0000000..9c4f0cb --- /dev/null +++ b/source/proxy-dll/demonware/servers/lobby_server.cpp @@ -0,0 +1,181 @@ +#include +#include "lobby_server.hpp" + +#include "../services.hpp" +#include "../keys.hpp" + +#include + +namespace demonware +{ + lobby_server::lobby_server(std::string name) : tcp_server(std::move(name)) + { + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + }; + + void lobby_server::send_reply(reply* data) + { + if (!data) return; + this->send(data->data()); + } + + void lobby_server::handle(const std::string& packet) + { + byte_buffer buffer(packet); + buffer.set_use_data_types(false); + + try + { + while (buffer.has_more_data()) + { + int size; + buffer.read_int32(&size); + + if (size <= 0) + { + const std::string zero("\x00\x00\x00\x00", 4); + raw_reply reply(zero); + this->send_reply(&reply); + return; + } + else if (size == 0xC8) + { +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[DW]: [lobby]: received client_header_ack."); +#endif + + int c8; + buffer.read_int32(&c8); + std::string packet_1 = buffer.get_remaining(); + demonware::queue_packet_to_hash(packet_1); + + /* msgType[BYTE] serverSelectedProto[DWORD] cypher210ConnID[QWORD] serverNonce[QWORD] */ + /* 0x81(129) 0xD2(210) 0x3713371337133713 0x3713371337133713 */ + + const std::string packet_2( + "\x16\x00\x00\x00\xab\x81\xd2\x00\x00\x00\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37", + 26); + demonware::queue_packet_to_hash(packet_2); + + raw_reply reply(packet_2); + this->send_reply(&reply); +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[DW]: [lobby]: sending server_header_ack."); +#endif + return; + } + + if (buffer.size() < size_t(size)) return; + + uint8_t check_ab; + buffer.read_ubyte(&check_ab); + if (check_ab == 0xAB) + { + uint8_t type; + buffer.read_ubyte(&type); + + if (type == 0x82) + { +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[DW]: [lobby]: received client_auth."); +#endif + std::string packet_3(packet.data(), packet.size() - 8); // this 8 are client hash check? + + demonware::queue_packet_to_hash(packet_3); + demonware::derive_keys_iw8(); + + char buff[14] = "\x0A\x00\x00\x00\xAB\x83"; + std::memcpy(&buff[6], demonware::get_response_id().data(), 8); + std::string response(buff, 14); + + raw_reply reply(response); + this->send_reply(&reply); + +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[DW]: [lobby]: sending server_auth_done."); +#endif + return; + } + else if (type == 0x85) + { + uint32_t msg_count; + buffer.read_uint32(&msg_count); + + char seed[16]; + buffer.read(16, &seed); + + std::string enc = buffer.get_remaining(); + + char hash[8]; + std::memcpy(hash, &(enc.data()[enc.size() - 8]), 8); + + std::string dec = utils::cryptography::aes::decrypt( + std::string(enc.data(), enc.size() - 8), std::string(seed, 16), + demonware::get_decrypt_key()); + + byte_buffer serv(dec); + serv.set_use_data_types(false); + + uint32_t serv_size; + serv.read_uint32(&serv_size); + + uint8_t magic; // 0x86 + serv.read_ubyte(&magic); + + uint8_t service_id; + serv.read_ubyte(&service_id); + + this->call_service(service_id, serv.get_remaining()); + + return; + } + } + + logger::write(logger::LOG_TYPE_DEBUG, "[DW]: [lobby]: ERROR! received unk message."); + return; + } + } + catch (...) + { + } + } + + void lobby_server::call_service(const uint8_t id, const std::string& data) + { + const auto& it = this->services_.find(id); + + if (it != this->services_.end()) + { + it->second->exec_task(this, data); + } + else + { + logger::write(logger::LOG_TYPE_DEBUG, "[DW]: [lobby]: missing service '%s'", utils::string::va("%d", id)); + + // return no error + byte_buffer buffer(data); + uint8_t task_id; + buffer.read_ubyte(&task_id); + + this->create_reply(task_id)->send(); + } + } +} diff --git a/source/proxy-dll/demonware/servers/lobby_server.hpp b/source/proxy-dll/demonware/servers/lobby_server.hpp new file mode 100644 index 0000000..8f1bda9 --- /dev/null +++ b/source/proxy-dll/demonware/servers/lobby_server.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "tcp_server.hpp" +#include "service_server.hpp" +#include "../service.hpp" + +namespace demonware +{ + class lobby_server : public tcp_server, service_server + { + public: + lobby_server(std::string name); + + template + void register_service() + { + static_assert(std::is_base_of::value, "service must inherit from service"); + + auto service = std::make_unique(); + const uint8_t id = service->id(); + + this->services_[id] = std::move(service); + } + + void send_reply(reply* data) override; + + private: + std::unordered_map> services_; + + void handle(const std::string& packet) override; + void call_service(uint8_t id, const std::string& data); + }; +} diff --git a/source/proxy-dll/demonware/servers/service_server.hpp b/source/proxy-dll/demonware/servers/service_server.hpp new file mode 100644 index 0000000..276a508 --- /dev/null +++ b/source/proxy-dll/demonware/servers/service_server.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "../reply.hpp" + +namespace demonware +{ + class service_server + { + public: + virtual ~service_server() = default; + + virtual std::shared_ptr create_message(uint8_t type) + { + auto reply = std::make_shared(this, type); + return reply; + } + + + virtual std::shared_ptr create_reply(uint8_t type, uint32_t error = 0) + { + auto reply = std::make_shared(this, type, error, false); + return reply; + } + + virtual std::shared_ptr create_structed_reply(uint8_t type) + { + auto reply = std::make_shared(this, type); + return reply; + } + + virtual void send_reply(reply* data) = 0; + }; +} diff --git a/source/proxy-dll/demonware/servers/stun_server.cpp b/source/proxy-dll/demonware/servers/stun_server.cpp new file mode 100644 index 0000000..2c4ebd3 --- /dev/null +++ b/source/proxy-dll/demonware/servers/stun_server.cpp @@ -0,0 +1,62 @@ +#include +#include "stun_server.hpp" + +#include "../byte_buffer.hpp" + +namespace demonware +{ + void stun_server::handle(const endpoint_data& endpoint, const std::string& packet) + { + uint8_t type, version, padding; + + byte_buffer buffer(packet); + buffer.set_use_data_types(false); + buffer.read_ubyte(&type); + buffer.read_ubyte(&version); + buffer.read_ubyte(&padding); + + switch (type) + { + case 30: + this->ip_discovery(endpoint); + break; + case 20: + this->nat_discovery(endpoint); + break; + default: + break; + } + } + + void stun_server::ip_discovery(const endpoint_data& endpoint) + { + const uint32_t ip = 0x0100007f; + + byte_buffer buffer; + buffer.set_use_data_types(false); + buffer.write_ubyte(31); // type + buffer.write_ubyte(2); // version + buffer.write_ubyte(0); // version + buffer.write_uint32(ip); // external ip + buffer.write_uint16(3074); // port + + this->send(endpoint, buffer.get_buffer()); + } + + void stun_server::nat_discovery(const endpoint_data& endpoint) + { + const uint32_t ip = 0x0100007f; + + byte_buffer buffer; + buffer.set_use_data_types(false); + buffer.write_ubyte(21); // type + buffer.write_ubyte(2); // version + buffer.write_ubyte(0); // version + buffer.write_uint32(ip); // external ip + buffer.write_uint16(3074); // port + buffer.write_uint32(this->get_address()); // server ip + buffer.write_uint16(3074); // server port + + this->send(endpoint, buffer.get_buffer()); + } +} diff --git a/source/proxy-dll/demonware/servers/stun_server.hpp b/source/proxy-dll/demonware/servers/stun_server.hpp new file mode 100644 index 0000000..cb4192e --- /dev/null +++ b/source/proxy-dll/demonware/servers/stun_server.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "udp_server.hpp" + +namespace demonware +{ + class stun_server : public udp_server + { + public: + using udp_server::udp_server; + + private: + void handle(const endpoint_data& endpoint, const std::string& packet) override; + + void ip_discovery(const endpoint_data& endpoint); + void nat_discovery(const endpoint_data& endpoint); + }; +} diff --git a/source/proxy-dll/demonware/servers/tcp_server.cpp b/source/proxy-dll/demonware/servers/tcp_server.cpp new file mode 100644 index 0000000..8e60d20 --- /dev/null +++ b/source/proxy-dll/demonware/servers/tcp_server.cpp @@ -0,0 +1,84 @@ +#include +#include "tcp_server.hpp" + +namespace demonware +{ + void tcp_server::handle_input(const char* buf, size_t size) + { + in_queue_.access([&](data_queue& queue) + { + queue.emplace(buf, size); + }); + } + + size_t tcp_server::handle_output(char* buf, size_t size) + { + if (out_queue_.get_raw().empty()) + { + return 0; + } + + return out_queue_.access([&](stream_queue& queue) + { + for (size_t i = 0; i < size; ++i) + { + if (queue.empty()) + { + return i; + } + + buf[i] = queue.front(); + queue.pop(); + } + + return size; + }); + } + + bool tcp_server::pending_data() + { + return !this->out_queue_.get_raw().empty(); + } + + void tcp_server::frame() + { + if (this->in_queue_.get_raw().empty()) + { + return; + } + + while (true) + { + std::string packet{}; + const auto result = this->in_queue_.access([&](data_queue& queue) + { + if (queue.empty()) + { + return false; + } + + packet = std::move(queue.front()); + queue.pop(); + return true; + }); + + if (!result) + { + break; + } + + this->handle(packet); + } + } + + void tcp_server::send(const std::string& data) + { + out_queue_.access([&](stream_queue& queue) + { + for (const auto& val : data) + { + queue.push(val); + } + }); + } +} diff --git a/source/proxy-dll/demonware/servers/tcp_server.hpp b/source/proxy-dll/demonware/servers/tcp_server.hpp new file mode 100644 index 0000000..3f6967e --- /dev/null +++ b/source/proxy-dll/demonware/servers/tcp_server.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "base_server.hpp" +#include + +namespace demonware +{ + class tcp_server : public base_server + { + public: + using base_server::base_server; + + void handle_input(const char* buf, size_t size); + size_t handle_output(char* buf, size_t size); + bool pending_data(); + void frame() override; + + protected: + virtual void handle(const std::string& data) = 0; + + void send(const std::string& data); + + private: + utils::concurrency::container in_queue_; + utils::concurrency::container out_queue_; + }; +} diff --git a/source/proxy-dll/demonware/servers/udp_server.cpp b/source/proxy-dll/demonware/servers/udp_server.cpp new file mode 100644 index 0000000..019c3f8 --- /dev/null +++ b/source/proxy-dll/demonware/servers/udp_server.cpp @@ -0,0 +1,103 @@ +#include +#include "udp_server.hpp" + +namespace demonware +{ + void udp_server::handle_input(const char* buf, size_t size, endpoint_data endpoint) + { + in_queue_.access([&](in_queue& queue) + { + in_packet p; + p.data = std::string{buf, size}; + p.endpoint = std::move(endpoint); + + queue.emplace(std::move(p)); + }); + } + + size_t udp_server::handle_output(SOCKET socket, char* buf, size_t size, sockaddr* address, int* addrlen) + { + return out_queue_.access([&](socket_queue_map& map) -> size_t + { + const auto entry = map.find(socket); + if (entry == map.end()) + { + return 0; + } + + auto& queue = entry->second; + + if (queue.empty()) + { + return 0; + } + + auto data = std::move(queue.front()); + queue.pop(); + + const auto copy_size = std::min(size, data.data.size()); + std::memcpy(buf, data.data.data(), copy_size); + std::memcpy(address, &data.address, sizeof(data.address)); + *addrlen = sizeof(data.address); + + return copy_size; + }); + } + + bool udp_server::pending_data(SOCKET socket) + { + return this->out_queue_.access([&](const socket_queue_map& map) + { + const auto entry = map.find(socket); + if (entry == map.end()) + { + return false; + } + + return !entry->second.empty(); + }); + } + + void udp_server::send(const endpoint_data& endpoint, std::string data) + { + out_queue_.access([&](socket_queue_map& map) + { + out_packet p; + p.data = std::move(data); + p.address = endpoint.address; + + map[endpoint.socket].emplace(std::move(p)); + }); + } + + void udp_server::frame() + { + if (this->in_queue_.get_raw().empty()) + { + return; + } + + while (true) + { + in_packet packet{}; + const auto result = this->in_queue_.access([&](in_queue& queue) + { + if (queue.empty()) + { + return false; + } + + packet = std::move(queue.front()); + queue.pop(); + return true; + }); + + if (!result) + { + break; + } + + this->handle(packet.endpoint, std::move(packet.data)); + } + } +} diff --git a/source/proxy-dll/demonware/servers/udp_server.hpp b/source/proxy-dll/demonware/servers/udp_server.hpp new file mode 100644 index 0000000..340c598 --- /dev/null +++ b/source/proxy-dll/demonware/servers/udp_server.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include "base_server.hpp" +#include + +namespace demonware +{ + class udp_server : public base_server + { + public: + struct endpoint_data + { + SOCKET socket{}; + sockaddr_in address{}; + + endpoint_data() = default; + + endpoint_data(const SOCKET sock, const sockaddr* addr, const int size) + { + if (size != sizeof(this->address)) + { + throw std::runtime_error("Invalid size"); + } + + this->socket = sock; + std::memcpy(&this->address, addr, sizeof(this->address)); + } + }; + + using base_server::base_server; + + void handle_input(const char* buf, size_t size, endpoint_data endpoint); + size_t handle_output(SOCKET socket, char* buf, size_t size, sockaddr* address, int* addrlen); + bool pending_data(SOCKET socket); + + void frame() override; + + protected: + virtual void handle(const endpoint_data& endpoint, const std::string& data) = 0; + void send(const endpoint_data& endpoint, std::string data); + + private: + struct in_packet + { + std::string data; + endpoint_data endpoint; + }; + + struct out_packet + { + std::string data; + sockaddr_in address; + }; + + using in_queue = std::queue; + using out_queue = std::queue; + using socket_queue_map = std::unordered_map; + + utils::concurrency::container in_queue_; + utils::concurrency::container out_queue_; + }; +} diff --git a/source/proxy-dll/demonware/servers/umbrella_server.cpp b/source/proxy-dll/demonware/servers/umbrella_server.cpp new file mode 100644 index 0000000..cadce9d --- /dev/null +++ b/source/proxy-dll/demonware/servers/umbrella_server.cpp @@ -0,0 +1,11 @@ +#include + +#include "umbrella_server.hpp" + +namespace demonware +{ + void umbrella_server::handle(const std::string& packet) + { + // TODO: + } +} diff --git a/source/proxy-dll/demonware/servers/umbrella_server.hpp b/source/proxy-dll/demonware/servers/umbrella_server.hpp new file mode 100644 index 0000000..6507270 --- /dev/null +++ b/source/proxy-dll/demonware/servers/umbrella_server.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "tcp_server.hpp" + +namespace demonware +{ + class umbrella_server : public tcp_server + { + public: + using tcp_server::tcp_server; + + private: + void handle(const std::string& packet) override; + }; +} diff --git a/source/proxy-dll/demonware/service.hpp b/source/proxy-dll/demonware/service.hpp new file mode 100644 index 0000000..e030665 --- /dev/null +++ b/source/proxy-dll/demonware/service.hpp @@ -0,0 +1,89 @@ +#pragma once +#include +#include "servers/service_server.hpp" + +namespace demonware +{ + class service + { + using callback_t = std::function; + + uint8_t id_; + std::string name_; + std::mutex mutex_; + uint8_t task_id_; + std::map tasks_; + + public: + virtual ~service() = default; + service(service&&) = delete; + service(const service&) = delete; + service& operator=(const service&) = delete; + + service(const uint8_t id, std::string name) : id_(id), name_(std::move(name)), task_id_(0) + { + } + + uint8_t id() const + { + return this->id_; + } + + const std::string& name() const + { + return this->name_; + } + + uint8_t task_id() const + { + return this->task_id_; + } + + virtual void exec_task(service_server* server, const std::string& data) + { + std::lock_guard _(this->mutex_); + + byte_buffer buffer(data); + + buffer.read_ubyte(&this->task_id_); + + const auto& it = this->tasks_.find(this->task_id_); + + if (it != this->tasks_.end()) + { +#ifndef NDEBUG + logger::write(logger::LOG_TYPE_DEBUG, "[DW] %s: executing task '%d'", name_.data(), this->task_id_); +#endif + + it->second(server, &buffer); + } + else + { + logger::write(logger::LOG_TYPE_DEBUG, "[DW] %s: missing task '%d'", name_.data(), this->task_id_); + + // return no error + server->create_reply(this->task_id_)->send(); + } + } + + protected: + + template + void register_task(const uint8_t id, T (Class::* callback)(Args ...) const) + { + this->tasks_[id] = [this, callback](Args ... args) -> T + { + return (reinterpret_cast(this)->*callback)(args...); + }; + } + + template + void register_task(const uint8_t id, T (Class::* callback)(Args ...)) + { + this->tasks_[id] = [this, callback](Args ... args) -> T + { + return (reinterpret_cast(this)->*callback)(args...); + }; + } + }; +} diff --git a/source/proxy-dll/demonware/services.hpp b/source/proxy-dll/demonware/services.hpp new file mode 100644 index 0000000..2e70e05 --- /dev/null +++ b/source/proxy-dll/demonware/services.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "bit_buffer.hpp" +#include "byte_buffer.hpp" +#include "data_types.hpp" +#include "reply.hpp" +#include "service.hpp" +#include "servers/service_server.hpp" + +#include "services/bdStats.hpp" // 4 [ UNKNOWN ] +#include "services/bdProfiles.hpp" // 8 +#include "services/bdTitleUtilities.hpp" // 12 [ ESSENTIAL ] +#include "services/bdKeyArchive.hpp" // 15 [ USELESS ] +#include "services/bdBandwidthTest.hpp" // 18 +#include "services/bdCounter.hpp" // 23 +#include "services/bdDML.hpp" // 27 [ ESSENTIAL ] +#include "services/bdGroup.hpp" // 28 [ USELESS ] +#include "services/bdAnticheat.hpp" // 38 [ UNKNOWN ] +#include "services/bdTags.hpp" // 52 +#include "services/bdPooledStorage.hpp" // 58 +#include "services/bdEventLog.hpp" // 67 +#include "services/bdRichPresence.hpp" // 68 +#include "services/bdMarketplace.hpp" // 80 +#include "services/bdPublisherVariables.hpp"// 95 +#include "services/bdMarketingComms.hpp" // 104 +#include "services/bdUNK125.hpp" // 125 [ PATCHED OUT ] +#include "services/bdObjectStore.hpp" // 193 [ ESSENTIAL ] +#include "services/bdLootGeneration.hpp" // 195 [ UNKNOWN ] \ No newline at end of file diff --git a/source/proxy-dll/demonware/services/bdAnticheat.cpp b/source/proxy-dll/demonware/services/bdAnticheat.cpp new file mode 100644 index 0000000..2e423f8 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdAnticheat.cpp @@ -0,0 +1,49 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdAnticheat::bdAnticheat() : service(38, "bdAnticheat") + { + this->register_task(2, &bdAnticheat::answerChallenges); + this->register_task(3, &bdAnticheat::reportConsoleID); + this->register_task(4, &bdAnticheat::reportConsoleDetails); + this->register_task(5, &bdAnticheat::answerTOTPChallenge); + this->register_task(6, &bdAnticheat::reportExtendedAuthInfo); + } + + void bdAnticheat::answerChallenges(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: Read data as soon as needed + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdAnticheat::reportConsoleID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: Read data as soon as needed + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdAnticheat::reportConsoleDetails(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: Read data as soon as needed + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdAnticheat::answerTOTPChallenge(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: Read data as soon as needed + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdAnticheat::reportExtendedAuthInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: Read data as soon as needed + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdAnticheat.hpp b/source/proxy-dll/demonware/services/bdAnticheat.hpp new file mode 100644 index 0000000..c3b9aeb --- /dev/null +++ b/source/proxy-dll/demonware/services/bdAnticheat.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace demonware +{ + class bdAnticheat final : public service + { + public: + bdAnticheat(); + + private: + void answerChallenges(service_server* server, byte_buffer* buffer) const; + void reportConsoleID(service_server* server, byte_buffer* buffer) const; + void reportConsoleDetails(service_server* server, byte_buffer* buffer) const; + void answerTOTPChallenge(service_server* server, byte_buffer* buffer) const; + void reportExtendedAuthInfo(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdBandwidthTest.cpp b/source/proxy-dll/demonware/services/bdBandwidthTest.cpp new file mode 100644 index 0000000..2c41e4d --- /dev/null +++ b/source/proxy-dll/demonware/services/bdBandwidthTest.cpp @@ -0,0 +1,27 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + static uint8_t bandwidth_iw6[51] = + { + 0x0F, 0xC1, 0x1C, 0x37, 0xB8, 0xEF, 0x7C, 0xD6, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0xD0, 0x07, + 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0xF4, 0x01, + 0x00, 0x00, 0x02, 0x0C, 0x88, 0xB3, 0x04, 0x65, 0x89, 0xBF, 0xC3, 0x6A, + 0x27, 0x94, 0xD4, 0x8F + }; + + bdBandwidthTest::bdBandwidthTest() : service(18, "bdBandwidthTest") + { + } + + void bdBandwidthTest::exec_task(service_server* server, const std::string& data) + { + byte_buffer buffer; + buffer.write(sizeof bandwidth_iw6, bandwidth_iw6); + + auto reply = server->create_message(5); + reply->send(&buffer, true); + } +} diff --git a/source/proxy-dll/demonware/services/bdBandwidthTest.hpp b/source/proxy-dll/demonware/services/bdBandwidthTest.hpp new file mode 100644 index 0000000..c4feb8e --- /dev/null +++ b/source/proxy-dll/demonware/services/bdBandwidthTest.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace demonware +{ + class bdBandwidthTest final : public service + { + public: + bdBandwidthTest(); + + private: + void exec_task(service_server* server, const std::string& data) override; + }; +} diff --git a/source/proxy-dll/demonware/services/bdCounter.cpp b/source/proxy-dll/demonware/services/bdCounter.cpp new file mode 100644 index 0000000..ee190ad --- /dev/null +++ b/source/proxy-dll/demonware/services/bdCounter.cpp @@ -0,0 +1,25 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdCounter::bdCounter() : service(23, "bdCounter") + { + this->register_task(1, &bdCounter::incrementCounters); + this->register_task(2, &bdCounter::getCounterTotals); + } + + void bdCounter::incrementCounters(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdCounter::getCounterTotals(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdCounter.hpp b/source/proxy-dll/demonware/services/bdCounter.hpp new file mode 100644 index 0000000..ef5553f --- /dev/null +++ b/source/proxy-dll/demonware/services/bdCounter.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdCounter final : public service + { + public: + bdCounter(); + + private: + void incrementCounters(service_server* server, byte_buffer* buffer) const; + void getCounterTotals(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdDML.cpp b/source/proxy-dll/demonware/services/bdDML.cpp new file mode 100644 index 0000000..19a4603 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdDML.cpp @@ -0,0 +1,37 @@ +#include +#include "../services.hpp" + +#include + +namespace demonware +{ + bdDML::bdDML() : service(27, "bdDML") + { + this->register_task(3, &bdDML::getUserHierarchicalData); + } + + void bdDML::getUserHierarchicalData(service_server* server, byte_buffer* /*buffer*/) const + { + auto result = new bdDMLHierarchicalInfo; + result->country_code = "US"; + result->country = "United States"; + result->region = "New York"; + result->city = "New York"; + result->latitude = 0; + result->longitude = 0; + + result->asn = 0x2119; + result->timezone = "+01:00"; + + result->m_tier0 = 0; + result->m_tier1 = 0; + result->m_tier2 = 0; + result->m_tier3 = 0; + result->m_confidence = 0; + + + auto reply = server->create_reply(this->task_id()); + reply->add(result); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdDML.hpp b/source/proxy-dll/demonware/services/bdDML.hpp new file mode 100644 index 0000000..ac4c2ab --- /dev/null +++ b/source/proxy-dll/demonware/services/bdDML.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace demonware +{ + class bdDML final : public service + { + public: + bdDML(); + + private: + void recordIP(service_server* server, byte_buffer* buffer) const; + void getUserData(service_server* server, byte_buffer* buffer) const; + void getUserHierarchicalData(service_server* server, byte_buffer* buffer) const; + void getUsersLastLogonData(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdEventLog.cpp b/source/proxy-dll/demonware/services/bdEventLog.cpp new file mode 100644 index 0000000..d2243f0 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdEventLog.cpp @@ -0,0 +1,57 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdEventLog::bdEventLog() : service(67, "bdEventLog") + { + this->register_task(1, &bdEventLog::recordEvent); + this->register_task(2, &bdEventLog::recordEventBin); + this->register_task(3, &bdEventLog::recordEvents); + this->register_task(4, &bdEventLog::recordEventsBin); + this->register_task(5, &bdEventLog::recordEventsMixed); + this->register_task(6, &bdEventLog::initializeFiltering); + } + + void bdEventLog::recordEvent(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdEventLog::recordEventBin(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdEventLog::recordEvents(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdEventLog::recordEventsBin(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdEventLog::recordEventsMixed(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdEventLog::initializeFiltering(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdEventLog.hpp b/source/proxy-dll/demonware/services/bdEventLog.hpp new file mode 100644 index 0000000..215142b --- /dev/null +++ b/source/proxy-dll/demonware/services/bdEventLog.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace demonware +{ + class bdEventLog final : public service + { + public: + bdEventLog(); + + private: + void recordEvent(service_server* server, byte_buffer* buffer) const; + void recordEventBin(service_server* server, byte_buffer* buffer) const; + void recordEvents(service_server* server, byte_buffer* buffer) const; + void recordEventsBin(service_server* server, byte_buffer* buffer) const; + void recordEventsMixed(service_server* server, byte_buffer* buffer) const; + void initializeFiltering(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdGroup.cpp b/source/proxy-dll/demonware/services/bdGroup.cpp new file mode 100644 index 0000000..195d38f --- /dev/null +++ b/source/proxy-dll/demonware/services/bdGroup.cpp @@ -0,0 +1,41 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdGroup::bdGroup() : service(28, "bdGroup") + { + this->register_task(1, &bdGroup::setGroups); + this->register_task(2, &bdGroup::setGroupsForEntity); + this->register_task(3, &bdGroup::getEntityGroups); + this->register_task(4, &bdGroup::getGroupCounts); + } + + void bdGroup::setGroups(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdGroup::setGroupsForEntity(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdGroup::getEntityGroups(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdGroup::getGroupCounts(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdGroup.hpp b/source/proxy-dll/demonware/services/bdGroup.hpp new file mode 100644 index 0000000..8810f25 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdGroup.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace demonware +{ + class bdGroup final : public service + { + public: + bdGroup(); + + private: + void setGroups(service_server* server, byte_buffer* buffer) const; + void setGroupsForEntity(service_server* server, byte_buffer* buffer) const; + void getEntityGroups(service_server* server, byte_buffer* buffer) const; + void getGroupCounts(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdKeyArchive.cpp b/source/proxy-dll/demonware/services/bdKeyArchive.cpp new file mode 100644 index 0000000..1388194 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdKeyArchive.cpp @@ -0,0 +1,48 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdKeyArchive::bdKeyArchive() : service(15, "bdKeyArchive") + { + this->register_task(1, &bdKeyArchive::write); + this->register_task(2, &bdKeyArchive::read); + this->register_task(3, &bdKeyArchive::readAll); + this->register_task(5, &bdKeyArchive::readMultipleEntityIDs); + this->register_task(6, &bdKeyArchive::writeMultipleEntityIDs); + } + + void bdKeyArchive::write(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdKeyArchive::read(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdKeyArchive::readAll(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdKeyArchive::readMultipleEntityIDs(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + void bdKeyArchive::writeMultipleEntityIDs(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdKeyArchive.hpp b/source/proxy-dll/demonware/services/bdKeyArchive.hpp new file mode 100644 index 0000000..c4888a8 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdKeyArchive.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace demonware +{ + class bdKeyArchive final : public service + { + public: + bdKeyArchive(); + + private: + void write(service_server* server, byte_buffer* buffer) const; + void read(service_server* server, byte_buffer* buffer) const; + void readAll(service_server* server, byte_buffer* buffer) const; + void readMultipleEntityIDs(service_server* server, byte_buffer* buffer) const; + void writeMultipleEntityIDs(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdLootGeneration.cpp b/source/proxy-dll/demonware/services/bdLootGeneration.cpp new file mode 100644 index 0000000..d1a96ec --- /dev/null +++ b/source/proxy-dll/demonware/services/bdLootGeneration.cpp @@ -0,0 +1,17 @@ +#include +#include "../services.hpp" + + +namespace demonware +{ + bdLootGeneration::bdLootGeneration() : service(195, "bdLootGeneration") + { + this->register_task(2, &bdLootGeneration::getPlayerState); + } + + void bdLootGeneration::getPlayerState(service_server* server, byte_buffer* buffer) const + { + auto reply = server->create_structed_reply(this->task_id()); + reply->send(""); // Un-handled + } +} diff --git a/source/proxy-dll/demonware/services/bdLootGeneration.hpp b/source/proxy-dll/demonware/services/bdLootGeneration.hpp new file mode 100644 index 0000000..b348751 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdLootGeneration.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace demonware +{ + class bdLootGeneration final : public service + { + public: + bdLootGeneration(); + + private: + void getPlayerState(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdMarketingComms.cpp b/source/proxy-dll/demonware/services/bdMarketingComms.cpp new file mode 100644 index 0000000..968cd06 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdMarketingComms.cpp @@ -0,0 +1,25 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdMarketingComms::bdMarketingComms() : service(104, "bdMarketingComms") + { + this->register_task(6, &bdMarketingComms::getMessages); + this->register_task(7, &bdMarketingComms::reportMessagesViewed); + } + + void bdMarketingComms::getMessages(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketingComms::reportMessagesViewed(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdMarketingComms.hpp b/source/proxy-dll/demonware/services/bdMarketingComms.hpp new file mode 100644 index 0000000..f297102 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdMarketingComms.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdMarketingComms final : public service + { + public: + bdMarketingComms(); + + private: + void getMessages(service_server* server, byte_buffer* buffer) const; + void reportMessagesViewed(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdMarketplace.cpp b/source/proxy-dll/demonware/services/bdMarketplace.cpp new file mode 100644 index 0000000..e96d8fe --- /dev/null +++ b/source/proxy-dll/demonware/services/bdMarketplace.cpp @@ -0,0 +1,26 @@ +#include +#include "../services.hpp" + + +namespace demonware +{ + bdMarketplace::bdMarketplace() : service(80, "bdMarketplace") + { + this->register_task(204, &bdMarketplace::getInventoryPaginated); + this->register_task(245, &bdMarketplace::getBalancesV3); + } + + void bdMarketplace::getInventoryPaginated(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketplace::getBalancesV3(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_structed_reply(this->task_id()); + reply->send(""); + } +} diff --git a/source/proxy-dll/demonware/services/bdMarketplace.hpp b/source/proxy-dll/demonware/services/bdMarketplace.hpp new file mode 100644 index 0000000..e986225 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdMarketplace.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdMarketplace final : public service + { + public: + bdMarketplace(); + + private: + void getInventoryPaginated(service_server* server, byte_buffer* buffer) const; + void getBalancesV3(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdObjectStore.cpp b/source/proxy-dll/demonware/services/bdObjectStore.cpp new file mode 100644 index 0000000..fca41ce --- /dev/null +++ b/source/proxy-dll/demonware/services/bdObjectStore.cpp @@ -0,0 +1,168 @@ +#include +#include "../services.hpp" +#include + +#include +#include "../objects.hpp" +#include +#include + +namespace demonware +{ + bdObjectStore::bdObjectStore() : service(193, "bdObjectStore") + { + this->register_task(3, &bdObjectStore::listUserObjects); + this->register_task(18, &bdObjectStore::getUserObjectCounts); + + this->register_task(8, &bdObjectStore::getPublisherObject); + this->register_task(9, &bdObjectStore::listPublisherObjectsByCategory); + + this->register_task(6, &bdObjectStore::getUserObjectsVectorized); + this->register_task(2, &bdObjectStore::uploadUserObject); + this->register_task(5, &bdObjectStore::getUserObject); // Un-handled; Not used by engine at all + this->register_task(7, &bdObjectStore::uploadUserObjectsVectorized); + + this->register_task(16, &bdObjectStore::getPublisherObjectMetadatas); // Un-handled; Not needed if user already has LPC + } + + void bdObjectStore::getUserObject(service_server* server, byte_buffer* buffer) const + { + auto reply = server->create_reply(this->task_id(), 20000/*BD_OBJECTSTORE_PROXY_OBJECT_NOT_FOUND*/); + reply->send(); + } + + void bdObjectStore::getPublisherObject(service_server* server, byte_buffer* buffer) const + { + auto reply = server->create_reply(this->task_id(), 20000/*BD_OBJECTSTORE_PROXY_OBJECT_NOT_FOUND*/); + reply->send(); + } + + void bdObjectStore::listUserObjects(service_server* server, byte_buffer* buffer) const + { + std::string response_json = generate_user_objects_list_json(); + std::string response_buff = serialize_objectstore_structed_buffer(response_json); + + auto reply = server->create_structed_reply(this->task_id()); + reply->send(response_buff); + } + + void bdObjectStore::getUserObjectCounts(service_server* server, byte_buffer* buffer) const + { + std::string response_json = generate_user_objects_count_json(); + std::string response_buff = serialize_objectstore_structed_buffer(response_json); + + auto reply = server->create_structed_reply(this->task_id()); + reply->send(response_buff); + } + + void bdObjectStore::listPublisherObjectsByCategory(service_server* server, byte_buffer* buffer) const + { + std::string response_json = generate_publisher_objects_list_json(""); + std::string response_buff = serialize_objectstore_structed_buffer(response_json); + + auto reply = server->create_structed_reply(this->task_id()); + reply->send(response_buff); + } + + void bdObjectStore::getUserObjectsVectorized(service_server* server, byte_buffer* buffer) const + { + std::string structed_data; + buffer->read_struct(&structed_data); + + picoproto::Message request_buffer; + request_buffer.ParseFromBytes(reinterpret_cast(structed_data.data()), structed_data.size()); + + std::string str4 = request_buffer.GetString(4); + picoproto::Message nested_buffer; + nested_buffer.ParseFromBytes(reinterpret_cast(str4.data()), str4.size()); + + std::vector requested_objects_list; + + rapidjson::Document requested_objects_list_json; + requested_objects_list_json.Parse(nested_buffer.GetString(2)); + for (rapidjson::SizeType i = 0; i < requested_objects_list_json.Size(); i++) { + requested_objects_list.push_back({ std::format("bnet-{}", platform::bnet_get_userid())/*requested_objects_list_json[i]["owner"].GetString()*/, requested_objects_list_json[i]["name"].GetString() }); + } + + std::string response_json = deliver_user_objects_vectorized_json(requested_objects_list); + std::string response_buff = serialize_objectstore_structed_buffer(response_json); + + auto reply = server->create_structed_reply(this->task_id()); + reply->send(response_buff); + } + + void bdObjectStore::getPublisherObjectMetadatas(service_server* server, byte_buffer* buffer) const + { + auto reply = server->create_structed_reply(this->task_id()); + reply->send(""); // Un-handled + } + + void bdObjectStore::uploadUserObject(service_server* server, byte_buffer* buffer) const + { + std::string structed_data; + buffer->read_struct(&structed_data); + + picoproto::Message request_buffer; + request_buffer.ParseFromBytes(reinterpret_cast(structed_data.data()), structed_data.size()); + + std::string request = request_buffer.GetString(1); + std::string url = request_buffer.GetString(2); + std::string data = request_buffer.GetString(3); + + std::string file = utils::string::split(url, '/')[8]; + std::string path = get_user_file_path(file); + + if (!utils::io::write_file(path, data)) + logger::write(logger::LOG_TYPE_DEBUG, "[bdObjectStore::uploadUserObject] error on writing '%s'", file.data()); + else + logger::write(logger::LOG_TYPE_DEBUG, "[bdObjectStore::uploadUserObject] saved user file '%s'", file.data()); + + std::string response_json = construct_file_upload_result_json(file); + std::string response_buff = serialize_objectstore_structed_buffer(response_json); + + auto reply = server->create_structed_reply(this->task_id()); + reply->send(response_buff); + } + + void bdObjectStore::uploadUserObjectsVectorized(service_server* server, byte_buffer* buffer) const + { + std::string structed_data; + buffer->read_struct(&structed_data); + + picoproto::Message request_buffer; + request_buffer.ParseFromBytes(reinterpret_cast(structed_data.data()), structed_data.size()); + + std::string request = request_buffer.GetString(1); + std::string payload = request_buffer.GetString(3); + + rapidjson::Document vectorized_upload_json; + vectorized_upload_json.Parse(payload.data()); + + std::vector uploaded_objects_list; + + const rapidjson::Value& objects = vectorized_upload_json["objects"]; + for (rapidjson::SizeType i = 0; i < objects.Size(); i++) // Uses SizeType instead of size_t + { + const rapidjson::Value& content = objects[i]["content"]; + const rapidjson::Value& name = objects[i]["metadata"]["name"]; + const rapidjson::Value& checksum = objects[i]["metadata"]["checksum"]; + + std::string data = utils::cryptography::base64::decode(content.GetString()); + const auto path = std::format("{}/{}", platform::get_userdata_directory(), name.GetString()); + + uploaded_objects_list.push_back(name.GetString()); + + if (!utils::io::write_file(path, data)) + logger::write(logger::LOG_TYPE_DEBUG, "[bdObjectStore::uploadUserObjectsVectorized] error on writing '%s'", name.GetString()); + else + logger::write(logger::LOG_TYPE_DEBUG, "[bdObjectStore::uploadUserObjectsVectorized] saved user file '%s'", name.GetString()); + } + + std::string response_json = construct_vectorized_upload_list_json(uploaded_objects_list); + std::string response_buff = serialize_objectstore_structed_buffer(response_json); + + auto reply = server->create_structed_reply(this->task_id()); + reply->send(response_buff); + } +} + diff --git a/source/proxy-dll/demonware/services/bdObjectStore.hpp b/source/proxy-dll/demonware/services/bdObjectStore.hpp new file mode 100644 index 0000000..af2902a --- /dev/null +++ b/source/proxy-dll/demonware/services/bdObjectStore.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace demonware +{ + class bdObjectStore final : public service + { + public: + bdObjectStore(); + + private: + void getUserObject(service_server* server, byte_buffer* buffer) const; + void getPublisherObject(service_server* server, byte_buffer* buffer) const; + void listUserObjects(service_server* server, byte_buffer* buffer) const; + void getUserObjectCounts(service_server* server, byte_buffer* buffer) const; + void listPublisherObjectsByCategory(service_server* server, byte_buffer* buffer) const; + void getUserObjectsVectorized(service_server* server, byte_buffer* buffer) const; + void getPublisherObjectMetadatas(service_server* server, byte_buffer* buffer) const; + void uploadUserObject(service_server* server, byte_buffer* buffer) const; + void uploadUserObjectsVectorized(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdPooledStorage.cpp b/source/proxy-dll/demonware/services/bdPooledStorage.cpp new file mode 100644 index 0000000..96423c8 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdPooledStorage.cpp @@ -0,0 +1,97 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdPooledStorage::bdPooledStorage() : service(58, "bdPooledStorage") + { + this->register_task(1, &bdPooledStorage::getPooledMetaDataByID); + this->register_task(5, &bdPooledStorage::_preUpload); + this->register_task(6, &bdPooledStorage::_postUploadFile); + this->register_task(8, &bdPooledStorage::remove); + this->register_task(9, &bdPooledStorage::_preDownload); + this->register_task(17, &bdPooledStorage::_preUploadSummary); + this->register_task(18, &bdPooledStorage::_postUploadSummary); + this->register_task(19, &bdPooledStorage::_preDownloadSummary); + this->register_task(20, &bdPooledStorage::_preUploadMultiPart); + this->register_task(21, &bdPooledStorage::_postUploadMultiPart); + this->register_task(22, &bdPooledStorage::_preDownloadMultiPart); + } + + void bdPooledStorage::getPooledMetaDataByID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::_preUpload(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::_postUploadFile(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::remove(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::_preDownload(service_server* server, byte_buffer* buffer) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::_preUploadSummary(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::_postUploadSummary(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::_preDownloadSummary(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::_preUploadMultiPart(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::_postUploadMultiPart(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPooledStorage::_preDownloadMultiPart(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} \ No newline at end of file diff --git a/source/proxy-dll/demonware/services/bdPooledStorage.hpp b/source/proxy-dll/demonware/services/bdPooledStorage.hpp new file mode 100644 index 0000000..84c4646 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdPooledStorage.hpp @@ -0,0 +1,23 @@ +#pragma once + +namespace demonware +{ + class bdPooledStorage final : public service + { + public: + bdPooledStorage(); + + private: + void getPooledMetaDataByID(service_server* server, byte_buffer* buffer) const; + void _preUpload(service_server* server, byte_buffer* buffer) const; + void _postUploadFile(service_server* server, byte_buffer* buffer) const; + void remove(service_server* server, byte_buffer* buffer) const; + void _preDownload(service_server* server, byte_buffer* buffer) const; + void _preUploadSummary(service_server* server, byte_buffer* buffer) const; + void _postUploadSummary(service_server* server, byte_buffer* buffer) const; + void _preDownloadSummary(service_server* server, byte_buffer* buffer) const; + void _preUploadMultiPart(service_server* server, byte_buffer* buffer) const; + void _postUploadMultiPart(service_server* server, byte_buffer* buffer) const; + void _preDownloadMultiPart(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdProfiles.cpp b/source/proxy-dll/demonware/services/bdProfiles.cpp new file mode 100644 index 0000000..8b1b725 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdProfiles.cpp @@ -0,0 +1,95 @@ +#include +#include "../services.hpp" +#include +#include + +namespace demonware +{ + bdProfiles::bdProfiles() : service(8, "bdProfiles") + { + this->register_task(1, &bdProfiles::getPublicInfos); + this->register_task(2, &bdProfiles::getPrivateInfo); + this->register_task(3, &bdProfiles::setPublicInfo); + this->register_task(4, &bdProfiles::setPrivateInfo); + this->register_task(5, &bdProfiles::deleteProfile); + this->register_task(6, &bdProfiles::setPrivateInfoByUserID); + this->register_task(7, &bdProfiles::getPrivateInfoByUserID); + this->register_task(8, &bdProfiles::setPublicInfoByUserID); + } + + void bdProfiles::getPublicInfos(service_server* server, byte_buffer* buffer) const + { + uint64_t entity_id; + buffer->read_uint64(&entity_id); + + auto* result = new bdPublicProfileInfo; + result->m_entityID = entity_id; + result->m_VERSION = 4; + + if (utils::io::read_file(std::format("{}/profileInfo_{}", platform::get_userdata_directory(), entity_id), &result->m_ddl)) + { + auto reply = server->create_reply(this->task_id()); + reply->add(result); + reply->send(); + } + else + { + auto reply = server->create_reply(this->task_id(), 800/*BD_NO_PROFILE_INFO_EXISTS*/); + reply->send(); + } + } + + void bdProfiles::setPublicInfo(service_server* server, byte_buffer* buffer) const + { + int32_t version; std::string ddl; + buffer->read_int32(&version); + buffer->read_blob(&ddl); + + utils::io::write_file(std::format("{}/profileInfo_{}", platform::get_userdata_directory(), platform::bnet_get_userid()), ddl); + + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::getPrivateInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::setPrivateInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::deleteProfile(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::setPrivateInfoByUserID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::getPrivateInfoByUserID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::setPublicInfoByUserID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdProfiles.hpp b/source/proxy-dll/demonware/services/bdProfiles.hpp new file mode 100644 index 0000000..b9d0b8e --- /dev/null +++ b/source/proxy-dll/demonware/services/bdProfiles.hpp @@ -0,0 +1,20 @@ +#pragma once + +namespace demonware +{ + class bdProfiles final : public service + { + public: + bdProfiles(); + + private: + void getPublicInfos(service_server* server, byte_buffer* buffer) const; + void getPrivateInfo(service_server* server, byte_buffer* buffer) const; + void setPublicInfo(service_server* server, byte_buffer* buffer) const; + void setPrivateInfo(service_server* server, byte_buffer* buffer) const; + void deleteProfile(service_server* server, byte_buffer* buffer) const; + void setPrivateInfoByUserID(service_server* server, byte_buffer* buffer) const; + void getPrivateInfoByUserID(service_server* server, byte_buffer* buffer) const; + void setPublicInfoByUserID(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdPublisherVariables.cpp b/source/proxy-dll/demonware/services/bdPublisherVariables.cpp new file mode 100644 index 0000000..73df8a2 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdPublisherVariables.cpp @@ -0,0 +1,17 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdPublisherVariables::bdPublisherVariables() : service(95, "bdPublisherVariables") + { + this->register_task(1, &bdPublisherVariables::retrievePublisherVariables); + } + + void bdPublisherVariables::retrievePublisherVariables(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdPublisherVariables.hpp b/source/proxy-dll/demonware/services/bdPublisherVariables.hpp new file mode 100644 index 0000000..9833602 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdPublisherVariables.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace demonware +{ + class bdPublisherVariables final : public service + { + public: + bdPublisherVariables(); + + private: + void retrievePublisherVariables(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdRichPresence.cpp b/source/proxy-dll/demonware/services/bdRichPresence.cpp new file mode 100644 index 0000000..adfb0fb --- /dev/null +++ b/source/proxy-dll/demonware/services/bdRichPresence.cpp @@ -0,0 +1,25 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdRichPresence::bdRichPresence() : service(68, "bdRichPresence") + { + this->register_task(1, &bdRichPresence::setInfo); + this->register_task(2, &bdRichPresence::getInfo); + } + + void bdRichPresence::setInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdRichPresence::getInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdRichPresence.hpp b/source/proxy-dll/demonware/services/bdRichPresence.hpp new file mode 100644 index 0000000..821270c --- /dev/null +++ b/source/proxy-dll/demonware/services/bdRichPresence.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdRichPresence final : public service + { + public: + bdRichPresence(); + + private: + void setInfo(service_server* server, byte_buffer* buffer) const; + void getInfo(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdStats.cpp b/source/proxy-dll/demonware/services/bdStats.cpp new file mode 100644 index 0000000..28053ee --- /dev/null +++ b/source/proxy-dll/demonware/services/bdStats.cpp @@ -0,0 +1,113 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdStats::bdStats() : service(4, "bdStats") + { + this->register_task(1, &bdStats::writeStats); + this->register_task(2, &bdStats::deleteStats); + this->register_task(3, &bdStats::readStatsByEntityID); + this->register_task(4, &bdStats::readStatsByRank); + this->register_task(5, &bdStats::readStatsByPivot); + this->register_task(6, &bdStats::readStatsByRating); + this->register_task(7, &bdStats::readStatsByMultipleRanks); + this->register_task(8, &bdStats::readExternalTitleStats); + this->register_task(10, &bdStats::readExternalTitleNamedStats); + this->register_task(11, &bdStats::readStatsByLeaderboardIDsAndEntityIDs); + this->register_task(12, &bdStats::readStatsByMultipleRatings); + this->register_task(13, &bdStats::startTaskStatReadByEntityIDV2); + //this->register_task(14, &bdStats::writeServerValidatedStats); + } + + void bdStats::writeStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::deleteStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByEntityID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByRank(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByPivot(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByRating(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByMultipleRanks(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readExternalTitleStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readExternalTitleNamedStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByLeaderboardIDsAndEntityIDs(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByMultipleRatings(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::startTaskStatReadByEntityIDV2(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::writeServerValidatedStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdStats.hpp b/source/proxy-dll/demonware/services/bdStats.hpp new file mode 100644 index 0000000..8f04b7e --- /dev/null +++ b/source/proxy-dll/demonware/services/bdStats.hpp @@ -0,0 +1,25 @@ +#pragma once + +namespace demonware +{ + class bdStats final : public service + { + public: + bdStats(); + + private: + void writeStats(service_server* server, byte_buffer* buffer) const; + void deleteStats(service_server* server, byte_buffer* buffer) const; + void readStatsByEntityID(service_server* server, byte_buffer* buffer) const; + void readStatsByRank(service_server* server, byte_buffer* buffer) const; + void readStatsByPivot(service_server* server, byte_buffer* buffer) const; + void readStatsByRating(service_server* server, byte_buffer* buffer) const; + void readStatsByMultipleRanks(service_server* server, byte_buffer* buffer) const; + void readExternalTitleStats(service_server* server, byte_buffer* buffer) const; + void readExternalTitleNamedStats(service_server* server, byte_buffer* buffer) const; + void readStatsByLeaderboardIDsAndEntityIDs(service_server* server, byte_buffer* buffer) const; + void readStatsByMultipleRatings(service_server* server, byte_buffer* buffer) const; + void startTaskStatReadByEntityIDV2(service_server* server, byte_buffer* buffer) const; + void writeServerValidatedStats(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdTags.cpp b/source/proxy-dll/demonware/services/bdTags.cpp new file mode 100644 index 0000000..7478e9f --- /dev/null +++ b/source/proxy-dll/demonware/services/bdTags.cpp @@ -0,0 +1,49 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdTags::bdTags() : service(52, "bdTags") + { + this->register_task(1, &bdTags::getTagsForEntityIDs); + this->register_task(2, &bdTags::setTagsForEntityID); + this->register_task(3, &bdTags::removeTagsForEntityID); + this->register_task(4, &bdTags::removeAllTagsForEntityID); + this->register_task(5, &bdTags::searchByTagsBase); + } + + void bdTags::getTagsForEntityIDs(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTags::setTagsForEntityID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTags::removeTagsForEntityID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTags::removeAllTagsForEntityID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTags::searchByTagsBase(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdTags.hpp b/source/proxy-dll/demonware/services/bdTags.hpp new file mode 100644 index 0000000..43fb304 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdTags.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace demonware +{ + class bdTags final : public service + { + public: + bdTags(); + + private: + void getTagsForEntityIDs(service_server* server, byte_buffer* buffer) const; + void setTagsForEntityID(service_server* server, byte_buffer* buffer) const; + void removeTagsForEntityID(service_server* server, byte_buffer* buffer) const; + void removeAllTagsForEntityID(service_server* server, byte_buffer* buffer) const; + void searchByTagsBase(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdTitleUtilities.cpp b/source/proxy-dll/demonware/services/bdTitleUtilities.cpp new file mode 100644 index 0000000..3a04b54 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdTitleUtilities.cpp @@ -0,0 +1,68 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdTitleUtilities::bdTitleUtilities() : service(12, "bdTitleUtilities") + { + this->register_task(1, &bdTitleUtilities::verifyString); + this->register_task(2, &bdTitleUtilities::getTitleStats); + this->register_task(6, &bdTitleUtilities::getServerTime); + this->register_task(7, &bdTitleUtilities::areUsersOnline); + this->register_task(8, &bdTitleUtilities::getMAC); + this->register_task(9, &bdTitleUtilities::getUserNames); + this->register_task(10, &bdTitleUtilities::getUserIDs); + } + + void bdTitleUtilities::getServerTime(service_server* server, byte_buffer* /*buffer*/) const + { + auto* const time_result = new bdTimeStamp; + time_result->unix_time = uint32_t(time(nullptr)); + + auto reply = server->create_reply(this->task_id()); + reply->add(time_result); + reply->send(); + } + + void bdTitleUtilities::verifyString(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTitleUtilities::getTitleStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTitleUtilities::areUsersOnline(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTitleUtilities::getMAC(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTitleUtilities::getUserNames(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTitleUtilities::getUserIDs(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/source/proxy-dll/demonware/services/bdTitleUtilities.hpp b/source/proxy-dll/demonware/services/bdTitleUtilities.hpp new file mode 100644 index 0000000..9b46784 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdTitleUtilities.hpp @@ -0,0 +1,19 @@ +#pragma once + +namespace demonware +{ + class bdTitleUtilities final : public service + { + public: + bdTitleUtilities(); + + private: + void verifyString(service_server* server, byte_buffer* buffer) const; + void getTitleStats(service_server* server, byte_buffer* buffer) const; + void getServerTime(service_server* server, byte_buffer* buffer) const; + void areUsersOnline(service_server* server, byte_buffer* buffer) const; + void getMAC(service_server* server, byte_buffer* buffer) const; + void getUserNames(service_server* server, byte_buffer* buffer) const; + void getUserIDs(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/demonware/services/bdUNK125.cpp b/source/proxy-dll/demonware/services/bdUNK125.cpp new file mode 100644 index 0000000..18d0de1 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdUNK125.cpp @@ -0,0 +1,41 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdUNK125::bdUNK125() : service(125, "bdUNK125") + { + this->register_task(1, &bdUNK125::task_unk1); + this->register_task(2, &bdUNK125::task_unk2); + this->register_task(3, &bdUNK125::task_unk3); + this->register_task(9, &bdUNK125::task_unk9); + } + + void bdUNK125::task_unk1(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_structed_reply(this->task_id()); + reply->send(""); + } + + void bdUNK125::task_unk2(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_structed_reply(this->task_id()); + reply->send(""); + } + + void bdUNK125::task_unk3(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_structed_reply(this->task_id()); + reply->send(""); + } + + void bdUNK125::task_unk9(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_structed_reply(this->task_id()); + reply->send(""); + } +} diff --git a/source/proxy-dll/demonware/services/bdUNK125.hpp b/source/proxy-dll/demonware/services/bdUNK125.hpp new file mode 100644 index 0000000..ffaa6f3 --- /dev/null +++ b/source/proxy-dll/demonware/services/bdUNK125.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace demonware +{ + class bdUNK125 final : public service + { + public: + bdUNK125(); + + private: + void task_unk1(service_server* server, byte_buffer* buffer) const; + void task_unk2(service_server* server, byte_buffer* buffer) const; + void task_unk3(service_server* server, byte_buffer* buffer) const; + void task_unk9(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/source/proxy-dll/resource.hpp b/source/proxy-dll/resource.hpp index 6b7ec13..3ea83cd 100644 --- a/source/proxy-dll/resource.hpp +++ b/source/proxy-dll/resource.hpp @@ -5,6 +5,9 @@ #define ID_ICON 102 #define IMAGE_SPLASH 103 +#define DW_PUBLISHER_OBJECTS_LIST 104 +#define DW_AUTH_TRAFFIC_SIGNING_KEY 105 + // Next default values for new objects // #ifdef APSTUDIO_INVOKED diff --git a/source/proxy-dll/resource.rc b/source/proxy-dll/resource.rc index fec81c1..fe03eba 100644 --- a/source/proxy-dll/resource.rc +++ b/source/proxy-dll/resource.rc @@ -51,7 +51,7 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -ID_ICON ICON "resources/icon.ico" +ID_ICON ICON "resources/icon.ico" ///////////////////////////////////////////////////////////////////////////// @@ -59,7 +59,10 @@ ID_ICON ICON "resources/icon.ico" // RCDATA // -IMAGE_SPLASH RCDATA "resources/splash.jpg" +IMAGE_SPLASH RCDATA "resources/splash.jpg" + +DW_PUBLISHER_OBJECTS_LIST RCDATA "resources/dw/publisher_objects_list.csv" +DW_AUTH_TRAFFIC_SIGNING_KEY RCDATA "resources/dw/auth_traffic_signing_key.dat" #endif // English (United States) resources diff --git a/source/proxy-dll/resources/dw/auth_traffic_signing_key.dat b/source/proxy-dll/resources/dw/auth_traffic_signing_key.dat new file mode 100644 index 0000000..340b0b3 Binary files /dev/null and b/source/proxy-dll/resources/dw/auth_traffic_signing_key.dat differ diff --git a/source/proxy-dll/resources/dw/publisher_objects_list.csv b/source/proxy-dll/resources/dw/publisher_objects_list.csv new file mode 100644 index 0000000..4b587e3 --- /dev/null +++ b/source/proxy-dll/resources/dw/publisher_objects_list.csv @@ -0,0 +1,12 @@ +arasafe_core_ffotd_tu23_639_cf92ecf4a75d3f79.ff,2432,FCB3F6D4A4A21A2AE399E3D8F2430126 +arasafe_core_playlists_tu23_639_cf92ecf4a75d3f79.ff,2432,2C20B802AEA7FCDFF599BA40B554FB58 +core_ffotd_tu23_639_cf92ecf4a75d3f79.ff,10315840,002F37761AF31051C400EC8DB5095062 +core_playlists_tu23_639_cf92ecf4a75d3f79.ff,30912,99DD77754218042A01D8F2D082028C68 +en_core_ffotd_tu23_639_cf92ecf4a75d3f79.ff,38784,8C6ED0DCA0ECA3AF1B9811AC2108E5CE +en_core_playlists_tu23_639_cf92ecf4a75d3f79.ff,25472,210E50AB866C9AFCBE7B866E5059C1DD +gersafe_core_ffotd_tu23_639_cf92ecf4a75d3f79.ff,2432,0120914E8A43330ECB5B211208E0763E +gersafe_core_playlists_tu23_639_cf92ecf4a75d3f79.ff,2432,814775B5E27BE462607537852C9C42F1 +jpsafe_core_ffotd_tu23_639_cf92ecf4a75d3f79.ff,2432,2D743D8D63B074A97151807CA502ABC4 +jpsafe_core_playlists_tu23_639_cf92ecf4a75d3f79.ff,2432,71BEA66B0385C06EB1F660A64F36654D +k15safe_core_ffotd_tu23_639_cf92ecf4a75d3f79.ff,2432,8125C3EC0729F0D33C1373E60CD6D61B +k15safe_core_playlists_tu23_639_cf92ecf4a75d3f79.ff,2432,4DB74A55B84648964E866132C66DFB3F \ No newline at end of file diff --git a/source/shared-code/utils/cryptography.cpp b/source/shared-code/utils/cryptography.cpp index e6ce36a..85347de 100644 --- a/source/shared-code/utils/cryptography.cpp +++ b/source/shared-code/utils/cryptography.cpp @@ -563,6 +563,46 @@ namespace utils::cryptography return string::dump_hex(hash, ""); } + std::string md5::compute(const std::string& data, const bool hex) + { + return compute(cs(data.data()), data.size(), hex); + } + + std::string md5::compute(const uint8_t* data, const size_t length, const bool hex) + { + uint8_t buffer[16] = { 0 }; + + hash_state state; + md5_init(&state); + md5_process(&state, data, ul(length)); + md5_done(&state, buffer); + + std::string hash(cs(buffer), sizeof(buffer)); + if (!hex) return hash; + + return string::dump_hex(hash, ""); + } + + uint32_t xxh32::compute(const std::string& data) + { + return compute(cs(data.data()), data.size()); + } + + uint32_t xxh32::compute(const uint8_t* data, const size_t length) + { + return XXHash32::hash(data, length, 0); + } + + uint64_t xxh64::compute(const std::string& data) + { + return compute(cs(data.data()), data.size()); + } + + uint64_t xxh64::compute(const uint8_t* data, const size_t length) + { + return XXHash64::hash(data, length, 0); + } + std::string base64::encode(const uint8_t* data, const size_t len) { std::string result; diff --git a/source/shared-code/utils/cryptography.hpp b/source/shared-code/utils/cryptography.hpp index 9538c5e..a5d6776 100644 --- a/source/shared-code/utils/cryptography.hpp +++ b/source/shared-code/utils/cryptography.hpp @@ -2,6 +2,8 @@ #include #include +#include +#include namespace utils::cryptography { @@ -96,6 +98,24 @@ namespace utils::cryptography std::string compute(const uint8_t* data, size_t length, bool hex = false); } + namespace md5 + { + std::string compute(const std::string& data, bool hex = false); + std::string compute(const uint8_t* data, size_t length, bool hex = false); + } + + namespace xxh32 + { + uint32_t compute(const std::string& data); + uint32_t compute(const uint8_t* data, size_t length); + } + + namespace xxh64 + { + uint64_t compute(const std::string& data); + uint64_t compute(const uint8_t* data, size_t length); + } + namespace base64 { std::string encode(const uint8_t* data, size_t len); diff --git a/source/shared-code/utils/smbios.cpp b/source/shared-code/utils/identity.cpp similarity index 85% rename from source/shared-code/utils/smbios.cpp rename to source/shared-code/utils/identity.cpp index a3282c2..5ba9495 100644 --- a/source/shared-code/utils/smbios.cpp +++ b/source/shared-code/utils/identity.cpp @@ -1,11 +1,12 @@ -#include "smbios.hpp" +#include "identity.hpp" #include "memory.hpp" #define WIN32_LEAN_AND_MEAN #include #include +#include -namespace utils::smbios +namespace utils::identity { namespace { @@ -61,7 +62,19 @@ namespace utils::smbios } } - std::string get_uuid() + std::string get_sys_username() + { + char username[UNLEN + 1]; + DWORD username_len = UNLEN + 1; + if (!GetUserNameA(username, &username_len)) + { + return "N/A"; + } + + return std::string{ username, username_len - 1 }; + } + + std::string get_sys_uuid() { auto smbios_data = get_smbios_data(); auto* raw_data = reinterpret_cast(smbios_data.data()); diff --git a/source/shared-code/utils/identity.hpp b/source/shared-code/utils/identity.hpp new file mode 100644 index 0000000..6196aba --- /dev/null +++ b/source/shared-code/utils/identity.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace utils::identity +{ + std::string get_sys_username(); + std::string get_sys_uuid(); +} diff --git a/source/shared-code/utils/io.cpp b/source/shared-code/utils/io.cpp index a03d1bb..e8eae30 100644 --- a/source/shared-code/utils/io.cpp +++ b/source/shared-code/utils/io.cpp @@ -78,6 +78,14 @@ namespace utils::io return false; } + std::string file_name(const std::string& path) + { + const auto pos = path.find_last_of('/'); + if (pos == std::string::npos) return path; + + return path.substr(pos + 1); + } + size_t file_size(const std::string& file) { if (file_exists(file)) @@ -94,6 +102,13 @@ namespace utils::io return 0; } + time_t file_timestamp(const std::string& file) + { + const auto time = std::chrono::clock_cast(std::filesystem::last_write_time(file)); + + return std::chrono::system_clock::to_time_t(time); + } + bool create_directory(const std::string& directory) { return std::filesystem::create_directories(directory); @@ -115,6 +130,8 @@ namespace utils::io for (auto& file : std::filesystem::directory_iterator(directory)) { + if (std::filesystem::is_directory(file.path())) continue; + files.push_back(file.path().generic_string()); } diff --git a/source/shared-code/utils/io.hpp b/source/shared-code/utils/io.hpp index ab4ebaa..c511085 100644 --- a/source/shared-code/utils/io.hpp +++ b/source/shared-code/utils/io.hpp @@ -12,7 +12,9 @@ namespace utils::io bool write_file(const std::string& file, const std::string& data, bool append = false); bool read_file(const std::string& file, std::string* data); std::string read_file(const std::string& file); + std::string file_name(const std::string& path); size_t file_size(const std::string& file); + time_t file_timestamp(const std::string& file); bool create_directory(const std::string& directory); bool directory_exists(const std::string& directory); bool directory_is_empty(const std::string& directory); diff --git a/source/shared-code/utils/json_config.cpp b/source/shared-code/utils/json_config.cpp new file mode 100644 index 0000000..7f11b5a --- /dev/null +++ b/source/shared-code/utils/json_config.cpp @@ -0,0 +1,184 @@ +#include "json_config.hpp" +#include "io.hpp" + +#include +#include +#include + +namespace utils::json_config +{ + rapidjson::Document json_doc{}; + std::string file_name = "project-bo4.json"; + + namespace + { + bool read_json_config() + { + std::string json_data{}; + if (!io::read_file(file_name, &json_data)) return false; + + json_doc.Parse(json_data); + + if (json_doc.HasParseError()) { + return false; + } + + return true; + } + + bool write_json_config() + { + rapidjson::StringBuffer buffer; + rapidjson::PrettyWriter writer(buffer); + json_doc.Accept(writer); + + std::string json_data(buffer.GetString(), buffer.GetLength()); + if (!io::write_file(file_name, json_data)) return false; + + return true; + } + } + + rapidjson::Document& get_json_document() + { + static bool json_initialized = false; + if (!json_initialized) + { + if (!read_json_config()) { + json_doc = rapidjson::Document(rapidjson::kObjectType); + } + + json_initialized = true; + } + + /*if (json_doc != NULL) */return json_doc; + } + + rapidjson::Value& get_json_section(const char* szSection) + { + rapidjson::Document& doc = get_json_document(); + + if (!doc.HasMember(szSection)) + { + doc.AddMember(rapidjson::StringRef(szSection), rapidjson::kObjectType, doc.GetAllocator()); + } + + rapidjson::Value& section = doc[szSection]; + + if (!section.IsObject()) section.SetObject(); + + return section; + } + + std::string ReadString(const char* szSection, const char* szKey, const std::string& strDefaultValue) + { + rapidjson::Document& doc = get_json_document(); + rapidjson::Value& section = get_json_section(szSection); + + if (!section.HasMember(szKey)) { + section.AddMember(rapidjson::StringRef(szKey), strDefaultValue, doc.GetAllocator()); + } + else if (!section[szKey].IsString()) { + section[szKey].SetString(strDefaultValue, doc.GetAllocator()); + } + else { + return section[szKey].GetString(); + } + + write_json_config(); + return section[szKey].GetString(); + } + + void WriteString(const char* szSection, const char* szKey, const std::string& strValue) + { + rapidjson::Document& doc = get_json_document(); + rapidjson::Value& section = get_json_section(szSection); + + if (!section.HasMember(szKey)) { + section.AddMember(rapidjson::StringRef(szKey), strValue, doc.GetAllocator()); + } + else { + section[szKey].SetString(strValue, doc.GetAllocator()); + } + + write_json_config(); + } + + unsigned int ReadUnsignedInteger(const char* szSection, const char* szKey, unsigned int iDefaultValue) + { + rapidjson::Document& doc = get_json_document(); + rapidjson::Value& section = get_json_section(szSection); + + if (!section.HasMember(szKey)) { + section.AddMember(rapidjson::StringRef(szKey), iDefaultValue, doc.GetAllocator()); + } + else if (!section[szKey].IsUint()) { + section[szKey].SetUint(iDefaultValue); + } + else { + return section[szKey].GetUint(); + } + + write_json_config(); + return section[szKey].GetUint(); + } + + int ReadInteger(const char* szSection, const char* szKey, int iDefaultValue) + { + return static_cast(ReadUnsignedInteger(szSection, szKey, static_cast(iDefaultValue))); + } + + void WriteUnsignedInteger(const char* szSection, const char* szKey, unsigned int iValue) + { + rapidjson::Document& doc = get_json_document(); + rapidjson::Value& section = get_json_section(szSection); + + if (!section.HasMember(szKey)) { + section.AddMember(rapidjson::StringRef(szKey), iValue, doc.GetAllocator()); + } + else { + section[szKey].SetUint(iValue); + } + + write_json_config(); + } + + void WriteInteger(const char* szSection, const char* szKey, int iValue) + { + ReadUnsignedInteger(szSection, szKey, static_cast(iValue)); + } + + bool ReadBoolean(const char* szSection, const char* szKey, bool bolDefaultValue) + { + rapidjson::Document& doc = get_json_document(); + rapidjson::Value& section = get_json_section(szSection); + + if (!section.HasMember(szKey)) { + section.AddMember(rapidjson::StringRef(szKey), bolDefaultValue, doc.GetAllocator()); + } + else if (!section[szKey].IsBool()) { + section[szKey].SetBool(bolDefaultValue); + } + else { + return section[szKey].GetBool(); + } + + write_json_config(); + return section[szKey].GetBool(); + } + + void WriteBoolean(const char* szSection, const char* szKey, bool bolValue) + { + rapidjson::Document& doc = get_json_document(); + rapidjson::Value& section = get_json_section(szSection); + + if (!section.HasMember(szKey)) { + section.AddMember(rapidjson::StringRef(szKey), bolValue, doc.GetAllocator()); + } + else { + section[szKey].SetBool(bolValue); + } + + write_json_config(); + } +} \ No newline at end of file diff --git a/source/shared-code/utils/json_config.hpp b/source/shared-code/utils/json_config.hpp new file mode 100644 index 0000000..4361e7a --- /dev/null +++ b/source/shared-code/utils/json_config.hpp @@ -0,0 +1,17 @@ +#pragma once +#include + +namespace utils::json_config +{ + std::string ReadString(const char* szSection, const char* szKey, const std::string& strDefaultValue); + void WriteString(const char* szSection, const char* szKey, const std::string& strValue); + + unsigned int ReadUnsignedInteger(const char* szSection, const char* szKey, unsigned int iDefaultValue); + void WriteUnsignedInteger(const char* szSection, const char* szKey, unsigned int iValue); + + int ReadInteger(const char* szSection, const char* szKey, int iDefaultValue); + void WriteInteger(const char* szSection, const char* szKey, int iValue); + + bool ReadBoolean(const char* szSection, const char* szKey, bool bolDefaultValue); + void WriteBoolean(const char* szSection, const char* szKey, bool bolValue); +} diff --git a/source/shared-code/utils/smbios.hpp b/source/shared-code/utils/smbios.hpp deleted file mode 100644 index bbd1939..0000000 --- a/source/shared-code/utils/smbios.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -namespace utils::smbios -{ - std::string get_uuid(); -} diff --git a/source/shared-code/utils/string.cpp b/source/shared-code/utils/string.cpp index 653ecff..9f5531f 100644 --- a/source/shared-code/utils/string.cpp +++ b/source/shared-code/utils/string.cpp @@ -34,6 +34,23 @@ namespace utils::string return elems; } + std::vector split(const std::string& s, const std::string& delim) + { + size_t pos_start = 0, pos_end, delim_len = delim.length(); + std::string token; + std::vector elems; + + while ((pos_end = s.find(delim, pos_start)) != std::string::npos) { + token = s.substr(pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + elems.push_back(token); + } + + elems.push_back(s.substr(pos_start)); + + return elems; + } + std::string to_lower(std::string text) { std::transform(text.begin(), text.end(), text.begin(), [](const char input) diff --git a/source/shared-code/utils/string.hpp b/source/shared-code/utils/string.hpp index 04042cb..2f7bde1 100644 --- a/source/shared-code/utils/string.hpp +++ b/source/shared-code/utils/string.hpp @@ -81,6 +81,7 @@ namespace utils::string const char* va(const char* fmt, ...); std::vector split(const std::string& s, char delim); + std::vector split(const std::string& s, const std::string& delim); std::string to_lower(std::string text); std::string to_upper(std::string text);