From c0f1354a301ce2a2fc867a89fafdde4571c07c02 Mon Sep 17 00:00:00 2001 From: boris Date: Wed, 2 Jan 2019 21:11:03 +1300 Subject: 6IX9INE "Billy" (WSHH Exclusive - Official Music Video) --- csgo-loader/csgo-client/Client.cpp | 35 +++--- csgo-loader/csgo-client/Login/RemoteLogin.cpp | 30 +++-- csgo-loader/csgo-client/Login/RemoteLogin.hpp | 3 +- csgo-loader/csgo-client/Networking/TCPClient.cpp | 4 - csgo-loader/csgo-client/Security/Encryption.cpp | 17 ++- .../csgo-client/Security/RuntimeSecurity.cpp | 124 ++++++++++++++++----- .../csgo-client/Security/RuntimeSecurity.hpp | 25 ++++- csgo-loader/csgo-server/Login/RemoteLogin.cpp | 4 +- csgo-loader/csgo-server/Login/RemoteLogin.hpp | 3 +- csgo-loader/csgo-server/Networking/TCPServer.cpp | 6 - csgo-loader/csgo-server/Server.cpp | 12 +- 11 files changed, 182 insertions(+), 81 deletions(-) diff --git a/csgo-loader/csgo-client/Client.cpp b/csgo-loader/csgo-client/Client.cpp index 52a288d..7285b6b 100644 --- a/csgo-loader/csgo-client/Client.cpp +++ b/csgo-loader/csgo-client/Client.cpp @@ -2,12 +2,6 @@ /* TODO: - - Finish off security on client: - - Add dump protection (closes csgo.exe if a handle is detected, probably explorer shellcode) - - Add HWID generation [half-assed atm] - - - Switch Themida with VMP (Superior VM imho and I can fuck people over with my cool script (: ) - - 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) @@ -15,16 +9,14 @@ - This will probably be the easiest thing to do. - Finish off injection wrapper: - - Allocate a 4KB page for trampoline imports (4096/6) { E9 ? ? ? ? C3 } - - Actually, allocate every memory page as a bunch of consecutive 4kb sections - - Trampoline for imports - - Everything is already laid out, tbh. + - 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 :^) TODO (Nave): - Make the UI look nice. - Adapt the server to work with your backend. + - Add dump protection (closes csgo.exe if a handle is detected, probably explorer shellcode) */ @@ -32,11 +24,11 @@ int __stdcall WinMain(HINSTANCE inst, HINSTANCE prev, char* str, int cmdshow) { - (Utils::OpenConsole()); + WRAP_IF_DEBUG(Utils::OpenConsole()); /////////////////////////////////////////////////////////////// - VMProtectBeginUltra("EntryPoint"); + VMProtectBeginMutation("EntryPoint"); /////////////////////////////////////////////////////////////// @@ -66,16 +58,33 @@ int __stdcall WinMain(HINSTANCE inst, HINSTANCE prev, char* str, int cmdshow) UserInterface->m_Data.m_ExecutionState = UserExperience::EXECUTION_WAITING; // Attempt to connect to the remote server. + WRAP_IF_DEBUG( + printf("[DEBUG] Server IP: %08x\n", inet_addr("35.165.60.229")); + ); + Networking::TCPClientPtr Client = std::make_unique(); if(!Client->Start(LOCAL_IP, SERVER_PORT)) ERROR_ASSERT(STR("[000F:0002A000] Server closed the connection unexpectedly.")); - + // Allow the user to input their log-in data. UserInterface->m_Data.m_ExecutionState = UserExperience::EXECUTION_LOG_IN; while(UserInterface->m_Data.m_ExecutionState != UserExperience::EXECUTION_WAITING) { Sleep(1); } + Login::RemoteLoginTransaction LoginTransaction; + LoginTransaction.Start(UserInterface->m_Data.m_Username, UserInterface->m_Data.m_Password); + + ByteArray Transaction = LoginTransaction.GetHeader(); + Client->SendBytes(Transaction); + + ByteArray LoginResponse = Client->ReceiveBytes(); + if(!LoginTransaction.TranslateResponse(LoginResponse)) + ExitProcess(0); + + // Allow the user to choose a cheat to inject. + UserInterface->m_Data.m_ExecutionState = UserExperience::EXECUTION_CHOOSE; + // TODO: Add game selection. while(1) { if(GetAsyncKeyState(VK_END) & 0x8000) break; Sleep(1); } diff --git a/csgo-loader/csgo-client/Login/RemoteLogin.cpp b/csgo-loader/csgo-client/Login/RemoteLogin.cpp index 6e70f86..7942c3b 100644 --- a/csgo-loader/csgo-client/Login/RemoteLogin.cpp +++ b/csgo-loader/csgo-client/Login/RemoteLogin.cpp @@ -18,13 +18,25 @@ namespace Login strcpy_s< 128 >(m_Header.m_Password, Password); // Initialise the header with the Hardware ID. - m_Header.m_HardwareId = 0xFFFFFFFF; + Security::HardwareIdentifier HardwareId = Protection->GetHardwareId(); + + WRAP_IF_DEBUG( + printf("[DEBUG] LoginTransactionStart\n"); + printf("[DEBUG] Processor count: %d\n", HardwareId.m_CpuCount); + printf("[DEBUG] Processor architecture: %d\n", HardwareId.m_CpuArchitecture); + printf("[DEBUG] Hard-drive Serial: %llx\n", HardwareId.m_HardDiskSerialHash); + + for(int i = 0; i < 4; ++i) + printf("[DEBUG] Safety check #%d: %s\n", i, HardwareId.m_SpecialMode[i] ? "TRUE" : "FALSE"); + ); + + m_Header.m_HardwareId = fnv::hash_runtime_data((void *)(&HardwareId), sizeof Security::HardwareIdentifier); // TODO: Verify integrity of system. // 0 for integrity passed, random bit for failure - m_Header.m_IntegrityBit1 = 0; - m_Header.m_IntegrityBit2 = 0; - m_Header.m_IntegrityBit3 = 0; + m_Header.m_IntegrityBit1 = HardwareId.m_SpecialMode[Security::DEBUGGING_MODE]; + m_Header.m_IntegrityBit2 = HardwareId.m_SpecialMode[Security::TEST_BUILD_MODE]; + m_Header.m_IntegrityBit3 = HardwareId.m_SpecialMode[Security::TEST_SIGN_MODE]; // The checksum bit, the server will check this first to detect possible tampering. m_Header.m_IntegrityBit4 = m_Header.m_IntegrityBit1 @@ -48,20 +60,20 @@ namespace Login case RemoteLoginResponse::ACCESS_AUTHORISED: return true; case RemoteLoginResponse::OUTDATED_CLIENT: - INFO_ASSERT(STR("[000A:%08x] Your client is outdated.\nPlease download the latest client at 'moneybot.cc'."), m_Header.m_HardwareId ^ ServerResponse); + INFO_ASSERT(STR("[000A:%llx] Your client is outdated.\nPlease download the latest client at 'moneybot.cc'."), m_Header.m_HardwareId ^ ServerResponse); break; case RemoteLoginResponse::USER_BANNED: - INFO_ASSERT(STR("[000D:%08x] Your account is banned.\nPlease contact 'admin@moneybot.cc' for additional information."), m_Header.m_HardwareId ^ ServerResponse); + INFO_ASSERT(STR("[000D:%llx] Your account is banned.\nPlease contact 'admin@moneybot.cc' for additional information."), m_Header.m_HardwareId ^ ServerResponse); break; case RemoteLoginResponse::INVALID_HARDWARE: - UserInterface->m_Data.m_Error = UserExperience::ERROR_INVALID_HWID; + INFO_ASSERT(STR("[000D:%llx] Your Hardware-ID is incorrect!\nPlease contact a staff member."), m_Header.m_HardwareId ^ ServerResponse); break; case RemoteLoginResponse::INVALID_CREDENTIALS: - INFO_ASSERT(STR("[000C:%08x] Your credentials are invalid. Please check your spelling and try again."), m_Header.m_HardwareId ^ ServerResponse); + INFO_ASSERT(STR("[000C:%llx] Your credentials are invalid. Please check your spelling and try again."), m_Header.m_HardwareId ^ ServerResponse); break; case RemoteLoginResponse::INTEGRITY_FAILURE: case RemoteLoginResponse::NO_SUBSCRIPTION: - INFO_ASSERT(STR("[0005:%08x] No active subscription found."), m_Header.m_HardwareId ^ ServerResponse); + INFO_ASSERT(STR("[0005:%llx] No active subscription found."), m_Header.m_HardwareId ^ ServerResponse); break; } diff --git a/csgo-loader/csgo-client/Login/RemoteLogin.hpp b/csgo-loader/csgo-client/Login/RemoteLogin.hpp index 071bb13..9575193 100644 --- a/csgo-loader/csgo-client/Login/RemoteLogin.hpp +++ b/csgo-loader/csgo-client/Login/RemoteLogin.hpp @@ -6,6 +6,7 @@ #include #include +#include using ByteArray = std::vector; @@ -24,7 +25,7 @@ namespace Login char m_Password[128]; // This will provide the hardware ID of the machine. - uint32_t m_HardwareId; + uint64_t m_HardwareId; // These fields will be set according // to security check results. diff --git a/csgo-loader/csgo-client/Networking/TCPClient.cpp b/csgo-loader/csgo-client/Networking/TCPClient.cpp index 0611d89..c20c831 100644 --- a/csgo-loader/csgo-client/Networking/TCPClient.cpp +++ b/csgo-loader/csgo-client/Networking/TCPClient.cpp @@ -40,10 +40,6 @@ namespace Networking break; } - // Stay in sync with client. - ByteArray Array = { 1 }; - SendRawBytes(Array); - return ReceivedBytes; } diff --git a/csgo-loader/csgo-client/Security/Encryption.cpp b/csgo-loader/csgo-client/Security/Encryption.cpp index 00b0fee..083460a 100644 --- a/csgo-loader/csgo-client/Security/Encryption.cpp +++ b/csgo-loader/csgo-client/Security/Encryption.cpp @@ -80,7 +80,7 @@ namespace Wrapper }; // Implementation of the AES256 encryption algorithm. - unsigned char rj_xtime(unsigned char x); + uint8_t rj_xtime(uint8_t x); Aes256::Aes256(const ByteArray& key) : m_key(ByteArray(key.size() > KEY_SIZE ? KEY_SIZE : key.size(), 0)) @@ -144,7 +144,7 @@ namespace Wrapper ByteArray::size_type Aes256::encrypt_start(const ByteArray::size_type plain_length, ByteArray& encrypted) { - VMProtectBegin("AESEncryptStart"); + VMProtectBeginUltra("AESEncryptStart"); m_remainingLength = plain_length; @@ -265,7 +265,7 @@ namespace Wrapper { unsigned char j; - VMProtectBegin("AESDecryptStart"); + VMProtectBeginUltra("AESDecryptStart"); m_remainingLength = encrypted_length; @@ -314,7 +314,7 @@ namespace Wrapper void Aes256::check_and_decrypt_buffer(ByteArray& plain) { - VMProtectBegin("AESDecryptBuffer"); + VMProtectBeginUltra("AESDecryptBuffer"); if(!m_decryptInitialized && m_buffer_pos == m_salt.size() + 1) { @@ -384,7 +384,7 @@ namespace Wrapper { unsigned char i; - VMProtectBegin("AESExpandKey"); + VMProtectBeginUltra("AESExpandKey"); m_rkey[0] = m_rkey[0] ^ sbox[m_rkey[29]] ^ (*rc); m_rkey[1] = m_rkey[1] ^ sbox[m_rkey[30]]; @@ -419,7 +419,7 @@ namespace Wrapper { unsigned char i; - VMProtectBegin("AESExpandKey"); + VMProtectBeginUltra("AESExpandKey"); for(i = 28; i > 16; i -= 4) { @@ -585,10 +585,7 @@ namespace Wrapper VMProtectEnd(); } - inline unsigned char rj_xtime(unsigned char x) - { - return (x & 0x80) ? ((x << 1) ^ 0x1b) : (x << 1); - } + 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() diff --git a/csgo-loader/csgo-client/Security/RuntimeSecurity.cpp b/csgo-loader/csgo-client/Security/RuntimeSecurity.cpp index 572c9b1..7f528e3 100644 --- a/csgo-loader/csgo-client/Security/RuntimeSecurity.cpp +++ b/csgo-loader/csgo-client/Security/RuntimeSecurity.cpp @@ -209,6 +209,9 @@ namespace Security for(;;) { + if(VMProtectIsVirtualMachinePresent()) + SecurityCallback(STR("Malicious activity [Virtualized environment].")); + // Don't put too much stress on the CPU. Sleep(1); } @@ -227,36 +230,18 @@ namespace Security // Offset for x64 is 0x60 ; mov ..., qword ptr gs:[0x60] PEB *ProcessEnvBlock = (PEB *)__readgsqword(0x60); - //if(ProcessEnvBlock->BeingDebugged) - // SecurityCallback(__FUNCSIG__); + if(ProcessEnvBlock->BeingDebugged) + SecurityCallback(STR("Malicious activity [Debugging attempt].")); // TODO: Check for x64dbg window? - /* - ------------------------------ - HWND: 000305A4 - HWND->m_Class = "ID" - HWND->m_Text = "Immunity - ------------------------------ - HWND: 00060574 - HWND->m_Class = || NON CONSTANT || - HWND->m_Text = "x64dbg" - ------------------------------ - HWND: 002C0680 - HWND->m_Class = || NON CONSTANT || - HWND->m_Text = "Progress Telerik Fiddler Web Debugger" - ------------------------------ - HWND: 000406E4 - HWND->m_Class = "OLLYDBG" - HWND->m_Text = "OllyDbg" - ------------------------------ - */ using WindowParams = std::pair; static std::vector BlackListedWindows = { {STR("ID"), STR("Immunity")}, // Immunity Debugger - {STR("Qt5QWindowIcon"), STR("x64dbg")}, // x64dbg - {STR("Qt5QWindowIcon"), STR("x32dbg")}, // x32dbg - {STR("OLLYDBG"), STR("OllyDbg")}, // OllyDbg - {nullptr, STR("Progress Telerik Fiddler Web Debugger")}, // Telerik Fiddler + {STR("Qt5QWindowIcon"), STR("x64dbg")}, // x64dbg + {STR("Qt5QWindowIcon"), STR("x32dbg")}, // x32dbg + {STR("Qt5QWindowIcon"), STR("The Wireshark Network Analyzer")}, // x32dbg + {STR("OLLYDBG"), STR("OllyDbg")}, // OllyDbg + {nullptr, STR("Progress Telerik Fiddler Web Debugger")}, // Telerik Fiddler }; for(auto &It : BlackListedWindows) @@ -284,7 +269,7 @@ namespace Security STR("NPF"), // WireShark / WinPCAP STR("acker"), // Process Hacker STR("CEDRI"), // Cheat Engine - //STR("VBox") // VirtualBox + //STR("VBox") // VirtualBox }; static const char *BlackListReasons[] = { @@ -303,7 +288,11 @@ namespace Security if(K32EnumDeviceDrivers(DriverList, sizeof DriverList, &Needed)) { if(Needed > sizeof DriverList) - ERROR_ASSERT(STR("[00DF:00001CFF] A security thread has failed. Contact an administrator.")); + { + ERROR_ASSERT( + STR("[00DF:00001CFF] A security thread has failed. Contact an administrator.") + ); + } char DriverName[1024]; uint32_t DriverCount = Needed / sizeof DriverList[0]; @@ -330,6 +319,12 @@ namespace Security { for(;;) { + if(!VMProtectIsProtected()) + SecurityCallback(STR("Malicious activity [Tampering].")); + + if(!VMProtectIsValidImageCRC()) + SecurityCallback(STR("Malicious activity [Tampering].")); + // Don't put too much stress on the CPU. Sleep(1); } @@ -358,22 +353,93 @@ 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; + } + HardwareIdentifier RuntimeSecurity::GetHardwareId() { + VMProtectBeginMutation("HardwareIdentifier"); + HardwareIdentifier Identifier{}; // CPU information Identifier.m_CpuCount = *(uint32_t *)(KUSER_SHARED_DATA + 0x3C0); Identifier.m_CpuArchitecture = *(uint16_t *)(KUSER_SHARED_DATA + 0x26A); - // CPU features + // HDD serial number + Identifier.m_HardDiskSerialHash = get_hdd_hash(); // Safe-mode Identifier.m_SpecialMode[0] = *(uint8_t *)(KUSER_SHARED_DATA + 0x2EC); // Test-signing mode + static auto ZwQuerySystemInformation = Syscalls->Find(FNV("ZwQuerySystemInformation")); + + // 0x02 CODEINTEGRITY_OPTION_TESTSIGN + // 0x20 CODEINTEGRITY_OPTION_TEST_BUILD + // 0x80 CODEINTEGRITY_OPTION_DEBUGMODE_ENABLED + + CodeIntegrityInformation Info{ sizeof CodeIntegrityInformation }; + NTSTATUS Status = ZwQuerySystemInformation(0x67, &Info, sizeof Info, nullptr); + + if(NT_ERROR(Status)) + ERROR_ASSERT(STR("[00CF:%08x] Critical execution error."), Status); + + if(Info.m_Options & 0x02) + Identifier.m_SpecialMode[1] = true; + + if(Info.m_Options & 0x20) + Identifier.m_SpecialMode[2] = true; + + if(Info.m_Options & 0x40) + Identifier.m_SpecialMode[3] = true; + + VMProtectEnd(); - return HardwareIdentifier{}; + return Identifier; } #pragma optimize("", off) diff --git a/csgo-loader/csgo-client/Security/RuntimeSecurity.hpp b/csgo-loader/csgo-client/Security/RuntimeSecurity.hpp index 08e6490..06df3bf 100644 --- a/csgo-loader/csgo-client/Security/RuntimeSecurity.hpp +++ b/csgo-loader/csgo-client/Security/RuntimeSecurity.hpp @@ -10,6 +10,9 @@ #include #include +// IOCTL +#include + // EnumDeviceDrivers #include @@ -47,6 +50,19 @@ namespace Security { + // Sigh.. + struct CodeIntegrityInformation { + uint32_t m_Size; + uint32_t m_Options; + }; + + enum SpecialMode { + SAFE_MODE, + TEST_SIGN_MODE, + TEST_BUILD_MODE, + DEBUGGING_MODE + }; + // Hardware ID structure (this is hashed and sent to server, but it's easier to use it // this way internally) struct HardwareIdentifier @@ -54,15 +70,12 @@ namespace Security // Generic CPU information. uint16_t m_CpuArchitecture; uint32_t m_CpuCount; - - // Contains list of CPU features. - char m_CpuFeatures[64]; - + // Hash of the hard disk serial identifier. - uint32_t m_HardDiskSerialHash; + uint64_t m_HardDiskSerialHash; // Safe-mode/Test-signing mode status - uint8_t m_SpecialMode[2]; + uint8_t m_SpecialMode[4]; }; // This class implements the runtime security system. diff --git a/csgo-loader/csgo-server/Login/RemoteLogin.cpp b/csgo-loader/csgo-server/Login/RemoteLogin.cpp index 3cc5c77..2f30e7f 100644 --- a/csgo-loader/csgo-server/Login/RemoteLogin.cpp +++ b/csgo-loader/csgo-server/Login/RemoteLogin.cpp @@ -33,6 +33,8 @@ namespace Login 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. @@ -41,7 +43,7 @@ namespace Login } // TODO: Check if the HWID is present in DB. - if(false) + if(m_Header.m_HardwareId != 0x2F769B06FA897376) return RemoteLoginResponse::INVALID_HARDWARE; // TODO: Check if the user has a subscription. diff --git a/csgo-loader/csgo-server/Login/RemoteLogin.hpp b/csgo-loader/csgo-server/Login/RemoteLogin.hpp index f69e588..c3ceea3 100644 --- a/csgo-loader/csgo-server/Login/RemoteLogin.hpp +++ b/csgo-loader/csgo-server/Login/RemoteLogin.hpp @@ -21,7 +21,7 @@ namespace Login char m_Password[128]; // This will provide the hardware ID of the machine. - uint32_t m_HardwareId; + uint64_t m_HardwareId; // These fields will be set according // to security check results. @@ -45,6 +45,7 @@ namespace Login NO_SUBSCRIPTION = 'G', // '[000G:{HWID}] No active subscription.' ACCESS_SPECIAL_USER = 'H', // Allows the user to continue, sets the m_SpecialAccess var }; + // Implementation of the server (handles login bullshit). class RemoteLoginServer { diff --git a/csgo-loader/csgo-server/Networking/TCPServer.cpp b/csgo-loader/csgo-server/Networking/TCPServer.cpp index 37a21bc..0652fe5 100644 --- a/csgo-loader/csgo-server/Networking/TCPServer.cpp +++ b/csgo-loader/csgo-server/Networking/TCPServer.cpp @@ -22,12 +22,6 @@ namespace Networking if(Result == -1) printf("[ E! ] Failed to send %zd bytes to %s. (Socket %04Ix)\n", Bytes.size(), m_IpAddress, m_Socket); - - // Stay in sync with client. - ByteArray Array = ReceiveRawBytes(); - - if(Array.empty()) - printf("[ E! ] No client reply.\n"); } ByteArray TCPConnection::ReceiveRawBytes() diff --git a/csgo-loader/csgo-server/Server.cpp b/csgo-loader/csgo-server/Server.cpp index 580c17a..a78bb9a 100644 --- a/csgo-loader/csgo-server/Server.cpp +++ b/csgo-loader/csgo-server/Server.cpp @@ -1,6 +1,16 @@ #include -void ConnectionHandler(Networking::TCPConnection &Connection) { } +void ConnectionHandler(Networking::TCPConnection &Connection) { + Login::RemoteLoginServer LoginServer; + + ByteArray LoginHeader = Connection.ReceiveBytes(); + + if(!LoginServer.Start(LoginHeader)) + return; + + ByteArray LoginReply = LoginServer.GetResponse(); + Connection.SendBytes(LoginReply); +} int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int) { -- cgit v1.2.3