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.

532 lines
13 KiB

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