/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /****************************************************************************** * * FILE * $Archive: /Commando/Code/launcher/Protect.cpp $ * * DESCRIPTION * * PROGRAMMER * Denzil E. Long, Jr. * $Author: Tom_s $ * * VERSION INFO * $Modtime: 3/04/02 5:55p $ * $Revision: 3 $ * ******************************************************************************/ #include "Protect.h" #ifdef COPY_PROTECT #include "BFish.h" #include #include #include #include #include #include #include #include "SafeDisk\CdaPfn.h" #include "..\combat\specialbuilds.h" // This GUID should be unique for each product. (CHANGE IT WHEN DOING THE // NEXT PRODUCT) Note that the game will need to agree on this GUID also, so // the game will have to be modified also. const char* const LAUNCHER_GUID = "48BC11BD-C4D7-466b-8A31-C6ABBAD47B3E"; #ifdef OLDWAY /****************************************************************************** * * NAME * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ Protect::Protect() : mLauncherMutex(NULL), mMappedFile(NULL) { // Secure launcher mutex mLauncherMutex = CreateMutex(NULL, FALSE, LAUNCHER_GUID); if ((mLauncherMutex == NULL) || ((mLauncherMutex != NULL) && (GetLastError() == ERROR_ALREADY_EXISTS))) { return; } // Create memory mapped file to mirror protected file File file("Conquer.dat", Rights_ReadOnly); if (!file.IsAvailable()) { return; } UInt32 fileSize = file.GetLength(); SECURITY_ATTRIBUTES security; security.nLength = sizeof(security); security.lpSecurityDescriptor = NULL; security.bInheritHandle = TRUE; mMappedFile = CreateFileMapping(INVALID_HANDLE_VALUE, &security, PAGE_READWRITE, 0, fileSize, NULL); if ((mMappedFile == NULL) || ((mMappedFile != NULL) && (GetLastError() == ERROR_ALREADY_EXISTS))) { PrintWin32Error("***** CreateFileMapping() Failed!"); CloseHandle(mMappedFile); mMappedFile = NULL; return; } // Map file to programs address space LPVOID mapAddress = MapViewOfFileEx(mMappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL); if (mapAddress == NULL) { PrintWin32Error("***** MapViewOfFileEx() Failed!"); return; } // Decrypt protected file contents to mapping file void* buffer = NULL; UInt32 bufferSize = 0; file.Load(buffer, bufferSize); if ((buffer != NULL) && (bufferSize > 0)) { // Retrieve protection key RefPtr passKey = GetPassKey(); Char key[64]; passKey->ConvertToANSI(key, sizeof(key)); DebugPrint("Retrieved PassKey: %s\n", key); // Decrypt data BlowfishEngine blowfish; blowfish.Submit_Key(key, strlen(key)); blowfish.Decrypt(buffer, bufferSize, mapAddress); DebugPrint("Decrypted data: %s\n", mapAddress); free(buffer); } UnmapViewOfFile(mapAddress); } /****************************************************************************** * * NAME * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ Protect::~Protect() { if (mMappedFile != NULL) { CloseHandle(mMappedFile); mMappedFile = NULL; } if (mLauncherMutex != NULL) { CloseHandle(mLauncherMutex); mLauncherMutex = NULL; } } /****************************************************************************** * * NAME * Protect::SendMappedFileHandle() * * DESCRIPTION * Send the handle to the memory mapped file to the application. * * INPUTS * Process - Process of the application to send handle to. * ThreadID - Thread of the applicatin to send handle to. * * RESULT * NONE * ******************************************************************************/ void Protect::SendMappedFileHandle(HANDLE process, DWORD threadID) const { DebugPrint("SendMappedFileHandle()\n"); DebugPrint("Creating running notification event.\n"); const char* const protectGUID = "D6E7FC97-64F9-4d28-B52C-754EDF721C6F"; HANDLE event = CreateEvent(NULL, FALSE, FALSE, protectGUID); if ((event == NULL) || ((event != NULL) && (GetLastError() == ERROR_ALREADY_EXISTS))) { PrintWin32Error("CreateEvent() Failed!"); return; } DebugPrint("Waiting for game (timeout in %.02f seconds)...\n", ((float)((5 * 60) * 1000) * 0.001)); #ifdef _DEBUG unsigned long start = timeGetTime(); #endif HANDLE handles[2]; handles[0] = event; handles[1] = process; DWORD waitResult = WaitForMultipleObjects(2, &handles[0], FALSE, ((5 * 60) * 1000)); #ifdef _DEBUG unsigned long stop = timeGetTime(); #endif DebugPrint("WaitResult = %ld (WAIT_OBJECT_0 = %ld)\n", waitResult, WAIT_OBJECT_0); if (waitResult == WAIT_OBJECT_0) { if (mMappedFile != NULL) { DebugPrint("Sending game the beef. (%lx)\n", mMappedFile); BOOL sent = PostThreadMessage(threadID, 0xBEEF, 0, (LPARAM)mMappedFile); assert(sent == TRUE); } } else { DebugPrint("***** Timeout!\n"); } #ifdef _DEBUG DebugPrint("Waited %.02f seconds\n", ((float)(stop - start) * 0.001)); #endif CloseHandle(event); } /****************************************************************************** * * NAME * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ RefPtr Protect::GetPassKey(void) const { RefPtr passKey = new UString; if (passKey.IsValid()) { unsigned char installPath[MAX_PATH]; installPath[0] = '\0'; // Get game information HKEY hKey; /* LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\Red Alert 2", 0, KEY_READ, &hKey); */ #if defined(FREEDEDICATEDSERVER) LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\RenegadeFDS", 0, KEY_READ, &hKey); #elif defined(MULTIPLAYERDEMO) LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\RenegadeMPDemo", 0, KEY_READ, &hKey); #elif defined(BETACLIENT) LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\RenegadeBeta", 0, KEY_READ, &hKey); #else LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\Renegade", 0, KEY_READ, &hKey); #endif if (result != ERROR_SUCCESS) { DebugPrint("***** Failed to open game registry key\n"); assert(result == ERROR_SUCCESS); } if (result == ERROR_SUCCESS) { // Retrieve install path DWORD type; DWORD sizeOfBuffer = sizeof(installPath); result = RegQueryValueEx(hKey, "InstallPath", NULL, &type, installPath, &sizeOfBuffer); if (result != ERROR_SUCCESS) { DebugPrint("***** Failed to obtain game install path!\n"); assert(result == ERROR_SUCCESS); } DebugPrint("Game install path: %s\n", installPath); if (strlen((const char*)installPath) == 0) { DebugPrint("***** Game install path invalid!\n"); } // Retrieve Hard drive S/N char drive[8]; _splitpath((const char*)installPath, drive, NULL, NULL, NULL); strcat(drive, "\\"); DWORD volumeSerialNumber = 0; DWORD maxComponentLength; DWORD fileSystemFlags; BOOL volInfoSuccess = GetVolumeInformation((const char*)drive, NULL, 0, &volumeSerialNumber, &maxComponentLength, &fileSystemFlags, NULL, 0); if (volInfoSuccess == FALSE) { PrintWin32Error("GetVolumeInformation() Failed!"); } DebugPrint("Drive Serial Number: %lx\n", volumeSerialNumber); // Add hard drive serial number portion char volumeSN[16]; sprintf(volumeSN, "%lx-", volumeSerialNumber); *passKey += volumeSN; // Retrieve game serial # unsigned char gameSerialNumber[64]; gameSerialNumber[0] = '\0'; sizeOfBuffer = sizeof(gameSerialNumber); result = RegQueryValueEx(hKey, "Serial", NULL, &type, gameSerialNumber, &sizeOfBuffer); if (result != ERROR_SUCCESS) { DebugPrint("***** Failed to obtain windows serial number!\n"); assert(result == ERROR_SUCCESS); } DebugPrint("Game serial number: %s\n", gameSerialNumber); if (strlen((const char*)gameSerialNumber) == 0) { DebugPrint("***** Game serial number invalid!\n"); } RegCloseKey(hKey); // Add game serial number portion *passKey += (char*)gameSerialNumber; } // Obtain windows product ID result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", 0, KEY_READ, &hKey); if (result != ERROR_SUCCESS) { DebugPrint("***** Failed to open windows registry key!\n"); assert(result == ERROR_SUCCESS); } if (result == ERROR_SUCCESS) { // Retrieve Windows Product ID unsigned char winProductID[64]; winProductID[0] = '\0'; DWORD type; DWORD sizeOfBuffer = sizeof(winProductID); result = RegQueryValueEx(hKey, "ProductID", NULL, &type, winProductID, &sizeOfBuffer); if (result != ERROR_SUCCESS) { DebugPrint("***** Failed to obtain windows product ID!\n"); assert(result == ERROR_SUCCESS); } DebugPrint("Windows Product ID: %s\n", winProductID); if (strlen((const char*)winProductID) == 0) { DebugPrint("***** Invalid windows product ID\n"); } RegCloseKey(hKey); // Add windows product ID portion *passKey += "-"; *passKey += (char*)winProductID; } } return passKey; } #else HANDLE mLauncherMutex = NULL; HANDLE mMappedFile = NULL; void InitializeProtect(void) { ShutdownProtect(); DebugPrint("Initializing protection\n"); mLauncherMutex = NULL; mMappedFile = NULL; // Secure launcher mutex mLauncherMutex = CreateMutex(NULL, FALSE, LAUNCHER_GUID); if ((mLauncherMutex == NULL) || (mLauncherMutex && (GetLastError() == ERROR_ALREADY_EXISTS))) { DebugPrint("***** Failed to create launcher mutex\n"); return; } // Create memory mapped file to mirror protected file File file("Conquer.dat", Rights_ReadOnly); if (!file.IsAvailable()) { DebugPrint("***** Unable to find Conquer.dat\n"); return; } UInt32 fileSize = file.GetLength(); SECURITY_ATTRIBUTES security; security.nLength = sizeof(security); security.lpSecurityDescriptor = NULL; security.bInheritHandle = TRUE; mMappedFile = CreateFileMapping(INVALID_HANDLE_VALUE, &security, PAGE_READWRITE, 0, fileSize, NULL); if ((mMappedFile == NULL) || (mMappedFile && (GetLastError() == ERROR_ALREADY_EXISTS))) { PrintWin32Error("***** CreateFileMapping() Failed!"); CloseHandle(mMappedFile); mMappedFile = NULL; return; } } CDAPFN_DECLARE_GLOBAL(SendProtectMessage, CDAPFN_OVERHEAD_L5, CDAPFN_CONSTRAINT_NONE); void SendProtectMessage(HANDLE process, DWORD threadID) { // Decrypt protected file contents to mapping file File file("Conquer.dat", Rights_ReadOnly); if (!file.IsAvailable()) { DebugPrint("***** Unable to find Conquer.dat\n"); return; } // Map file to programs address space LPVOID mapAddress = MapViewOfFileEx(mMappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL); if (mapAddress == NULL) { PrintWin32Error("***** MapViewOfFileEx() Failed!"); return; } void* buffer = NULL; UInt32 bufferSize = 0; file.Load(buffer, bufferSize); if (buffer && (bufferSize > 0)) { DebugPrint("Generating PassKey\n"); // Generate passkey char passKey[64]; passKey[0] = '\0'; // Get game information HKEY hKey; /* #ifdef FREEDEDICATEDSERVER LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\RenegadeFDS", 0, KEY_READ, &hKey); #else //FREEDEDICATEDSERVER LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\Renegade", 0, KEY_READ, &hKey); #endif //FREEDEDICATEDSERVER */ #if defined(FREEDEDICATEDSERVER) LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\RenegadeFDS", 0, KEY_READ, &hKey); #elif defined(MULTIPLAYERDEMO) LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\RenegadeMPDemo", 0, KEY_READ, &hKey); #elif defined(BETACLIENT) LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\RenegadeBeta", 0, KEY_READ, &hKey); #else LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Westwood\\Renegade", 0, KEY_READ, &hKey); #endif assert((result == ERROR_SUCCESS) && "Failed to open game registry key"); if (result == ERROR_SUCCESS) { // Retrieve install path unsigned char installPath[MAX_PATH]; DWORD type; DWORD sizeOfBuffer = sizeof(installPath); result = RegQueryValueEx(hKey, "InstallPath", NULL, &type, installPath, &sizeOfBuffer); assert((result == ERROR_SUCCESS) && "Failed to obtain game install path!"); assert((strlen((const char*)installPath) > 0) && "Game install path invalid!"); DebugPrint("Game install path: %s\n", installPath); // Retrieve Hard drive S/N char drive[8]; _splitpath((const char*)installPath, drive, NULL, NULL, NULL); strcat(drive, "\\"); DWORD volumeSerialNumber = 0; DWORD maxComponentLength; DWORD fileSystemFlags; BOOL volInfoSuccess = GetVolumeInformation((const char*)drive, NULL, 0, &volumeSerialNumber, &maxComponentLength, &fileSystemFlags, NULL, 0); if (volInfoSuccess == FALSE) { PrintWin32Error("***** GetVolumeInformation() Failed!"); } DebugPrint("Drive Serial Number: %lx\n", volumeSerialNumber); // Add hard drive serial number portion char volumeSN[16]; sprintf(volumeSN, "%lx-", volumeSerialNumber); strcat(passKey, volumeSN); // Retrieve game serial # unsigned char gameSerialNumber[64]; gameSerialNumber[0] = '\0'; sizeOfBuffer = sizeof(gameSerialNumber); result = RegQueryValueEx(hKey, "Serial", NULL, &type, gameSerialNumber, &sizeOfBuffer); assert((result == ERROR_SUCCESS) && "Failed to obtain windows serial number!"); assert((strlen((const char*)gameSerialNumber) > 0) && "Game serial number invalid!"); DebugPrint("Game serial number: %s\n", gameSerialNumber); RegCloseKey(hKey); // Add game serial number portion strcat(passKey, (char*)gameSerialNumber); } // Obtain windows product ID result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", 0, KEY_READ, &hKey); assert((result == ERROR_SUCCESS) && "Failed to open windows registry key!"); if (result == ERROR_SUCCESS) { // Retrieve Windows Product ID unsigned char winProductID[64]; winProductID[0] = '\0'; DWORD type; DWORD sizeOfBuffer = sizeof(winProductID); result = RegQueryValueEx(hKey, "ProductID", NULL, &type, winProductID, &sizeOfBuffer); assert((result == ERROR_SUCCESS) && "Failed to obtain windows product ID!"); assert((strlen((const char*)winProductID) > 0) && "Invalid windows product ID"); DebugPrint("Windows Product ID: %s\n", winProductID); RegCloseKey(hKey); // Add windows product ID portion strcat(passKey, "-"); strcat(passKey, (char*)winProductID); } DebugPrint("Retrieved PassKey: %s\n", passKey); // Decrypt protected data into the memory mapped file BlowfishEngine blowfish; blowfish.Submit_Key(passKey, strlen(passKey)); blowfish.Decrypt(buffer, bufferSize, mapAddress); DebugPrint("Decrypted data: %s\n", mapAddress); free(buffer); } UnmapViewOfFile(mapAddress); //--------------------------------------------------------------------------- // Send protection message //--------------------------------------------------------------------------- DebugPrint("Sending protect message\n"); DebugPrint("Creating running notification event.\n"); const char* const protectGUID = "D6E7FC97-64F9-4d28-B52C-754EDF721C6F"; HANDLE event = CreateEvent(NULL, FALSE, FALSE, protectGUID); if ((event == NULL) || (event && (GetLastError() == ERROR_ALREADY_EXISTS))) { PrintWin32Error("***** CreateEvent() Failed!"); return; } DebugPrint("Waiting for game (timeout in %.02f seconds)...\n", ((float)((5 * 60) * 1000) * 0.001)); #ifdef _DEBUG unsigned long start = timeGetTime(); #endif HANDLE handles[2]; handles[0] = event; handles[1] = process; DWORD waitResult = WaitForMultipleObjects(2, &handles[0], FALSE, ((5 * 60) * 1000)); #ifdef _DEBUG unsigned long stop = timeGetTime(); #endif DebugPrint("WaitResult = %ld (WAIT_OBJECT_0 = %ld)\n", waitResult, WAIT_OBJECT_0); if (waitResult == WAIT_OBJECT_0) { if (mMappedFile != NULL) { DebugPrint("Sending game the beef. (%lx)\n", mMappedFile); BOOL sent = PostThreadMessage(threadID, 0xBEEF, 0, (LPARAM)mMappedFile); assert(sent == TRUE); } } else { DebugPrint("***** Timeout!\n"); } #ifdef _DEBUG DebugPrint("Waited %.02f seconds\n", ((float)(stop - start) * 0.001)); #endif CloseHandle(event); CDAPFN_ENDMARK(SendProtectMessage); } void ShutdownProtect(void) { if (mMappedFile) { CloseHandle(mMappedFile); mMappedFile = NULL; } if (mLauncherMutex) { CloseHandle(mLauncherMutex); mLauncherMutex = NULL; } } #endif #endif // COPY_PROTECT