前言
之前学习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,需要满足两个条件:
- 在项目中包含 aes.hpp(C++)或 aes.h(C)。
- 将 aes.c 文件添加到项目中。
Tiny-AES 库的缺点
在深入研究代码之前,了解 Tiny-AES 库的缺点至关重要。
-
该库不支持填充。所有缓冲区的大小必须是 16 字节的倍数。
-
库中使用的数组可以被杀软通过二进制扫描检测到,从而检测到 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)