简单的Payload加密方法

前言

之前学习MalDev Modules中的相关笔记,对其进行了简单的优化,如有错误望指正。

AES

这里讨论一种更安全的加密算法------高级加密标准 (AES)。它是一种对称密钥算法,这意味着加密和解密使用相同的密钥。AES 加密有多种类型,例如 AES128、AES192 和 AES256,它们的区别在于密钥长度。例如,AES128 使用 128 位密钥,而 AES256 使用 256 位密钥。

此外,AES 可以使用不同的分组密码模式,例如 CBC 和 GCM。根据 AES 模式的不同,AES 算法除了加密密钥外,还需要一个称为初始化向量 (IV) 的额外组件。提供 IV 可以为加密过程增加一层额外的安全保障。

无论选择哪种AES类型,AES始终需要128位输入,并生成128位输出块。需要注意的是,输入数据必须是16字节(128位)的倍数。如果加密的有效载荷不是16字节的倍数,则需要填充以增加有效载荷的大小,使其成为16字节的倍数。

使用 WinAPI 的 AES(bCrypt 库)

实现 AES 加密算法有多种方式。本节利用 bCrypt 库(bcrypt.h)进行 AES 加密。

AES 结构

首先,创建一个 AES 结构,包含执行加密和解密所需的数据。

c 复制代码
typedef struct _AES_CTX {

	PBYTE	pPlainText;         // base address of the plain text data 
	DWORD	dwPlainSize;        // size of the plain text data

	PBYTE	pCipherText;        // base address of the encrypted data
	DWORD	dwCipherSize;       // size of it (this can change from dwPlainSize in case there was padding)

	PBYTE	pKey;               // the 32 byte key
	PBYTE	pIv;                // the 16 byte iv

} AES_CTX, * PAES_CTX;

SimpleEncryption 函数封装

SimpleEncryption函数有六个参数,用于初始化 AES 结构。结构初始化完成后,该函数会调用 InstallAesEncryption 来执行 AES 加密过程。请注意,其中两个参数是输出参数,因此该函数返回以下值:

  • pCipherTextData - 指向新分配的堆缓冲区的指针,其中包含密文数据
  • sCipherTextSize - 密文缓冲区的大小。

如果 InstallAesEncryption成功,该函数返回 TRUE,否则返回 FALSE

c 复制代码
// Wrapper function for InstallAesEncryption that makes things easier
BOOL SimpleEncryption(IN PBYTE pPlainTextData, IN DWORD sPlainTextSize, IN PBYTE pKey, IN PBYTE pIv, OUT PBYTE* pCipherTextData, OUT DWORD* sCipherTextSize) {

	if (pPlainTextData == NULL || sPlainTextSize == 0 || pKey == NULL || pIv == NULL || pCipherTextData == NULL || sCipherTextSize == NULL)
		return FALSE;

	// Intializing the struct
	AES_CTX Aes = {
		pPlainTextData,     // pPlainText
		sPlainTextSize,     // dwPlainSize
		NULL,               // pCipherText
		0,                  // dwCipherSize
		pKey,               // pKey
		pIv                 // pIv
	};

	if (!InstallAesEncryption(&Aes)) {
		return FALSE;
	}

	// Saving output
	*pCipherTextData = Aes.pCipherText;
	*sCipherTextSize = Aes.dwCipherSize;

	return TRUE;
}

SimpleDecryption 函数封装

SimpleDecryption函数也有六个参数,其行为类似于 SimpleEncryption,区别在于调用 InstallAesDecryption函数,返回两个不同的值。

  • pPlainTextData - 指向新分配的堆缓冲区的指针,该缓冲区包含明文数据。

  • sPlainTextSize - 明文缓冲区的大小。

如果 InstallAesDecryption成功,该函数返回 TRUE,否则返回 FALSE

c 复制代码
// Wrapper function for InstallAesDecryption that make things easier
BOOL SimpleDecryption(IN PBYTE pCipherTextData, IN DWORD sCipherTextSize, IN PBYTE pKey, IN PBYTE pIv, OUT PBYTE* pPlainTextData, OUT DWORD* sPlainTextSize) {

	if (pCipherTextData == NULL || sCipherTextSize == 0 || pKey == NULL || pIv == NULL || pPlainTextData == NULL || sPlainTextSize == NULL)
		return FALSE;

	// Intializing the struct
	AES_CTX Aes = {
		NULL,               // pPlainText
		0,                  // dwPlainSize
		pCipherTextData,    // pCipherText
		sCipherTextSize,    // dwCipherSize
		pKey,               // pKey
		pIv                 // pIv
	};

	if (!InstallAesDecryption(&Aes)) {
		return FALSE;
	}

	// Saving output
	*pPlainTextData = Aes.pPlainText;
	*sPlainTextSize = Aes.dwPlainSize;

	return TRUE;
}

Cryptographic Next Generation

Cryptographic Next Generation (CNG) 提供了一组可供操作系统应用程序使用的加密函数。CNG 为加密操作提供了一个标准化的接口,使开发人员能够更轻松地在其应用程序中实现安全功能。nstallAesEncryptionInstallAesDecryption函数都使用了 CNG。

有关 CNG 的更多信息:https://learn.microsoft.com/en-us/windows/win32/seccng/cng-portal

InstallAesEncryption Function

InstallAesEncryption函数用于执行 AES 加密。该函数有一个参数 PAES,它是指向已填充 AES 结构的指针。以下列出了该函数中使用的 bCrypt 库函数:

BCryptOpenAlgorithmProvider - 用于加载 BCRYPT_AES_ALGORITHM 加密下一代 (CNG) 提供程序,以启用加密函数。

BCryptGetProperty - 此函数调用两次,第一次用于检索 BCRYPT_OBJECT_LENGTH 的值,第二次用于获取 BCRYPT_BLOCK_LENGTH 属性标识符的值。

BCryptSetProperty - 用于初始化 BCRYPT_OBJECT_LENGTH 属性标识符。

BCryptGenerateSymmetricKey - 用于根据指定的输入 AES 密钥创建密钥对象。

BCryptEncrypt - 用于加密指定的数据块。此函数调用两次,第一次调用获取加密数据的大小,并分配相应大小的堆缓冲区。第二次调用加密数据并将密文存储在已分配的堆中。

BCryptDestroyKey - 用于清理,销毁使用 BCryptGenerateSymmetricKey 创建的密钥对象。

BCryptCloseAlgorithmProvider - 用于清理,关闭之前使用 BCryptOpenAlgorithmProvider 创建的算法提供程序的对象句柄。

如果成功加密payload,该函数返回 TRUE,否则 FALSE

