From 4db29589a61f2e7cb663c5734f911c02206c7997 Mon Sep 17 00:00:00 2001 From: boris Date: Wed, 9 Jan 2019 20:51:16 +1300 Subject: whole buncha shit FIXME: loader currently corrupts heap on injection because i am retarded --- csgo-loader/csgo-client/Client.cpp | 55 +- csgo-loader/csgo-client/Client.hpp | 5 +- csgo-loader/csgo-client/Login/RemoteLogin.cpp | 3 + csgo-loader/csgo-client/Networking/TCPClient.cpp | 4 +- .../csgo-client/RemoteCode/RemoteCodeClient.cpp | 69 --- .../csgo-client/RemoteCode/RemoteCodeClient.hpp | 33 -- .../RemoteCode/RemoteInjectionClient.cpp | 6 - .../RemoteCode/RemoteInjectionClient.hpp | 69 --- .../csgo-client/RemoteCode/RemoteMapper.cpp | 139 +++++ .../csgo-client/RemoteCode/RemoteMapper.hpp | 26 + .../csgo-client/RemoteCode/RemoteProcess.cpp | 2 +- .../csgo-client/RemoteCode/RemoteProcess.hpp | 10 +- .../csgo-client/Security/RuntimeSecurity.cpp | 113 ++-- csgo-loader/csgo-client/csgo-client.vcxproj | 6 +- .../csgo-client/csgo-client.vcxproj.filters | 18 +- csgo-loader/csgo-loader.sln | 14 + csgo-loader/csgo-module/Module.cpp | 76 +++ csgo-loader/csgo-module/Module.hpp | 55 ++ csgo-loader/csgo-module/Networking/TCPClient.cpp | 119 ++++ csgo-loader/csgo-module/Networking/TCPClient.hpp | 46 ++ csgo-loader/csgo-module/Security/Encryption.cpp | 614 +++++++++++++++++++++ csgo-loader/csgo-module/Security/Encryption.hpp | 101 ++++ csgo-loader/csgo-module/Security/FnvHash.hpp | 100 ++++ .../csgo-module/Security/SyscallManager.cpp | 159 ++++++ .../csgo-module/Security/SyscallManager.hpp | 77 +++ csgo-loader/csgo-module/csgo-module.vcxproj | 209 +++++++ .../csgo-module/csgo-module.vcxproj.filters | 38 ++ csgo-loader/csgo-server/Login/RemoteLogin.cpp | 20 +- csgo-loader/csgo-server/Networking/TCPServer.hpp | 3 + .../RemoteCode/RemoteInjectionServer.cpp | 1 - .../RemoteCode/RemoteInjectionServer.hpp | 62 --- csgo-loader/csgo-server/Server.cpp | 99 +++- csgo-loader/csgo-server/Server.hpp | 18 +- csgo-loader/csgo-server/csgo-server.vcxproj | 2 - .../csgo-server/csgo-server.vcxproj.filters | 6 - 35 files changed, 2001 insertions(+), 376 deletions(-) delete mode 100644 csgo-loader/csgo-client/RemoteCode/RemoteCodeClient.cpp delete mode 100644 csgo-loader/csgo-client/RemoteCode/RemoteCodeClient.hpp delete mode 100644 csgo-loader/csgo-client/RemoteCode/RemoteInjectionClient.cpp delete mode 100644 csgo-loader/csgo-client/RemoteCode/RemoteInjectionClient.hpp create mode 100644 csgo-loader/csgo-client/RemoteCode/RemoteMapper.cpp create mode 100644 csgo-loader/csgo-client/RemoteCode/RemoteMapper.hpp create mode 100644 csgo-loader/csgo-module/Module.cpp create mode 100644 csgo-loader/csgo-module/Module.hpp create mode 100644 csgo-loader/csgo-module/Networking/TCPClient.cpp create mode 100644 csgo-loader/csgo-module/Networking/TCPClient.hpp create mode 100644 csgo-loader/csgo-module/Security/Encryption.cpp create mode 100644 csgo-loader/csgo-module/Security/Encryption.hpp create mode 100644 csgo-loader/csgo-module/Security/FnvHash.hpp create mode 100644 csgo-loader/csgo-module/Security/SyscallManager.cpp create mode 100644 csgo-loader/csgo-module/Security/SyscallManager.hpp create mode 100644 csgo-loader/csgo-module/csgo-module.vcxproj create mode 100644 csgo-loader/csgo-module/csgo-module.vcxproj.filters delete mode 100644 csgo-loader/csgo-server/RemoteCode/RemoteInjectionServer.cpp delete mode 100644 csgo-loader/csgo-server/RemoteCode/RemoteInjectionServer.hpp diff --git a/csgo-loader/csgo-client/Client.cpp b/csgo-loader/csgo-client/Client.cpp index 05ceb0b..d7b266f 100644 --- a/csgo-loader/csgo-client/Client.cpp +++ b/csgo-loader/csgo-client/Client.cpp @@ -1,23 +1,11 @@ #include /* - TODO: - - Finish off shellcode execution wrapper: - - The shellcode can be executed via two ways - - Either the code is mapped and called via CreateRemoteThread (allows custom param) - - or the code is mapped and called via DX9 (does not allow custom param) - - This will probably be the easiest thing to do. - --nave note: you don't need any custom params it's entirely pointless, we can use a wiped PE header to pass all the data we need plus passing shit via dllmain is too obvious anyway, dumb idea - - Finish off injection wrapper: - - Allocate every memory page as a bunch of consecutive 4kb sections (in order to fuck with people trying to dump) - - - Have the loader inject a .DLL :^) - --nave note 2: what you haven't done and what will completely fuck us up if anyone competent gets the loader: loading the binary directly via the loader itself is completely stupid and unreasonable @@ -46,11 +34,7 @@ int __stdcall WinMain(HINSTANCE inst, HINSTANCE prev, char* str, int cmdshow) Utils::OpenConsole(); ); - /////////////////////////////////////////////////////////////// - - //VMProtectBeginMutation("EntryPoint"); - - /////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////// // Create a thread to handle UI. std::thread WindowThread([] @@ -64,6 +48,8 @@ int __stdcall WinMain(HINSTANCE inst, HINSTANCE prev, char* str, int cmdshow) while(!UserInterface->m_Data.m_Ready) { Sleep(1); } + ////////////////////////////////////////////////////////////////////////////////////////// + // Initialize the syscall manager. if(!Syscalls->Start()) ERROR_ASSERT(STR("[000F:00001B00] Failed to initialize. Please contact an administrator.")); @@ -80,11 +66,20 @@ int __stdcall WinMain(HINSTANCE inst, HINSTANCE prev, char* str, int cmdshow) printf("[DEBUG] Server IP: %08x\n", inet_addr("35.165.60.229")); ); + ////////////////////////////////////////////////////////////////////////////////////////// + + // Connect to server. Networking::TCPClientPtr Client = std::make_unique(); if(!Client->Start(LOCAL_IP, SERVER_PORT)) ERROR_ASSERT(STR("[000F:0002A000] Server closed the connection unexpectedly.")); + // Header for Client. + ByteArray Header{ 0x0A, 0x31, 0x42, 0x4D }; + Client->SendRawBytes(Header); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Allow the user to input their log-in data. UserInterface->m_Data.m_ExecutionState = UserExperience::EXECUTION_LOG_IN; @@ -100,17 +95,29 @@ int __stdcall WinMain(HINSTANCE inst, HINSTANCE prev, char* str, int cmdshow) if(!LoginTransaction.TranslateResponse(LoginResponse)) ExitProcess(0); - // Allow the user to choose a cheat to inject. - UserInterface->m_Data.m_ExecutionState = UserExperience::EXECUTION_CHOOSE; + // Echo back to server. + Client->SendBytes(LoginResponse); + + ////////////////////////////////////////////////////////////////////////////////////////// + + // Receive loader module. + ByteArray LdrModule = Client->ReceiveBytes(); + + // Map loader module. + RemoteCode::RemoteProcess Process; - // TODO: Add game selection. - while(1) { if(GetAsyncKeyState(VK_END) & 0x8000) break; Sleep(1); } + if(!Process.Start(STR("explorer.exe"))) + ExitProcess(0); - /////////////////////////////////////////////////////////////// + RemoteCode::RemoteMapper Mapper; - //VMProtectEnd(); + if(!Mapper.Start(Process)) + ExitProcess(0); - /////////////////////////////////////////////////////////////// + if(!Mapper.WriteCodeToMap(LdrModule)) + ExitProcess(0); + + Mapper.ExecuteCodeFromMap(); } #pragma optimize("", on) \ No newline at end of file diff --git a/csgo-loader/csgo-client/Client.hpp b/csgo-loader/csgo-client/Client.hpp index 0f05a34..79f0a17 100644 --- a/csgo-loader/csgo-client/Client.hpp +++ b/csgo-loader/csgo-client/Client.hpp @@ -7,6 +7,8 @@ #define SERVER_IP 0xE53CA523 // Hexadecimal representation of the server IP, obtained by inet_addr() #define SERVER_PORT 0xF2C // Hexadecimal representation of the server port. +#define CLIENT_HEADER 0x4D42310A + // Security features (these will be initialised and ran // first, failure will terminate loader execution). #include @@ -20,8 +22,7 @@ #include #include -#include -#include +#include // It looked nasty in Client.cpp, so I'm putting it here. namespace Utils diff --git a/csgo-loader/csgo-client/Login/RemoteLogin.cpp b/csgo-loader/csgo-client/Login/RemoteLogin.cpp index 4a46da3..2385ab5 100644 --- a/csgo-loader/csgo-client/Login/RemoteLogin.cpp +++ b/csgo-loader/csgo-client/Login/RemoteLogin.cpp @@ -55,6 +55,9 @@ namespace Login bool RemoteLoginTransaction::TranslateResponse(ByteArray &RawResponse) { + if(RawResponse.empty()) + return false; + RemoteLoginResponse ServerResponse = *(RemoteLoginResponse *)&RawResponse[0]; WRAP_IF_DEBUG( diff --git a/csgo-loader/csgo-client/Networking/TCPClient.cpp b/csgo-loader/csgo-client/Networking/TCPClient.cpp index c974f88..2ff5095 100644 --- a/csgo-loader/csgo-client/Networking/TCPClient.cpp +++ b/csgo-loader/csgo-client/Networking/TCPClient.cpp @@ -144,12 +144,12 @@ namespace Networking if(EncryptionKey.empty()) return false; + std::memcpy(m_EncryptionKey, EncryptionKey.data(), EncryptionKey.size()); + WRAP_IF_DEBUG( printf("[DEBUG] Server handshake successful!\n"); ) - std::memcpy(m_EncryptionKey, EncryptionKey.data(), EncryptionKey.size()); - return true; } diff --git a/csgo-loader/csgo-client/RemoteCode/RemoteCodeClient.cpp b/csgo-loader/csgo-client/RemoteCode/RemoteCodeClient.cpp deleted file mode 100644 index 21d7851..0000000 --- a/csgo-loader/csgo-client/RemoteCode/RemoteCodeClient.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include - -namespace RemoteCode -{ - uint8_t ShellcodeStub[] = { - 0x55, - 0x8B, 0xEC, - 0x56, - 0x8B, 0x75, 0x08, - 0x57, - 0x80, 0x3E, 0x00, - 0x74, 0x2F, - 0x8B, 0x7E, 0x14, - 0x8D, 0x45, 0x08, - 0x50, - 0x8B, 0x46, 0x18, - 0x81, 0xC7, 0xA8, 0x00, 0x00, 0x00, - 0x6A, 0x40, - 0x6A, 0x04, - 0x57, - 0xFF, 0xD0, - 0x84, 0xC0, - 0x74, 0x4D, - 0x8B, 0x46, 0x10, - 0x89, 0x07, - 0x8D, 0x45, 0x08, - 0x50, - 0xFF, 0x75, 0x08, - 0x8B, 0x46, 0x18, - 0x6A, 0x04, - 0x57, - 0xFF, 0xD0, - - 0x8B, 0x46, 0x08, - 0x85, 0xC0, - 0x74, 0x09, - 0x6A, 0x00, - 0x6A, 0x01, - 0xFF, 0x76, 0x04, - 0xFF, 0xD0, - - 0x53, - 0x8B, 0x5E, 0x0C, - 0x85, 0xDB, - 0x74, 0x20, - 0x8B, 0x5B, 0x0C, - 0x33, 0xFF, - 0x8B, 0x03, - 0x85, 0xC0, - 0x74, 0x15, - 0x90, - - 0x6A, 0x00, - 0x6A, 0x01, - 0xFF, 0x76, 0x04, - 0xFF, 0xD0, - 0x8B, 0x44, 0xBB, 0x04, - 0x8D, 0x7F, 0x01, - 0x85, 0xC0, - 0x75, 0xEC, - - 0x5B, - - 0x5F, - 0x5E, - 0x5D, - 0xC2, 0x04, 0x00 - }; -} \ No newline at end of file diff --git a/csgo-loader/csgo-client/RemoteCode/RemoteCodeClient.hpp b/csgo-loader/csgo-client/RemoteCode/RemoteCodeClient.hpp deleted file mode 100644 index 84021c6..0000000 --- a/csgo-loader/csgo-client/RemoteCode/RemoteCodeClient.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -namespace RemoteCode -{ - class RemoteCodeClient - { - using MemProtect = bool(__stdcall *)(void *, size_t, uint32_t, uint32_t *); - - struct ShellcodeParameters - { - // Specifies whether or not the thread hijacking - // exploit will be used for code execution. - uint8_t m_ThreadExploit; - - // Address of the module allocation base - uintptr_t m_AllocationBase; - - // Specifies the entry-point / optional TLS directory - // to invoke. - uintptr_t m_EntryPoint; - uintptr_t m_TlsDirectory; - - // Thread hijacking (original address & VMT) - uintptr_t m_ThreadOriginal; - uintptr_t m_ThreadVirtual; - - // Function parameters that will be passed - MemProtect m_ProtectMemory; - }; - }; -} \ No newline at end of file diff --git a/csgo-loader/csgo-client/RemoteCode/RemoteInjectionClient.cpp b/csgo-loader/csgo-client/RemoteCode/RemoteInjectionClient.cpp deleted file mode 100644 index 01f52be..0000000 --- a/csgo-loader/csgo-client/RemoteCode/RemoteInjectionClient.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -namespace RemoteCode -{ - -} \ No newline at end of file diff --git a/csgo-loader/csgo-client/RemoteCode/RemoteInjectionClient.hpp b/csgo-loader/csgo-client/RemoteCode/RemoteInjectionClient.hpp deleted file mode 100644 index 2e5d216..0000000 --- a/csgo-loader/csgo-client/RemoteCode/RemoteInjectionClient.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include - -namespace RemoteCode -{ - // Used for TransactionStart - using ImportedModule = char[64]; - using ImportList = std::vector; - - // Used for TransactionContinue - struct ExportedFunction - { - // I've never seen modules / functions with names - // that were larger than 64 characters. - char m_Module[64]; - char m_Function[64]; - - // Address of exported module / function - uintptr_t m_ModuleAddress; - uintptr_t m_FunctionAddress; - }; - - using ExportList = std::vector; - - // Used for TransactionCommit - struct RemoteInjectionHeader - { - // Used to decrypt the cheat header (first 1000 bytes of image sent back). - uint8_t m_HeaderKey; - - // Used to call entrypoint/TLS callbacks. - uintptr_t m_EntryPoint; - uintptr_t m_TlsDirectory; - }; - - struct RemoteInjectionCode - { - RemoteInjectionHeader m_Header; - - // Actual injection code. - ByteArray m_Code; - }; - - // Implementation of client mapping code - class RemoteInjectionClient - { - RemoteInjectionHeader m_Header; - RemoteProcess m_Process; - - public: - // Receive hash of selected cheat. - // Reply with size of image to allocate. - ByteArray Start(ByteArray &Response); - - // Receive client header, send over list of imported functions - ByteArray TransactionStart(ByteArray &Response); - - // Receive list of modules & export addresses - ByteArray TransactionContinue(ByteArray &Response); - - // Write the file to the - void TransactionCommit(ByteArray &Response); - - RemoteProcess GetProcess() { return m_Process; } - RemoteInjectionHeader GetHeader() { return m_Header; } - }; -} \ No newline at end of file diff --git a/csgo-loader/csgo-client/RemoteCode/RemoteMapper.cpp b/csgo-loader/csgo-client/RemoteCode/RemoteMapper.cpp new file mode 100644 index 0000000..7b72550 --- /dev/null +++ b/csgo-loader/csgo-client/RemoteCode/RemoteMapper.cpp @@ -0,0 +1,139 @@ +#include + +namespace RemoteCode +{ + bool RemoteMapper::Start(RemoteProcess Process) + { + m_Process = Process; + return true; + } + + bool RemoteMapper::WriteCodeToMap(ByteArray Code) + { + // Check if the PE file is valid. + /*uint8_t *Buffer = Code.data(); + IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER *)Buffer; + + if(!DosHeader || DosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return false; + + IMAGE_NT_HEADERS *NtHeaders = (IMAGE_NT_HEADERS *)(Buffer + DosHeader->e_lfanew); + + if(!NtHeaders || NtHeaders->Signature != IMAGE_NT_SIGNATURE) + return false; + + WRAP_IF_DEBUG( + printf("[DEBUG] PE file validated!\n"); + ); + + IMAGE_SECTION_HEADER *Sections = (IMAGE_SECTION_HEADER *)((uintptr_t)&NtHeaders->OptionalHeader + NtHeaders->FileHeader.SizeOfOptionalHeader); + + if(!Sections) + return false; + + // Allocate map in process. + m_Map = m_Process.Allocate(Code.size()); + + if(!m_Map) + return false; + + // Ghetto workaround so heap doesn't get corrupted (i'm lazy) + size_t SizeOfCode = Code.size(); + m_Mapped = new uint8_t[SizeOfCode]; + + uint16_t SectionCount = NtHeaders->FileHeader.NumberOfSections; + + // Write each section to the process. + for(uint16_t n{}; n < SectionCount; n++) + { + uint32_t VirtualAddress = Sections[n].VirtualAddress; + uint32_t PointerToData = Sections[n].PointerToRawData; + uint32_t SizeOfData = Sections[n].SizeOfRawData; + + WRAP_IF_DEBUG( + printf("[DEBUG] Writing PE section #%d!\n", n); + printf("[DEBUG] Virtual address: 0x%x\n", VirtualAddress); + printf("[DEBUG] Pointer to data: 0x%x\n", PointerToData); + printf("[DEBUG] Size of data : %d bytes\n", SizeOfData); + ); + + // Write the data to map. + uint8_t *Data = (uint8_t *)((uintptr_t)Buffer + PointerToData); + void *ImageRva = (void *)((uintptr_t)&m_Mapped[0] + VirtualAddress); + + std::memcpy(ImageRva, Data, SizeOfData); + } + + // Copy over the PE header (temporarily). + uint32_t SizeOfHeaders = NtHeaders->OptionalHeader.SizeOfHeaders; + std::memcpy(&m_Mapped[0], Buffer, SizeOfHeaders); + + WRAP_IF_DEBUG( + printf("[DEBUG] Successfully copied over PE header!\n"); + printf("[DEBUG] Relocating image...\n"); + ); + + // Relocate the image. + IMAGE_BASE_RELOCATION *Reloc = (IMAGE_BASE_RELOCATION *)(&m_Mapped[0] + NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); + + uintptr_t ImageDelta = (uintptr_t)m_Map - NtHeaders->OptionalHeader.ImageBase; + + while(Reloc->SizeOfBlock > 0) + { + // address in binary where we need to relocate an address used by code + uintptr_t RelocationAddress = (uintptr_t)&m_Mapped[Reloc->VirtualAddress]; + uintptr_t RelocationDataAddress = (uintptr_t)Reloc + sizeof IMAGE_BASE_RELOCATION; + size_t RelocationCount = (Reloc->SizeOfBlock - sizeof IMAGE_BASE_RELOCATION) / sizeof uint16_t; + + for(size_t i = 0; i < RelocationCount; i++) + { + uint16_t RelocationData = *(uint16_t *)(RelocationDataAddress + sizeof uint16_t * i); + uint16_t RelocationType = RelocationData >> 12; + uint16_t RelocationOffset = RelocationData & 0x0FFF; + + WRAP_IF_DEBUG( + printf("[DEBUG] Processing relocation at %llx!\n", RelocationAddress + RelocationOffset); + ); + + switch(RelocationType) + { + case IMAGE_REL_BASED_MIPS_JMPADDR: + case IMAGE_REL_BASED_HIGH: + case IMAGE_REL_BASED_LOW: + case IMAGE_REL_BASED_HIGHLOW: + { + *(uint32_t*)(RelocationAddress + RelocationOffset) += ImageDelta; + break; + } + + case IMAGE_REL_BASED_ABSOLUTE: + default: + break; + }; + } + + Reloc = (IMAGE_BASE_RELOCATION*)((uintptr_t)Reloc + Reloc->SizeOfBlock); + } + + // Cripple the entire PE header. + std::memset(&m_Mapped[0], 0, SizeOfHeaders); + + WRAP_IF_DEBUG( + printf("[DEBUG] Successfully crippled PE header!\n"); + ); + + // Write the mapped image to the process. + m_Process.Write(m_Map, &m_Mapped[0], Code.size());*/ + + return true; + } + + bool RemoteMapper::ExecuteCodeFromMap() + { + WRAP_IF_DEBUG( + printf("[DEBUG] Executing payload!\n"); + ) + + return true; + } +} \ No newline at end of file diff --git a/csgo-loader/csgo-client/RemoteCode/RemoteMapper.hpp b/csgo-loader/csgo-client/RemoteCode/RemoteMapper.hpp new file mode 100644 index 0000000..21eb569 --- /dev/null +++ b/csgo-loader/csgo-client/RemoteCode/RemoteMapper.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace RemoteCode +{ + class RemoteMapper + { + RemoteProcess m_Process; + uint8_t *m_Mapped; + + void *m_Map; + + public: + RemoteMapper() = default; + + // Copy process & shellcode to class. + bool Start(RemoteProcess Process); + + // Writes code to process in allocated page. + bool WriteCodeToMap(ByteArray Code); + + // Calls shellcode to call the library ;D + bool ExecuteCodeFromMap(); + }; +} \ No newline at end of file diff --git a/csgo-loader/csgo-client/RemoteCode/RemoteProcess.cpp b/csgo-loader/csgo-client/RemoteCode/RemoteProcess.cpp index 1bbfed0..a6b90d4 100644 --- a/csgo-loader/csgo-client/RemoteCode/RemoteProcess.cpp +++ b/csgo-loader/csgo-client/RemoteCode/RemoteProcess.cpp @@ -30,7 +30,7 @@ namespace RemoteCode m_Process = OpenProcess(PROCESS_ALL_ACCESS, false, ProcessEntry.th32ProcessID); WRAP_IF_DEBUG( - printf("[DEBUG] Found process \"%s\" -> %p", ProcessEntry.szExeFile, m_Process); + printf("[DEBUG] Found process \"%s\" -> %p\n", ProcessEntry.szExeFile, m_Process); ); if(!m_Process) diff --git a/csgo-loader/csgo-client/RemoteCode/RemoteProcess.hpp b/csgo-loader/csgo-client/RemoteCode/RemoteProcess.hpp index a58320f..5fb3c75 100644 --- a/csgo-loader/csgo-client/RemoteCode/RemoteProcess.hpp +++ b/csgo-loader/csgo-client/RemoteCode/RemoteProcess.hpp @@ -48,11 +48,11 @@ namespace RemoteCode } // Release the handle when the process goes out of scope. - ~RemoteProcess() - { - if(m_Process) - CloseHandle(m_Process); - } + //~RemoteProcess() + //{ + // if(m_Process) + // CloseHandle(m_Process); + //} // Find the process ID and initialise the process. bool Start(const char *ProcessName); diff --git a/csgo-loader/csgo-client/Security/RuntimeSecurity.cpp b/csgo-loader/csgo-client/Security/RuntimeSecurity.cpp index 6054790..bfe45c5 100644 --- a/csgo-loader/csgo-client/Security/RuntimeSecurity.cpp +++ b/csgo-loader/csgo-client/Security/RuntimeSecurity.cpp @@ -92,9 +92,7 @@ namespace Security // Let's meme anyone who tries to reverse this. if(ReturnModule != LoaderModule) - { return []() { Protection->SecurityCallback(STR("Malicious activity [Tampering].")); return -1; }(); - } // Call original function return oWSARecv(Socket, Buffer, Length, Flags); @@ -115,9 +113,7 @@ namespace Security // Let's meme anyone who tries to reverse this. if(ReturnModule != LoaderModule) - { return []() { Protection->SecurityCallback(STR("Malicious activity [Tampering].")); return -1; }(); - } // Call original function return oWSASend(Socket, Buffer, Length, Flags); @@ -129,9 +125,9 @@ namespace Security /////////////////////////////////////////////////////////// // Sick macros, retard. -#define CreateMinHook() MH_STATUS Status; Status = MH_Initialize(); -#define CheckStatus() if(Status != MH_OK) { return false; } -#define SafeCallTo(Function) Status = Function; CheckStatus(); + #define CreateMinHook() MH_STATUS Status; Status = MH_Initialize(); + #define CheckStatus() if(Status != MH_OK) { return false; } + #define SafeCallTo(Function) Status = Function; CheckStatus(); #pragma optimize("", off) @@ -266,8 +262,6 @@ namespace Security for(auto &It : BlackListedWindows) { - // size_t Index = std::distance(...); - if(FindWindowA(It.first, It.second)) SecurityCallback(STR("Malicious activity [Debugging attempt].")); } @@ -291,7 +285,8 @@ namespace Security STR("NPF"), // WireShark / WinPCAP STR("acker"), // Process Hacker STR("CEDRI"), // Cheat Engine - //STR("VBox") // VirtualBox + STR("nHide"), // TitanHide + //STR("VBox") // VirtualBox }; static const char *BlackListReasons[] = { @@ -299,7 +294,8 @@ namespace Security STR("Please uninstall WireShark."), STR("Please close Process Hacker."), STR("Please close Cheat Engine."), - STR("Please uninstall VirtualBox.") + STR("Please uninstall TitanHide."), + //STR("Please uninstall VirtualBox.") }; uint16_t Length = sizeof BlackListedDrivers / sizeof(BlackListedDrivers[0]); @@ -357,14 +353,6 @@ namespace Security if(m_Identifier.m_SpecialMode[TEST_BUILD_MODE]) SecurityCallback(STR("Malicious activity [Plausible].")); - // Check if the file was unpacked. - //if(!VMProtectIsProtected()) - // SecurityCallback(STR("Malicious activity [Tampering].")); - - // Check if the image in memory was partially unpacked or patched. - //if(!VMProtectIsValidImageCRC()) - // SecurityCallback(STR("Malicious activity [Tampering].")); - // Don't put too much stress on the CPU. Sleep(1); } @@ -380,50 +368,49 @@ namespace Security constexpr uintptr_t KUSER_SHARED_DATA = 0x7FFE0000; - __forceinline uint64_t get_hdd_hash() { - STORAGE_PROPERTY_QUERY query{ }; - STORAGE_DESCRIPTOR_HEADER desc_header{ }; - STORAGE_DEVICE_DESCRIPTOR* device_descriptor{ }; - HANDLE device; - DWORD bytes_returned; - uint8_t* out_buffer; - - const wchar_t* device_path = L"\\??\\PhysicalDrive0"; - device = CreateFileA("\\\\.\\PhysicalDrive0", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); - if(!device) return uint64_t{ }; - - query.PropertyId = StorageDeviceProperty; - query.QueryType = PropertyStandardQuery; - - if(!DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, - &query, sizeof(STORAGE_PROPERTY_QUERY), - &desc_header, sizeof(STORAGE_DESCRIPTOR_HEADER), - &bytes_returned, 0)) { - return uint64_t{ }; - } - - out_buffer = new uint8_t[desc_header.Size]; - memset(out_buffer, 0, desc_header.Size); - - if(!DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, - &query, sizeof(STORAGE_PROPERTY_QUERY), - out_buffer, desc_header.Size, - &bytes_returned, 0)) { - delete[] out_buffer; - return uint64_t{ }; - } - - device_descriptor = (STORAGE_DEVICE_DESCRIPTOR*)out_buffer; - if(device_descriptor->SerialNumberOffset) { - std::string serial_num = reinterpret_cast( - out_buffer + device_descriptor->SerialNumberOffset); - - delete[] out_buffer; - CloseHandle(device); - return fnv::hash_runtime(serial_num.c_str()); - } - - return 0; + __forceinline uint64_t get_hdd_hash() { + STORAGE_PROPERTY_QUERY query{ }; + STORAGE_DESCRIPTOR_HEADER desc_header{ }; + STORAGE_DEVICE_DESCRIPTOR* device_descriptor{ }; + HANDLE device; + DWORD bytes_returned; + uint8_t* out_buffer; + + device = CreateFileA(STR("\\\\.\\PhysicalDrive0"), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + if(!device) return uint64_t{ }; + + query.PropertyId = StorageDeviceProperty; + query.QueryType = PropertyStandardQuery; + + if(!DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, + &query, sizeof(STORAGE_PROPERTY_QUERY), + &desc_header, sizeof(STORAGE_DESCRIPTOR_HEADER), + &bytes_returned, 0)) { + return uint64_t{ }; + } + + out_buffer = new uint8_t[desc_header.Size]; + memset(out_buffer, 0, desc_header.Size); + + if(!DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, + &query, sizeof(STORAGE_PROPERTY_QUERY), + out_buffer, desc_header.Size, + &bytes_returned, 0)) { + delete[] out_buffer; + return uint64_t{ }; + } + + device_descriptor = (STORAGE_DEVICE_DESCRIPTOR*)out_buffer; + if(device_descriptor->SerialNumberOffset) { + std::string serial_num = reinterpret_cast( + out_buffer + device_descriptor->SerialNumberOffset); + + delete[] out_buffer; + CloseHandle(device); + return fnv::hash_runtime(serial_num.c_str()); + } + + return 0; } void RuntimeSecurity::SetupSystemIdentifier() diff --git a/csgo-loader/csgo-client/csgo-client.vcxproj b/csgo-loader/csgo-client/csgo-client.vcxproj index 40af0a8..d29454f 100644 --- a/csgo-loader/csgo-client/csgo-client.vcxproj +++ b/csgo-loader/csgo-client/csgo-client.vcxproj @@ -30,8 +30,7 @@ - - + @@ -47,8 +46,7 @@ - - + diff --git a/csgo-loader/csgo-client/csgo-client.vcxproj.filters b/csgo-loader/csgo-client/csgo-client.vcxproj.filters index 0957ab9..e4b42c3 100644 --- a/csgo-loader/csgo-client/csgo-client.vcxproj.filters +++ b/csgo-loader/csgo-client/csgo-client.vcxproj.filters @@ -55,15 +55,12 @@ RemoteCode - - RemoteCode - - - RemoteCode - Security + + RemoteCode + @@ -159,17 +156,14 @@ RemoteCode - - RemoteCode - - - RemoteCode - Security + + RemoteCode + diff --git a/csgo-loader/csgo-loader.sln b/csgo-loader/csgo-loader.sln index 741e3e5..45eeb13 100644 --- a/csgo-loader/csgo-loader.sln +++ b/csgo-loader/csgo-loader.sln @@ -7,6 +7,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "csgo-client", "csgo-client\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "csgo-server", "csgo-server\csgo-server.vcxproj", "{97FAC435-B009-4571-AE91-31DC52D47598}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "csgo-module", "csgo-module\csgo-module.vcxproj", "{3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -41,6 +43,18 @@ Global {97FAC435-B009-4571-AE91-31DC52D47598}.Release|x64.Build.0 = Release|x64 {97FAC435-B009-4571-AE91-31DC52D47598}.Release|x86.ActiveCfg = Release|Win32 {97FAC435-B009-4571-AE91-31DC52D47598}.Release|x86.Build.0 = Release|Win32 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.Debug|x64.ActiveCfg = Debug|x64 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.Debug|x64.Build.0 = Debug|x64 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.Debug|x86.ActiveCfg = Debug|Win32 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.Debug|x86.Build.0 = Debug|Win32 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.FuckMSVC|x64.ActiveCfg = FuckMSVC|x64 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.FuckMSVC|x64.Build.0 = FuckMSVC|x64 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.FuckMSVC|x86.ActiveCfg = Release|Win32 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.FuckMSVC|x86.Build.0 = Release|Win32 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.Release|x64.ActiveCfg = Release|x64 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.Release|x64.Build.0 = Release|x64 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.Release|x86.ActiveCfg = Release|Win32 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/csgo-loader/csgo-module/Module.cpp b/csgo-loader/csgo-module/Module.cpp new file mode 100644 index 0000000..1fa638c --- /dev/null +++ b/csgo-loader/csgo-module/Module.cpp @@ -0,0 +1,76 @@ +#include + +/* + TODO: + - Finish off shellcode execution wrapper: + - The shellcode can be executed via two ways + - Either the code is mapped and called via CreateRemoteThread (allows custom param) + - or the code is mapped and called via DX9 (does not allow custom param) + - This will probably be the easiest thing to do. + + - Allocate via consecutive 64kb sections (TODO: Figure out how) + + - Free and wipe the module from memory once done + - Have the module authenticate via HWID or some shit.. idk (AAAAAAAAAAA) + - Do reloc/mapping stuff here +*/ + +DWORD ModuleThread(void *) +{ + ////////////////////////////////////////////////////////////////////////////////////////// + + // Initialize the syscall manager. + if(!Syscalls->Start()) + return 0; + + // Attempt to connect to the remote server. + WRAP_IF_DEBUG( + printf("[DEBUG] Server IP: %08x\n", inet_addr("35.165.60.229")); + ); + + ////////////////////////////////////////////////////////////////////////////////////////// + + // Connect to server. + Networking::TCPClientPtr Client = std::make_unique(); + + if(!Client->Start(LOCAL_IP, SERVER_PORT)) + return 0; + + // Header for Module. + ByteArray Header{ 0x0A, 0x32, 0x42, 0x4D }; + Client->SendRawBytes(Header); + + ////////////////////////////////////////////////////////////////////////////////////////// + + return 1; +} + +int __stdcall DllMain(void *, unsigned Reason, void *) +{ + if(Reason != 1) + return false; + + HANDLE Thread = CreateThread(nullptr, 0, ModuleThread, nullptr, 0, nullptr); + + if(Thread) + { + // Wait for thread to finish execution + WaitForSingleObject(Thread, INFINITE); + + // Get exit code from thread. + DWORD ExitCode; + GetExitCodeThread(Thread, &ExitCode); + + // If the HWND is 0, the loader will reveal that MessageBoxA was called from + // explorer.exe... This meme will get around that. + HWND Window = FindWindowA(STR("Valve001"), nullptr); + + if(!Window) + return true; + + if(!ExitCode) + MessageBoxA(Window, STR("[000F:00004A00] Failed to initialize. Please contact an administrator."), "", MB_ICONERROR | MB_OK); + } + + return true; +} \ No newline at end of file diff --git a/csgo-loader/csgo-module/Module.hpp b/csgo-loader/csgo-module/Module.hpp new file mode 100644 index 0000000..18a3ff1 --- /dev/null +++ b/csgo-loader/csgo-module/Module.hpp @@ -0,0 +1,55 @@ +#pragma once + +// NOTE: +// THE FOLLOWING MACROS ARE USED ONLY IN CLIENT.CPP +// PLEASE UPDATE THEM ACCORDINGLY. +#define LOCAL_IP 0x0100007F // '127.0.0.1' +#define SERVER_IP 0xE53CA523 // Hexadecimal representation of the server IP, obtained by inet_addr() +#define SERVER_PORT 0xF2C // Hexadecimal representation of the server port. + + +// Security features (these will be initialised and ran +// first, failure will terminate loader execution). +#include + +// Core functionality +#include + +// Required for the SDK from VMP which offers +// virtual machines and string encryption, as +// well as debug/VM checks. +#include + +// Just a neat little feature that I decided to implement :-) +#ifdef DEBUG + // Sick macros, retard. +#define WRAP_IF_RELEASE( s ) +#define WRAP_IF_DEBUG( s ) { s; } + +#define STR( s ) s +#else + // Sick macros, retard. +#define WRAP_IF_RELEASE( s ) { s; } +#define WRAP_IF_DEBUG( s ) + +#define STR( s ) VMProtectDecryptStringA( s ) +#endif + +// It looked nasty in Module.cpp, so I'm putting it here. +namespace Utils +{ + inline void OpenConsole() + { + // Create instance of console. + AllocConsole(); + + // Allow console to access output stream. + FILE *file; + freopen_s(&file, STR("CONOUT$"), STR("w"), stdout); + + // :^) + SetConsoleTitleA(STR("moneymodule $")); + + printf(STR("[DEBUG] Ready\n")); + } +} \ No newline at end of file diff --git a/csgo-loader/csgo-module/Networking/TCPClient.cpp b/csgo-loader/csgo-module/Networking/TCPClient.cpp new file mode 100644 index 0000000..e22bfdf --- /dev/null +++ b/csgo-loader/csgo-module/Networking/TCPClient.cpp @@ -0,0 +1,119 @@ +#include + +namespace Networking +{ + // We will only receive up to 256 bytes per cycle. + constexpr int BufferSize = 256; + + void TCPClient::SendRawBytes(ByteArray &Bytes) + { + // Send data. + send(m_Socket, (char *)Bytes.data(), (int)Bytes.size(), 0); + } + + ByteArray TCPClient::ReceiveRawBytes() + { + ByteArray ReceivedBytes; + uint8_t RecvBuffer[BufferSize]; + + // Attempt to receive a packet. + while(true) + { + int32_t Received = recv(m_Socket, (char*)RecvBuffer, BufferSize, 0); + + // No more bytes left to receive. + if(Received < 0) + break; + + // Emplace all received bytes. + for(int n = 0; n < Received; ++n) + { + ReceivedBytes.push_back(RecvBuffer[n]); + } + + // No more bytes left to receive. + if(Received < BufferSize) + break; + } + + return ReceivedBytes; + } + + void TCPClient::SendBytes(ByteArray &Bytes) + { + // Encrypt outgoing data. + ByteArray EncryptionKey; + EncryptionKey.insert( + EncryptionKey.begin(), + m_EncryptionKey, + m_EncryptionKey + sizeof m_EncryptionKey + ); + + Wrapper::Encryption Encryption; Encryption.Start(EncryptionKey); + + ByteArray Encrypted = Encryption.Encrypt(Bytes); + + SendRawBytes(Encrypted); + } + + ByteArray TCPClient::ReceiveBytes() + { + // Decrypt incoming data. + ByteArray ReceivedBytes = ReceiveRawBytes(); + + ByteArray EncryptionKey; + EncryptionKey.insert( + EncryptionKey.begin(), + m_EncryptionKey, + m_EncryptionKey + sizeof m_EncryptionKey + ); + + Wrapper::Encryption Encryption; Encryption.Start(EncryptionKey); + + ByteArray Decrypted = Encryption.Decrypt(ReceivedBytes); + + return Decrypted; + } + + bool TCPClient::Start(uint32_t ServerAddress, uint16_t ServerPort) + { + const int32_t version = 0x101; + + // Initialise WinSocks. + if(WSAStartup(version, &m_WinSocks)) + return false; + + // Create an IPv4 socket. + m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if(m_Socket == INVALID_SOCKET) + return false; + + // Set up client context. + m_Context.sin_addr.s_addr = ServerAddress; + m_Context.sin_family = AF_INET; + m_Context.sin_port = htons(ServerPort); + + // Attempt connection. + if(connect(m_Socket, (sockaddr *)&m_Context, sizeof m_Context)) + return false; + + // Initialise encryption wrapper. + ByteArray EncryptionKey = ReceiveRawBytes(); + + if(EncryptionKey.empty()) + return false; + + std::memcpy(m_EncryptionKey, EncryptionKey.data(), EncryptionKey.size()); + + return true; + } + + void TCPClient::Kill() + { + if(m_Socket) + closesocket(m_Socket); + + WSACleanup(); + } +} \ No newline at end of file diff --git a/csgo-loader/csgo-module/Networking/TCPClient.hpp b/csgo-loader/csgo-module/Networking/TCPClient.hpp new file mode 100644 index 0000000..a5794e9 --- /dev/null +++ b/csgo-loader/csgo-module/Networking/TCPClient.hpp @@ -0,0 +1,46 @@ +#pragma once + +// For encryption wrappers. +#include + +// WinSocks +#include +#pragma comment(lib, "ws2_32.lib") + +// std::min +#include + +// std::unique_ptr +#include + +namespace Networking +{ + // A TCPClient is essentially the same as the TCPConnection counterpart on the server, + // however, it independently handles connection. + class TCPClient + { + WSADATA m_WinSocks; + SOCKET m_Socket; + sockaddr_in m_Context; + uint8_t m_EncryptionKey[32]; + + public: + TCPClient() = default; + + // Connects to a remote server. + // Also handles the initial handshake between server and client. + bool Start(uint32_t ServerAddress, uint16_t ServerPort); + + // Kills the client. + void Kill(); + + // Wrappers for sending/receiving data. + void SendRawBytes(ByteArray &Bytes); + ByteArray ReceiveRawBytes(); + + void SendBytes(ByteArray &Bytes); + ByteArray ReceiveBytes(); + }; + + using TCPClientPtr = std::unique_ptr; +} \ No newline at end of file diff --git a/csgo-loader/csgo-module/Security/Encryption.cpp b/csgo-loader/csgo-module/Security/Encryption.cpp new file mode 100644 index 0000000..2763d23 --- /dev/null +++ b/csgo-loader/csgo-module/Security/Encryption.cpp @@ -0,0 +1,614 @@ +#include + +#define FE(x) (((x) << 1) ^ ((((x)>>7) & 1) * 0x1b)) +#define FD(x) (((x) >> 1) ^ (((x) & 1) ? 0x8d : 0)) + +#define KEY_SIZE 32 +#define NUM_ROUNDS 14 + +namespace Wrapper +{ + // Constants used for the AES256 algorithm. + uint8_t sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, + 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, + 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, + 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, + 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, + 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, + 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, + 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, + 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, + 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 + }; + + uint8_t sboxinv[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, + 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, + 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, + 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, + 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, + 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, + 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, + 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, + 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, + 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, + 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, + 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d + }; + + // Implementation of the AES256 encryption algorithm. + uint8_t rj_xtime(uint8_t x); + + Aes256::Aes256(const ByteArray& key) + : m_key(ByteArray(key.size() > KEY_SIZE ? KEY_SIZE : key.size(), 0)) + , m_salt(ByteArray(KEY_SIZE - m_key.size(), 0)) + , m_rkey(ByteArray(KEY_SIZE, 0)) + , m_buffer_pos(0) + , m_remainingLength(0) + , m_decryptInitialized(false) + { + for(ByteArray::size_type i = 0; i < m_key.size(); ++i) + m_key[i] = key[i]; + } + + Aes256::~Aes256() + { + } + + ByteArray::size_type Aes256::encrypt(const ByteArray& key, const ByteArray& plain, ByteArray& encrypted) + { + Aes256 aes(key); + + aes.encrypt_start(plain.size(), encrypted); + aes.encrypt_continue(plain, encrypted); + aes.encrypt_end(encrypted); + + return encrypted.size(); + } + + ByteArray::size_type Aes256::encrypt(const ByteArray& key, const unsigned char* plain, const ByteArray::size_type plain_length, ByteArray& encrypted) + { + Aes256 aes(key); + + aes.encrypt_start(plain_length, encrypted); + aes.encrypt_continue(plain, plain_length, encrypted); + aes.encrypt_end(encrypted); + + return encrypted.size(); + } + + ByteArray::size_type Aes256::decrypt(const ByteArray& key, const ByteArray& encrypted, ByteArray& plain) + { + Aes256 aes(key); + + aes.decrypt_start(encrypted.size()); + aes.decrypt_continue(encrypted, plain); + aes.decrypt_end(plain); + + return plain.size(); + } + + ByteArray::size_type Aes256::decrypt(const ByteArray& key, const unsigned char* encrypted, const ByteArray::size_type encrypted_length, ByteArray& plain) + { + Aes256 aes(key); + + aes.decrypt_start(encrypted_length); + aes.decrypt_continue(encrypted, encrypted_length, plain); + aes.decrypt_end(plain); + + return plain.size(); + } + + ByteArray::size_type Aes256::encrypt_start(const ByteArray::size_type plain_length, ByteArray& encrypted) + { + m_remainingLength = plain_length; + + // Generate salt + ByteArray::iterator it = m_salt.begin(), itEnd = m_salt.end(); + while(it != itEnd) + *(it++) = (rand() & 0xFF); + + // Calculate padding + ByteArray::size_type padding = 0; + if(m_remainingLength % BLOCK_SIZE != 0) + padding = (BLOCK_SIZE - (m_remainingLength % BLOCK_SIZE)); + m_remainingLength += padding; + + // Add salt + encrypted.insert(encrypted.end(), m_salt.begin(), m_salt.end()); + m_remainingLength += m_salt.size(); + + // Add 1 bytes for padding size + encrypted.push_back(padding & 0xFF); + ++m_remainingLength; + + // Reset buffer + m_buffer_pos = 0; + + return encrypted.size(); + } + + ByteArray::size_type Aes256::encrypt_continue(const ByteArray& plain, ByteArray& encrypted) + { + ByteArray::const_iterator it = plain.begin(), itEnd = plain.end(); + + while(it != itEnd) + { + m_buffer[m_buffer_pos++] = *(it++); + + check_and_encrypt_buffer(encrypted); + } + + return encrypted.size(); + } + + ByteArray::size_type Aes256::encrypt_continue(const unsigned char* plain, const ByteArray::size_type plain_length, ByteArray& encrypted) + { + ByteArray::size_type i = 0; + + while(i < plain_length) + { + m_buffer[m_buffer_pos++] = plain[i++]; + + check_and_encrypt_buffer(encrypted); + } + + return encrypted.size(); + } + + void Aes256::check_and_encrypt_buffer(ByteArray& encrypted) + { + if(m_buffer_pos == BLOCK_SIZE) + { + encrypt(m_buffer); + + for(m_buffer_pos = 0; m_buffer_pos < BLOCK_SIZE; ++m_buffer_pos) + { + encrypted.push_back(m_buffer[m_buffer_pos]); + --m_remainingLength; + } + + m_buffer_pos = 0; + } + } + + ByteArray::size_type Aes256::encrypt_end(ByteArray& encrypted) + { + if(m_buffer_pos > 0) + { + while(m_buffer_pos < BLOCK_SIZE) + m_buffer[m_buffer_pos++] = 0; + + encrypt(m_buffer); + + for(m_buffer_pos = 0; m_buffer_pos < BLOCK_SIZE; ++m_buffer_pos) + { + encrypted.push_back(m_buffer[m_buffer_pos]); + --m_remainingLength; + } + + m_buffer_pos = 0; + } + + return encrypted.size(); + } + + void Aes256::encrypt(unsigned char* buffer) + { + unsigned char i, rcon; + + copy_key(); + add_round_key(buffer, 0); + for(i = 1, rcon = 1; i < NUM_ROUNDS; ++i) + { + sub_bytes(buffer); + shift_rows(buffer); + mix_columns(buffer); + if(!(i & 1)) + expand_enc_key(&rcon); + add_round_key(buffer, i); + } + sub_bytes(buffer); + shift_rows(buffer); + expand_enc_key(&rcon); + add_round_key(buffer, i); + } + + ByteArray::size_type Aes256::decrypt_start(const ByteArray::size_type encrypted_length) + { + unsigned char j; + + m_remainingLength = encrypted_length; + + // Reset salt + for(j = 0; j < m_salt.size(); ++j) + m_salt[j] = 0; + m_remainingLength -= m_salt.size(); + + // Reset buffer + m_buffer_pos = 0; + + m_decryptInitialized = false; + + return m_remainingLength; + } + + ByteArray::size_type Aes256::decrypt_continue(const ByteArray& encrypted, ByteArray& plain) + { + ByteArray::const_iterator it = encrypted.begin(), itEnd = encrypted.end(); + + while(it != itEnd) + { + m_buffer[m_buffer_pos++] = *(it++); + + check_and_decrypt_buffer(plain); + } + + return plain.size(); + } + + ByteArray::size_type Aes256::decrypt_continue(const unsigned char* encrypted, const ByteArray::size_type encrypted_length, ByteArray& plain) + { + ByteArray::size_type i = 0; + + while(i < encrypted_length) + { + m_buffer[m_buffer_pos++] = encrypted[i++]; + + check_and_decrypt_buffer(plain); + } + + return plain.size(); + } + + void Aes256::check_and_decrypt_buffer(ByteArray& plain) + { + if(!m_decryptInitialized && m_buffer_pos == m_salt.size() + 1) + { + unsigned char j; + ByteArray::size_type padding; + + // Get salt + for(j = 0; j < m_salt.size(); ++j) + m_salt[j] = m_buffer[j]; + + // Get padding + padding = (m_buffer[j] & 0xFF); + m_remainingLength -= padding + 1; + + // Start decrypting + m_buffer_pos = 0; + + m_decryptInitialized = true; + } + else if(m_decryptInitialized && m_buffer_pos == BLOCK_SIZE) + { + decrypt(m_buffer); + + for(m_buffer_pos = 0; m_buffer_pos < BLOCK_SIZE; ++m_buffer_pos) + if(m_remainingLength > 0) + { + plain.push_back(m_buffer[m_buffer_pos]); + --m_remainingLength; + } + + m_buffer_pos = 0; + } + } + + ByteArray::size_type Aes256::decrypt_end(ByteArray& plain) + { + return plain.size(); + } + + void Aes256::decrypt(unsigned char* buffer) + { + unsigned char i, rcon = 1; + + copy_key(); + for(i = NUM_ROUNDS / 2; i > 0; --i) + expand_enc_key(&rcon); + + add_round_key(buffer, NUM_ROUNDS); + shift_rows_inv(buffer); + sub_bytes_inv(buffer); + + for(i = NUM_ROUNDS, rcon = 0x80; --i;) + { + if((i & 1)) + expand_dec_key(&rcon); + add_round_key(buffer, i); + mix_columns_inv(buffer); + shift_rows_inv(buffer); + sub_bytes_inv(buffer); + } + add_round_key(buffer, i); + } + + void Aes256::expand_enc_key(unsigned char* rc) + { + unsigned char i; + + m_rkey[0] = m_rkey[0] ^ sbox[m_rkey[29]] ^ (*rc); + m_rkey[1] = m_rkey[1] ^ sbox[m_rkey[30]]; + m_rkey[2] = m_rkey[2] ^ sbox[m_rkey[31]]; + m_rkey[3] = m_rkey[3] ^ sbox[m_rkey[28]]; + *rc = FE(*rc); + + for(i = 4; i < 16; i += 4) + { + m_rkey[i] = m_rkey[i] ^ m_rkey[i - 4]; + m_rkey[i + 1] = m_rkey[i + 1] ^ m_rkey[i - 3]; + m_rkey[i + 2] = m_rkey[i + 2] ^ m_rkey[i - 2]; + m_rkey[i + 3] = m_rkey[i + 3] ^ m_rkey[i - 1]; + } + m_rkey[16] = m_rkey[16] ^ sbox[m_rkey[12]]; + m_rkey[17] = m_rkey[17] ^ sbox[m_rkey[13]]; + m_rkey[18] = m_rkey[18] ^ sbox[m_rkey[14]]; + m_rkey[19] = m_rkey[19] ^ sbox[m_rkey[15]]; + + for(i = 20; i < 32; i += 4) + { + m_rkey[i] = m_rkey[i] ^ m_rkey[i - 4]; + m_rkey[i + 1] = m_rkey[i + 1] ^ m_rkey[i - 3]; + m_rkey[i + 2] = m_rkey[i + 2] ^ m_rkey[i - 2]; + m_rkey[i + 3] = m_rkey[i + 3] ^ m_rkey[i - 1]; + } + } + + void Aes256::expand_dec_key(unsigned char* rc) + { + unsigned char i; + + for(i = 28; i > 16; i -= 4) + { + m_rkey[i + 0] = m_rkey[i + 0] ^ m_rkey[i - 4]; + m_rkey[i + 1] = m_rkey[i + 1] ^ m_rkey[i - 3]; + m_rkey[i + 2] = m_rkey[i + 2] ^ m_rkey[i - 2]; + m_rkey[i + 3] = m_rkey[i + 3] ^ m_rkey[i - 1]; + } + + m_rkey[16] = m_rkey[16] ^ sbox[m_rkey[12]]; + m_rkey[17] = m_rkey[17] ^ sbox[m_rkey[13]]; + m_rkey[18] = m_rkey[18] ^ sbox[m_rkey[14]]; + m_rkey[19] = m_rkey[19] ^ sbox[m_rkey[15]]; + + for(i = 12; i > 0; i -= 4) + { + m_rkey[i + 0] = m_rkey[i + 0] ^ m_rkey[i - 4]; + m_rkey[i + 1] = m_rkey[i + 1] ^ m_rkey[i - 3]; + m_rkey[i + 2] = m_rkey[i + 2] ^ m_rkey[i - 2]; + m_rkey[i + 3] = m_rkey[i + 3] ^ m_rkey[i - 1]; + } + + *rc = FD(*rc); + m_rkey[0] = m_rkey[0] ^ sbox[m_rkey[29]] ^ (*rc); + m_rkey[1] = m_rkey[1] ^ sbox[m_rkey[30]]; + m_rkey[2] = m_rkey[2] ^ sbox[m_rkey[31]]; + m_rkey[3] = m_rkey[3] ^ sbox[m_rkey[28]]; + } + + void Aes256::sub_bytes(unsigned char* buffer) + { + unsigned char i = KEY_SIZE / 2; + + while(i--) + buffer[i] = sbox[buffer[i]]; + } + + void Aes256::sub_bytes_inv(unsigned char* buffer) + { + unsigned char i = KEY_SIZE / 2; + + while(i--) + buffer[i] = sboxinv[buffer[i]]; + } + + void Aes256::copy_key() + { + ByteArray::size_type i; + + for(i = 0; i < m_key.size(); ++i) + m_rkey[i] = m_key[i]; + for(i = 0; i < m_salt.size(); ++i) + m_rkey[i + m_key.size()] = m_salt[i]; + } + + void Aes256::add_round_key(unsigned char* buffer, const unsigned char round) + { + unsigned char i = KEY_SIZE / 2; + + while(i--) + buffer[i] ^= m_rkey[(round & 1) ? i + 16 : i]; + } + + void Aes256::shift_rows(unsigned char* buffer) + { + unsigned char i, j, k, l; /* to make it potentially parallelable :) */ + + i = buffer[1]; + buffer[1] = buffer[5]; + buffer[5] = buffer[9]; + buffer[9] = buffer[13]; + buffer[13] = i; + + j = buffer[10]; + buffer[10] = buffer[2]; + buffer[2] = j; + + k = buffer[3]; + buffer[3] = buffer[15]; + buffer[15] = buffer[11]; + buffer[11] = buffer[7]; + buffer[7] = k; + + l = buffer[14]; + buffer[14] = buffer[6]; + buffer[6] = l; + } + + void Aes256::shift_rows_inv(unsigned char* buffer) + { + unsigned char i, j, k, l; /* same as above :) */ + + i = buffer[1]; + buffer[1] = buffer[13]; + buffer[13] = buffer[9]; + buffer[9] = buffer[5]; + buffer[5] = i; + + j = buffer[2]; + buffer[2] = buffer[10]; + buffer[10] = j; + + k = buffer[3]; + buffer[3] = buffer[7]; + buffer[7] = buffer[11]; + buffer[11] = buffer[15]; + buffer[15] = k; + + l = buffer[6]; + buffer[6] = buffer[14]; + buffer[14] = l; + } + + void Aes256::mix_columns(unsigned char* buffer) + { + unsigned char i, a, b, c, d, e; + + for(i = 0; i < 16; i += 4) + { + a = buffer[i]; + b = buffer[i + 1]; + c = buffer[i + 2]; + d = buffer[i + 3]; + + e = a ^ b ^ c ^ d; + + buffer[i] ^= e ^ rj_xtime(a^b); + buffer[i + 1] ^= e ^ rj_xtime(b^c); + buffer[i + 2] ^= e ^ rj_xtime(c^d); + buffer[i + 3] ^= e ^ rj_xtime(d^a); + } + } + + void Aes256::mix_columns_inv(unsigned char* buffer) + { + unsigned char i, a, b, c, d, e, x, y, z; + + for(i = 0; i < 16; i += 4) + { + a = buffer[i]; + b = buffer[i + 1]; + c = buffer[i + 2]; + d = buffer[i + 3]; + + e = a ^ b ^ c ^ d; + z = rj_xtime(e); + x = e ^ rj_xtime(rj_xtime(z^a^c)); y = e ^ rj_xtime(rj_xtime(z^b^d)); + + buffer[i] ^= x ^ rj_xtime(a^b); + buffer[i + 1] ^= y ^ rj_xtime(b^c); + buffer[i + 2] ^= x ^ rj_xtime(c^d); + buffer[i + 3] ^= y ^ rj_xtime(d^a); + } + } + + inline uint8_t rj_xtime(uint8_t x) { return (x & 0x80) ? ((x << 1) ^ 0x1b) : (x << 1); } + + // Wrapper for the AES256 encryption algorithm. + void Encryption::Start() + { + // Create cryptographic context. + if(!CryptAcquireContextA(&m_CryptProvider, nullptr, nullptr, PROV_RSA_AES, 0)) + { + if(!CryptAcquireContextA(&m_CryptProvider, nullptr, nullptr, PROV_RSA_AES, CRYPT_NEWKEYSET)) + return; + } + + uint8_t RandomBytes[32]; + uint32_t RandomBytesCount = sizeof RandomBytes; + + // Generate random bytes to use as encryption key. + if(CryptGenRandom(m_CryptProvider, RandomBytesCount, RandomBytes)) + std::memcpy(m_EncryptionKey, RandomBytes, RandomBytesCount); + + // Release context. + if(m_CryptProvider) + CryptReleaseContext(m_CryptProvider, 0); + } + + void Encryption::Start(ByteArray &EncryptionKey) + { + // If an encryption key is provided, initialise the wrapper with + // the passed parameter. + if(!EncryptionKey.empty()) + std::memcpy(m_EncryptionKey, EncryptionKey.data(), EncryptionKey.size()); + + if(EncryptionKey.empty()) + Start(); + } + + ByteArray Encryption::Encrypt(ByteArray &Data) + { + // Encrypt outgoing data. + ByteArray Encrypted; + + Aes256::encrypt(GetKey(), Data, Encrypted); + + return Encrypted; + } + + ByteArray Encryption::Decrypt(ByteArray &Data) + { + // Decrypt incoming data. + ByteArray Decrypted; + + Aes256::decrypt(GetKey(), Data, Decrypted); + + return Decrypted; + } +} \ No newline at end of file diff --git a/csgo-loader/csgo-module/Security/Encryption.hpp b/csgo-loader/csgo-module/Security/Encryption.hpp new file mode 100644 index 0000000..a69b349 --- /dev/null +++ b/csgo-loader/csgo-module/Security/Encryption.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include +#include +#include +#include + +using ByteArray = std::vector; + +#define BLOCK_SIZE 16 + +namespace Wrapper +{ + // AES256 implementation. + class Aes256 + { + + public: + Aes256(const ByteArray& key); + ~Aes256(); + + static ByteArray::size_type encrypt(const ByteArray& key, const ByteArray& plain, ByteArray& encrypted); + static ByteArray::size_type encrypt(const ByteArray& key, const unsigned char* plain, const ByteArray::size_type plain_length, ByteArray& encrypted); + static ByteArray::size_type decrypt(const ByteArray& key, const ByteArray& encrypted, ByteArray& plain); + static ByteArray::size_type decrypt(const ByteArray& key, const unsigned char* encrypted, const ByteArray::size_type encrypted_length, ByteArray& plain); + + ByteArray::size_type encrypt_start(const ByteArray::size_type plain_length, ByteArray& encrypted); + ByteArray::size_type encrypt_continue(const ByteArray& plain, ByteArray& encrypted); + ByteArray::size_type encrypt_continue(const unsigned char* plain, const ByteArray::size_type plain_length, ByteArray& encrypted); + ByteArray::size_type encrypt_end(ByteArray& encrypted); + + ByteArray::size_type decrypt_start(const ByteArray::size_type encrypted_length); + ByteArray::size_type decrypt_continue(const ByteArray& encrypted, ByteArray& plain); + ByteArray::size_type decrypt_continue(const unsigned char* encrypted, const ByteArray::size_type encrypted_length, ByteArray& plain); + ByteArray::size_type decrypt_end(ByteArray& plain); + + private: + ByteArray m_key; + ByteArray m_salt; + ByteArray m_rkey; + + unsigned char m_buffer[3 * BLOCK_SIZE]; + unsigned char m_buffer_pos; + ByteArray::size_type m_remainingLength; + + bool m_decryptInitialized; + + void check_and_encrypt_buffer(ByteArray& encrypted); + void check_and_decrypt_buffer(ByteArray& plain); + + void encrypt(unsigned char *buffer); + void decrypt(unsigned char *buffer); + + void expand_enc_key(unsigned char *rc); + void expand_dec_key(unsigned char *rc); + + void sub_bytes(unsigned char *buffer); + void sub_bytes_inv(unsigned char *buffer); + + void copy_key(); + + void add_round_key(unsigned char *buffer, const unsigned char round); + + void shift_rows(unsigned char *buffer); + void shift_rows_inv(unsigned char *buffer); + + void mix_columns(unsigned char *buffer); + void mix_columns_inv(unsigned char *buffer); + }; + + // Encryption wrapper. + class Encryption + { + uint8_t m_EncryptionKey[32]; + HCRYPTPROV m_CryptProvider; + + public: + // Generate a random cryptographic key. + // OPTIONAL: You can pass a premade encryption key as a parameter. + void Start(); + void Start(ByteArray &EncryptionKey); + + // Handles encryption/decryption of data. + ByteArray Encrypt(ByteArray &Data); + ByteArray Decrypt(ByteArray &Data); + + // Exposes the encryption key. + ByteArray GetKey() + { + ByteArray TemporaryKey; + + TemporaryKey.insert( + TemporaryKey.begin(), + m_EncryptionKey, + m_EncryptionKey + sizeof m_EncryptionKey + ); + + return TemporaryKey; + } + }; +} \ No newline at end of file diff --git a/csgo-loader/csgo-module/Security/FnvHash.hpp b/csgo-loader/csgo-module/Security/FnvHash.hpp new file mode 100644 index 0000000..35c9ad0 --- /dev/null +++ b/csgo-loader/csgo-module/Security/FnvHash.hpp @@ -0,0 +1,100 @@ +#pragma once +#include +#include + +// Credits: namazso +// Implements FNV-1a hash algorithm +namespace detail +{ + template + struct SizeDependantData + { + using type = Type; + + constexpr static auto k_offset_basis = OffsetBasis; + constexpr static auto k_prime = Prime; + }; + + template + struct SizeSelector : std::false_type {}; + + template <> + struct SizeSelector<32> : SizeDependantData {}; + + template <> + struct SizeSelector<64> : SizeDependantData {}; + + template + class FnvHash + { + private: + using data_t = SizeSelector; + + public: + using hash = typename data_t::type; + + private: + constexpr static auto k_offset_basis = data_t::k_offset_basis; + constexpr static auto k_prime = data_t::k_prime; + + public: + static __forceinline constexpr auto hash_init( + ) -> hash + { + return k_offset_basis; + } + + static __forceinline constexpr auto hash_byte( + hash current, + std::uint8_t byte + ) -> hash + { + return (current ^ byte) * k_prime; + } + + template + static __forceinline constexpr auto hash_constexpr( + const char(&str)[N], + const std::size_t size = N - 1 /* do not hash the null */ + ) -> hash + { + const auto prev_hash = size == 1 ? hash_init() : hash_constexpr(str, size - 1); + const auto cur_hash = hash_byte(prev_hash, str[size - 1]); + return cur_hash; + } + + static auto __forceinline hash_runtime_data( + const void* data, + const std::size_t sz + ) -> hash + { + const auto bytes = static_cast(data); + const auto end = bytes + sz; + auto result = hash_init(); + for(auto it = bytes; it < end; ++it) + result = hash_byte(result, *it); + + return result; + } + + static auto __forceinline hash_runtime( + const char* str + ) -> hash + { + auto result = hash_init(); + do + result = hash_byte(result, *str++); + while(*str != '\0'); + + return result; + } + }; +} + +using fnv32 = ::detail::FnvHash<32>; +using fnv64 = ::detail::FnvHash<64>; +using fnv = ::detail::FnvHash; + +#define FNV(str) (std::integral_constant::value) +#define FNV32(str) (std::integral_constant::value) +#define FNV64(str) (std::integral_constant::value) \ No newline at end of file diff --git a/csgo-loader/csgo-module/Security/SyscallManager.cpp b/csgo-loader/csgo-module/Security/SyscallManager.cpp new file mode 100644 index 0000000..0338d88 --- /dev/null +++ b/csgo-loader/csgo-module/Security/SyscallManager.cpp @@ -0,0 +1,159 @@ +#include + +// Global accessor for SyscallManager. +Wrapper::SyscallManagerPtr Syscalls = std::make_unique(); + +namespace Wrapper +{ + void SyscallStub::SetIndex(uint32_t Index) + { + DWORD OldProtection{}; + + // Make the code executable and set the index. + if(VirtualProtect(m_Shellcode, sizeof m_Shellcode, PAGE_EXECUTE_READWRITE, &OldProtection)) + { + *(uint32_t *)(&m_Shellcode[4]) = Index; + } + } + + ByteArray SyscallManager::GetNtdllFromDisk() + { + char SystemPath[MAX_PATH]; + GetSystemDirectoryA(SystemPath, MAX_PATH); + + // Append 'ntdll.dll' to path. + strcat_s(SystemPath, "\\ntdll.dll"); + + // Open handle to 'ntdll.dll'. + std::ifstream FileHandle(SystemPath, std::ios::in | std::ios::binary); + FileHandle.unsetf(std::ios::skipws); + + if(!FileHandle.is_open()) + return ByteArray{}; + + // Read bytes to ByteArray. + ByteArray Bytes; + Bytes.insert( + Bytes.begin(), + std::istream_iterator(FileHandle), + std::istream_iterator() + ); + + FileHandle.close(); + + return Bytes; + } + + // Stolen :-) + uintptr_t SyscallManager::GetRawOffsetByRva(IMAGE_SECTION_HEADER *SectionHeader, uintptr_t Sections, uintptr_t FileSize, uintptr_t Rva) + { + IMAGE_SECTION_HEADER *Header = GetSectionByRva(SectionHeader, Sections, Rva); + + if(!Header) + return 0; + + uintptr_t Delta = Rva - Header->VirtualAddress; + uintptr_t Offset = Header->PointerToRawData + Delta; + + // Sanity check, otherwise this would crash on versions below Windows 10... + // for whatever reason.. + if(Offset >= FileSize) + return 0; + + return Offset; + } + + IMAGE_SECTION_HEADER *SyscallManager::GetSectionByRva(IMAGE_SECTION_HEADER *SectionHeader, uintptr_t Sections, uintptr_t Rva) + { + IMAGE_SECTION_HEADER *Header = SectionHeader; + + for(size_t i{}; i < Sections; ++i, ++Header) + { + uintptr_t VirtualAddress = Header->VirtualAddress; + uintptr_t AddressBounds = VirtualAddress + Header->SizeOfRawData; + + if(Rva >= VirtualAddress && Rva < AddressBounds) + return Header; + } + + return nullptr; + } + + // Sick macros, retard. +#define GetRvaPointer(Rva) (Buffer + GetRawOffsetByRva(SectionHeader, SectionCount, FileSize, Rva)) + + bool SyscallManager::Start() + { + // Read contents of NTDLL. + ByteArray Ntdll = GetNtdllFromDisk(); + + if(Ntdll.empty()) + return false; + + uint8_t *Buffer = Ntdll.data(); + size_t FileSize = Ntdll.size(); + + // Ghetto check to see if the file is a valid PE. + if(*(uint16_t*)Buffer != IMAGE_DOS_SIGNATURE) + return false; + + // Read PE headers. + IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER *)Buffer; + IMAGE_NT_HEADERS *NtHeaders = (IMAGE_NT_HEADERS *)(Buffer + DosHeader->e_lfanew); + + uint64_t SectionCount = NtHeaders->FileHeader.NumberOfSections; + + // Read the first section header. + IMAGE_SECTION_HEADER *SectionHeader = (IMAGE_SECTION_HEADER *)(Buffer + DosHeader->e_lfanew + sizeof IMAGE_NT_HEADERS); + + if(!SectionHeader) + return false; + + uintptr_t ExportRva = NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + uintptr_t ExportSize = NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; + uintptr_t ExportRaw = GetRawOffsetByRva(SectionHeader, SectionCount, FileSize, ExportRva); + + if(!ExportRva || !ExportSize || !ExportRaw) + return false; + + // Now let's parse the export directory. + IMAGE_EXPORT_DIRECTORY *ExportDirectory = (IMAGE_EXPORT_DIRECTORY *)(Buffer + ExportRaw); + + uint32_t *Functions = (uint32_t *)GetRvaPointer(ExportDirectory->AddressOfFunctions); + uint16_t *Ordinals = (uint16_t *)GetRvaPointer(ExportDirectory->AddressOfNameOrdinals); + uint32_t *Names = (uint32_t *)GetRvaPointer(ExportDirectory->AddressOfNames); + + if(!Functions || !Ordinals || !Names) + return false; + + + for(uint32_t n{}; n < ExportDirectory->NumberOfNames; ++n) + { + uint32_t NameRva = Names[n]; + uint32_t FunctionRva = Functions[Ordinals[n]]; + + uintptr_t NameRawOffset = GetRawOffsetByRva(SectionHeader, SectionCount, FileSize, NameRva); + uintptr_t FunctionRawOffset = GetRawOffsetByRva(SectionHeader, SectionCount, FileSize, FunctionRva); + + // We've found a syscall. + uint8_t *Opcodes = (uint8_t *)(Buffer + FunctionRawOffset); + + if(!memcmp(Opcodes, "\x4C\x8B\xD1\xB8", 4)) + { + uint32_t SyscallIndex = *(uint32_t *)(Buffer + FunctionRawOffset + 4); + + // Get hash of syscall name. + char *SyscallName = (char *)(Buffer + NameRawOffset); + uint64_t SyscallNameHash = fnv::hash_runtime(SyscallName); + + // Emplace the syscall in the syscall map. + m_Syscalls[SyscallNameHash].SetIndex(SyscallIndex); + } + } + + if(m_Syscalls.empty()) + return false; + + return true; + } +} \ No newline at end of file diff --git a/csgo-loader/csgo-module/Security/SyscallManager.hpp b/csgo-loader/csgo-module/Security/SyscallManager.hpp new file mode 100644 index 0000000..d8981e9 --- /dev/null +++ b/csgo-loader/csgo-module/Security/SyscallManager.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using ByteArray = std::vector; + +namespace Wrapper +{ + // A stub used for our syscalls. + class SyscallStub + { + // The shellcode which executes a low latency system call. + uint8_t m_Shellcode[11] = { + 0x4C, 0x8B, 0xD1, // mov r10, rcx + 0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, [syscall index] + 0x0F, 0x05, // syscall + 0xC3 + }; + public: + // Constructors. + SyscallStub() = default; + + // Sets the syscall index. + void SetIndex(uint32_t Index); + + __forceinline uintptr_t Get() + { + return (uintptr_t)m_Shellcode; + } + }; + + // Manager for system calls. Used to iterate NTDLL for all syscall indices. + // Read: https://www.evilsocket.net/2014/02/11/on-windows-syscall-mechanism-and-syscall-numbers-extraction-methods/ + class SyscallManager + { + // Reading NTDLL from disk because it cannot be modified + // due to restrictions put in place by PatchGuard. + ByteArray GetNtdllFromDisk(); + + // Container for all syscall stubs. + std::map m_Syscalls; + + // Helper functions. + uintptr_t GetRawOffsetByRva(IMAGE_SECTION_HEADER *SectionHeader, uintptr_t Sections, uintptr_t FileSize, uintptr_t Rva); + IMAGE_SECTION_HEADER *GetSectionByRva(IMAGE_SECTION_HEADER *SectionHeader, uintptr_t Sections, uintptr_t Rva); + + public: + // Initialises the syscall manager, dumping all the + // syscall indices. + bool Start(); + + // Finds a syscall by hash. + template < typename T > + T Find(uint64_t Hash) + { + uint64_t Syscall = m_Syscalls[Hash].Get(); + + if(!Syscall) + return T{}; + + return (T)m_Syscalls[Hash].Get(); + } + }; + + using SyscallManagerPtr = std::unique_ptr; +} + +extern Wrapper::SyscallManagerPtr Syscalls; \ No newline at end of file diff --git a/csgo-loader/csgo-module/csgo-module.vcxproj b/csgo-loader/csgo-module/csgo-module.vcxproj new file mode 100644 index 0000000..cc2f6a4 --- /dev/null +++ b/csgo-loader/csgo-module/csgo-module.vcxproj @@ -0,0 +1,209 @@ + + + + + Debug + Win32 + + + FuckMSVC + Win32 + + + FuckMSVC + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {3A767D6A-B6D0-4D39-92B6-62FD4537F3BD} + csgomodule + 10.0.17763.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + false + v141 + true + MultiByte + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)shared\include;$(ProjectDir);$(VC_IncludePath);$(WindowsSDK_IncludePath); + $(SolutionDir)shared\lib;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64 + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)build\$(Configuration)\ + + + $(SolutionDir)shared\include;$(ProjectDir);$(VC_IncludePath);$(WindowsSDK_IncludePath); + $(SolutionDir)shared\lib;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64 + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)build\$(Configuration)\ + + + $(SolutionDir)shared\include;$(ProjectDir);$(VC_IncludePath);$(WindowsSDK_IncludePath); + $(SolutionDir)shared\lib;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64 + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)build\$(Configuration)\ + + + + Level3 + MaxSpeed + true + true + true + true + WIN32_LEAN_AND_MEAN;_WINDLL;%(PreprocessorDefinitions) + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + _WINSOCK_DEPRECATED_NO_WARNINGS;DEBUG;WIN32_LEAN_AND_MEAN;_WINDLL;%(PreprocessorDefinitions) + + + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + WIN32_LEAN_AND_MEAN;DEBUG;_WINDLL;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/csgo-loader/csgo-module/csgo-module.vcxproj.filters b/csgo-loader/csgo-module/csgo-module.vcxproj.filters new file mode 100644 index 0000000..2d62a91 --- /dev/null +++ b/csgo-loader/csgo-module/csgo-module.vcxproj.filters @@ -0,0 +1,38 @@ + + + + + + Networking + + + Security + + + Security + + + + + + Networking + + + Security + + + Security + + + Security + + + + + {5c3d9ea2-e561-435a-b858-e71f87afe818} + + + {bc2e1992-5068-4c22-842f-8f6bf028f0d1} + + + \ No newline at end of file diff --git a/csgo-loader/csgo-server/Login/RemoteLogin.cpp b/csgo-loader/csgo-server/Login/RemoteLogin.cpp index 2f30e7f..0351f27 100644 --- a/csgo-loader/csgo-server/Login/RemoteLogin.cpp +++ b/csgo-loader/csgo-server/Login/RemoteLogin.cpp @@ -13,7 +13,20 @@ namespace Login // Epic direct casts :---DDDD m_Header = *(RemoteLoginHeader *)&RawLoginHeader[0]; - return true; + + printf("[ => ] User attempting login: %s\n", m_Header.m_Username); + printf("[ => ] User HWID: %llx\n", m_Header.m_HardwareId); + + RemoteLoginResponse Response = GetLoginResponse(); + + printf("[ => ] User response: %02x\n", Response); + + if(Response & ACCESS_AUTHORISED || Response & ACCESS_SPECIAL_USER) + return true; + + printf("[ => ] Login for user %s was rejected!\n", m_Header.m_Username); + + return false; } RemoteLoginResponse RemoteLoginServer::GetLoginResponse() @@ -32,9 +45,6 @@ namespace Login if(strcmp(m_Header.m_Password, "betapassword")) return RemoteLoginResponse::INVALID_CREDENTIALS; - // User failed to obtain HWID? - printf("[ => ] User HWID: %llx\n", m_Header.m_HardwareId); - if(!m_Header.m_HardwareId) { // TODO: Shadow ban the user. @@ -43,7 +53,7 @@ namespace Login } // TODO: Check if the HWID is present in DB. - if(m_Header.m_HardwareId != 0x2F769B06FA897376) + if(m_Header.m_HardwareId != 0xd33a13f59ae35130) return RemoteLoginResponse::INVALID_HARDWARE; // TODO: Check if the user has a subscription. diff --git a/csgo-loader/csgo-server/Networking/TCPServer.hpp b/csgo-loader/csgo-server/Networking/TCPServer.hpp index a29a796..9459136 100644 --- a/csgo-loader/csgo-server/Networking/TCPServer.hpp +++ b/csgo-loader/csgo-server/Networking/TCPServer.hpp @@ -24,6 +24,7 @@ namespace Networking SOCKET m_Socket; Wrapper::Encryption m_Encryption; char m_IpAddress[32]; + public: // Initialiser for TCPConnection class. TCPConnection(SOCKET Connection, const char *IpAddress, Wrapper::Encryption &RSA) : @@ -54,6 +55,8 @@ namespace Networking { return m_Encryption.GetKey(); } + + char *GetIpAddress() { return m_IpAddress; } }; // Basic TCP server. Supports custom connection handling (pass a lambda to the handler list). diff --git a/csgo-loader/csgo-server/RemoteCode/RemoteInjectionServer.cpp b/csgo-loader/csgo-server/RemoteCode/RemoteInjectionServer.cpp deleted file mode 100644 index fd3efc0..0000000 --- a/csgo-loader/csgo-server/RemoteCode/RemoteInjectionServer.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/csgo-loader/csgo-server/RemoteCode/RemoteInjectionServer.hpp b/csgo-loader/csgo-server/RemoteCode/RemoteInjectionServer.hpp deleted file mode 100644 index b8659ff..0000000 --- a/csgo-loader/csgo-server/RemoteCode/RemoteInjectionServer.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include -#include -#include - -using ByteArray = std::vector; - -namespace RemoteCode -{ - // Used for TransactionStart - using ImportedModule = char[64]; - using ImportList = std::vector; - - // Used for TransactionContinue - struct ExportedFunction - { - // I've never seen modules / functions with names - // that were larger than 64 characters. - char m_Module[64]; - char m_Function[64]; - - // Address of exported module / function - uintptr_t m_ModuleAddress; - uintptr_t m_FunctionAddress; - }; - - using ExportList = std::vector; - - // Used for TransactionCommit - struct RemoteInjectionHeader - { - // Used to decrypt the cheat header (first 1000 bytes of image sent back). - uint8_t m_HeaderKey; - - // Used to call entrypoint/TLS callbacks. - uintptr_t m_EntryPoint; - uintptr_t m_TlsDirectory; - }; - - struct RemoteInjectionCode - { - RemoteInjectionHeader m_Header; - - // Actual injection code. - ByteArray m_Code; - }; - - class RemoteInjectionServer - { - public: - // Receive hash of selected cheat. - // Reply with size of image to allocate. - ByteArray Start(ByteArray &Response); - - // Receive client header, send over list of imported functions - ByteArray TransactionStart(ByteArray &Response); - - // Receive list of modules & export addresses - ByteArray TransactionContinue(ByteArray &Response); - }; -} \ No newline at end of file diff --git a/csgo-loader/csgo-server/Server.cpp b/csgo-loader/csgo-server/Server.cpp index f822753..e0f1455 100644 --- a/csgo-loader/csgo-server/Server.cpp +++ b/csgo-loader/csgo-server/Server.cpp @@ -1,15 +1,96 @@ #include -void ConnectionHandler(Networking::TCPConnection &Connection) { - Login::RemoteLoginServer LoginServer; +// 'M1' -> cl request +// 'M2' -> mod request +// 'M3' -> ban request - ByteArray LoginHeader = Connection.ReceiveBytes(); - - if(!LoginServer.Start(LoginHeader)) - return; +namespace Handler +{ + void OnClientConnection(Networking::TCPConnection &Connection) + { + printf("[ !! ] Client at %s requested connection!\n", Connection.GetIpAddress()); + + // Initialize login server for the client. + Login::RemoteLoginServer LoginServer; + + ByteArray LoginHeader = Connection.ReceiveBytes(); + + // Invalid login header. + if(!LoginServer.Start(LoginHeader)) + return; + + printf("[ !! ] Received login header from %s!\n", Connection.GetIpAddress()); + + // Reply with server header. + ByteArray LoginReply = LoginServer.GetResponse(); + Connection.SendBytes(LoginReply); - ByteArray LoginReply = LoginServer.GetResponse(); - Connection.SendBytes(LoginReply); + ByteArray LoginReplyEcho = Connection.ReceiveBytes(); + + if(LoginReply.size() != LoginReplyEcho.size()) + { + printf("[ !! ] Echo from %s invalid, dropping connection!", Connection.GetIpAddress()); + return; + } + + RemoteCode::FileReader File; + + if(!File.Start("csgo-module.dll")) + return; + + // Send them the loader module to inject the cheat. + printf("[ !! ] Sending latest loader module!\n"); + + ByteArray RawLdrModule; + RawLdrModule.insert( + RawLdrModule.begin(), + (uint8_t *)File, + (uint8_t *)(File + File.GetFileLength()) + ); + + Connection.SendBytes(RawLdrModule); + } + + void OnModuleConnection(Networking::TCPConnection &Connection) + { + // The output of this function will be verbose by default. + printf("[ !! ] Module hello from %s!\n", Connection.GetIpAddress()); + } + + void OnBanReqConnection(Networking::TCPConnection &Connection) + { + // Use for forum IP-ban purposes or whatever.. + printf("[ !! ] Client at %s requested ban!\n", Connection.GetIpAddress()); + + // TODO: Ban user? + } + + void OnReceiveConnection(Networking::TCPConnection &Connection) + { + ByteArray Header = Connection.ReceiveRawBytes(); + + if(Header.empty()) + { + printf("[ !! ] Client at %s sent malformed request!\n", Connection.GetIpAddress()); + return; + } + + uint32_t HeaderCode = *(uint32_t *)&Header[0]; + + switch(HeaderCode) + { + case CLIENT_HEADER: // "MB1" + OnClientConnection(Connection); break; + case MODULE_HEADER: // "MB2" + OnModuleConnection(Connection); break; + case BANREQ_HEADER: // "MB3"; + OnBanReqConnection(Connection); break; + + // Drop any malformed clients. + default: + printf("[ !! ] Client at %s sent malformed request!\n", Connection.GetIpAddress()); + } + } } int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int) @@ -25,7 +106,7 @@ int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int) if(Result) { // Attach our connection handler. - Server += ConnectionHandler; + Server += Handler::OnReceiveConnection; // Accept any incoming connections. for(;;) diff --git a/csgo-loader/csgo-server/Server.hpp b/csgo-loader/csgo-server/Server.hpp index af07bb0..90def2a 100644 --- a/csgo-loader/csgo-server/Server.hpp +++ b/csgo-loader/csgo-server/Server.hpp @@ -5,6 +5,10 @@ // PLEASE UPDATE THEM ACCORDINGLY. #define SERVER_PORT 0xF2C // Hexadecimal representation of the server port. +#define CLIENT_HEADER 0x4D42310A +#define MODULE_HEADER 0x4D42320A +#define BANREQ_HEADER 0x4D42330A + // Core functionality #include #include @@ -13,7 +17,19 @@ #include #include -#include +//#include + +// Let's separate up the connection handlers :) +namespace Handler +{ + // Branches + void OnClientConnection(Networking::TCPConnection &Connection); + void OnModuleConnection(Networking::TCPConnection &Connection); + void OnBanReqConnection(Networking::TCPConnection &Connection); + + // Default handler + void OnReceiveConnection(Networking::TCPConnection &Connection); +} // It looked nasty in Server.cpp, so I'm putting it here. namespace Utils diff --git a/csgo-loader/csgo-server/csgo-server.vcxproj b/csgo-loader/csgo-server/csgo-server.vcxproj index ad924f6..c702cbb 100644 --- a/csgo-loader/csgo-server/csgo-server.vcxproj +++ b/csgo-loader/csgo-server/csgo-server.vcxproj @@ -31,7 +31,6 @@ - @@ -40,7 +39,6 @@ - diff --git a/csgo-loader/csgo-server/csgo-server.vcxproj.filters b/csgo-loader/csgo-server/csgo-server.vcxproj.filters index 0480d6d..b0c12d7 100644 --- a/csgo-loader/csgo-server/csgo-server.vcxproj.filters +++ b/csgo-loader/csgo-server/csgo-server.vcxproj.filters @@ -31,9 +31,6 @@ RemoteCode - - RemoteCode - @@ -51,9 +48,6 @@ RemoteCode - - RemoteCode - Security -- cgit v1.2.3