#include #include "loader/component_loader.hpp" #include "scheduler.hpp" #include "game/game.hpp" #include #include namespace network { namespace { using data_view = std::basic_string_view; using callback = std::function; std::unordered_map& get_callbacks() { static std::unordered_map callbacks{}; return callbacks; } bool handle_command(const game::netadr_t* address, const char* command, const game::msg_t* message) { const auto cmd_string = utils::string::to_lower(command); auto& callbacks = get_callbacks(); const auto handler = callbacks.find(cmd_string); const auto offset = cmd_string.size() + 5; if (message->cursize < 0 || static_cast(message->cursize) < offset || handler == callbacks.end()) { return false; } const std::basic_string_view data(message->data + offset, message->cursize - offset); handler->second(*address, data); return true; } void handle_command_stub(utils::hook::assembler& a) { a.pushad64(); a.mov(r8, r12); // msg a.mov(rdx, rcx); // command a.mov(rcx, r15); // address a.call_aligned(handle_command); a.movzx(rax, al); a.mov(qword_ptr(rsp, 0x78), rax); a.popad64(); a.jmp(0x14134D14B_g); } bool socket_set_blocking(const SOCKET s, const bool blocking) { unsigned long mode = blocking ? 0 : 1; return ioctlsocket(s, FIONBIO, &mode) == 0; } void create_ip_socket() { auto& s = *game::ip_socket; if (s) { return; } s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (s == INVALID_SOCKET) { throw std::runtime_error("Unable to create socket"); } socket_set_blocking(s, false); const auto address = htonl(INADDR_ANY); const auto port = htons(28960); sockaddr_in server_addr{}; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = address; server_addr.sin_port = port; if (bind(s, reinterpret_cast(&server_addr), sizeof(server_addr)) == SOCKET_ERROR) { throw std::runtime_error("Failed to bind socket"); } } void on(const std::string& command, const callback& callback) { get_callbacks()[utils::string::to_lower(command)] = callback; } } class component final : public component_interface { public: void post_unpack() override { // redirect dw_sendto to raw socket //utils::hook::jump(0x14233307E_g, 0x1423330C7_g); utils::hook::nop(0x142333056_g, 5); // don't add sock to packet utils::hook::set(0x14233305E_g, 0); // don't add checksum to packet utils::hook::nop(0x142332E43_g, 5); // don't read local net id utils::hook::set(0x142332E55_g, 0); // clear local net id utils::hook::jump(0x142332E72_g, 0x142332E8E_g); // skip checksum parsing // Fix netadr types utils::hook::set(0x142332DE5_g, game::NA_RAWIP); // raw socket type utils::hook::set(0x142332E00_g, game::NA_IP); // dw socket type // intercept command handling utils::hook::jump(0x14134D146_g, utils::hook::assemble(handle_command_stub)); on("getServersResponse", [](const game::netadr_t& /*source*/, const data_view& data) { OutputDebugStringA(utils::string::va("%.*s", static_cast(data.size()), data.data())); }); // TODO: Fix that scheduler::once([] { create_ip_socket(); }, scheduler::main); /*std::thread([] { while (true) { { MessageBoxA(0, 0, 0, 0); std::string data = utils::string::va("\xFF\xFF\xFF\xFF" "getservers S1 %i full empty", 1); game::netadr_t addr{}; addr.type = game::NA_RAWIP; addr.port = 20810; *reinterpret_cast(&addr.ipv4.a) = inet_addr("116.203.183.23"); addr.localNetID = game::NS_CLIENT1; game::NET_SendPacket(game::NS_CLIENT1, static_cast(data.size()), data.data(), &addr); } } }).detach();*/ } }; } REGISTER_COMPONENT(network::component)