c 复制代码
// The encryption implementation
BOOL InstallAesEncryption(PAES_CTX pAes) {

	BOOL                  bSTATE = TRUE;
	BCRYPT_ALG_HANDLE     hAlgorithm = NULL;
	BCRYPT_KEY_HANDLE     hKeyHandle = NULL;
	NTSTATUS              STATUS = 0;

	ULONG       		cbResult = 0;
	DWORD       		dwBlockSize = 0;

	DWORD       		cbKeyObject = 0;
	PBYTE       		pbKeyObject = NULL;

	PBYTE      		pbCipherText = NULL;
	DWORD       		cbCipherText = 0;
	BYTE                  ivCopy[IVSIZE] = { 0 };


	// Intializing "hAlgorithm" as AES algorithm Handle
	STATUS = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptOpenAlgorithmProvider Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the key object variable pbKeyObject. This is used by the BCryptGenerateSymmetricKey function later 
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the block used in the encryption. Since this is AES it must be 16 bytes.
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_BLOCK_LENGTH, (PBYTE)&dwBlockSize, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Checking if block size is 16 bytes
	if (dwBlockSize != 16) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating memory for the key object 
	pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
	if (pbKeyObject == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Setting Block Cipher Mode to CBC. This uses a 32 byte key and a 16 byte IV.
	STATUS = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptSetProperty Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Generating the key object from the AES key "pAes->pKey". The output will be saved in pbKeyObject and will be of size cbKeyObject 
	STATUS = BCryptGenerateSymmetricKey(hAlgorithm, &hKeyHandle, pbKeyObject, cbKeyObject, (PBYTE)pAes->pKey, KEYSIZE, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGenerateSymmetricKey Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptEncrypt first time with NULL output parameters to retrieve the size of the output buffer which is saved in cbCipherText
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptEncrypt(hKeyHandle, (PUCHAR)pAes->pPlainText, (ULONG)pAes->dwPlainSize, NULL, ivCopy, IVSIZE, NULL, 0, &cbCipherText, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptEncrypt[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating enough memory for the output buffer, cbCipherText
	pbCipherText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbCipherText);
	if (pbCipherText == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptEncrypt again with pbCipherText as the output buffer
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptEncrypt(hKeyHandle, (PUCHAR)pAes->pPlainText, (ULONG)pAes->dwPlainSize, NULL, ivCopy, IVSIZE, pbCipherText, cbCipherText, &cbResult, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptEncrypt[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}


	// Clean up
_EndOfFunc:
	if (hKeyHandle)
		BCryptDestroyKey(hKeyHandle);
	if (hAlgorithm)
		BCryptCloseAlgorithmProvider(hAlgorithm, 0);
	if (pbKeyObject)
		HeapFree(GetProcessHeap(), 0, pbKeyObject);
	if (pbCipherText != NULL && bSTATE) {
		// If everything worked, save pbCipherText and cbCipherText 
		pAes->pCipherText = pbCipherText;
		pAes->dwCipherSize = cbCipherText;
	}
	else if (pbCipherText != NULL) {
		HeapFree(GetProcessHeap(), 0, pbCipherText);
	}
	return bSTATE;
}

InstallAesDecryption Function

InstallAesDecryption 函数用于执行 AES 解密。该函数有一个参数 PAES,它是指向已填充 AES 结构的指针该函数中使用的 bCrypt 库函数与上述 InstallAesEncryption 函数中的相同,唯一的区别在于前者使用 BCryptDecrypt 代替了 BCryptEncrypt

BCryptDecrypt - 用于解密指定的数据块。该函数会被调用两次:第一次调用获取解密后数据的大小,并分配一个大小相同的堆缓冲区;第二次调用解密数据,并将明文数据存储在已分配的堆缓冲区中。

如果成功解密payload,该函数返回 TRUE;否则返回 FALSE

c 复制代码
// The decryption implementation
BOOL InstallAesDecryption(PAES_CTX pAes) {

	BOOL                  bSTATE = TRUE;
	BCRYPT_ALG_HANDLE     hAlgorithm = NULL;
	BCRYPT_KEY_HANDLE     hKeyHandle = NULL;
	NTSTATUS              STATUS = 0;

	ULONG                 cbResult = 0;
	DWORD                 dwBlockSize = 0;

	DWORD                 cbKeyObject = 0;
	PBYTE                 pbKeyObject = NULL;

	PBYTE                 pbPlainText = NULL;
	DWORD                 cbPlainText = 0;
	BYTE                  ivCopy[IVSIZE] = { 0 };

	// Intializing "hAlgorithm" as AES algorithm Handle
	STATUS = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptOpenAlgorithmProvider Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the key object variable pbKeyObject. This is used by the BCryptGenerateSymmetricKey function later
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the block used in the encryption. Since this is AES it should be 16 bytes.
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_BLOCK_LENGTH, (PBYTE)&dwBlockSize, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Checking if block size is 16 bytes
	if (dwBlockSize != 16) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating memory for the key object 
	pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
	if (pbKeyObject == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Setting Block Cipher Mode to CBC. This uses a 32 byte key and a 16 byte IV.
	STATUS = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptSetProperty Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Generating the key object from the AES key "pAes->pKey". The output will be saved in pbKeyObject of size cbKeyObject 
	STATUS = BCryptGenerateSymmetricKey(hAlgorithm, &hKeyHandle, pbKeyObject, cbKeyObject, (PBYTE)pAes->pKey, KEYSIZE, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGenerateSymmetricKey Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptDecrypt first time with NULL output parameters to retrieve the size of the output buffer which is saved in cbPlainText
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptDecrypt(hKeyHandle, (PUCHAR)pAes->pCipherText, (ULONG)pAes->dwCipherSize, NULL, ivCopy, IVSIZE, NULL, 0, &cbPlainText, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptDecrypt[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating enough memory for the output buffer, cbPlainText
	pbPlainText = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbPlainText + 1);
	if (pbPlainText == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptDecrypt again with pbPlainText as the output buffer
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptDecrypt(hKeyHandle, (PUCHAR)pAes->pCipherText, (ULONG)pAes->dwCipherSize, NULL, ivCopy, IVSIZE, pbPlainText, cbPlainText, &cbResult, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptDecrypt[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Clean up
_EndOfFunc:
	if (hKeyHandle)
		BCryptDestroyKey(hKeyHandle);
	if (hAlgorithm)
		BCryptCloseAlgorithmProvider(hAlgorithm, 0);
	if (pbKeyObject)
		HeapFree(GetProcessHeap(), 0, pbKeyObject);
	if (pbPlainText != NULL && bSTATE) {
		// if everything went well, we save pbPlainText and cbPlainText
		pAes->pPlainText = pbPlainText;
		pAes->dwPlainSize = cbPlainText;
	}
	else if (pbPlainText != NULL) {
		HeapFree(GetProcessHeap(), 0, pbPlainText);
	}
	return bSTATE;

}

附加辅助函数

该代码还包含两个小的辅助函数,即 PrintHexDataGenerateRandomBytes

第一个函数 PrintHexData 将输入缓冲区的内容以 C 语言语法的字符数组形式打印到控制台

c 复制代码
VOID PrintHexData(LPCSTR Name, PBYTE Data, SIZE_T Size)
{
	printf("unsigned char %s[] = {", Name);

	for (int i = 0; i < Size; i++) {
		if (i % 16 == 0)
			printf("\n\t");

		if (i < Size - 1)
		{
			printf("0x%0.2X, ", Data[i]);
		}
		else
		{
			printf("0x%0.2X ", Data[i]);
		}
	}

	printf("\n};\n\n");
}

另一个函数 GenerateRandomBytes 会用随机字节填充输入缓冲区,在本例中,这些随机字节用于生成随机密钥和 IV

c 复制代码
// Generate random bytes of size sSize
VOID GenerateRandomBytes(PBYTE pByte, SIZE_T sSize) {

	for (int i = 0; i < sSize; i++)
	{
		pByte[i] = (BYTE)rand() % 0xFF;
	}
}

填充

InstallAesEncryptionInstallAesDecryption 函数分别使用 BCryptEncryptBCryptDecrypt bcrypt 函数的 BCRYPT_BLOCK_PADDING 标志,如果需要,会自动将输入缓冲区填充为 16 字节的倍数,从而解决 AES 填充问题。

加密

c 复制代码
// The plaintext, in hex format, that will be encrypted
// this is the following string in hex "This is a plain text string, we'll try to encrypt/decrypt !"
unsigned char Data[] = {
	0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x70, 0x6C,
	0x61, 0x69, 0x6E, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x73, 0x74, 0x72,
	0x69, 0x6E, 0x67, 0x2C, 0x20, 0x77, 0x65, 0x27, 0x6C, 0x6C, 0x20, 0x74,
	0x72, 0x79, 0x20, 0x74, 0x6F, 0x20, 0x65, 0x6E, 0x63, 0x72, 0x79, 0x70,
	0x74, 0x2F, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x21
};

int main() {

	BYTE pKey[KEYSIZE];                    // KEYSIZE is 32 bytes
	BYTE pIv[IVSIZE];                      // IVSIZE is 16 bytes

	srand(time(NULL));                      // The seed to generate the key. This is used to further randomize the key.
	GenerateRandomBytes(pKey, KEYSIZE);     // Generating a key with the helper function

	srand(time(NULL) ^ pKey[0]);            // The seed to generate the IV. Use the first byte of the key to add more randomness.
	GenerateRandomBytes(pIv, IVSIZE);       // Generating the IV with the helper function

	// Printing both key and IV onto the console 
	PrintHexData("pKey", pKey, KEYSIZE);
	PrintHexData("pIv", pIv, IVSIZE);

	// Defining two variables the output buffer and its respective size which will be used in SimpleEncryption
	PBYTE pCipherText = NULL;
	DWORD dwCipherSize = 0;

	// Encrypting
	if (!SimpleEncryption(Data, sizeof(Data), pKey, pIv, &pCipherText, &dwCipherSize)) {
		return -1;
	}

	// Print the encrypted buffer as a hex array
	PrintHexData("CipherText", pCipherText, dwCipherSize);

	// Clean up
	HeapFree(GetProcessHeap(), 0, pCipherText);
	system("PAUSE");
	return 0;
}

解密

以下主函数用于执行解密程序。解密程序需要解密密钥、初始化向量 (IV) 和密文。

c 复制代码
unsigned char pKey[] = {
		0xC7, 0x8F, 0x00, 0x46, 0xDD, 0x44, 0x3C, 0xB8, 0x4D, 0xE1, 0xD7, 0xCF, 0x8B, 0xE2, 0xEC, 0x8B,
		0xA6, 0xE2, 0x38, 0x05, 0x64, 0x61, 0xCD, 0x53, 0x94, 0xC4, 0x09, 0xAC, 0xA0, 0x9D, 0xC8, 0xCC
};

unsigned char pIv[] = {
		0xA2, 0x9E, 0x62, 0x18, 0x61, 0xF7, 0xD2, 0x98, 0xB2, 0xD1, 0x22, 0x56, 0xB3, 0x5E, 0x07, 0x3F
};

unsigned char CipherText[] = {
		0x32, 0x9D, 0xD4, 0xEB, 0x71, 0xDA, 0x7D, 0x29, 0x87, 0xD7, 0xAA, 0x45, 0xE4, 0xDC, 0xFA, 0x57,
		0x04, 0x02, 0x01, 0x98, 0xED, 0x84, 0x04, 0xAE, 0x84, 0x15, 0xA8, 0x38, 0x63, 0x14, 0x7A, 0x47,
		0x76, 0x4E, 0xA8, 0x19, 0xBD, 0x29, 0xB3, 0x65, 0x24, 0xB1, 0xD5, 0x0A, 0x13, 0x98, 0xD4, 0xFA,
		0x95, 0x52, 0xB9, 0x69, 0xF1, 0x29, 0xD4, 0xD4, 0xB3, 0x83, 0xB6, 0x12, 0x9F, 0xF5, 0x35, 0x10
};


int main() {

	// Defining two variables the output buffer and its respective size which will be used in SimpleDecryption
	PBYTE	pPlaintext = NULL;
	DWORD	dwPlainSize = 0;

	// Decrypting
	if (!SimpleDecryption(CipherText, sizeof(CipherText), pKey, pIv, &pPlaintext, &dwPlainSize)) {
		return -1;
	}

	// Printing the decrypted data to the screen in hex format
	PrintHexData("PlainText", pPlaintext, dwPlainSize);

	// this will print: "This is a plain text string, we'll try to encrypt/decrypt !"
	printf("Data: %.*s\n", (int)dwPlainSize, (char*)pPlaintext);

	// Clean up
	HeapFree(GetProcessHeap(), 0, pPlaintext);
	system("PAUSE");
	return 0;
}

完整代码

c 复制代码
#include <windows.h>
#include <bcrypt.h>
#include <stdio.h>
#include <ctime>
#include <stdlib.h>

#pragma comment(lib, "bcrypt.lib")

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

#define KEYSIZE 32
#define IVSIZE  16

typedef struct _AES_CTX {

	PBYTE	pPlainText;         // base address of the plain text data 
	DWORD	dwPlainSize;        // size of the plain text data

	PBYTE	pCipherText;        // base address of the encrypted data
	DWORD	dwCipherSize;       // size of it (this can change from dwPlainSize in case there was padding)

	PBYTE	pKey;               // the 32 byte key
	PBYTE	pIv;                // the 16 byte iv

} AES_CTX, * PAES_CTX;
// Generate random bytes of size sSize
VOID GenerateRandomBytes(PBYTE pByte, SIZE_T sSize) {

	for (int i = 0; i < sSize; i++)
	{
		pByte[i] = (BYTE)rand() % 0xFF;
	}
}

VOID PrintHexData(LPCSTR Name, PBYTE Data, SIZE_T Size)
{
	printf("unsigned char %s[] = {", Name);

	for (int i = 0; i < Size; i++) {
		if (i % 16 == 0)
			printf("\n\t");

		if (i < Size - 1)
		{
			printf("0x%0.2X, ", Data[i]);
		}
		else
		{
			printf("0x%0.2X ", Data[i]);
		}
	}

	printf("\n};\n\n");
}
// The encryption implementation
BOOL InstallAesEncryption(PAES_CTX pAes) {

	BOOL                  bSTATE = TRUE;
	BCRYPT_ALG_HANDLE     hAlgorithm = NULL;
	BCRYPT_KEY_HANDLE     hKeyHandle = NULL;
	NTSTATUS              STATUS = 0;

	ULONG       		cbResult = 0;
	DWORD       		dwBlockSize = 0;

	DWORD       		cbKeyObject = 0;
	PBYTE       		pbKeyObject = NULL;

	PBYTE      		pbCipherText = NULL;
	DWORD       		cbCipherText = 0;
	BYTE                  ivCopy[IVSIZE] = { 0 };


	// Intializing "hAlgorithm" as AES algorithm Handle
	STATUS = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptOpenAlgorithmProvider Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the key object variable pbKeyObject. This is used by the BCryptGenerateSymmetricKey function later 
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the block used in the encryption. Since this is AES it must be 16 bytes.
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_BLOCK_LENGTH, (PBYTE)&dwBlockSize, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Checking if block size is 16 bytes
	if (dwBlockSize != 16) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating memory for the key object 
	pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
	if (pbKeyObject == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Setting Block Cipher Mode to CBC. This uses a 32 byte key and a 16 byte IV.
	STATUS = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptSetProperty Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Generating the key object from the AES key "pAes->pKey". The output will be saved in pbKeyObject and will be of size cbKeyObject 
	STATUS = BCryptGenerateSymmetricKey(hAlgorithm, &hKeyHandle, pbKeyObject, cbKeyObject, (PBYTE)pAes->pKey, KEYSIZE, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGenerateSymmetricKey Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptEncrypt first time with NULL output parameters to retrieve the size of the output buffer which is saved in cbCipherText
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptEncrypt(hKeyHandle, (PUCHAR)pAes->pPlainText, (ULONG)pAes->dwPlainSize, NULL, ivCopy, IVSIZE, NULL, 0, &cbCipherText, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptEncrypt[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating enough memory for the output buffer, cbCipherText
	pbCipherText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbCipherText);
	if (pbCipherText == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptEncrypt again with pbCipherText as the output buffer
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptEncrypt(hKeyHandle, (PUCHAR)pAes->pPlainText, (ULONG)pAes->dwPlainSize, NULL, ivCopy, IVSIZE, pbCipherText, cbCipherText, &cbResult, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptEncrypt[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}


	// Clean up
_EndOfFunc:
	if (hKeyHandle)
		BCryptDestroyKey(hKeyHandle);
	if (hAlgorithm)
		BCryptCloseAlgorithmProvider(hAlgorithm, 0);
	if (pbKeyObject)
		HeapFree(GetProcessHeap(), 0, pbKeyObject);
	if (pbCipherText != NULL && bSTATE) {
		// If everything worked, save pbCipherText and cbCipherText 
		pAes->pCipherText = pbCipherText;
		pAes->dwCipherSize = cbCipherText;
	}
	else if (pbCipherText != NULL) {
		HeapFree(GetProcessHeap(), 0, pbCipherText);
	}
	return bSTATE;
}

// Wrapper function for InstallAesEncryption that makes things easier
BOOL SimpleEncryption(IN PBYTE pPlainTextData, IN DWORD sPlainTextSize, IN PBYTE pKey, IN PBYTE pIv, OUT PBYTE* pCipherTextData, OUT DWORD* sCipherTextSize) {

	if (pPlainTextData == NULL || sPlainTextSize == 0 || pKey == NULL || pIv == NULL || pCipherTextData == NULL || sCipherTextSize == NULL)
		return FALSE;

	// Intializing the struct
	AES_CTX Aes = {
		pPlainTextData,     // pPlainText
		sPlainTextSize,     // dwPlainSize
		NULL,               // pCipherText
		0,                  // dwCipherSize
		pKey,               // pKey
		pIv                 // pIv
	};

	if (!InstallAesEncryption(&Aes)) {
		return FALSE;
	}

	// Saving output
	*pCipherTextData = Aes.pCipherText;
	*sCipherTextSize = Aes.dwCipherSize;

	return TRUE;
}
// The plaintext, in hex format, that will be encrypted
//this is the following string in hex "This is a plain text string, we'll try to encrypt/decrypt !"
unsigned char Data[] = {
	0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x70, 0x6C,
	0x61, 0x69, 0x6E, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x73, 0x74, 0x72,
	0x69, 0x6E, 0x67, 0x2C, 0x20, 0x77, 0x65, 0x27, 0x6C, 0x6C, 0x20, 0x74,
	0x72, 0x79, 0x20, 0x74, 0x6F, 0x20, 0x65, 0x6E, 0x63, 0x72, 0x79, 0x70,
	0x74, 0x2F, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x21
};

//int main() {
//
//	BYTE pKey[KEYSIZE];                    // KEYSIZE is 32 bytes
//	BYTE pIv[IVSIZE];                      // IVSIZE is 16 bytes
//
//	srand(time(NULL));                      // The seed to generate the key. This is used to further randomize the key.
//	GenerateRandomBytes(pKey, KEYSIZE);     // Generating a key with the helper function
//
//	srand(time(NULL) ^ pKey[0]);            // The seed to generate the IV. Use the first byte of the key to add more randomness.
//	GenerateRandomBytes(pIv, IVSIZE);       // Generating the IV with the helper function
//
//	// Printing both key and IV onto the console 
//	PrintHexData("pKey", pKey, KEYSIZE);
//	PrintHexData("pIv", pIv, IVSIZE);
//
//	// Defining two variables the output buffer and its respective size which will be used in SimpleEncryption
//	PBYTE pCipherText = NULL;
//	DWORD dwCipherSize = 0;
//
//	// Encrypting
//	if (!SimpleEncryption(Data, sizeof(Data), pKey, pIv, &pCipherText, &dwCipherSize)) {
//		return -1;
//	}
//
//	// Print the encrypted buffer as a hex array
//	PrintHexData("CipherText", pCipherText, dwCipherSize);
//
//	// Clean up
//	HeapFree(GetProcessHeap(), 0, pCipherText);
//	system("PAUSE");
//	return 0;
//}





// The decryption implementation
BOOL InstallAesDecryption(PAES_CTX pAes) {

	BOOL                  bSTATE = TRUE;
	BCRYPT_ALG_HANDLE     hAlgorithm = NULL;
	BCRYPT_KEY_HANDLE     hKeyHandle = NULL;
	NTSTATUS              STATUS = 0;

	ULONG                 cbResult = 0;
	DWORD                 dwBlockSize = 0;

	DWORD                 cbKeyObject = 0;
	PBYTE                 pbKeyObject = NULL;

	PBYTE                 pbPlainText = NULL;
	DWORD                 cbPlainText = 0;
	BYTE                  ivCopy[IVSIZE] = { 0 };

	// Intializing "hAlgorithm" as AES algorithm Handle
	STATUS = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptOpenAlgorithmProvider Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the key object variable pbKeyObject. This is used by the BCryptGenerateSymmetricKey function later
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the block used in the encryption. Since this is AES it should be 16 bytes.
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_BLOCK_LENGTH, (PBYTE)&dwBlockSize, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Checking if block size is 16 bytes
	if (dwBlockSize != 16) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating memory for the key object 
	pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
	if (pbKeyObject == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Setting Block Cipher Mode to CBC. This uses a 32 byte key and a 16 byte IV.
	STATUS = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptSetProperty Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Generating the key object from the AES key "pAes->pKey". The output will be saved in pbKeyObject of size cbKeyObject 
	STATUS = BCryptGenerateSymmetricKey(hAlgorithm, &hKeyHandle, pbKeyObject, cbKeyObject, (PBYTE)pAes->pKey, KEYSIZE, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGenerateSymmetricKey Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptDecrypt first time with NULL output parameters to retrieve the size of the output buffer which is saved in cbPlainText
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptDecrypt(hKeyHandle, (PUCHAR)pAes->pCipherText, (ULONG)pAes->dwCipherSize, NULL, ivCopy, IVSIZE, NULL, 0, &cbPlainText, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptDecrypt[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating enough memory for the output buffer, cbPlainText
	pbPlainText = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbPlainText + 1);
	if (pbPlainText == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptDecrypt again with pbPlainText as the output buffer
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptDecrypt(hKeyHandle, (PUCHAR)pAes->pCipherText, (ULONG)pAes->dwCipherSize, NULL, ivCopy, IVSIZE, pbPlainText, cbPlainText, &cbResult, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptDecrypt[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Clean up
_EndOfFunc:
	if (hKeyHandle)
		BCryptDestroyKey(hKeyHandle);
	if (hAlgorithm)
		BCryptCloseAlgorithmProvider(hAlgorithm, 0);
	if (pbKeyObject)
		HeapFree(GetProcessHeap(), 0, pbKeyObject);
	if (pbPlainText != NULL && bSTATE) {
		// if everything went well, we save pbPlainText and cbPlainText
		pAes->pPlainText = pbPlainText;
		pAes->dwPlainSize = cbPlainText;
	}
	else if (pbPlainText != NULL) {
		HeapFree(GetProcessHeap(), 0, pbPlainText);
	}
	return bSTATE;

}
// Wrapper function for InstallAesDecryption that make things easier
BOOL SimpleDecryption(IN PBYTE pCipherTextData, IN DWORD sCipherTextSize, IN PBYTE pKey, IN PBYTE pIv, OUT PBYTE* pPlainTextData, OUT DWORD* sPlainTextSize) {

	if (pCipherTextData == NULL || sCipherTextSize == 0 || pKey == NULL || pIv == NULL || pPlainTextData == NULL || sPlainTextSize == NULL)
		return FALSE;

	// Intializing the struct
	AES_CTX Aes = {
		NULL,               // pPlainText
		0,                  // dwPlainSize
		pCipherTextData,    // pCipherText
		sCipherTextSize,    // dwCipherSize
		pKey,               // pKey
		pIv                 // pIv
	};

	if (!InstallAesDecryption(&Aes)) {
		return FALSE;
	}

	// Saving output
	*pPlainTextData = Aes.pPlainText;
	*sPlainTextSize = Aes.dwPlainSize;

	return TRUE;
}

unsigned char pKey[] = {
		0xC7, 0x8F, 0x00, 0x46, 0xDD, 0x44, 0x3C, 0xB8, 0x4D, 0xE1, 0xD7, 0xCF, 0x8B, 0xE2, 0xEC, 0x8B,
		0xA6, 0xE2, 0x38, 0x05, 0x64, 0x61, 0xCD, 0x53, 0x94, 0xC4, 0x09, 0xAC, 0xA0, 0x9D, 0xC8, 0xCC
};

unsigned char pIv[] = {
		0xA2, 0x9E, 0x62, 0x18, 0x61, 0xF7, 0xD2, 0x98, 0xB2, 0xD1, 0x22, 0x56, 0xB3, 0x5E, 0x07, 0x3F
};

unsigned char CipherText[] = {
		0x32, 0x9D, 0xD4, 0xEB, 0x71, 0xDA, 0x7D, 0x29, 0x87, 0xD7, 0xAA, 0x45, 0xE4, 0xDC, 0xFA, 0x57,
		0x04, 0x02, 0x01, 0x98, 0xED, 0x84, 0x04, 0xAE, 0x84, 0x15, 0xA8, 0x38, 0x63, 0x14, 0x7A, 0x47,
		0x76, 0x4E, 0xA8, 0x19, 0xBD, 0x29, 0xB3, 0x65, 0x24, 0xB1, 0xD5, 0x0A, 0x13, 0x98, 0xD4, 0xFA,
		0x95, 0x52, 0xB9, 0x69, 0xF1, 0x29, 0xD4, 0xD4, 0xB3, 0x83, 0xB6, 0x12, 0x9F, 0xF5, 0x35, 0x10
};


int main() {

	// Defining two variables the output buffer and its respective size which will be used in SimpleDecryption
	PBYTE	pPlaintext = NULL;
	DWORD	dwPlainSize = 0;

	// Decrypting
	if (!SimpleDecryption(CipherText, sizeof(CipherText), pKey, pIv, &pPlaintext, &dwPlainSize)) {
		return -1;
	}

	// Printing the decrypted data to the screen in hex format
	PrintHexData("PlainText", pPlaintext, dwPlainSize);

	// this will print: "This is a plain text string, we'll try to encrypt/decrypt !"
	printf("Data: %.*s\n", (int)dwPlainSize, (char*)pPlaintext);

	// Clean up
	HeapFree(GetProcessHeap(), 0, pPlaintext);
	system("PAUSE");
	return 0;
}

bCrypt 库的缺点

使用上述方法实现 AES 加密的主要缺点之一是,加密 WinAPI 的使用会导致它们在二进制文件的导入地址表 (IAT) 中可见。安全解决方案可以通过扫描 IAT 来检测加密函数的使用,这可能表明存在恶意行为或引起怀疑。可以将 WinAPI 隐藏在 IAT 中。

下图展示了使用 Windows API 进行 AES 加密的二进制文件的 IAT(接口架构测试)。可以清晰地看到 crypt.dll 库和加密函数的使用情况。

优化

现在的代码,每次使用时应该修改unsigned char pIv[16]

原因是:

  • 如果同一个 Key 搭配不同 IV,这是正常安全用法
  • 如果同一个 Key 和同一个 IV 被重复使用,CBC 会泄露明文模式,安全性变差
  • IV 不需要保密,但必须唯一/随机,通常和密文一起保存或传输
  • 还有一个安全点建议改掉:不要用 rand() 生成 Key/IV,应该用 BCryptGenRandom。rand() 不适合密码学。

更正确的方式:

  • Key 固定
  • 每次加密自动生成新 IV,把 IV + CipherText 一起输出
  • 改用 BCryptGenRandom 生成随机数
c 复制代码
#include <windows.h>
#include <bcrypt.h>
#include <stdio.h>
#pragma comment(lib, "bcrypt.lib")

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

#define KEYSIZE 32
#define IVSIZE  16

typedef struct _AES_CTX {

	PBYTE	pPlainText;         // base address of the plain text data 
	DWORD	dwPlainSize;        // size of the plain text data

	PBYTE	pCipherText;        // base address of the encrypted data
	DWORD	dwCipherSize;       // size of it (this can change from dwPlainSize in case there was padding)

	PBYTE	pKey;               // the 32 byte key
	PBYTE	pIv;                // the 16 byte iv

} AES_CTX, * PAES_CTX;
BOOL GenerateSecureRandomBytes(PBYTE pByte, SIZE_T sSize) {

	return NT_SUCCESS(BCryptGenRandom(NULL, pByte, (ULONG)sSize, BCRYPT_USE_SYSTEM_PREFERRED_RNG));
}

VOID PrintHexData(LPCSTR Name, PBYTE Data, SIZE_T Size)
{
	printf("unsigned char %s[] = {", Name);

	for (int i = 0; i < Size; i++) {
		if (i % 16 == 0)
			printf("\n\t");

		if (i < Size - 1)
		{
			printf("0x%0.2X, ", Data[i]);
		}
		else
		{
			printf("0x%0.2X ", Data[i]);
		}
	}

	printf("\n};\n\n");

}
// The encryption implementation
BOOL InstallAesEncryption(PAES_CTX pAes) {

	BOOL                  bSTATE = TRUE;
	BCRYPT_ALG_HANDLE     hAlgorithm = NULL;
	BCRYPT_KEY_HANDLE     hKeyHandle = NULL;
	NTSTATUS              STATUS = 0;

	ULONG       		cbResult = 0;
	DWORD       		dwBlockSize = 0;

	DWORD       		cbKeyObject = 0;
	PBYTE       		pbKeyObject = NULL;

	PBYTE      		pbCipherText = NULL;
	DWORD       		cbCipherText = 0;
	BYTE                  ivCopy[IVSIZE] = { 0 };


	// Intializing "hAlgorithm" as AES algorithm Handle
	STATUS = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptOpenAlgorithmProvider Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the key object variable pbKeyObject. This is used by the BCryptGenerateSymmetricKey function later 
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the block used in the encryption. Since this is AES it must be 16 bytes.
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_BLOCK_LENGTH, (PBYTE)&dwBlockSize, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Checking if block size is 16 bytes
	if (dwBlockSize != 16) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating memory for the key object 
	pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
	if (pbKeyObject == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Setting Block Cipher Mode to CBC. This uses a 32 byte key and a 16 byte IV.
	STATUS = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptSetProperty Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Generating the key object from the AES key "pAes->pKey". The output will be saved in pbKeyObject and will be of size cbKeyObject 
	STATUS = BCryptGenerateSymmetricKey(hAlgorithm, &hKeyHandle, pbKeyObject, cbKeyObject, (PBYTE)pAes->pKey, KEYSIZE, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGenerateSymmetricKey Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptEncrypt first time with NULL output parameters to retrieve the size of the output buffer which is saved in cbCipherText
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptEncrypt(hKeyHandle, (PUCHAR)pAes->pPlainText, (ULONG)pAes->dwPlainSize, NULL, ivCopy, IVSIZE, NULL, 0, &cbCipherText, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptEncrypt[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating enough memory for the output buffer, cbCipherText
	pbCipherText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbCipherText);
	if (pbCipherText == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptEncrypt again with pbCipherText as the output buffer
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptEncrypt(hKeyHandle, (PUCHAR)pAes->pPlainText, (ULONG)pAes->dwPlainSize, NULL, ivCopy, IVSIZE, pbCipherText, cbCipherText, &cbResult, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptEncrypt[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}


	// Clean up
_EndOfFunc:
	if (hKeyHandle)
		BCryptDestroyKey(hKeyHandle);
	if (hAlgorithm)
		BCryptCloseAlgorithmProvider(hAlgorithm, 0);
	if (pbKeyObject)
		HeapFree(GetProcessHeap(), 0, pbKeyObject);
	if (pbCipherText != NULL && bSTATE) {
		// If everything worked, save pbCipherText and cbCipherText 
		pAes->pCipherText = pbCipherText;
		pAes->dwCipherSize = cbCipherText;
	}
	else if (pbCipherText != NULL) {
		HeapFree(GetProcessHeap(), 0, pbCipherText);
	}
	return bSTATE;
}

// Wrapper function for InstallAesEncryption that makes things easier
BOOL SimpleEncryption(IN PBYTE pPlainTextData, IN DWORD sPlainTextSize, IN PBYTE pKey, IN PBYTE pIv, OUT PBYTE* pCipherTextData, OUT DWORD* sCipherTextSize) {

	if (pPlainTextData == NULL || sPlainTextSize == 0 || pKey == NULL || pIv == NULL || pCipherTextData == NULL || sCipherTextSize == NULL)
		return FALSE;

	// Intializing the struct
	AES_CTX Aes = {
		pPlainTextData,     // pPlainText
		sPlainTextSize,     // dwPlainSize
		NULL,               // pCipherText
		0,                  // dwCipherSize
		pKey,               // pKey
		pIv                 // pIv
	};

	if (!InstallAesEncryption(&Aes)) {
		return FALSE;
	}

	// Saving output
	*pCipherTextData = Aes.pCipherText;
	*sCipherTextSize = Aes.dwCipherSize;

	return TRUE;
}

BOOL SimpleEncryptionWithIvPrefix(IN PBYTE pPlainTextData, IN DWORD sPlainTextSize, IN PBYTE pKey, OUT PBYTE* pEncryptedData, OUT DWORD* sEncryptedSize) {

	BOOL  bState = FALSE;
	BYTE  iv[IVSIZE] = { 0 };
	PBYTE pCipherText = NULL;
	DWORD dwCipherSize = 0;
	PBYTE pOutput = NULL;

	if (pPlainTextData == NULL || sPlainTextSize == 0 || pKey == NULL || pEncryptedData == NULL || sEncryptedSize == NULL)
		return FALSE;

	if (!GenerateSecureRandomBytes(iv, sizeof(iv))) {
		printf("[!] BCryptGenRandom Failed\n");
		return FALSE;
	}

	if (!SimpleEncryption(pPlainTextData, sPlainTextSize, pKey, iv, &pCipherText, &dwCipherSize))
		goto _EndOfFunc;

	pOutput = (PBYTE)HeapAlloc(GetProcessHeap(), 0, IVSIZE + dwCipherSize);
	if (pOutput == NULL)
		goto _EndOfFunc;

	CopyMemory(pOutput, iv, IVSIZE);
	CopyMemory(pOutput + IVSIZE, pCipherText, dwCipherSize);

	*pEncryptedData = pOutput;
	*sEncryptedSize = IVSIZE + dwCipherSize;
	pOutput = NULL;
	bState = TRUE;

_EndOfFunc:
	if (pCipherText != NULL)
		HeapFree(GetProcessHeap(), 0, pCipherText);
	if (pOutput != NULL)
		HeapFree(GetProcessHeap(), 0, pOutput);
	return bState;
}
// The plaintext, in hex format, that will be encrypted
// this is the following string in hex "This is a plain text string, we'll try to encrypt/decrypt !"
unsigned char Data[] = {
	0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x70, 0x6C,
	0x61, 0x69, 0x6E, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x73, 0x74, 0x72,
	0x69, 0x6E, 0x67, 0x2C, 0x20, 0x77, 0x65, 0x27, 0x6C, 0x6C, 0x20, 0x74,
	0x72, 0x79, 0x20, 0x74, 0x6F, 0x20, 0x65, 0x6E, 0x63, 0x72, 0x79, 0x70,
	0x74, 0x2F, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x21
};

// The decryption implementation
BOOL InstallAesDecryption(PAES_CTX pAes) {

	BOOL                  bSTATE = TRUE;
	BCRYPT_ALG_HANDLE     hAlgorithm = NULL;
	BCRYPT_KEY_HANDLE     hKeyHandle = NULL;
	NTSTATUS              STATUS = 0;

	ULONG                 cbResult = 0;
	DWORD                 dwBlockSize = 0;

	DWORD                 cbKeyObject = 0;
	PBYTE                 pbKeyObject = NULL;

	PBYTE                 pbPlainText = NULL;
	DWORD                 cbPlainText = 0;
	BYTE                  ivCopy[IVSIZE] = { 0 };

	// Intializing "hAlgorithm" as AES algorithm Handle
	STATUS = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptOpenAlgorithmProvider Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the key object variable pbKeyObject. This is used by the BCryptGenerateSymmetricKey function later
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Getting the size of the block used in the encryption. Since this is AES it should be 16 bytes.
	STATUS = BCryptGetProperty(hAlgorithm, BCRYPT_BLOCK_LENGTH, (PBYTE)&dwBlockSize, sizeof(DWORD), &cbResult, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGetProperty[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Checking if block size is 16 bytes
	if (dwBlockSize != 16) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating memory for the key object 
	pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
	if (pbKeyObject == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Setting Block Cipher Mode to CBC. This uses a 32 byte key and a 16 byte IV.
	STATUS = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptSetProperty Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Generating the key object from the AES key "pAes->pKey". The output will be saved in pbKeyObject of size cbKeyObject 
	STATUS = BCryptGenerateSymmetricKey(hAlgorithm, &hKeyHandle, pbKeyObject, cbKeyObject, (PBYTE)pAes->pKey, KEYSIZE, 0);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptGenerateSymmetricKey Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptDecrypt first time with NULL output parameters to retrieve the size of the output buffer which is saved in cbPlainText
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptDecrypt(hKeyHandle, (PUCHAR)pAes->pCipherText, (ULONG)pAes->dwCipherSize, NULL, ivCopy, IVSIZE, NULL, 0, &cbPlainText, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptDecrypt[1] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Allocating enough memory for the output buffer, cbPlainText
	pbPlainText = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbPlainText + 1);
	if (pbPlainText == NULL) {
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Running BCryptDecrypt again with pbPlainText as the output buffer
	CopyMemory(ivCopy, pAes->pIv, IVSIZE);
	STATUS = BCryptDecrypt(hKeyHandle, (PUCHAR)pAes->pCipherText, (ULONG)pAes->dwCipherSize, NULL, ivCopy, IVSIZE, pbPlainText, cbPlainText, &cbResult, BCRYPT_BLOCK_PADDING);
	if (!NT_SUCCESS(STATUS)) {
		printf("[!] BCryptDecrypt[2] Failed With Error: 0x%0.8X \n", STATUS);
		bSTATE = FALSE; goto _EndOfFunc;
	}

	// Clean up
_EndOfFunc:
	if (hKeyHandle)
		BCryptDestroyKey(hKeyHandle);
	if (hAlgorithm)
		BCryptCloseAlgorithmProvider(hAlgorithm, 0);
	if (pbKeyObject)
		HeapFree(GetProcessHeap(), 0, pbKeyObject);
	if (pbPlainText != NULL && bSTATE) {
		// if everything went well, we save pbPlainText and cbPlainText
		pAes->pPlainText = pbPlainText;
		pAes->dwPlainSize = cbPlainText;
	}
	else if (pbPlainText != NULL) {
		HeapFree(GetProcessHeap(), 0, pbPlainText);
	}
	return bSTATE;

}
// Wrapper function for InstallAesDecryption that make things easier
BOOL SimpleDecryption(IN PBYTE pCipherTextData, IN DWORD sCipherTextSize, IN PBYTE pKey, IN PBYTE pIv, OUT PBYTE* pPlainTextData, OUT DWORD* sPlainTextSize) {

	if (pCipherTextData == NULL || sCipherTextSize == 0 || pKey == NULL || pIv == NULL || pPlainTextData == NULL || sPlainTextSize == NULL)
		return FALSE;

	// Intializing the struct
	AES_CTX Aes = {
		NULL,               // pPlainText
		0,                  // dwPlainSize
		pCipherTextData,    // pCipherText
		sCipherTextSize,    // dwCipherSize
		pKey,               // pKey
		pIv                 // pIv
	};

	if (!InstallAesDecryption(&Aes)) {
		return FALSE;
	}

	// Saving output
	*pPlainTextData = Aes.pPlainText;
	*sPlainTextSize = Aes.dwPlainSize;

	return TRUE;
}

BOOL SimpleDecryptionWithIvPrefix(IN PBYTE pEncryptedData, IN DWORD sEncryptedSize, IN PBYTE pKey, OUT PBYTE* pPlainTextData, OUT DWORD* sPlainTextSize) {

	if (pEncryptedData == NULL || sEncryptedSize <= IVSIZE || pKey == NULL || pPlainTextData == NULL || sPlainTextSize == NULL)
		return FALSE;

	return SimpleDecryption(
		pEncryptedData + IVSIZE,
		sEncryptedSize - IVSIZE,
		pKey,
		pEncryptedData,
		pPlainTextData,
		sPlainTextSize
	);
}

unsigned char pKey[] = {
		0xA9, 0xE5, 0x2C, 0xD2, 0x1F, 0x6B, 0x1F, 0xA7, 0xD3, 0xFA, 0xB6, 0x5B, 0x8F, 0x80, 0x32, 0xF9,
		0xC0, 0x08, 0x88, 0x70, 0x2A, 0x96, 0xB7, 0x98, 0xDB, 0x52, 0x58, 0x70, 0x5D, 0xE9, 0x77, 0xAA
};

int main() {

	PBYTE pEncryptedBlob = NULL;
	DWORD dwEncryptedBlobSize = 0;
	PBYTE pPlaintext = NULL;
	DWORD dwPlainSize = 0;

	PrintHexData("pKey", pKey, KEYSIZE);

	if (!SimpleEncryptionWithIvPrefix(Data, sizeof(Data), pKey, &pEncryptedBlob, &dwEncryptedBlobSize)) {
		return -1;
	}

	// The encrypted blob layout is: [16-byte IV][cipher text bytes...]
	PrintHexData("IvAndCipherText", pEncryptedBlob, dwEncryptedBlobSize);
	PrintHexData("GeneratedIv", pEncryptedBlob, IVSIZE);
	PrintHexData("CipherText", pEncryptedBlob + IVSIZE, dwEncryptedBlobSize - IVSIZE);

	if (!SimpleDecryptionWithIvPrefix(pEncryptedBlob, dwEncryptedBlobSize, pKey, &pPlaintext, &dwPlainSize)) {
		HeapFree(GetProcessHeap(), 0, pEncryptedBlob);
		return -1;
	}

	PrintHexData("PlainText", pPlaintext, dwPlainSize);
	printf("Data: %.*s\n", (int)dwPlainSize, (char*)pPlaintext);

	HeapFree(GetProcessHeap(), 0, pEncryptedBlob);
	HeapFree(GetProcessHeap(), 0, pPlaintext);
	system("PAUSE");
	return 0;
}

使用 Tiny-AES 库的 AES

本节使用了第三方加密库 tiny-AES-c,该库无需使用 WinAPI 即可执行 AES 加密。tiny-AES-c 是一个小型可移植库,可在 C 语言中执行 AES128/192/256 加密。

https://github.com/kokke/tiny-AES-c

设置 Tiny-AES

要开始使用 Tiny-AES,需要满足两个条件:

  1. 在项目中包含 aes.hpp(C++)或 aes.h(C)。
  2. 将 aes.c 文件添加到项目中。

Tiny-AES 库的缺点

在深入研究代码之前,了解 Tiny-AES 库的缺点至关重要。

  1. 该库不支持填充。所有缓冲区的大小必须是 16 字节的倍数。

  2. 库中使用的数组可以被杀软通过二进制扫描检测到,从而检测到 Tiny-AES 的使用。这些数组用于应用 AES 算法,因此无法删减,但是我们可以避免这些数组以明文的形式出现在代码中,例如,通过对这些数组进行异或运算,运行时再进行解密还原,这样杀软在静态分析时看到的就是异或后的密文,但是实际运行时再异或回去确保AES能够正常工作,把"可被检测的静态特征" → 转换成"运行时才还原的数据"

自定义填充

可以通过创建自定义填充函数来解决缺少填充支持的问题,如下面的代码片段所示。

c 复制代码
BOOL PaddBuffer(IN PBYTE InputBuffer, IN SIZE_T InputBufferSize, OUT PBYTE* OutputPaddedBuffer, OUT SIZE_T* OutputPaddedSize) {

	PBYTE	PaddedBuffer        = NULL;
	SIZE_T	PaddedSize          = NULL;

	// calculate the nearest number that is multiple of 16 and saving it to PaddedSize
	PaddedSize = InputBufferSize + 16 - (InputBufferSize % 16);
	// allocating buffer of size "PaddedSize"
	PaddedBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, PaddedSize);
	if (!PaddedBuffer){
		return FALSE;
	}
	// cleaning the allocated buffer
	ZeroMemory(PaddedBuffer, PaddedSize);
	// copying old buffer to new padded buffer
	memcpy(PaddedBuffer, InputBuffer, InputBufferSize);
	//saving results :
	*OutputPaddedBuffer = PaddedBuffer;
	*OutputPaddedSize   = PaddedSize;

	return TRUE;
}

Tiny-AES Encryption

与本模块前面解释的 bCrypt 库的加密和解密过程类似,下面的代码片段解释了 Tiny-AES 的加密和解密过程。

c 复制代码
#include <windows.h>
#include <bcrypt.h>
#include <stdio.h>
#include <ctime>
#include <stdlib.h>
#include "aes.hpp"

#define KEYSIZE 32
#define IVSIZE 16
// "this is plaintext string, we'll try to encrypt... lets hope everything goes well :)" in hex
// since the upper string is 82 byte in size, and 82 is not mulitple of 16, we cant encrypt this directly using tiny-aes
unsigned char Data[] = {
	0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x70, 0x6C, 0x61, 0x6E,
	0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x73, 0x74, 0x69, 0x6E, 0x67,
	0x2C, 0x20, 0x77, 0x65, 0x27, 0x6C, 0x6C, 0x20, 0x74, 0x72, 0x79, 0x20,
	0x74, 0x6F, 0x20, 0x65, 0x6E, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2E, 0x2E,
	0x2E, 0x20, 0x6C, 0x65, 0x74, 0x73, 0x20, 0x68, 0x6F, 0x70, 0x65, 0x20,
	0x65, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x67, 0x6E, 0x20, 0x67,
	0x6F, 0x20, 0x77, 0x65, 0x6C, 0x6C, 0x20, 0x3A, 0x29, 0x00
};

int main() {
	// struct needed for Tiny-AES library
	struct AES_ctx ctx;

	BYTE pKey[KEYSIZE];                             // KEYSIZE is 32 bytes
	BYTE pIv[IVSIZE];                                // IVSIZE is 16 bytes

	srand(time(NULL));                              // the seed to generate the key
	GenerateRandomBytes(pKey, KEYSIZE);             // generating the key bytes

	srand(time(NULL) ^ pKey[0]);                    // The seed to generate the IV. Use the first byte of the key to add more randomness.
	GenerateRandomBytes(pIv, IVSIZE);               // Generating the IV

	// Prints both key and IV to the console
	PrintHexData("pKey", pKey, KEYSIZE);
	PrintHexData("pIv", pIv, IVSIZE);

	// Initializing the Tiny-AES Library
	AES_init_ctx_iv(&ctx, pKey, pIv);

	// Initializing variables that will hold the new buffer base address in the case where padding is required and its size
	PBYTE	PaddedBuffer = NULL;
	SIZE_T	PAddedSize = NULL;

	// Padding the buffer, if required
	if (sizeof(Data) % 16 != 0) {
		PaddBuffer(Data, sizeof(Data), &PaddedBuffer, &PAddedSize);
		// Encrypting the padded buffer instead
		AES_CBC_encrypt_buffer(&ctx, PaddedBuffer, PAddedSize);
		// Printing the encrypted buffer to the console
		PrintHexData("CipherText", PaddedBuffer, PAddedSize);
	}
	// No padding is required, encrypt 'Data' directly
	else {
		AES_CBC_encrypt_buffer(&ctx, Data, sizeof(Data));
		// Printing the encrypted buffer to the console
		PrintHexData("CipherText", Data, sizeof(Data));
	}
	// Freeing PaddedBuffer, if necessary
	if (PaddedBuffer != NULL) {
		HeapFree(GetProcessHeap(), 0, PaddedBuffer);
	}
	system("PAUSE");
	return 0;
}

assets/image-20260428155730-wacuo2p.png

Tiny-AES 解密

c 复制代码
#include <windows.h>
#include <bcrypt.h>
#include <stdio.h>
#include <ctime>
#include <stdlib.h>
#include "aes.hpp"

unsigned char pKey[] = {
		0x81, 0x40, 0x1A, 0xF8, 0x0B, 0xFE, 0x10, 0xC6, 0xD4, 0x4A, 0x77, 0xF4, 0x5C, 0xB6, 0x65, 0xD5,
		0x00, 0x3F, 0xB5, 0x63, 0x1A, 0xDD, 0x4E, 0x79, 0x6F, 0x59, 0xA9, 0x43, 0xCB, 0x5F, 0x8D, 0xF2
};

unsigned char pIv[] = {
		0x20, 0x78, 0x68, 0xA1, 0x0A, 0x05, 0x21, 0x92, 0x20, 0x45, 0x17, 0xCE, 0x51, 0x3D, 0xC9, 0x27
};

unsigned char CipherText[] = {
		0xCF, 0xE5, 0xC9, 0x46, 0xB8, 0x58, 0xDB, 0x4A, 0xB8, 0x5C, 0xED, 0x8D, 0x85, 0x64, 0x2A, 0x01,
		0xB8, 0xA5, 0x83, 0x1B, 0xC6, 0xE1, 0x74, 0x35, 0x37, 0x5D, 0xC1, 0x1A, 0x7A, 0x83, 0xBE, 0xC9,
		0xC0, 0x53, 0x78, 0x90, 0xF0, 0x8C, 0xC6, 0xE7, 0x64, 0xE3, 0x17, 0x28, 0xA4, 0x81, 0x67, 0x32,
		0x05, 0x75, 0x92, 0xAC, 0x0B, 0xD7, 0x03, 0x52, 0x99, 0xA6, 0x80, 0xAF, 0xFF, 0x3B, 0x88, 0x0B,
		0x76, 0xDA, 0x4C, 0x37, 0xE4, 0xDD, 0x6E, 0x1C, 0xA7, 0xD8, 0xDB, 0xC1, 0xDA, 0x8E, 0x19, 0xA7,
		0x77, 0xD0, 0x47, 0x6E, 0x59, 0x25, 0xF7, 0xA3, 0xEA, 0xB0, 0xD1, 0x69, 0x55, 0xAE, 0xD4, 0xBE
};

int main() {

	// Struct needed for Tiny-AES library
	struct AES_ctx ctx;
	// Initializing the Tiny-AES Library
	AES_init_ctx_iv(&ctx, pKey, pIv);

	// Decrypting
	AES_CBC_decrypt_buffer(&ctx, CipherText, sizeof(CipherText));

	// Print the decrypted buffer to the console
	PrintHexData("PlainText", CipherText, sizeof(CipherText));

	// Print the string
	printf("Data: %s \n", CipherText);

	// exit
	system("PAUSE");
	return 0;
}

assets/image-20260428160136-y1og68f.png

Tiny-AES IAT

下图显示了一个二进制文件的 IAT(接口附加文本),该二进制文件使用 Tiny-AES 而非 WinAPI 进行加密。在该二进制文件的 IAT 中,看不到任何加密函数。

完整代码

c 复制代码
#include <windows.h>
#include <bcrypt.h>
#include <stdio.h>
#include <ctime>
#include <stdlib.h>
#include "aes.hpp"

#define KEYSIZE 32
#define IVSIZE 16

VOID PrintHexData(LPCSTR Name, PBYTE Data, SIZE_T Size)
{
	printf("unsigned char %s[] = {", Name);

	for (int i = 0; i < Size; i++) {
		if (i % 16 == 0)
			printf("\n\t");

		if (i < Size - 1)
		{
			printf("0x%0.2X, ", Data[i]);
		}
		else
		{
			printf("0x%0.2X ", Data[i]);
		}
	}

	printf("\n};\n\n");
}

// Generate random bytes of size sSize
VOID GenerateRandomBytes(PBYTE pByte, SIZE_T sSize) {

	for (int i = 0; i < sSize; i++)
	{
		pByte[i] = (BYTE)rand() % 0xFF;
	}
}

BOOL PaddBuffer(IN PBYTE InputBuffer, IN SIZE_T InputBufferSize, OUT PBYTE* OutputPaddedBuffer, OUT SIZE_T* OutputPaddedSize) {

	PBYTE	PaddedBuffer = NULL;
	SIZE_T	PaddedSize = NULL;

	// calculate the nearest number that is multiple of 16 and saving it to PaddedSize
	PaddedSize = InputBufferSize + 16 - (InputBufferSize % 16);
	// allocating buffer of size "PaddedSize"
	PaddedBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, PaddedSize);
	if (!PaddedBuffer) {
		return FALSE;
	}
	// cleaning the allocated buffer
	ZeroMemory(PaddedBuffer, PaddedSize);
	// copying old buffer to new padded buffer
	memcpy(PaddedBuffer, InputBuffer, InputBufferSize);
	//saving results :
	*OutputPaddedBuffer = PaddedBuffer;
	*OutputPaddedSize = PaddedSize;

	return TRUE;
}

// "this is plaintext string, we'll try to encrypt... lets hope everything goes well :)" in hex
// since the upper string is 82 byte in size, and 82 is not mulitple of 16, we cant encrypt this directly using tiny-aes
unsigned char Data[] = {
	0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x70, 0x6C, 0x61, 0x6E,
	0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x73, 0x74, 0x69, 0x6E, 0x67,
	0x2C, 0x20, 0x77, 0x65, 0x27, 0x6C, 0x6C, 0x20, 0x74, 0x72, 0x79, 0x20,
	0x74, 0x6F, 0x20, 0x65, 0x6E, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2E, 0x2E,
	0x2E, 0x20, 0x6C, 0x65, 0x74, 0x73, 0x20, 0x68, 0x6F, 0x70, 0x65, 0x20,
	0x65, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x67, 0x6E, 0x20, 0x67,
	0x6F, 0x20, 0x77, 0x65, 0x6C, 0x6C, 0x20, 0x3A, 0x29, 0x00
};

//int main() {
//	// struct needed for Tiny-AES library
//	struct AES_ctx ctx;
//
//	BYTE pKey[KEYSIZE];                             // KEYSIZE is 32 bytes
//	BYTE pIv[IVSIZE];                                // IVSIZE is 16 bytes
//
//	srand(time(NULL));                              // the seed to generate the key
//	GenerateRandomBytes(pKey, KEYSIZE);             // generating the key bytes
//
//	srand(time(NULL) ^ pKey[0]);                    // The seed to generate the IV. Use the first byte of the key to add more randomness.
//	GenerateRandomBytes(pIv, IVSIZE);               // Generating the IV
//
//	// Prints both key and IV to the console
//	PrintHexData("pKey", pKey, KEYSIZE);
//	PrintHexData("pIv", pIv, IVSIZE);
//
//	// Initializing the Tiny-AES Library
//	AES_init_ctx_iv(&ctx, pKey, pIv);
//
//	// Initializing variables that will hold the new buffer base address in the case where padding is required and its size
//	PBYTE	PaddedBuffer = NULL;
//	SIZE_T	PAddedSize = NULL;
//
//	// Padding the buffer, if required
//	if (sizeof(Data) % 16 != 0) {
//		PaddBuffer(Data, sizeof(Data), &PaddedBuffer, &PAddedSize);
//		// Encrypting the padded buffer instead
//		AES_CBC_encrypt_buffer(&ctx, PaddedBuffer, PAddedSize);
//		// Printing the encrypted buffer to the console
//		PrintHexData("CipherText", PaddedBuffer, PAddedSize);
//	}
//	// No padding is required, encrypt 'Data' directly
//	else {
//		AES_CBC_encrypt_buffer(&ctx, Data, sizeof(Data));
//		// Printing the encrypted buffer to the console
//		PrintHexData("CipherText", Data, sizeof(Data));
//	}
//	// Freeing PaddedBuffer, if necessary
//	if (PaddedBuffer != NULL) {
//		HeapFree(GetProcessHeap(), 0, PaddedBuffer);
//	}
//	system("PAUSE");
//	return 0;
//}



unsigned char pKey[] = {
		0x81, 0x40, 0x1A, 0xF8, 0x0B, 0xFE, 0x10, 0xC6, 0xD4, 0x4A, 0x77, 0xF4, 0x5C, 0xB6, 0x65, 0xD5,
		0x00, 0x3F, 0xB5, 0x63, 0x1A, 0xDD, 0x4E, 0x79, 0x6F, 0x59, 0xA9, 0x43, 0xCB, 0x5F, 0x8D, 0xF2
};

unsigned char pIv[] = {
		0x20, 0x78, 0x68, 0xA1, 0x0A, 0x05, 0x21, 0x92, 0x20, 0x45, 0x17, 0xCE, 0x51, 0x3D, 0xC9, 0x27
};

unsigned char CipherText[] = {
		0xCF, 0xE5, 0xC9, 0x46, 0xB8, 0x58, 0xDB, 0x4A, 0xB8, 0x5C, 0xED, 0x8D, 0x85, 0x64, 0x2A, 0x01,
		0xB8, 0xA5, 0x83, 0x1B, 0xC6, 0xE1, 0x74, 0x35, 0x37, 0x5D, 0xC1, 0x1A, 0x7A, 0x83, 0xBE, 0xC9,
		0xC0, 0x53, 0x78, 0x90, 0xF0, 0x8C, 0xC6, 0xE7, 0x64, 0xE3, 0x17, 0x28, 0xA4, 0x81, 0x67, 0x32,
		0x05, 0x75, 0x92, 0xAC, 0x0B, 0xD7, 0x03, 0x52, 0x99, 0xA6, 0x80, 0xAF, 0xFF, 0x3B, 0x88, 0x0B,
		0x76, 0xDA, 0x4C, 0x37, 0xE4, 0xDD, 0x6E, 0x1C, 0xA7, 0xD8, 0xDB, 0xC1, 0xDA, 0x8E, 0x19, 0xA7,
		0x77, 0xD0, 0x47, 0x6E, 0x59, 0x25, 0xF7, 0xA3, 0xEA, 0xB0, 0xD1, 0x69, 0x55, 0xAE, 0xD4, 0xBE
};

int main() {

	// Struct needed for Tiny-AES library
	struct AES_ctx ctx;
	// Initializing the Tiny-AES Library
	AES_init_ctx_iv(&ctx, pKey, pIv);

	// Decrypting
	AES_CBC_decrypt_buffer(&ctx, CipherText, sizeof(CipherText));

	// Print the decrypted buffer to the console
	PrintHexData("PlainText", CipherText, sizeof(CipherText));

	// Print the string
	printf("Data: %s \n", CipherText);

	// exit
	system("PAUSE");
	return 0;
}

优化

针对被杀软静态扫描的情况,对sbox、rsbox、Rcon进行了xor,只需要替换aes.c文件即可

c 复制代码
/*

This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.

The implementation is verified against the test vectors in:
  National Institute of Standards and Technology Special Publication 800-38A 2001 ED

ECB-AES128
----------

  plain-text:
    6bc1bee22e409f96e93d7e117393172a
    ae2d8a571e03ac9c9eb76fac45af8e51
    30c81c46a35ce411e5fbc1191a0a52ef
    f69f2445df4f9b17ad2b417be66c3710

  key:
    2b7e151628aed2a6abf7158809cf4f3c

  resulting cipher
    3ad77bb40d7a3660a89ecaf32466ef97
    f5d3d58503b9699de785895a96fdbaaf
    43b1cd7f598ece23881b00e3ed030688
    7b0c785e27e8ad3f8223207104725dd4


NOTE:   String length must be evenly divisible by 16byte (str_len % 16 == 0)
        You should pad the end of the string with zeros if this is not the case.
        For AES192/256 the key size is proportionally larger.

*/


/*****************************************************************************/
/* Includes:                                                                 */
/*****************************************************************************/
#include <stdint.h>
#include <string.h> // CBC mode, for memset
#include "aes.h"

/*****************************************************************************/
/* Defines:                                                                  */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4

#if defined(AES256) && (AES256 == 1)
#define Nk 8
#define Nr 14
#elif defined(AES192) && (AES192 == 1)
#define Nk 6
#define Nr 12
#else
#define Nk 4        // The number of 32 bit words in a key.
#define Nr 10       // The number of rounds in AES Cipher.
#endif

// jcallan@github points out that declaring Multiply as a function 
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif

#ifndef XOR_KEY
#define XOR_KEY 0x5A
#endif



/*****************************************************************************/
/* Private variables:                                                        */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];



// The lookup-tables stay in read-only storage, but the values below are stored
// XOR-obfuscated and are unmasked on access with (XOR_KEY + index).
static const uint8_t sbox[256] = {
    //0     1    2      3     4    5     6     7      8    9     A      B    C     D     E     F
    0x39, 0x27, 0x2b, 0x26, 0xac, 0x34, 0x0f, 0xa4, 0x52, 0x62, 0x03, 0x4e, 0x98, 0xb0, 0xc3, 0x1f,
    0xa0, 0xe9, 0xa5, 0x10, 0x94, 0x36, 0x37, 0x81, 0xdf, 0xa7, 0xd6, 0xda, 0xea, 0xd3, 0x0a, 0xb9,
    0xcd, 0x86, 0xef, 0x5b, 0x48, 0x40, 0x77, 0x4d, 0xb6, 0x26, 0x61, 0x74, 0xf7, 0x5f, 0xb9, 0x9c,
    0x8e, 0x4c, 0xaf, 0x4e, 0x96, 0x19, 0x95, 0x0b, 0x95, 0x81, 0x14, 0x77, 0x7d, 0xb0, 0x2a, 0xec,
    0x93, 0x18, 0xb0, 0x87, 0x85, 0xf1, 0xfa, 0x01, 0xf0, 0x98, 0x72, 0x16, 0x8f, 0x44, 0x87, 0x2d,
    0xf9, 0x7a, 0xac, 0x40, 0x8e, 0x53, 0x01, 0xea, 0xd8, 0x78, 0x0a, 0x8c, 0xfc, 0xfb, 0xe0, 0x76,
    0x6a, 0x54, 0x16, 0x46, 0xfd, 0xf2, 0xf3, 0x44, 0x87, 0x3a, 0xc6, 0xba, 0x96, 0xfb, 0x57, 0x61,
    0x9b, 0x68, 0x8c, 0x42, 0x5c, 0x52, 0xe8, 0x24, 0x6e, 0x65, 0x0e, 0xf4, 0xc6, 0x28, 0x2b, 0x0b,
    0x17, 0xd7, 0xcf, 0x31, 0x81, 0x48, 0xa4, 0xf6, 0x26, 0x44, 0x9a, 0xd8, 0x82, 0xba, 0xf1, 0x9a,
    0x8a, 0x6a, 0xa3, 0x31, 0xcc, 0xc5, 0x60, 0x79, 0xb4, 0x1d, 0x4c, 0xe1, 0x28, 0xa9, 0xf3, 0x22,
    0x1a, 0xc9, 0xc6, 0xf7, 0xb7, 0xf9, 0x24, 0x5d, 0xc0, 0xd0, 0xa8, 0x67, 0x97, 0x92, 0xec, 0x70,
    0xed, 0xc3, 0x3b, 0x60, 0x83, 0xda, 0x5e, 0xb8, 0x7e, 0x45, 0xe0, 0xff, 0x73, 0x6d, 0xb6, 0x11,
    0xa0, 0x63, 0x39, 0x33, 0x02, 0xb9, 0x94, 0xe7, 0xca, 0xfe, 0x50, 0x3a, 0x6d, 0x9a, 0xa3, 0xa3,
    0x5a, 0x15, 0x99, 0x4b, 0x66, 0x2c, 0xc6, 0x3f, 0x53, 0x06, 0x63, 0x8c, 0xb0, 0xf6, 0x25, 0xa7,
    0xdb, 0xc3, 0xa4, 0x2c, 0x57, 0xe6, 0xce, 0xd5, 0xd9, 0x5d, 0xc3, 0xac, 0x88, 0x12, 0x60, 0x96,
    0xc6, 0xea, 0xc5, 0x40, 0xf1, 0xa9, 0x12, 0x39, 0x13, 0xca, 0x79, 0x5a, 0xe6, 0x03, 0xe3, 0x4f };

static const uint8_t rsbox[256] = {
  0x08, 0x52, 0x36, 0x88, 0x6e, 0x69, 0xc5, 0x59, 0xdd, 0x23, 0xc7, 0xfb, 0xe7, 0x94, 0xbf, 0x92,
  0x16, 0x88, 0x55, 0xef, 0xf5, 0x40, 0x8f, 0xf6, 0x46, 0xfd, 0x37, 0x31, 0xb2, 0xa9, 0x91, 0xb2,
  0x2e, 0x00, 0xe8, 0x4f, 0xd8, 0xbd, 0xa3, 0xbc, 0x6c, 0xcf, 0x11, 0x8e, 0xc4, 0x7d, 0x4b, 0xc7,
  0x82, 0xa5, 0x2d, 0xeb, 0xa6, 0x56, 0xb4, 0x23, 0xe4, 0xc8, 0x36, 0xdc, 0xfb, 0x1c, 0x49, 0xbc,
  0xe8, 0x63, 0x6a, 0xf9, 0x18, 0xf7, 0x38, 0xb7, 0x76, 0x07, 0xf8, 0x69, 0xfb, 0xc2, 0x1e, 0x3b,
  0xc6, 0xdb, 0xe4, 0xfd, 0x53, 0x42, 0x09, 0x6b, 0xec, 0xa6, 0xf2, 0xe2, 0x11, 0x3a, 0x25, 0x3d,
  0x2a, 0x63, 0x17, 0xbd, 0x32, 0x03, 0x13, 0xcb, 0x35, 0x27, 0x9c, 0xc0, 0x7e, 0x74, 0x8d, 0xcf,
  0x1a, 0xe7, 0xd2, 0x42, 0x04, 0xf0, 0xdf, 0xd3, 0x13, 0x7c, 0x69, 0xd6, 0xd7, 0xc4, 0x52, 0xb2,
  0xe0, 0x4a, 0xcd, 0x9c, 0x91, 0xb8, 0x3c, 0x0b, 0x75, 0x11, 0x2b, 0x2b, 0x16, 0x53, 0x0e, 0x9a,
  0x7c, 0x47, 0x98, 0xcf, 0x09, 0x42, 0xc5, 0x74, 0x10, 0x0a, 0xc3, 0x1d, 0xea, 0x82, 0x27, 0x97,
  0xbd, 0x0a, 0xe6, 0x8c, 0xe3, 0xd6, 0xc5, 0x88, 0x6d, 0xb4, 0x66, 0x0b, 0xac, 0x1f, 0xb6, 0x12,
  0xf6, 0x5d, 0x32, 0x46, 0xc8, 0xdd, 0x69, 0x31, 0x88, 0xc8, 0xd4, 0xeb, 0x6e, 0xda, 0x42, 0xed,
  0x05, 0xc6, 0xb4, 0x2e, 0x96, 0x18, 0xe7, 0x10, 0x93, 0x31, 0x34, 0x7c, 0x01, 0xa7, 0xc4, 0x76,
  0x4a, 0x7a, 0x53, 0x84, 0x37, 0x9a, 0x7a, 0x3c, 0x1f, 0xd6, 0x4e, 0xaa, 0xa5, 0xfe, 0xa4, 0xd6,
  0x9a, 0xdb, 0x07, 0x70, 0x90, 0x15, 0xb5, 0xf1, 0x8a, 0xa8, 0xff, 0x79, 0xc5, 0x14, 0xd1, 0x28,
  0x5d, 0x60, 0x48, 0x33, 0xf4, 0x38, 0x86, 0x77, 0xb3, 0x3a, 0x40, 0x36, 0x03, 0x76, 0x54, 0x24 };

// The round constant word array, Rcon[i], contains the values given by 
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
  0xd7, 0x5a, 0x5e, 0x59, 0x56, 0x4f, 0x40, 0x21, 0xe2, 0x78, 0x52 };

/*
 * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
 * that you can remove most of the elements in the Rcon array, because they are unused.
 *
 * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
 *
 * "Only the first some of these constants are actually used -- up to rcon[10] for AES-128 (as 11 round keys are needed),
 *  up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
 */


 /*****************************************************************************/
 /* Private functions:                                                        */
 /*****************************************************************************/
 /*
 static uint8_t getSBoxValue(uint8_t num)
 {
   return sbox[num];
 }
 */
static uint8_t getXorTableValue(const uint8_t* table, uint16_t index)
{
    return (uint8_t)(table[index] ^ (uint8_t)(XOR_KEY + index));
}

#define getSBoxValue(num) getXorTableValue(sbox, (num))
/*
static uint8_t getSBoxInvert(uint8_t num)
{
  return rsbox[num];
}
*/
#define getSBoxInvert(num) getXorTableValue(rsbox, (num))
#define getRconValue(num) getXorTableValue(Rcon, (num))

// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
    unsigned i, j, k;
    uint8_t tempa[4]; // Used for the column/row operations

    // The first round key is the key itself.
    for (i = 0; i < Nk; ++i)
    {
        RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
        RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
        RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
        RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
    }

    // All other round keys are found from the previous round keys.
    for (i = Nk; i < Nb * (Nr + 1); ++i)
    {
        {
            k = (i - 1) * 4;
            tempa[0] = RoundKey[k + 0];
            tempa[1] = RoundKey[k + 1];
            tempa[2] = RoundKey[k + 2];
            tempa[3] = RoundKey[k + 3];

        }

        if (i % Nk == 0)
        {
            // This function shifts the 4 bytes in a word to the left once.
            // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]

            // Function RotWord()
            {
                const uint8_t u8tmp = tempa[0];
                tempa[0] = tempa[1];
                tempa[1] = tempa[2];
                tempa[2] = tempa[3];
                tempa[3] = u8tmp;
            }

            // SubWord() is a function that takes a four-byte input word and 
            // applies the S-box to each of the four bytes to produce an output word.

            // Function Subword()
            {
                tempa[0] = getSBoxValue(tempa[0]);
                tempa[1] = getSBoxValue(tempa[1]);
                tempa[2] = getSBoxValue(tempa[2]);
                tempa[3] = getSBoxValue(tempa[3]);
            }

            tempa[0] = tempa[0] ^ getRconValue(i / Nk);
        }
#if defined(AES256) && (AES256 == 1)
        if (i % Nk == 4)
        {
            // Function Subword()
            {
                tempa[0] = getSBoxValue(tempa[0]);
                tempa[1] = getSBoxValue(tempa[1]);
                tempa[2] = getSBoxValue(tempa[2]);
                tempa[3] = getSBoxValue(tempa[3]);
            }
        }
#endif
        j = i * 4; k = (i - Nk) * 4;
        RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
        RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
        RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
        RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
    }
}

void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
    KeyExpansion(ctx->RoundKey, key);
}
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
    KeyExpansion(ctx->RoundKey, key);
    memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
    memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
#endif

// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, uint8_t* RoundKey)
{
    uint8_t i, j;
    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 4; ++j)
        {
            (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
        }
    }
}

// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state)
{
    uint8_t i, j;
    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 4; ++j)
        {
            (*state)[j][i] = getSBoxValue((*state)[j][i]);
        }
    }
}

// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state)
{
    uint8_t temp;

    // Rotate first row 1 columns to left  
    temp = (*state)[0][1];
    (*state)[0][1] = (*state)[1][1];
    (*state)[1][1] = (*state)[2][1];
    (*state)[2][1] = (*state)[3][1];
    (*state)[3][1] = temp;

    // Rotate second row 2 columns to left  
    temp = (*state)[0][2];
    (*state)[0][2] = (*state)[2][2];
    (*state)[2][2] = temp;

    temp = (*state)[1][2];
    (*state)[1][2] = (*state)[3][2];
    (*state)[3][2] = temp;

    // Rotate third row 3 columns to left
    temp = (*state)[0][3];
    (*state)[0][3] = (*state)[3][3];
    (*state)[3][3] = (*state)[2][3];
    (*state)[2][3] = (*state)[1][3];
    (*state)[1][3] = temp;
}

static uint8_t xtime(uint8_t x)
{
    return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}

// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
    uint8_t i;
    uint8_t Tmp, Tm, t;
    for (i = 0; i < 4; ++i)
    {
        t = (*state)[i][0];
        Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3];
        Tm = (*state)[i][0] ^ (*state)[i][1]; Tm = xtime(Tm);  (*state)[i][0] ^= Tm ^ Tmp;
        Tm = (*state)[i][1] ^ (*state)[i][2]; Tm = xtime(Tm);  (*state)[i][1] ^= Tm ^ Tmp;
        Tm = (*state)[i][2] ^ (*state)[i][3]; Tm = xtime(Tm);  (*state)[i][2] ^= Tm ^ Tmp;
        Tm = (*state)[i][3] ^ t;              Tm = xtime(Tm);  (*state)[i][3] ^= Tm ^ Tmp;
    }
}

// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
//       The compiler seems to be able to vectorize the operation better this way.
//       See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
    return (((y & 1) * x) ^
        ((y >> 1 & 1) * xtime(x)) ^
        ((y >> 2 & 1) * xtime(xtime(x))) ^
        ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
        ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y)                                \
      (  ((y & 1) * x) ^                              \
      ((y>>1 & 1) * xtime(x)) ^                       \
      ((y>>2 & 1) * xtime(xtime(x))) ^                \
      ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^         \
      ((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))))   \

#endif

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(state_t* state)
{
    int i;
    uint8_t a, b, c, d;
    for (i = 0; i < 4; ++i)
    {
        a = (*state)[i][0];
        b = (*state)[i][1];
        c = (*state)[i][2];
        d = (*state)[i][3];

        (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
        (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
        (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
        (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
    }
}


// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state)
{
    uint8_t i, j;
    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 4; ++j)
        {
            (*state)[j][i] = getSBoxInvert((*state)[j][i]);
        }
    }
}

static void InvShiftRows(state_t* state)
{
    uint8_t temp;

    // Rotate first row 1 columns to right  
    temp = (*state)[3][1];
    (*state)[3][1] = (*state)[2][1];
    (*state)[2][1] = (*state)[1][1];
    (*state)[1][1] = (*state)[0][1];
    (*state)[0][1] = temp;

    // Rotate second row 2 columns to right 
    temp = (*state)[0][2];
    (*state)[0][2] = (*state)[2][2];
    (*state)[2][2] = temp;

    temp = (*state)[1][2];
    (*state)[1][2] = (*state)[3][2];
    (*state)[3][2] = temp;

    // Rotate third row 3 columns to right
    temp = (*state)[0][3];
    (*state)[0][3] = (*state)[1][3];
    (*state)[1][3] = (*state)[2][3];
    (*state)[2][3] = (*state)[3][3];
    (*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)

// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, uint8_t* RoundKey)
{
    uint8_t round = 0;

    // Add the First round key to the state before starting the rounds.
    AddRoundKey(0, state, RoundKey);

    // There will be Nr rounds.
    // The first Nr-1 rounds are identical.
    // These Nr-1 rounds are executed in the loop below.
    for (round = 1; round < Nr; ++round)
    {
        SubBytes(state);
        ShiftRows(state);
        MixColumns(state);
        AddRoundKey(round, state, RoundKey);
    }

    // The last round is given below.
    // The MixColumns function is not here in the last round.
    SubBytes(state);
    ShiftRows(state);
    AddRoundKey(Nr, state, RoundKey);
}

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, uint8_t* RoundKey)
{
    uint8_t round = 0;

    // Add the First round key to the state before starting the rounds.
    AddRoundKey(Nr, state, RoundKey);

    // There will be Nr rounds.
    // The first Nr-1 rounds are identical.
    // These Nr-1 rounds are executed in the loop below.
    for (round = (Nr - 1); round > 0; --round)
    {
        InvShiftRows(state);
        InvSubBytes(state);
        AddRoundKey(round, state, RoundKey);
        InvMixColumns(state);
    }

    // The last round is given below.
    // The MixColumns function is not here in the last round.
    InvShiftRows(state);
    InvSubBytes(state);
    AddRoundKey(0, state, RoundKey);
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)

/*****************************************************************************/
/* Public functions:                                                         */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)


void AES_ECB_encrypt(struct AES_ctx* ctx, uint8_t* buf)
{
    // The next function call encrypts the PlainText with the Key using AES algorithm.
    Cipher((state_t*)buf, ctx->RoundKey);
}

void AES_ECB_decrypt(struct AES_ctx* ctx, uint8_t* buf)
{
    // The next function call decrypts the PlainText with the Key using AES algorithm.
    InvCipher((state_t*)buf, ctx->RoundKey);
}


#endif // #if defined(ECB) && (ECB == 1)





#if defined(CBC) && (CBC == 1)


static void XorWithIv(uint8_t* buf, uint8_t* Iv)
{
    uint8_t i;
    for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
    {
        buf[i] ^= Iv[i];
    }
}

void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
    uintptr_t i;
    uint8_t* Iv = ctx->Iv;
    for (i = 0; i < length; i += AES_BLOCKLEN)
    {
        XorWithIv(buf, Iv);
        Cipher((state_t*)buf, ctx->RoundKey);
        Iv = buf;
        buf += AES_BLOCKLEN;
        //printf("Step %d - %d", i/16, i);
    }
    /* store Iv in ctx for next call */
    memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}

void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
    uintptr_t i;
    uint8_t storeNextIv[AES_BLOCKLEN];
    for (i = 0; i < length; i += AES_BLOCKLEN)
    {
        memcpy(storeNextIv, buf, AES_BLOCKLEN);
        InvCipher((state_t*)buf, ctx->RoundKey);
        XorWithIv(buf, ctx->Iv);
        memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
        buf += AES_BLOCKLEN;
    }

}

#endif // #if defined(CBC) && (CBC == 1)



#if defined(CTR) && (CTR == 1)

/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
    uint8_t buffer[AES_BLOCKLEN];

    unsigned i;
    int bi;
    for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
    {
        if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
        {

            memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
            Cipher((state_t*)buffer, ctx->RoundKey);

            /* Increment Iv and handle overflow */
            for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
            {
                /* inc will owerflow */
                if (ctx->Iv[bi] == 255)
                {
                    ctx->Iv[bi] = 0;
                    continue;
                }
                ctx->Iv[bi] += 1;
                break;
            }
            bi = 0;
        }

        buf[i] = (buf[i] ^ buffer[bi]);
    }
}

#endif // #if defined(CTR) && (CTR == 1)
相关推荐
再玩一会儿看代码2 小时前
Java浅拷贝和深拷贝理解笔记
java·linux·开发语言·笔记·python·学习
love530love2 小时前
【笔记】ComfyUI 源码部署版更新后一键修复:从手动补丁到自动化工作流
运维·人工智能·windows·笔记·python·自动化·comfyui
星恒随风3 小时前
C++入门(一):第一个 C++ 程序、命名空间、输入输出和缺省参数
开发语言·c++·笔记·学习
数据皮皮侠AI3 小时前
中国土地利用驱动因子数据集(9种驱动因子/裁剪到省市/Tif)
大数据·人工智能·笔记·能源·1024程序员节
Cloud_Shy6183 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第三章 Item 17 - 20)
开发语言·笔记·python
三品吉他手会点灯3 小时前
C语言学习笔记 - 42.数据类型 - scanf函数深度解析
c语言·开发语言·笔记·学习
来生硬件工程师3 小时前
【51单片机学习笔记】:P01 STC89C52RC
笔记·学习·51单片机
中屹指纹浏览器3 小时前
2026浏览器本地缓存分区机制演进与沙箱缓存差异化运维探究
经验分享·笔记
憧憬成为web高手3 小时前
[0CTF 2016]piapiapia 脚本和总结
web安全·网络安全