简单的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 为加密操作提供了一个标准化的接口,使开发人员能够更轻松地在其应用程序中实现安全功能。nstallAesEncryption和 InstallAesDecryption函数都使用了 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;

}

附加辅助函数

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

第一个函数 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;
	}
}

填充

InstallAesEncryption 和 InstallAesDecryption 函数分别使用 BCryptEncrypt 和 BCryptDecrypt 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)
相关推荐
silvamux_1 小时前
10款模型写种草笔记:可发布性、返工来源与成本数据缺口复盘
java·数据库·笔记
蒸蒸yyyyzwd2 小时前
kafka、redis、mysql不熟悉问题总结
笔记·八股
hgy89692 小时前
深度学习(小白笔记)——迁徙
人工智能·笔记·深度学习
摇滚侠2 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 11
java·笔记·python
HackTwoHub3 小时前
双通道红队 C2,MCP 接入 AI,全自动渗透实战利器,具备自动提权、进程伪装、无文件上线能力,载荷 AES 加密
安全·web安全·网络安全·系统安全·密码学·可信计算技术·安全架构
前进的李工5 小时前
Agent 学习笔记 02:Harness 与工作流怎么理解
笔记·学习·agent·工作流·harness
海鸥-w7 小时前
数据库冗余字段设计笔记
java·数据库·笔记·oracle
滴啦嘟啦哒7 小时前
【具身笔记】【原理探究】一、对PI05源码的实践笔记
笔记·python·vla·具身
览讯冷却9 小时前
冷却塔噪声的健康危害与法规要求的技术梳理
经验分享·笔记·科技·其他·百度·制造·业界资讯
Dory_Youth9 小时前
电脑的搜索功能不能用,一搜就黑屏闪退
运维·笔记