#include #include "loader/component_loader.hpp" #include "demonware.hpp" #include "game_module.hpp" #include #include #include #include #include "game/game.hpp" #include "game/demonware/stun_server.hpp" #include "game/demonware/service_server.hpp" #include "game/demonware/services/bdLSGHello.hpp" // 7 #include "game/demonware/services/bdStorage.hpp" // 10 #include "game/demonware/services/bdDediAuth.hpp" // 12 #include "game/demonware/services/bdTitleUtilities.hpp" // 12 #include "game/demonware/services/bdBandwidthTest.hpp" // 18 #include "game/demonware/services/bdMatchMaking.hpp" // 21 #include "game/demonware/services/bdDediRSAAuth.hpp" // 26 #include "game/demonware/services/bdDML.hpp" // 27 #include "game/demonware/services/bdGroup.hpp" // 28 #include "game/demonware/services/bdSteamAuth.hpp" // 28 #include "game/demonware/services/bdAnticheat.hpp" // 38 #include "game/demonware/services/bdRelayService.hpp" // 86 #define TCP_BLOCKING true #define UDP_BLOCKING false namespace demonware { namespace { volatile bool terminate; std::thread message_thread; std::recursive_mutex server_mutex; std::map blocking_sockets; std::map> socket_links; std::map> servers; std::map> stun_servers; std::map>> datagram_packets; uint8_t encryption_key_[24]; uint8_t decryption_key_[24]; std::shared_ptr find_server_by_address(const unsigned long address) { std::lock_guard _(server_mutex); const auto server = servers.find(address); if (server != servers.end()) { return server->second; } return std::shared_ptr(); } std::shared_ptr find_server_by_name(const std::string& name) { std::lock_guard _(server_mutex); return find_server_by_address(utils::cryptography::jenkins_one_at_a_time::compute(name)); } std::shared_ptr find_stun_server_by_address(const unsigned long address) { std::lock_guard _(server_mutex); const auto server = stun_servers.find(address); if (server != stun_servers.end()) { return server->second; } return std::shared_ptr(); } std::shared_ptr find_stun_server_by_name(const std::string& name) { std::lock_guard _(server_mutex); return find_stun_server_by_address(utils::cryptography::jenkins_one_at_a_time::compute(name)); } std::shared_ptr find_server_by_socket(const SOCKET s) { std::lock_guard _(server_mutex); const auto server = socket_links.find(s); if (server != socket_links.end()) { return server->second; } return std::shared_ptr(); } bool link_socket(const SOCKET s, const unsigned long address) { std::lock_guard _(server_mutex); const auto server = find_server_by_address(address); if (!server) return false; socket_links[s] = server; return true; } void unlink_socket(const SOCKET sock) { std::lock_guard _(server_mutex); const auto server = socket_links.find(sock); if (server != socket_links.end()) { socket_links.erase(server); } const auto dgram_packets = datagram_packets.find(sock); if (dgram_packets != datagram_packets.end()) { datagram_packets.erase(dgram_packets); } } bool is_blocking_socket(const SOCKET s, const bool def) { std::lock_guard _(server_mutex); if (blocking_sockets.find(s) != blocking_sockets.end()) { return blocking_sockets[s]; } return def; } int recv_datagam_packet(const SOCKET s, char* buf, const int len, sockaddr* from, int* fromlen) { std::unique_lock lock(server_mutex); auto queue = datagram_packets.find(s); if (queue != datagram_packets.end()) { const auto blocking = is_blocking_socket(s, UDP_BLOCKING); lock.unlock(); while (blocking && queue->second.empty()) { std::this_thread::sleep_for(1ms); } lock.lock(); if (!queue->second.empty()) { auto [address, data] = queue->second.front(); queue->second.pop(); *fromlen = INT(address.size()); std::memcpy(from, address.data(), address.size()); const auto size = std::min(size_t(len), data.size()); std::memcpy(buf, data.data(), size); return static_cast(size); } WSASetLastError(WSAEWOULDBLOCK); return -1; } return 0; } void remove_blocking_socket(const SOCKET s) { std::lock_guard _(server_mutex); const auto entry = blocking_sockets.find(s); if (entry != blocking_sockets.end()) { blocking_sockets.erase(entry); } } void set_blocking_socket(const SOCKET s, const bool blocking) { std::lock_guard _(server_mutex); blocking_sockets[s] = blocking; } void server_thread() { terminate = false; while (!terminate) { std::unique_lock lock(server_mutex); for (auto& server : servers) { server.second->run_frame(); } lock.unlock(); std::this_thread::sleep_for(50ms); } } void bd_logger_stub(int /*type*/, const char* const /*channelName*/, const char*, const char* const /*file*/, const char* const function, const unsigned int /*line*/, const char* const msg, ...) { static const auto* bd_logger_enabled = game::Dvar_RegisterBool("bd_logger_enabled", false, game::DVAR_FLAG_NONE, "Enable bdLogger"); if (!bd_logger_enabled->current.enabled) { return; } char buffer[2048]{}; va_list ap; va_start(ap, msg); vsnprintf(buffer, sizeof(buffer), msg, ap); printf("%s: %s\n", function, buffer); va_end(ap); } namespace io { int WINAPI send_to(const SOCKET s, const char* buf, const int len, const int flags, const sockaddr* to, const int tolen) { if (tolen == sizeof(sockaddr_in)) { const auto* in_addr = reinterpret_cast(to); const auto server = find_stun_server_by_address(in_addr->sin_addr.s_addr); if (server) return server->send(s, buf, len, to, tolen); } return sendto(s, buf, len, flags, to, tolen); } int WINAPI recv_from(const SOCKET s, char* buf, const int len, const int flags, sockaddr* from, int* fromlen) { auto res = recv_datagam_packet(s, buf, len, from, fromlen); if (res != 0) return res; res = recvfrom(s, buf, len, flags, from, fromlen); return res; } int WINAPI send(const SOCKET s, const char* buf, const int len, const int flags) { auto server = find_server_by_socket(s); if (server) return server->send(buf, len); return ::send(s, buf, len, flags); } int WINAPI recv(const SOCKET s, char* buf, const int len, const int flags) { auto server = find_server_by_socket(s); if (server) { const auto blocking = is_blocking_socket(s, TCP_BLOCKING); int result; do { result = server->recv(buf, len); if (blocking && result < 0) std::this_thread::sleep_for(1ms); } while (blocking && result < 0); if (!blocking && result < 0) { WSASetLastError(WSAEWOULDBLOCK); } return result; } return ::recv(s, buf, len, flags); } int WINAPI connect(const SOCKET s, const sockaddr* addr, const int len) { if (len == sizeof(sockaddr_in)) { const auto* in_addr = reinterpret_cast(addr); if (link_socket(s, in_addr->sin_addr.s_addr)) return 0; } return ::connect(s, addr, len); } int WINAPI close_socket(const SOCKET s) { remove_blocking_socket(s); unlink_socket(s); return closesocket(s); } int WINAPI ioctl_socket(const SOCKET s, const long cmd, u_long* argp) { if (static_cast(cmd) == (FIONBIO)) { set_blocking_socket(s, *argp == 0); } return ioctlsocket(s, cmd, argp); } hostent* WINAPI get_host_by_name(char* name) { unsigned long addr = 0; const auto server = find_server_by_name(name); if (server) addr = server->get_address(); const auto stun_server = find_stun_server_by_name(name); if (stun_server) addr = stun_server->get_address(); if (server || stun_server) { static thread_local in_addr address; address.s_addr = addr; static thread_local in_addr* addr_list[2]; addr_list[0] = &address; addr_list[1] = nullptr; static thread_local hostent host; host.h_name = 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; } #pragma warning(push) #pragma warning(disable: 4996) return gethostbyname(name); #pragma warning(pop) } bool register_hook(const std::string& process, void* stub) { const auto game_module = game_module::get_game_module(); auto result = false; result = result || utils::hook::iat(game_module, "wsock32.dll", process, stub); result = result || utils::hook::iat(game_module, "WS2_32.dll", process, stub); return result; } } template std::shared_ptr register_server(Args ... args) { std::lock_guard _(server_mutex); auto server = std::make_shared(args...); servers[server->get_address()] = server; return server; } std::shared_ptr register_stun_server(const std::string& name) { std::lock_guard _(server_mutex); auto server = std::make_shared(name); stun_servers[server->get_address()] = server; return server; } void startup_dw() { register_stun_server("ghosts-stun.us.demonware.net"); register_stun_server("ghosts-stun.eu.demonware.net"); register_stun_server("ghosts-stun.jp.demonware.net"); register_stun_server("ghosts-stun.au.demonware.net"); auto lsg_server = register_server("ghosts-pc-lobby.prod.demonware.net"); auto auth_server = register_server("ghosts-pc-auth.prod.demonware.net"); auth_server->register_service(); auth_server->register_service(); auth_server->register_service(); lsg_server->register_service(); lsg_server->register_service(); lsg_server->register_service(); lsg_server->register_service(); lsg_server->register_service(); lsg_server->register_service(); lsg_server->register_service(); lsg_server->register_service(); lsg_server->register_service(); } } void send_datagram_packet(const SOCKET s, const std::string& data, const sockaddr* to, const int tolen) { std::lock_guard _(server_mutex); datagram_packets[s].push({std::string(LPSTR(to), size_t(tolen)), data}); } uint8_t* get_key(const bool encrypt) { return encrypt ? encryption_key_ : decryption_key_; } void set_key(const bool encrypt, uint8_t* key) { static_assert(sizeof encryption_key_ == sizeof decryption_key_); std::memcpy(encrypt ? encryption_key_ : decryption_key_, key, sizeof encryption_key_); } class component final : public component_interface { public: void post_load() override { startup_dw(); message_thread = utils::thread::create_named_thread("Demonware", server_thread); io::register_hook("send", io::send); io::register_hook("recv", io::recv); io::register_hook("sendto", io::send_to); io::register_hook("recvfrom", io::recv_from); io::register_hook("connect", io::connect); io::register_hook("closesocket", io::close_socket); io::register_hook("ioctlsocket", io::ioctl_socket); io::register_hook("gethostbyname", io::get_host_by_name); } void post_unpack() override { utils::hook::jump(SELECT_VALUE(0x140602230, 0x1406F54D0), bd_logger_stub); } void pre_destroy() override { std::lock_guard _(server_mutex); terminate = true; if (message_thread.joinable()) { message_thread.join(); } servers.clear(); stun_servers.clear(); socket_links.clear(); blocking_sockets.clear(); datagram_packets.clear(); } }; } REGISTER_COMPONENT(demonware::component)