/*++ Copyright (c) 2002 Microsoft Corporation Module Name: Money2002.cpp Abstract: Retry passwords at 128-bit encryption if 40-bit fails. Most code from Money team. We have to patch the API directly since it is called from within it's own DLL, i.e. it doesn't go through the import table. The function we're patching is cdecl and is referenced by it's ordinal because the name is mangled. Notes: This is an app specific shim. History: 07/11/2002 linstev Created --*/ #include "precomp.h" IMPLEMENT_SHIM_BEGIN(Money2002) #include "ShimHookMacro.h" #include APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY(LoadLibraryA) APIHOOK_ENUM_ENTRY(FreeLibrary) APIHOOK_ENUM_END #define Assert(a) void *crtmalloc(size_t size) { HMODULE hMod = GetModuleHandleW(L"msvcrt.dll"); if (hMod) { typedef void * (__cdecl *_pfn_malloc)(size_t size); _pfn_malloc pfnmalloc = (_pfn_malloc) GetProcAddress(hMod, "malloc"); if (pfnmalloc) { return pfnmalloc(size); } } return malloc(size); } void crtfree(void *memblock) { HMODULE hMod = GetModuleHandleW(L"msvcrt.dll"); if (hMod) { _pfn_free pfnfree = (_pfn_free) GetProcAddress(hMod, "free"); if (pfnfree) { pfnfree(memblock); return; } } free(memblock); } // // This section from the Money team // #define MAXLEN (100) #define ENCRYPT_BLOCK_SIZE (8) #define ENCRYPT_ALGORITHM CALG_RC2 #define KEY_LENGTH (128) #define KEY_LENGTH40 (40) #define LgidMain(lcid) PRIMARYLANGID(LANGIDFROMLCID(lcid)) #define LgidSub(lcid) SUBLANGID(LANGIDFROMLCID(lcid)) // this function is only used for conversion BOOL FFrenchLCID() { LCID lcidSys=::GetSystemDefaultLCID(); if (LgidMain(lcidSys)==LANG_FRENCH && LgidSub(lcidSys)==SUBLANG_FRENCH) return TRUE; else return FALSE; } // the smallest buffer size is 8 #define BLOCKSIZE 8 // process a block, either encrypt it or decrypt it // this function is only used for conversion void ProcessBlock(BOOL fEncrypt, BYTE * buffer) { BYTE mask[BLOCKSIZE]; // mask array BYTE temp[BLOCKSIZE]; // temporary array int rgnScramble[BLOCKSIZE]; // scramble array int i; // initialized scramble array for (i=0; i= BLOCKSIZE); Assert(cbEncryptedBlob % BLOCKSIZE == 0); *pcbDecryptedBlob = 0; if (cbEncryptedBlob < BLOCKSIZE || cbEncryptedBlob % BLOCKSIZE != 0) return NULL; // calculate initial seed while (*szPassword) seed += *szPassword++; srand(seed); // retrieve the first block for (i=0; i0; i++, cbResult--) *pb++ = buffer[i]; } } } if (!pbResult) *pcbDecryptedBlob = 0; return pbResult; } HCRYPTKEY CreateSessionKey(HCRYPTPROV hCryptProv, LPCSTR szPassword, BOOL fConvert, BOOL f40bit) { HCRYPTHASH hHash = 0; HCRYPTKEY hKey = 0; DWORD dwEffectiveKeyLen; DWORD dwPadding = PKCS5_PADDING; DWORD dwMode = CRYPT_MODE_CBC; if (f40bit) dwEffectiveKeyLen=KEY_LENGTH40; else dwEffectiveKeyLen= KEY_LENGTH; //-------------------------------------------------------------------- // The file will be encrypted with a session key derived from a // password. // The session key will be recreated when the file is decrypted. //-------------------------------------------------------------------- // Create a hash object. if (!CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hHash)) { goto CLEANUP; } //-------------------------------------------------------------------- // Hash the password. if (!CryptHashData( hHash, (BYTE *)szPassword, strlen(szPassword)*sizeof(CHAR), 0)) { goto CLEANUP; } //-------------------------------------------------------------------- // Derive a session key from the hash object. if (!CryptDeriveKey( hCryptProv, ENCRYPT_ALGORITHM, hHash, (fConvert ? 0 : (KEY_LENGTH << 16) | CRYPT_EXPORTABLE), &hKey)) { goto CLEANUP; } // set effective key length explicitly if (!CryptSetKeyParam( hKey, KP_EFFECTIVE_KEYLEN, (BYTE*)&dwEffectiveKeyLen, 0)) { if(hKey) CryptDestroyKey(hKey); hKey = 0; goto CLEANUP; } if (!fConvert && !f40bit) { // set padding explicitly if (!CryptSetKeyParam( hKey, KP_PADDING, (BYTE*)&dwPadding, 0)) { if(hKey) CryptDestroyKey(hKey); hKey = 0; goto CLEANUP; } // set mode explicitly if (!CryptSetKeyParam( hKey, KP_MODE, (BYTE*)&dwMode, 0)) { if(hKey) CryptDestroyKey(hKey); hKey = 0; goto CLEANUP; } } //-------------------------------------------------------------------- // Destroy the hash object. CLEANUP: if (hHash) CryptDestroyHash(hHash); return hKey; } BYTE * DecryptWorker(const BYTE * pbEncryptedBlob, DWORD cbEncryptedBlob, DWORD * pcbDecryptedBlob, LPCSTR szPassword, BOOL fConvert, BOOL f40bit, BOOL* pfRet) { HCRYPTPROV hCryptProv = NULL; // CSP handle HCRYPTKEY hKey = 0; DWORD cbDecryptedMessage = 0; BYTE* pbDecryptedMessage = NULL; DWORD dwBlockLen; DWORD dwBufferLen; Assert(pfRet); *pfRet=TRUE; //-------------------------------------------------------------------- // Begin processing. Assert(pcbDecryptedBlob); *pcbDecryptedBlob = 0; if (!pbEncryptedBlob || cbEncryptedBlob == 0) return NULL; if (FFrenchLCID() && fConvert) return DecryptFrench(pbEncryptedBlob, cbEncryptedBlob, pcbDecryptedBlob, szPassword); //-------------------------------------------------------------------- // Get a handle to a cryptographic provider. if (!CryptAcquireContext( &hCryptProv, // Address for handle to be returned. NULL, // Container (fConvert ? NULL : MS_ENHANCED_PROV), // Use the default provider. PROV_RSA_FULL, // Need to both encrypt and sign. CRYPT_VERIFYCONTEXT)) // flags. { Assert(FALSE); goto CLEANUP; } //-------------------------------------------------------------------- // Create the session key. hKey = CreateSessionKey(hCryptProv, szPassword, fConvert, f40bit); if (!hKey) { goto CLEANUP; } dwBlockLen = cbEncryptedBlob; dwBufferLen = dwBlockLen; //-------------------------------------------------------------------- // Allocate memory. pbDecryptedMessage = (BYTE *)crtmalloc(dwBufferLen); if (!pbDecryptedMessage) { // Out of memory goto CLEANUP; } memcpy(pbDecryptedMessage, pbEncryptedBlob, cbEncryptedBlob); cbDecryptedMessage = cbEncryptedBlob; //-------------------------------------------------------------------- // Decrypt data. if (!CryptDecrypt( hKey, 0, TRUE, 0, pbDecryptedMessage, &cbDecryptedMessage)) { crtfree(pbDecryptedMessage); pbDecryptedMessage = NULL; cbDecryptedMessage = 0; *pfRet=FALSE; goto CLEANUP; } //-------------------------------------------------------------------- // Clean up memory. CLEANUP: if(hKey) CryptDestroyKey(hKey); if (hCryptProv) { CryptReleaseContext(hCryptProv,0); // The CSP has been released. } *pcbDecryptedBlob = cbDecryptedMessage; return pbDecryptedMessage; } BYTE * __cdecl Decrypt(const BYTE * pbEncryptedBlob, DWORD cbEncryptedBlob, DWORD * pcbDecryptedBlob, LPCSTR szEncryptionPassword, BOOL fConvert) { BYTE* pbDecryptedMessage; BOOL fRet; // try 128 bit first, if we fail, try 40 bit again. pbDecryptedMessage = DecryptWorker(pbEncryptedBlob, cbEncryptedBlob, pcbDecryptedBlob, szEncryptionPassword, fConvert, FALSE, &fRet); if (!fRet) { if (pbDecryptedMessage) crtfree(pbDecryptedMessage); pbDecryptedMessage = DecryptWorker(pbEncryptedBlob, cbEncryptedBlob, pcbDecryptedBlob, szEncryptionPassword, fConvert, TRUE, &fRet); } return pbDecryptedMessage; } // // End section from Money team // /*++ Patch the Decrypt entry point. --*/ CRITICAL_SECTION g_csPatch; DWORD g_dwDecrypt = (DWORD_PTR)&Decrypt; HINSTANCE APIHOOK(LoadLibraryA)( LPCSTR lpLibFileName ) { HMODULE hMod = ORIGINAL_API(LoadLibraryA)(lpLibFileName); // // Wrap the patch in a critical section so we know the library won't be // freed underneath us // EnterCriticalSection(&g_csPatch); HMODULE hMoney = GetModuleHandleW(L"mnyutil.dll"); if (hMoney) { // Patch the dll with a jump to our function LPBYTE lpProc = (LPBYTE) GetProcAddress(hMoney, (LPCSTR)290); if (lpProc) { __try { DWORD dwOldProtect; if (VirtualProtect((PVOID)lpProc, 5, PAGE_READWRITE, &dwOldProtect)) { *(WORD *)lpProc = 0x25ff; lpProc += 2; *(DWORD *)lpProc = (DWORD_PTR)&g_dwDecrypt; } } __except(1) { LOGN(eDbgLevelError, "[LoadLibraryA] Exception while patching entry point"); } } } LeaveCriticalSection(&g_csPatch); return hMod; } BOOL APIHOOK(FreeLibrary)( HMODULE hModule ) { EnterCriticalSection(&g_csPatch); BOOL bRet = ORIGINAL_API(FreeLibrary)(hModule); LeaveCriticalSection(&g_csPatch); return bRet; } /*++ Register hooked functions --*/ BOOL NOTIFY_FUNCTION( DWORD fdwReason ) { if (fdwReason == DLL_PROCESS_ATTACH) { if (!InitializeCriticalSectionAndSpinCount(&g_csPatch, 0x80000000)) { LOGN(eDbgLevelError, "[NotifyFn] Failed to initialize critical section"); return FALSE; } } return TRUE; } HOOK_BEGIN CALL_NOTIFY_FUNCTION APIHOOK_ENTRY(KERNEL32.DLL, LoadLibraryA) APIHOOK_ENTRY(KERNEL32.DLL, FreeLibrary) HOOK_END IMPLEMENT_SHIM_END