Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

540 lines
13 KiB

  1. /*++
  2. Copyright (c) 2002 Microsoft Corporation
  3. Module Name:
  4. Money2001.cpp
  5. Abstract:
  6. Retry passwords at 128-bit encryption if 40-bit fails. Most code from Money
  7. team.
  8. We have to patch the API directly since it is called from within it's own
  9. DLL, i.e. it doesn't go through the import table.
  10. The function we're patching is cdecl and is referenced by it's ordinal
  11. because the name is mangled.
  12. Notes:
  13. This is an app specific shim.
  14. History:
  15. 07/11/2002 linstev Created
  16. 08/07/2002 linstev Fixed allocations to go through crt
  17. --*/
  18. #include "precomp.h"
  19. IMPLEMENT_SHIM_BEGIN(Money2001)
  20. #include "ShimHookMacro.h"
  21. #include <wincrypt.h>
  22. APIHOOK_ENUM_BEGIN
  23. APIHOOK_ENUM_ENTRY(LoadLibraryA)
  24. APIHOOK_ENUM_ENTRY(FreeLibrary)
  25. APIHOOK_ENUM_END
  26. #define Assert(a)
  27. void *crtmalloc(size_t size)
  28. {
  29. HMODULE hMod = GetModuleHandleW(L"msvcrt.dll");
  30. if (hMod) {
  31. typedef void * (__cdecl *_pfn_malloc)(size_t size);
  32. _pfn_malloc pfnmalloc = (_pfn_malloc) GetProcAddress(hMod, "malloc");
  33. if (pfnmalloc) {
  34. return pfnmalloc(size);
  35. }
  36. }
  37. return malloc(size);
  38. }
  39. void crtfree(void *memblock)
  40. {
  41. HMODULE hMod = GetModuleHandleW(L"msvcrt.dll");
  42. if (hMod) {
  43. _pfn_free pfnfree = (_pfn_free) GetProcAddress(hMod, "free");
  44. if (pfnfree) {
  45. pfnfree(memblock);
  46. return;
  47. }
  48. }
  49. free(memblock);
  50. }
  51. //
  52. // This section from the Money team
  53. //
  54. #define MAXLEN (100)
  55. #define ENCRYPT_BLOCK_SIZE (8)
  56. #define ENCRYPT_ALGORITHM CALG_RC2
  57. #define KEY_LENGTH (128)
  58. #define KEY_LENGTH40 (40)
  59. #define LgidMain(lcid) PRIMARYLANGID(LANGIDFROMLCID(lcid))
  60. #define LgidSub(lcid) SUBLANGID(LANGIDFROMLCID(lcid))
  61. BOOL FFrenchLCID()
  62. {
  63. LCID lcidSys=::GetSystemDefaultLCID();
  64. if (LgidMain(lcidSys)==LANG_FRENCH && LgidSub(lcidSys)==SUBLANG_FRENCH)
  65. return TRUE;
  66. else
  67. return FALSE;
  68. }
  69. // the smallest buffer size is 8
  70. #define BLOCKSIZE 8
  71. // process a block, either encrypt it or decrypt it
  72. void ProcessBlock(BOOL fEncrypt, BYTE * buffer)
  73. {
  74. BYTE mask[BLOCKSIZE]; // mask array
  75. BYTE temp[BLOCKSIZE]; // temporary array
  76. int rgnScramble[BLOCKSIZE]; // scramble array
  77. int i;
  78. // initialized scramble array
  79. for (i=0; i<BLOCKSIZE; i++)
  80. rgnScramble[i] = i;
  81. // generate mask and scramble indice
  82. for (i=0; i<BLOCKSIZE; i++)
  83. mask[i] = (BYTE)rand();
  84. for (i=0; i<4*BLOCKSIZE; i++)
  85. {
  86. int temp;
  87. int ind = rand() % BLOCKSIZE;
  88. temp = rgnScramble[i%BLOCKSIZE];
  89. rgnScramble[i%BLOCKSIZE] = rgnScramble[ind];
  90. rgnScramble[ind] = temp;
  91. }
  92. if (fEncrypt)
  93. {
  94. // xor encryption
  95. for (i=0; i<BLOCKSIZE; i++)
  96. mask[i] ^= buffer[i];
  97. // scramble the data
  98. for (i=0; i<BLOCKSIZE; i++)
  99. buffer[rgnScramble[i]] = mask[i];
  100. }
  101. else
  102. {
  103. // descramble the data
  104. for (i=0; i<BLOCKSIZE; i++)
  105. temp[i] = buffer[rgnScramble[i]];
  106. // xor decryption
  107. for (i=0; i<BLOCKSIZE; i++)
  108. buffer[i] = (BYTE) (temp[i] ^ mask[i]);
  109. }
  110. }
  111. BYTE * DecryptFrench(const BYTE * pbEncryptedBlob, DWORD cbEncryptedBlob, DWORD * pcbDecryptedBlob, LPCSTR szPassword)
  112. {
  113. BYTE buffer[BLOCKSIZE];
  114. int i;
  115. unsigned int seed = 0;
  116. unsigned int seedAdd = 0;
  117. BYTE * pb;
  118. BYTE * pbResult = NULL;
  119. unsigned int cBlocks;
  120. unsigned int cbResult = 0;
  121. unsigned int iBlocks;
  122. // make sure blob is at least 1 block long
  123. // and it's an integral number of blocks
  124. Assert(cbEncryptedBlob >= BLOCKSIZE);
  125. Assert(cbEncryptedBlob % BLOCKSIZE == 0);
  126. *pcbDecryptedBlob = 0;
  127. if (cbEncryptedBlob < BLOCKSIZE || cbEncryptedBlob % BLOCKSIZE != 0)
  128. return NULL;
  129. // calculate initial seed
  130. while (*szPassword)
  131. seed += *szPassword++;
  132. srand(seed);
  133. // retrieve the first block
  134. for (i=0; i<BLOCKSIZE; i++)
  135. buffer[i] = *pbEncryptedBlob++;
  136. ProcessBlock(FALSE, buffer);
  137. // find out the byte count and addon seed
  138. cbResult = *pcbDecryptedBlob = *((DWORD*)buffer);
  139. seedAdd = *(((DWORD*)buffer) + 1);
  140. // find out how many blocks we need
  141. cBlocks = 1 + (*pcbDecryptedBlob-1)/BLOCKSIZE;
  142. // make sure we have the right number of blocks
  143. Assert(cBlocks + 1 == cbEncryptedBlob / BLOCKSIZE);
  144. if (cBlocks + 1 == cbEncryptedBlob / BLOCKSIZE)
  145. {
  146. // allocate output memory
  147. pbResult = (BYTE*)crtmalloc(*pcbDecryptedBlob);
  148. if (pbResult)
  149. {
  150. // re-seed
  151. srand(seed + seedAdd);
  152. pb = pbResult;
  153. // process all blocks of data
  154. for (iBlocks=0; iBlocks<cBlocks; iBlocks++)
  155. {
  156. for (i=0; i<BLOCKSIZE; i++)
  157. buffer[i] = *pbEncryptedBlob++;
  158. ProcessBlock(FALSE, buffer);
  159. for (i=0; i<BLOCKSIZE && cbResult>0; i++, cbResult--)
  160. *pb++ = buffer[i];
  161. }
  162. }
  163. }
  164. if (!pbResult)
  165. *pcbDecryptedBlob = 0;
  166. return pbResult;
  167. }
  168. HCRYPTKEY CreateSessionKey(HCRYPTPROV hCryptProv, LPCSTR szPassword, BOOL f40bit)
  169. {
  170. HCRYPTHASH hHash = 0;
  171. HCRYPTKEY hKey = 0;
  172. DWORD dwEffectiveKeyLen;
  173. DWORD dwPadding = PKCS5_PADDING;
  174. DWORD dwMode = CRYPT_MODE_CBC;
  175. if (f40bit)
  176. dwEffectiveKeyLen=KEY_LENGTH40;
  177. else
  178. dwEffectiveKeyLen= KEY_LENGTH;
  179. //--------------------------------------------------------------------
  180. // The file will be encrypted with a session key derived from a
  181. // password.
  182. // The session key will be recreated when the file is decrypted.
  183. //--------------------------------------------------------------------
  184. // Create a hash object.
  185. if (!CryptCreateHash(
  186. hCryptProv,
  187. CALG_MD5,
  188. 0,
  189. 0,
  190. &hHash))
  191. {
  192. goto CLEANUP;
  193. }
  194. //--------------------------------------------------------------------
  195. // Hash the password.
  196. if (!CryptHashData(
  197. hHash,
  198. (BYTE *)szPassword,
  199. strlen(szPassword)*sizeof(CHAR),
  200. 0))
  201. {
  202. goto CLEANUP;
  203. }
  204. //--------------------------------------------------------------------
  205. // Derive a session key from the hash object.
  206. if (!CryptDeriveKey(
  207. hCryptProv,
  208. ENCRYPT_ALGORITHM,
  209. hHash,
  210. 0,
  211. &hKey))
  212. {
  213. goto CLEANUP;
  214. }
  215. // set effective key length explicitly
  216. if (!CryptSetKeyParam(
  217. hKey,
  218. KP_EFFECTIVE_KEYLEN,
  219. (BYTE*)&dwEffectiveKeyLen,
  220. 0))
  221. {
  222. if(hKey)
  223. CryptDestroyKey(hKey);
  224. hKey = 0;
  225. goto CLEANUP;
  226. }
  227. if (!f40bit)
  228. {
  229. // set padding explicitly
  230. if (!CryptSetKeyParam(
  231. hKey,
  232. KP_PADDING,
  233. (BYTE*)&dwPadding,
  234. 0))
  235. {
  236. if(hKey)
  237. CryptDestroyKey(hKey);
  238. hKey = 0;
  239. goto CLEANUP;
  240. }
  241. // set mode explicitly
  242. if (!CryptSetKeyParam(
  243. hKey,
  244. KP_MODE,
  245. (BYTE*)&dwMode,
  246. 0))
  247. {
  248. if(hKey)
  249. CryptDestroyKey(hKey);
  250. hKey = 0;
  251. goto CLEANUP;
  252. }
  253. }
  254. //--------------------------------------------------------------------
  255. // Destroy the hash object.
  256. CLEANUP:
  257. if (hHash)
  258. CryptDestroyHash(hHash);
  259. return hKey;
  260. }
  261. BYTE * DecryptWorker(const BYTE * pbEncryptedBlob, DWORD cbEncryptedBlob, DWORD * pcbDecryptedBlob, LPCSTR szPassword, BOOL f40bit, BOOL* pfRet)
  262. {
  263. HCRYPTPROV hCryptProv = NULL; // CSP handle
  264. HCRYPTKEY hKey = 0;
  265. DWORD cbDecryptedMessage = 0;
  266. BYTE* pbDecryptedMessage = NULL;
  267. DWORD dwBlockLen;
  268. DWORD dwBufferLen;
  269. BOOL fCreateKeyset = FALSE;
  270. Assert(pfRet);
  271. *pfRet=TRUE;
  272. //--------------------------------------------------------------------
  273. // Begin processing.
  274. Assert(pcbDecryptedBlob);
  275. *pcbDecryptedBlob = 0;
  276. if (!pbEncryptedBlob || cbEncryptedBlob == 0)
  277. return NULL;
  278. if (FFrenchLCID())
  279. return DecryptFrench(pbEncryptedBlob, cbEncryptedBlob, pcbDecryptedBlob, szPassword);
  280. //--------------------------------------------------------------------
  281. // Get a handle to a cryptographic provider.
  282. while (!CryptAcquireContext(
  283. &hCryptProv, // Address for handle to be returned.
  284. NULL, // Container
  285. NULL, // Use the default provider.
  286. PROV_RSA_FULL, // Need to both encrypt and sign.
  287. (fCreateKeyset ? CRYPT_NEWKEYSET:0))) // flags.
  288. {
  289. // Cryptographic context could not be acquired
  290. DWORD nError = GetLastError();
  291. if (!fCreateKeyset && (nError == NTE_BAD_KEYSET || nError == NTE_KEYSET_NOT_DEF))
  292. {
  293. fCreateKeyset = TRUE;
  294. continue;
  295. }
  296. Assert(FALSE);
  297. goto CLEANUP;
  298. }
  299. //--------------------------------------------------------------------
  300. // Create the session key.
  301. hKey = CreateSessionKey(hCryptProv, szPassword, f40bit);
  302. if (!hKey)
  303. {
  304. goto CLEANUP;
  305. }
  306. dwBlockLen = cbEncryptedBlob;
  307. dwBufferLen = dwBlockLen;
  308. //--------------------------------------------------------------------
  309. // Allocate memory.
  310. pbDecryptedMessage = (BYTE *)crtmalloc(dwBufferLen);
  311. if (!pbDecryptedMessage)
  312. {
  313. // Out of memory
  314. goto CLEANUP;
  315. }
  316. memcpy(pbDecryptedMessage, pbEncryptedBlob, cbEncryptedBlob);
  317. cbDecryptedMessage = cbEncryptedBlob;
  318. //--------------------------------------------------------------------
  319. // Decrypt data.
  320. if (!CryptDecrypt(
  321. hKey,
  322. 0,
  323. TRUE,
  324. 0,
  325. pbDecryptedMessage,
  326. &cbDecryptedMessage))
  327. {
  328. crtfree(pbDecryptedMessage);
  329. pbDecryptedMessage = NULL;
  330. cbDecryptedMessage = 0;
  331. *pfRet=FALSE;
  332. goto CLEANUP;
  333. }
  334. //--------------------------------------------------------------------
  335. // Clean up memory.
  336. CLEANUP:
  337. if(hKey)
  338. CryptDestroyKey(hKey);
  339. if (hCryptProv)
  340. {
  341. CryptReleaseContext(hCryptProv,0);
  342. // The CSP has been released.
  343. }
  344. *pcbDecryptedBlob = cbDecryptedMessage;
  345. return pbDecryptedMessage;
  346. }
  347. BYTE * __cdecl Decrypt(const BYTE * pbEncryptedBlob, DWORD cbEncryptedBlob, DWORD * pcbDecryptedBlob, LPCSTR szEncryptionPassword)
  348. {
  349. BYTE* pbDecryptedMessage;
  350. BOOL fRet;
  351. // try 128 bit first, if we fail, try 40 bit again.
  352. pbDecryptedMessage = DecryptWorker(pbEncryptedBlob, cbEncryptedBlob, pcbDecryptedBlob, szEncryptionPassword, FALSE, &fRet);
  353. if (!fRet)
  354. {
  355. if (pbDecryptedMessage)
  356. crtfree(pbDecryptedMessage);
  357. pbDecryptedMessage = DecryptWorker(pbEncryptedBlob, cbEncryptedBlob, pcbDecryptedBlob, szEncryptionPassword, TRUE, &fRet);
  358. }
  359. return pbDecryptedMessage;
  360. }
  361. //
  362. // End section from Money team
  363. //
  364. /*++
  365. Patch the Decrypt entry point.
  366. --*/
  367. CRITICAL_SECTION g_csPatch;
  368. DWORD g_dwDecrypt = (DWORD_PTR)&Decrypt;
  369. HINSTANCE
  370. APIHOOK(LoadLibraryA)(
  371. LPCSTR lpLibFileName
  372. )
  373. {
  374. HMODULE hMod = ORIGINAL_API(LoadLibraryA)(lpLibFileName);
  375. //
  376. // Wrap the patch in a critical section so we know the library won't be
  377. // freed underneath us
  378. //
  379. EnterCriticalSection(&g_csPatch);
  380. HMODULE hMoney = GetModuleHandleW(L"mnyutil.dll");
  381. if (hMoney) {
  382. // Patch the dll with a jump to our function
  383. LPBYTE lpProc = (LPBYTE) GetProcAddress(hMoney, (LPCSTR)274);
  384. if (lpProc) {
  385. __try {
  386. DWORD dwOldProtect;
  387. if (VirtualProtect((PVOID)lpProc, 5, PAGE_READWRITE, &dwOldProtect)) {
  388. *(WORD *)lpProc = 0x25ff; lpProc += 2;
  389. *(DWORD *)lpProc = (DWORD_PTR)&g_dwDecrypt;
  390. }
  391. } __except(1) {
  392. LOGN(eDbgLevelError, "[LoadLibraryA] Exception while patching entry point");
  393. }
  394. }
  395. }
  396. LeaveCriticalSection(&g_csPatch);
  397. return hMod;
  398. }
  399. BOOL
  400. APIHOOK(FreeLibrary)(
  401. HMODULE hModule
  402. )
  403. {
  404. EnterCriticalSection(&g_csPatch);
  405. BOOL bRet = ORIGINAL_API(FreeLibrary)(hModule);
  406. LeaveCriticalSection(&g_csPatch);
  407. return bRet;
  408. }
  409. /*++
  410. Register hooked functions
  411. --*/
  412. BOOL
  413. NOTIFY_FUNCTION(
  414. DWORD fdwReason
  415. )
  416. {
  417. if (fdwReason == DLL_PROCESS_ATTACH) {
  418. if (!InitializeCriticalSectionAndSpinCount(&g_csPatch, 0x80000000)) {
  419. LOGN(eDbgLevelError, "[NotifyFn] Failed to initialize critical section");
  420. return FALSE;
  421. }
  422. }
  423. return TRUE;
  424. }
  425. HOOK_BEGIN
  426. CALL_NOTIFY_FUNCTION
  427. APIHOOK_ENTRY(KERNEL32.DLL, LoadLibraryA)
  428. APIHOOK_ENTRY(KERNEL32.DLL, FreeLibrary)
  429. HOOK_END
  430. IMPLEMENT_SHIM_END