Protect.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /******************************************************************************
  19. *
  20. * FILE
  21. * $Archive: /APILauncher/Protect.cpp $
  22. *
  23. * DESCRIPTION
  24. *
  25. * PROGRAMMER
  26. * Denzil E. Long, Jr.
  27. * $Author: Mcampbell $
  28. *
  29. * VERSION INFO
  30. * $Modtime: 6/21/01 5:09p $
  31. * $Revision: 6 $
  32. *
  33. ******************************************************************************/
  34. #include "Protect.h"
  35. #ifdef COPY_PROTECT
  36. #include "BFish.h"
  37. #include <Support\UString.h>
  38. #include <Support\RefPtr.h>
  39. #include <Storage\File.h>
  40. #include <windows.h>
  41. #include <memory>
  42. #include <assert.h>
  43. #include <stdio.h>
  44. #include <Debug\DebugPrint.h>
  45. #include "SafeDisk\CdaPfn.h"
  46. // This GUID should be unique for each product. (CHANGE IT WHEN DOING THE
  47. // NEXT PRODUCT) Note that the game will need to agree on this GUID also, so
  48. // the game will have to be modified also.
  49. const char* const LAUNCHER_GUID =
  50. "150C6462-4E49-4ccf-B073-57579569D994"; // Generals Multiplayer Test Launcher GUID
  51. const char* const protectGUID =
  52. "6096561D-8A70-48ed-9FF8-18552419E50D"; // Generals Multiplayer Test Protect GUID
  53. /*
  54. const char* const LAUNCHER_GUID =
  55. "FB327081-64F2-43d8-9B72-503C3B765134"; // Generals Launcher GUID
  56. const char* const protectGUID =
  57. "7BEB9006-CC19-4aca-913A-C870A88DE01A"; // Generals Protect GUID
  58. */
  59. HANDLE mLauncherMutex = NULL;
  60. HANDLE mMappedFile = NULL;
  61. void InitializeProtect(void)
  62. {
  63. ShutdownProtect();
  64. DebugPrint("Initializing protection\n");
  65. mLauncherMutex = NULL;
  66. mMappedFile = NULL;
  67. // Secure launcher mutex
  68. mLauncherMutex = CreateMutex(NULL, FALSE, LAUNCHER_GUID);
  69. if ((mLauncherMutex == NULL) || (mLauncherMutex && (GetLastError() == ERROR_ALREADY_EXISTS)))
  70. {
  71. DebugPrint("***** Failed to create launcher mutex\n");
  72. return;
  73. }
  74. // Create memory mapped file to mirror protected file
  75. File file("Generals.dat", Rights_ReadOnly);
  76. if (!file.IsAvailable())
  77. {
  78. DebugPrint("***** Unable to find Generals.dat\n");
  79. return;
  80. }
  81. UInt32 fileSize = file.GetLength();
  82. SECURITY_ATTRIBUTES security;
  83. security.nLength = sizeof(security);
  84. security.lpSecurityDescriptor = NULL;
  85. security.bInheritHandle = TRUE;
  86. mMappedFile = CreateFileMapping(INVALID_HANDLE_VALUE, &security, PAGE_READWRITE, 0, fileSize, NULL);
  87. if ((mMappedFile == NULL) || (mMappedFile && (GetLastError() == ERROR_ALREADY_EXISTS)))
  88. {
  89. PrintWin32Error("***** CreateFileMapping() Failed!");
  90. CloseHandle(mMappedFile);
  91. mMappedFile = NULL;
  92. return;
  93. }
  94. }
  95. CDAPFN_DECLARE_GLOBAL(SendProtectMessage, CDAPFN_OVERHEAD_L5, CDAPFN_CONSTRAINT_NONE);
  96. void SendProtectMessage(HANDLE process, DWORD threadID)
  97. {
  98. // Decrypt protected file contents to mapping file
  99. File file("Generals.dat", Rights_ReadOnly);
  100. if (!file.IsAvailable())
  101. {
  102. DebugPrint("***** Unable to find Generals.dat\n");
  103. return;
  104. }
  105. // Map file to programs address space
  106. LPVOID mapAddress = MapViewOfFileEx(mMappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
  107. if (mapAddress == NULL)
  108. {
  109. PrintWin32Error("***** MapViewOfFileEx() Failed!");
  110. return;
  111. }
  112. void* buffer = NULL;
  113. UInt32 bufferSize = 0;
  114. file.Load(buffer, bufferSize);
  115. if (buffer && (bufferSize > 0))
  116. {
  117. DebugPrint("Generating PassKey\n");
  118. // Generate passkey
  119. char passKey[128];
  120. passKey[0] = '\0';
  121. // Get game information
  122. HKEY hKey;
  123. bool usesHKeycurrentUser = false;
  124. LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Electronic Arts\\EA Games\\Command and Conquer Generals Zero Hour", 0, KEY_READ, &hKey);
  125. if (result != ERROR_SUCCESS)
  126. {
  127. result = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Electronic Arts\\EA Games\\Command and Conquer Generals Zero Hour", 0, KEY_READ, &hKey);
  128. usesHKeycurrentUser = true;
  129. }
  130. assert((result == ERROR_SUCCESS) && "Failed to open game registry key");
  131. if (result == ERROR_SUCCESS)
  132. {
  133. // Retrieve install path
  134. unsigned char installPath[MAX_PATH];
  135. DWORD type;
  136. DWORD sizeOfBuffer = sizeof(installPath);
  137. result = RegQueryValueEx(hKey, "InstallPath", NULL, &type, installPath, &sizeOfBuffer);
  138. assert((result == ERROR_SUCCESS) && "Failed to obtain game install path!");
  139. assert((strlen((const char*)installPath) > 0) && "Game install path invalid!");
  140. DebugPrint("Game install path: %s\n", installPath);
  141. // Retrieve Hard drive S/N
  142. char drive[8];
  143. _splitpath((const char*)installPath, drive, NULL, NULL, NULL);
  144. strcat(drive, "\\");
  145. DWORD volumeSerialNumber = 0;
  146. DWORD maxComponentLength;
  147. DWORD fileSystemFlags;
  148. BOOL volInfoSuccess = GetVolumeInformation((const char*)drive, NULL, 0,
  149. &volumeSerialNumber, &maxComponentLength, &fileSystemFlags, NULL, 0);
  150. if (volInfoSuccess == FALSE)
  151. {
  152. PrintWin32Error("***** GetVolumeInformation() Failed!");
  153. }
  154. DebugPrint("Drive Serial Number: %lx\n", volumeSerialNumber);
  155. // Add hard drive serial number portion
  156. char volumeSN[16];
  157. sprintf(volumeSN, "%lx-", volumeSerialNumber);
  158. strcat(passKey, volumeSN);
  159. // Retrieve game serial #
  160. unsigned char gameSerialNumber[64];
  161. gameSerialNumber[0] = '\0';
  162. sizeOfBuffer = sizeof(gameSerialNumber);
  163. if (usesHKeycurrentUser)
  164. {
  165. result = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Electronic Arts\\EA Games\\Command and Conquer Generals Zero Hour\\ergc", 0, KEY_READ, &hKey);
  166. }
  167. else
  168. {
  169. result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Electronic Arts\\EA Games\\Command and Conquer Generals Zero Hour\\ergc", 0, KEY_READ, &hKey);
  170. }
  171. assert((result == ERROR_SUCCESS) && "Failed to open game serial registry key");
  172. if (result == ERROR_SUCCESS)
  173. {
  174. result = RegQueryValueEx(hKey, "", NULL, &type, gameSerialNumber, &sizeOfBuffer);
  175. assert((result == ERROR_SUCCESS) && "Failed to obtain game serial number!");
  176. assert((strlen((const char*)gameSerialNumber) > 0) && "Game serial number invalid!");
  177. }
  178. DebugPrint("Game serial number: %s\n", gameSerialNumber);
  179. RegCloseKey(hKey);
  180. // Add game serial number portion
  181. strcat(passKey, (char*)gameSerialNumber);
  182. }
  183. // Obtain windows product ID
  184. result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", 0, KEY_READ, &hKey);
  185. assert((result == ERROR_SUCCESS) && "Failed to open windows registry key!");
  186. if (result == ERROR_SUCCESS)
  187. {
  188. // Retrieve Windows Product ID
  189. unsigned char winProductID[64];
  190. winProductID[0] = '\0';
  191. DWORD type;
  192. DWORD sizeOfBuffer = sizeof(winProductID);
  193. result = RegQueryValueEx(hKey, "ProductID", NULL, &type, winProductID, &sizeOfBuffer);
  194. assert((result == ERROR_SUCCESS) && "Failed to obtain windows product ID!");
  195. assert((strlen((const char*)winProductID) > 0) && "Invalid windows product ID");
  196. DebugPrint("Windows Product ID: %s\n", winProductID);
  197. RegCloseKey(hKey);
  198. // Add windows product ID portion
  199. strcat(passKey, "-");
  200. strcat(passKey, (char*)winProductID);
  201. }
  202. DebugPrint("Retrieved PassKey: %s\n", passKey);
  203. // Decrypt protected data into the memory mapped file
  204. BlowfishEngine blowfish;
  205. int len = strlen(passKey);
  206. if (len > BlowfishEngine::MAX_KEY_LENGTH)
  207. len = BlowfishEngine::MAX_KEY_LENGTH;
  208. blowfish.Submit_Key(passKey, len);
  209. blowfish.Decrypt(buffer, bufferSize, mapAddress);
  210. DebugPrint("Decrypted data: %s\n", mapAddress);
  211. free(buffer);
  212. }
  213. UnmapViewOfFile(mapAddress);
  214. //---------------------------------------------------------------------------
  215. // Send protection message
  216. //---------------------------------------------------------------------------
  217. DebugPrint("Sending protect message\n");
  218. DebugPrint("Creating running notification event.\n");
  219. HANDLE event = CreateEvent(NULL, FALSE, FALSE, protectGUID);
  220. if ((event == NULL) || (event && (GetLastError() == ERROR_ALREADY_EXISTS)))
  221. {
  222. PrintWin32Error("***** CreateEvent() Failed!");
  223. return;
  224. }
  225. DebugPrint("Waiting for game (timeout in %.02f seconds)...\n", ((float)((5 * 60) * 1000) * 0.001));
  226. #ifdef _DEBUG
  227. unsigned long start = timeGetTime();
  228. #endif
  229. HANDLE handles[2];
  230. handles[0] = event;
  231. handles[1] = process;
  232. DWORD waitResult = WaitForMultipleObjects(2, &handles[0], FALSE, ((5 * 60) * 1000));
  233. #ifdef _DEBUG
  234. unsigned long stop = timeGetTime();
  235. #endif
  236. DebugPrint("WaitResult = %ld (WAIT_OBJECT_0 = %ld)\n", waitResult, WAIT_OBJECT_0);
  237. if (waitResult == WAIT_OBJECT_0)
  238. {
  239. if (mMappedFile != NULL)
  240. {
  241. DebugPrint("Sending game the beef. (%lx)\n", mMappedFile);
  242. BOOL sent = PostThreadMessage(threadID, 0xBEEF, 0, (LPARAM)mMappedFile);
  243. assert(sent == TRUE);
  244. }
  245. }
  246. else
  247. {
  248. DebugPrint("***** Timeout!\n");
  249. }
  250. #ifdef _DEBUG
  251. DebugPrint("Waited %.02f seconds\n", ((float)(stop - start) * 0.001));
  252. #endif
  253. CloseHandle(event);
  254. CDAPFN_ENDMARK(SendProtectMessage);
  255. }
  256. void ShutdownProtect(void)
  257. {
  258. if (mMappedFile)
  259. {
  260. CloseHandle(mMappedFile);
  261. mMappedFile = NULL;
  262. }
  263. if (mLauncherMutex)
  264. {
  265. CloseHandle(mLauncherMutex);
  266. mLauncherMutex = NULL;
  267. }
  268. }
  269. #endif // COPY_PROTECT