├── HuanLoader ├── HuanLoader.vcxproj.user ├── HuanLoader.vcxproj.filters ├── Loader.cpp └── HuanLoader.vcxproj ├── aes.hpp ├── NewSection.h ├── Crypto.h ├── Huan.vcxproj.user ├── Crypto.cpp ├── Huan.vcxproj.filters ├── README.md ├── Huan.sln ├── aes.h ├── Main.cpp ├── NewSection.cpp ├── Huan.vcxproj └── aes.c /HuanLoader/HuanLoader.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /aes.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _AES_HPP_ 2 | #define _AES_HPP_ 3 | 4 | #ifndef __cplusplus 5 | #error Do not include the hpp header in a c project! 6 | #endif //__cplusplus 7 | 8 | extern "C" { 9 | #include "aes.h" 10 | } 11 | 12 | #endif //_AES_HPP_ 13 | -------------------------------------------------------------------------------- /NewSection.h: -------------------------------------------------------------------------------- 1 | char* readBinary(const char* fileName, size_t *fileSize); 2 | char* createNewSectionHeader(char* imageBase,unsigned char* packedContent, size_t packedLength, size_t *newFileSize); 3 | bool saveNewPE(char* newFile, size_t lengthOfFile, const char* fileName); -------------------------------------------------------------------------------- /Crypto.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define KEYSIZE 16 4 | void encryptData(unsigned char* dataBuffer, size_t dataLength, unsigned char* keyBuffer, unsigned char* IVBuffer); 5 | void decryptData(unsigned char* dataBuffer, size_t dataLength, unsigned char* keyBuffer, unsigned char* IVBuffer); 6 | unsigned char* paddingForInput(unsigned char* dataBuffer, size_t originalSize); -------------------------------------------------------------------------------- /Huan.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | C:\Users\picus\source\repos\DummyFile\x64\Release\DummyFile.exe test.exe 5 | WindowsLocalDebugger 6 | 7 | -------------------------------------------------------------------------------- /Crypto.cpp: -------------------------------------------------------------------------------- 1 | #include "aes.hpp" 2 | #include "Crypto.h" 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | void randomBytes(unsigned char* output, size_t length) { 9 | for (int i = 0; i < length; i++) { 10 | output[i] = rand() % 256; 11 | } 12 | } 13 | 14 | unsigned char *paddingForInput(unsigned char *dataBuffer, size_t originalSize) { 15 | int paddingLength = 16-originalSize % 16; 16 | unsigned char* newBuffer = (unsigned char*)malloc(originalSize + paddingLength); 17 | memcpy(newBuffer, dataBuffer, originalSize); 18 | memset(newBuffer + originalSize, 0x00, paddingLength); 19 | return newBuffer; 20 | } 21 | 22 | void encryptData(unsigned char *dataBuffer,size_t dataLength, unsigned char *keyBuffer, unsigned char *IVBuffer) { 23 | struct AES_ctx ctx; 24 | randomBytes(keyBuffer, KEYSIZE); 25 | randomBytes(IVBuffer, 16); 26 | AES_init_ctx_iv(&ctx, keyBuffer, IVBuffer); 27 | AES_CBC_encrypt_buffer(&ctx, dataBuffer, dataLength); 28 | } 29 | 30 | void decryptData(unsigned char *dataBuffer, size_t dataLength, unsigned char *keyBuffer, unsigned char *IVBuffer) { 31 | struct AES_ctx ctx; 32 | AES_init_ctx_iv(&ctx, keyBuffer, IVBuffer); 33 | AES_CBC_decrypt_buffer(&ctx, dataBuffer, dataLength); 34 | } -------------------------------------------------------------------------------- /HuanLoader/HuanLoader.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | -------------------------------------------------------------------------------- /Huan.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Huan 2 | 3 | Huan is an encrypted PE Loader Generator that I developed for learning PE file structure and PE loading processes. It encrypts the PE file to be run with different keys each time and embeds it in a new section of the loader binary. Currently, it works on 64 bit PE files. 4 | 5 | # How It Works? 6 | 7 | First, Huan reads the given PE file and encrypts it with CBC mode AES-128 encryption algorithm. For the encryption, I used `Tiny AES in C` and prepared a padding code for the requirement of this library. When the encryption is complete, it compiles the loader using the Visual Studio compiler (`MsBuild.exe`) and creates an executable. After that, it creates a section (called `.huan`) on that executable, and embed the encrypted content, size information, IV and symmetric key. Both keys are created randomly for each copy of the loader. The layout of this section can be seen below. 8 |

9 | 10 |

11 | 12 | When the loader is executed, it first takes the image base of itself by reading the `Process Environment Block`. After learning the image base, it parses the loaded copy of itself to find `.huan`section. Then, it decrypts the whole content, buffers it, and loads the binary which relies on the memory. 13 | # Quick Demo 14 |

15 | 16 |

17 | 18 | # TO-DO List 19 | - 32 Bit support 20 | - Improvements on PE loader 21 | - Blog post about PE loading process 22 | - Reducing the detection rate of the loader 23 | 24 | # References 25 | 26 | - https://github.com/kokke/tiny-AES-c 27 | - https://relearex.wordpress.com/2017/12/26/hooking-series-part-i-import-address-table-hooking/ 28 | - http://research32.blogspot.com/2015/01/base-relocation-table.html 29 | - https://blog.kowalczyk.info/articles/pefileformat.html 30 | - http://sandsprite.com/CodeStuff/Understanding_imports.html 31 | 32 | # Disclaimer 33 | I shared this tool only for showing the code snippets of well known TTPs. I'm not responsible for the use of this tool for malicious activities. 34 | -------------------------------------------------------------------------------- /Huan.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31229.75 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Huan", "Huan.vcxproj", "{DB5465F0-98B9-497C-BE03-AC3276E7DF46}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HuanLoader", "HuanLoader\HuanLoader.vcxproj", "{2234DF93-108C-41FA-A284-25D78FBBBD5E}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | JustLoader|x64 = JustLoader|x64 15 | JustLoader|x86 = JustLoader|x86 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.Debug|x64.ActiveCfg = Debug|x64 21 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.Debug|x64.Build.0 = Debug|x64 22 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.Debug|x86.ActiveCfg = Debug|Win32 23 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.Debug|x86.Build.0 = Debug|Win32 24 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.JustLoader|x64.ActiveCfg = JustLoader|x64 25 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.JustLoader|x86.ActiveCfg = JustLoader|Win32 26 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.JustLoader|x86.Build.0 = JustLoader|Win32 27 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.Release|x64.ActiveCfg = Release|x64 28 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.Release|x64.Build.0 = Release|x64 29 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.Release|x86.ActiveCfg = Release|Win32 30 | {DB5465F0-98B9-497C-BE03-AC3276E7DF46}.Release|x86.Build.0 = Release|Win32 31 | {2234DF93-108C-41FA-A284-25D78FBBBD5E}.Debug|x64.ActiveCfg = Debug|x64 32 | {2234DF93-108C-41FA-A284-25D78FBBBD5E}.Debug|x86.ActiveCfg = Debug|Win32 33 | {2234DF93-108C-41FA-A284-25D78FBBBD5E}.Debug|x86.Build.0 = Debug|Win32 34 | {2234DF93-108C-41FA-A284-25D78FBBBD5E}.JustLoader|x64.ActiveCfg = JustLoader|x64 35 | {2234DF93-108C-41FA-A284-25D78FBBBD5E}.JustLoader|x64.Build.0 = JustLoader|x64 36 | {2234DF93-108C-41FA-A284-25D78FBBBD5E}.JustLoader|x86.ActiveCfg = JustLoader|Win32 37 | {2234DF93-108C-41FA-A284-25D78FBBBD5E}.Release|x64.ActiveCfg = Release|x64 38 | {2234DF93-108C-41FA-A284-25D78FBBBD5E}.Release|x86.ActiveCfg = Release|Win32 39 | {2234DF93-108C-41FA-A284-25D78FBBBD5E}.Release|x86.Build.0 = Release|Win32 40 | EndGlobalSection 41 | GlobalSection(SolutionProperties) = preSolution 42 | HideSolutionNode = FALSE 43 | EndGlobalSection 44 | GlobalSection(ExtensibilityGlobals) = postSolution 45 | SolutionGuid = {C4B86B29-7CA3-4906-8F85-511EB9A1F928} 46 | EndGlobalSection 47 | EndGlobal 48 | -------------------------------------------------------------------------------- /aes.h: -------------------------------------------------------------------------------- 1 | #ifndef _AES_H_ 2 | #define _AES_H_ 3 | 4 | #include 5 | #include 6 | 7 | // #define the macros below to 1/0 to enable/disable the mode of operation. 8 | // 9 | // CBC enables AES encryption in CBC-mode of operation. 10 | // CTR enables encryption in counter-mode. 11 | // ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. 12 | 13 | // The #ifndef-guard allows it to be configured before #include'ing or at compile time. 14 | #ifndef CBC 15 | #define CBC 1 16 | #endif 17 | 18 | #ifndef ECB 19 | #define ECB 1 20 | #endif 21 | 22 | #ifndef CTR 23 | #define CTR 1 24 | #endif 25 | 26 | 27 | #define AES128 1 28 | //#define AES192 1 29 | //#define AES256 1 30 | 31 | #define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only 32 | 33 | #if defined(AES256) && (AES256 == 1) 34 | #define AES_KEYLEN 32 35 | #define AES_keyExpSize 240 36 | #elif defined(AES192) && (AES192 == 1) 37 | #define AES_KEYLEN 24 38 | #define AES_keyExpSize 208 39 | #else 40 | #define AES_KEYLEN 16 // Key length in bytes 41 | #define AES_keyExpSize 176 42 | #endif 43 | 44 | struct AES_ctx 45 | { 46 | uint8_t RoundKey[AES_keyExpSize]; 47 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 48 | uint8_t Iv[AES_BLOCKLEN]; 49 | #endif 50 | }; 51 | 52 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); 53 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 54 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); 55 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); 56 | #endif 57 | 58 | #if defined(ECB) && (ECB == 1) 59 | // buffer size is exactly AES_BLOCKLEN bytes; 60 | // you need only AES_init_ctx as IV is not used in ECB 61 | // NB: ECB is considered insecure for most uses 62 | void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); 63 | void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); 64 | 65 | #endif // #if defined(ECB) && (ECB == !) 66 | 67 | 68 | #if defined(CBC) && (CBC == 1) 69 | // buffer size MUST be mutile of AES_BLOCKLEN; 70 | // Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 71 | // NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() 72 | // no IV should ever be reused with the same key 73 | void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); 74 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); 75 | 76 | #endif // #if defined(CBC) && (CBC == 1) 77 | 78 | 79 | #if defined(CTR) && (CTR == 1) 80 | 81 | // Same function for encrypting as for decrypting. 82 | // IV is incremented for every block, and used after encryption as XOR-compliment for output 83 | // Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 84 | // NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() 85 | // no IV should ever be reused with the same key 86 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); 87 | 88 | #endif // #if defined(CTR) && (CTR == 1) 89 | 90 | 91 | #endif // _AES_H_ 92 | -------------------------------------------------------------------------------- /Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "NewSection.h" 5 | #include 6 | 7 | void printBanner() { 8 | const char* banner = 9 | " .S S. .S S. .S_SSSs .S_sSSs \n" 10 | ".SS SS. .SS SS. .SS~SSSSS .SS~YS%%b \n" 11 | "S%S S%S S%S S%S S%S SSSS S%S `S%b \n" 12 | "S%S S%S S%S S%S S%S S%S S%S S%S \n" 13 | "S%S SSSS%S S&S S&S S%S SSSS%S S%S S&S \n" 14 | "S&S SSS&S S&S S&S S&S SSS%S S&S S&S \n" 15 | "S&S S&S S&S S&S S&S S&S S&S S&S \n" 16 | "S&S S&S S&S S&S S&S S&S S&S S&S \n" 17 | "S*S S*S S*b d*S S*S S&S S*S S*S \n" 18 | "S*S S*S S*S. .S*S S*S S*S S*S S*S \n" 19 | "S*S S*S SSSbs_sdSSS S*S S*S S*S S*S \n" 20 | "SSS S*S YSSP~YSSY SSS S*S S*S SSS \n" 21 | " SP SP SP \n" 22 | " Y Y Y \n" 23 | " by @R0h1rr1m \n"; 24 | std::cout < " << std::endl << std::endl; 30 | } 31 | 32 | void DeleteDirectory(char *strPath) 33 | { 34 | SHFILEOPSTRUCTA strOper = { 0 }; 35 | strOper.hwnd = NULL; 36 | strOper.wFunc = FO_DELETE; 37 | strOper.pFrom = strPath; 38 | strOper.fFlags = FOF_SILENT | FOF_NOCONFIRMATION; 39 | 40 | if (SHFileOperationA(&strOper)){ 41 | std::cout << "[!] Unicode directory deletion problem" << std::endl; 42 | } 43 | } 44 | 45 | bool directoryExists(const std::string& dirName) 46 | { 47 | DWORD fileType = GetFileAttributesA(dirName.c_str()); 48 | if (fileType == INVALID_FILE_ATTRIBUTES) { 49 | return false; 50 | } 51 | if (fileType & FILE_ATTRIBUTE_DIRECTORY) { 52 | return true; 53 | } 54 | return false; 55 | } 56 | 57 | void clearDirectory() { 58 | char removedDir1[MAX_PATH] = { 0 }; 59 | char removedDir2[MAX_PATH] = { 0 }; 60 | sprintf(removedDir1, "%sx64\\JustLoader\\", SOLUTIONDIR); 61 | sprintf(removedDir2, "%sHuanLoader\\x64\\", SOLUTIONDIR); 62 | if (directoryExists(removedDir1)) { 63 | DeleteDirectory(removedDir1); 64 | } 65 | if (directoryExists(removedDir2)) { 66 | DeleteDirectory(removedDir2); 67 | } 68 | } 69 | 70 | char *compileLoader() { 71 | clearDirectory(); 72 | const char* vsWhere = "\"\"C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe\" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath\""; 73 | FILE* pipe = _popen(vsWhere, "rt"); 74 | if (pipe != NULL) { 75 | char compilerPath[MAX_PATH] = { 0 }; 76 | char fullCommand[MAX_PATH] = { 0 }; 77 | if (fgets(compilerPath, MAX_PATH, pipe) != NULL) { 78 | //Remove new line 79 | compilerPath[strlen(compilerPath) - 1] = '\0'; 80 | sprintf(fullCommand, "\"\"%s\\MSBuild\\Current\\Bin\\MSBuild.exe\" %s\\Huan.sln /t:HuanLoader /property:Configuration=JustLoader /property:RuntimeLibrary=MT\"\n", compilerPath, SOLUTIONDIR); 81 | FILE* pipe2 = _popen(fullCommand, "rt"); 82 | _pclose(pipe2); 83 | char* loaderBinaryPath = (char *) malloc(MAX_PATH); 84 | sprintf(loaderBinaryPath, "%sx64\\JustLoader\\HuanLoader.exe", SOLUTIONDIR); 85 | if (INVALID_FILE_ATTRIBUTES == GetFileAttributesA(loaderBinaryPath) && GetLastError() == ERROR_FILE_NOT_FOUND){ 86 | std::cout << "[!] Compiled binary not found!" << std::endl; 87 | free(loaderBinaryPath); 88 | return NULL; 89 | } 90 | else { 91 | return loaderBinaryPath; 92 | } 93 | } 94 | else { 95 | std::cout << "[!] Visual Studio compiler path is not found! " << std::endl; 96 | return NULL; 97 | } 98 | _pclose(pipe); 99 | return NULL; 100 | } 101 | return NULL; 102 | } 103 | 104 | 105 | 106 | int main(int argc, char *argv[]) { 107 | printBanner(); 108 | if (argc != 3) { 109 | printHelp(argv[0]); 110 | return 0; 111 | } 112 | srand(time(NULL)); 113 | size_t fileSize = 0; 114 | char* binaryContent = readBinary(argv[1], &fileSize); 115 | if (binaryContent == NULL || fileSize == 0) { 116 | std::cout << "[!] Error on reading the exe file !" << std::endl; 117 | return 0; 118 | } 119 | std::cout << "[+] " << argv[1] << " is readed!" << std::endl; 120 | size_t newFileSize = 0; 121 | char *loaderPath = compileLoader(); 122 | size_t loaderSize = 0; 123 | if (loaderPath == NULL) { 124 | std::cout << std::endl << "[!] Error on compiling loader !" << std::endl; 125 | return 0; 126 | } 127 | char* loaderContent = readBinary(loaderPath, &loaderSize); 128 | std::cout << "[+] Loader is compiled and readed!" << std::endl; 129 | char* newBinary = createNewSectionHeader(loaderContent, (unsigned char *) binaryContent, fileSize,&newFileSize); 130 | if (newBinary == NULL) { 131 | std::cout << std::endl << "[!] Error on adding a new section header !" << std::endl; 132 | return 0; 133 | } 134 | std::cout << "[+] New section is added!" << std::endl; 135 | bool returnResult = saveNewPE(newBinary,newFileSize, argv[2]); 136 | clearDirectory(); 137 | if (returnResult) { 138 | std::cout << "[+] Loader is created as " << argv[2] << std::endl; 139 | } 140 | delete[] binaryContent; 141 | delete[] loaderContent; 142 | free(loaderPath); 143 | return 0; 144 | } -------------------------------------------------------------------------------- /NewSection.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "Crypto.h" 6 | 7 | //IMAGE_NT_HEADERS --> PE Signature + PE Header + PE Optional Header structure --> https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_nt_headers32 8 | #define ntHeaders(imageBase) ((IMAGE_NT_HEADERS *)((size_t)imageBase + ((IMAGE_DOS_HEADER *)imageBase)->e_lfanew)) 9 | //Section headers are located sequentially right after the optional header in the PE file format. Each section header is 40 bytes with no padding between them. Section headers are defined as in the following structure 10 | #define sectionHeaderArrays(imageBase) ((IMAGE_SECTION_HEADER *)((size_t)ntHeaders(imageBase) + sizeof(IMAGE_NT_HEADERS))) 11 | 12 | #define P2ALIGNUP(size, align) ((((size) / (align)) + 1) * (align)) 13 | 14 | 15 | 16 | char* readBinary(const char* fileName,size_t *givenFileSize) { 17 | FILE* fileHandler = fopen(fileName, "rb+"); 18 | char* binaryContent = NULL; 19 | size_t fileSize = 0; 20 | if (fileHandler) { 21 | //Move cursor to the end of executable 22 | fseek(fileHandler, 0, SEEK_END); 23 | fileSize = ftell(fileHandler); 24 | binaryContent = new char[fileSize+1]; 25 | //Move cursor to the beginning 26 | fseek(fileHandler, 0, SEEK_SET); 27 | fread(binaryContent, sizeof(char), fileSize, fileHandler); 28 | fclose(fileHandler); 29 | } 30 | *givenFileSize = fileSize; 31 | return binaryContent; 32 | } 33 | 34 | bool saveNewPE(char* newFile, size_t lengthOfFile, const char* fileName) { 35 | ntHeaders(newFile)->OptionalHeader.SizeOfImage = 36 | sectionHeaderArrays(newFile)[ntHeaders(newFile)->FileHeader.NumberOfSections - 1].VirtualAddress + 37 | sectionHeaderArrays(newFile)[ntHeaders(newFile)->FileHeader.NumberOfSections - 1].Misc.VirtualSize; 38 | 39 | ntHeaders(newFile)->OptionalHeader.DllCharacteristics = 0x8160; 40 | FILE* fileHandler = fopen(fileName, "wb"); 41 | if (fileHandler) { 42 | fwrite(newFile, 1, lengthOfFile, fileHandler); 43 | fclose(fileHandler); 44 | return true; 45 | } 46 | else { 47 | std::cout << "[!] Error on writing new content" << std::endl; 48 | return false; 49 | } 50 | 51 | } 52 | 53 | char * createNewSectionHeader(char* imageBase, unsigned char* packedContent, size_t packedLength, size_t* newFileSize){ 54 | IMAGE_NT_HEADERS* ntHeaderOfImage = ntHeaders(imageBase); 55 | IMAGE_SECTION_HEADER* sectionHeaderArray = sectionHeaderArrays(imageBase); 56 | int numberOfSections = ntHeaderOfImage->FileHeader.NumberOfSections; 57 | //Area after the last section in disk 58 | size_t newSectionOffset = sectionHeaderArray[numberOfSections - 1].PointerToRawData + sectionHeaderArray[numberOfSections - 1].SizeOfRawData; 59 | //Area after the last element in the section header array 60 | IMAGE_SECTION_HEADER* newSectionHeader = §ionHeaderArray[numberOfSections]; 61 | 62 | //check the section header boundary with the first section --> Does new section header (get offset) overwrite the first section (.text section)? 63 | bool checkBoundary = ((char *) newSectionHeader + sizeof(IMAGE_SECTION_HEADER) - imageBase) < sectionHeaderArray[0].PointerToRawData; 64 | if (checkBoundary) { 65 | unsigned char keyBuffer[KEYSIZE] = { 0x00 }; 66 | unsigned char IVBuffer[16] = { 0x00 }; 67 | unsigned char* newBuffer; 68 | size_t paddedLength; 69 | size_t totalLengthForSection = 0; 70 | if (packedLength % 16) { 71 | newBuffer = paddingForInput(packedContent, packedLength); 72 | paddedLength = (packedLength / 16 + 1) * 16; 73 | } 74 | else { 75 | newBuffer = packedContent; 76 | paddedLength = packedLength; 77 | } 78 | encryptData(newBuffer, paddedLength, keyBuffer, IVBuffer); 79 | std::cout << "[+] Given exe is encrypted!" << std::endl; 80 | printf("[+] Symmetric Key: "); 81 | for (int i = 0; i < KEYSIZE; i++) { 82 | printf(" 0x%02x", keyBuffer[i]); 83 | } 84 | printf("\n"); 85 | printf("[+] IV Key: "); 86 | for (int i = 0; i < 16; i++) { 87 | printf(" 0x%02x", IVBuffer[i]); 88 | } 89 | printf("\n"); 90 | //We are safe 91 | memcpy(newSectionHeader->Name, ".huan", IMAGE_SIZEOF_SHORT_NAME); 92 | //KEY + IV + Encrypted Content + int size for original length + int size for encrypted length 93 | totalLengthForSection = KEYSIZE + 16 + paddedLength + 4 + 4; 94 | //In memory, sections should be multiple of page size, this alignment variable arranges this alignment. 95 | newSectionHeader->VirtualAddress = P2ALIGNUP( 96 | sectionHeaderArray[numberOfSections - 1].VirtualAddress + sectionHeaderArray[numberOfSections - 1].Misc.VirtualSize, 97 | ntHeaderOfImage->OptionalHeader.SectionAlignment 98 | ); 99 | //File alignment for PE File, same alignment problem but this is for disk 100 | newSectionHeader->SizeOfRawData = P2ALIGNUP(totalLengthForSection, ntHeaderOfImage->OptionalHeader.FileAlignment); 101 | //Section alignment for memory 102 | newSectionHeader->Misc.VirtualSize = P2ALIGNUP((totalLengthForSection), ntHeaderOfImage->OptionalHeader.SectionAlignment); 103 | newSectionHeader->Characteristics = 0x40000040; 104 | //Offset for file 105 | newSectionHeader->PointerToRawData = newSectionOffset; 106 | // Section Alignment trick and put correct address wrt last section 107 | ntHeaderOfImage->FileHeader.NumberOfSections += 1; 108 | //Now it has new section size 109 | *newFileSize = P2ALIGNUP(totalLengthForSection, ntHeaderOfImage->OptionalHeader.FileAlignment); 110 | //New Section Offset is actually end of the file 111 | char* newExeBuffer = new char[newSectionOffset + *newFileSize]; 112 | memcpy(newExeBuffer, imageBase, newSectionOffset); 113 | //New Section contains 4 byte original length + 4 byte encrypted length + 16 byte Key + 16 byte IV + encrypted data 114 | int* originalLength =(int *) (newExeBuffer + newSectionOffset); 115 | int* encryptedLength = (int*)(newExeBuffer + newSectionOffset+4); 116 | memcpy(newExeBuffer + newSectionOffset + 8, keyBuffer, 16); 117 | memcpy(newExeBuffer + newSectionOffset + 8 + 16, IVBuffer, 16); 118 | memcpy(newExeBuffer + newSectionOffset + 8 + 16 + 16, newBuffer, paddedLength); 119 | *originalLength = packedLength; 120 | *encryptedLength = paddedLength; 121 | *newFileSize += newSectionOffset; 122 | return newExeBuffer; 123 | } 124 | else { 125 | std::cout << "[!] Section Header Problem" << std::endl; 126 | //TODO: Is there a way to fix this boundary problem? 127 | return NULL; 128 | } 129 | } -------------------------------------------------------------------------------- /HuanLoader/Loader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include"../Crypto.h" 6 | 7 | #define ntHeaders(imageBase) ((IMAGE_NT_HEADERS *)((size_t)imageBase + ((IMAGE_DOS_HEADER *)imageBase)->e_lfanew)) 8 | #define sectionHeaderArrays(imageBase) ((IMAGE_SECTION_HEADER *)((size_t)ntHeaders(imageBase) + sizeof(IMAGE_NT_HEADERS))) 9 | 10 | typedef struct _BASE_RELOCATION_ENTRY { 11 | WORD Offset : 12; 12 | WORD Type : 4; 13 | } BASE_RELOCATION_ENTRY; 14 | 15 | 16 | IMAGE_DATA_DIRECTORY* getRelocTable(IMAGE_NT_HEADERS *ntHeader) { 17 | IMAGE_DATA_DIRECTORY *returnTable = &ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 18 | if (returnTable->VirtualAddress == NULL) { 19 | return NULL; 20 | } 21 | else { 22 | return returnTable; 23 | } 24 | } 25 | 26 | void fixImportAddressTable(BYTE* baseAddress) { 27 | std::cout << "[+] IAT Fix starts..." << std::endl; 28 | IMAGE_NT_HEADERS* ntHeader = ntHeaders(baseAddress); 29 | IMAGE_DATA_DIRECTORY* iatDirectory = &ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 30 | if (iatDirectory->VirtualAddress == NULL) { 31 | std::cout << "[!] Import Table not found" << std::endl; 32 | } 33 | else { 34 | size_t iatSize = iatDirectory->Size; 35 | size_t iatRVA = iatDirectory->VirtualAddress; 36 | IMAGE_IMPORT_DESCRIPTOR* ITEntryCursor = NULL; 37 | size_t parsedSize = 0; 38 | for (; parsedSize < iatSize; parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR)) { 39 | ITEntryCursor = (IMAGE_IMPORT_DESCRIPTOR*)(iatRVA+(ULONG_PTR) baseAddress+ parsedSize); 40 | if (ITEntryCursor->OriginalFirstThunk == NULL && ITEntryCursor->FirstThunk == NULL) { 41 | break; 42 | } 43 | LPSTR dllName = (LPSTR)((ULONGLONG)baseAddress + ITEntryCursor->Name); 44 | std::cout << "[+] Imported DLL Name: " << dllName << std::endl; 45 | //Address 46 | size_t firstThunkRVA = ITEntryCursor->FirstThunk; 47 | //Name 48 | size_t originalFirstThunkRVA = ITEntryCursor->OriginalFirstThunk; 49 | if (originalFirstThunkRVA == NULL) { 50 | originalFirstThunkRVA = ITEntryCursor->FirstThunk; 51 | } 52 | size_t cursorFirstThunk = 0; 53 | size_t cursorOriginalFirstThunk = 0; 54 | while (true){ 55 | IMAGE_THUNK_DATA* firstThunkData = (IMAGE_THUNK_DATA*)(baseAddress + cursorFirstThunk + firstThunkRVA); 56 | IMAGE_THUNK_DATA* originalFirstThunkData = (IMAGE_THUNK_DATA*)(baseAddress + cursorOriginalFirstThunk + originalFirstThunkRVA); 57 | if (firstThunkData->u1.Function == NULL) { 58 | //end of the list 59 | break; 60 | } 61 | else if (IMAGE_SNAP_BY_ORDINAL64(originalFirstThunkData->u1.Ordinal)) { 62 | unsigned int printOrdinal = originalFirstThunkData->u1.Ordinal & 0xFFFF; 63 | size_t functionAddr = (size_t) GetProcAddress(LoadLibraryA(dllName), (char*)(originalFirstThunkData->u1.Ordinal & 0xFFFF)); 64 | std::cout << " [+] Import by ordinal: " << printOrdinal << std::endl; 65 | firstThunkData->u1.Function = (ULONGLONG) functionAddr; 66 | } 67 | else { 68 | PIMAGE_IMPORT_BY_NAME nameOfFunc = (PIMAGE_IMPORT_BY_NAME)(size_t(baseAddress) + originalFirstThunkData->u1.AddressOfData); 69 | size_t functionAddr = (size_t)GetProcAddress(LoadLibraryA(dllName), nameOfFunc->Name); 70 | std::cout << " [+] Import by name: " << nameOfFunc->Name << std::endl; 71 | firstThunkData->u1.Function = (ULONGLONG)functionAddr; 72 | } 73 | cursorFirstThunk += sizeof(IMAGE_THUNK_DATA); 74 | cursorOriginalFirstThunk += sizeof(IMAGE_THUNK_DATA); 75 | } 76 | } 77 | } 78 | } 79 | 80 | 81 | void fixRelocTable(BYTE* loadedAddr, BYTE* preferableAddr, IMAGE_DATA_DIRECTORY* relocDir) { 82 | size_t maxSizeOfDir = relocDir->Size; 83 | size_t relocBlocks = relocDir->VirtualAddress; 84 | IMAGE_BASE_RELOCATION* relocBlockMetadata = NULL; 85 | 86 | size_t relocBlockOffset = 0; 87 | for (; relocBlockOffset < maxSizeOfDir; relocBlockOffset += relocBlockMetadata->SizeOfBlock) { 88 | relocBlockMetadata = (IMAGE_BASE_RELOCATION*)(relocBlocks + relocBlockOffset + loadedAddr); 89 | if (relocBlockMetadata->VirtualAddress == NULL || relocBlockMetadata->SizeOfBlock == 0) { 90 | //No more block 91 | break; 92 | } 93 | size_t entriesNum = (relocBlockMetadata->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(BASE_RELOCATION_ENTRY); 94 | size_t pageStart = relocBlockMetadata->VirtualAddress; 95 | //printf("Entries Num: %d %d\n", entriesNum, pageStart); 96 | BASE_RELOCATION_ENTRY* relocEntryCursor = (BASE_RELOCATION_ENTRY*)((BYTE*)relocBlockMetadata + sizeof(IMAGE_BASE_RELOCATION)); 97 | for (int i = 0; i < entriesNum; i++) { 98 | if (relocEntryCursor->Type == 0) { 99 | continue; 100 | } 101 | DWORD* relocationAddr = (DWORD *) (pageStart + loadedAddr+relocEntryCursor->Offset); 102 | *relocationAddr = *relocationAddr + loadedAddr - preferableAddr; 103 | relocEntryCursor = (BASE_RELOCATION_ENTRY*)((BYTE*)relocEntryCursor + sizeof(BASE_RELOCATION_ENTRY)); 104 | } 105 | } 106 | if (relocBlockOffset == 0) { 107 | //Nothing happened 108 | std::cout << "[!] There is a problem in relocation directory" << std::endl; 109 | } 110 | } 111 | 112 | void peLoader(unsigned char* baseAddr) { 113 | IMAGE_NT_HEADERS* ntHeader = ntHeaders(baseAddr); 114 | IMAGE_DATA_DIRECTORY* relocTable = getRelocTable(ntHeader); 115 | ULONGLONG preferableAddress = ntHeader->OptionalHeader.ImageBase; 116 | HMODULE ntdllHandler = LoadLibraryA("ntdll.dll"); 117 | //Unmap the preferable address 118 | ((int(WINAPI*)(HANDLE, PVOID))GetProcAddress(ntdllHandler, "NtUnmapViewOfSection"))((HANDLE)-1, (LPVOID)ntHeader->OptionalHeader.ImageBase); 119 | BYTE *imageBaseForPE = (BYTE*)VirtualAlloc((LPVOID) preferableAddress, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 120 | if (!imageBaseForPE && !relocTable){ 121 | std::cout << "[!] No Relocation Table and Cannot load to the preferable address" << std::endl; 122 | return; 123 | } 124 | if (!imageBaseForPE && relocTable){ 125 | std::cout << "[+] Cannot load to the preferable address" << std::endl; 126 | imageBaseForPE = (BYTE*)VirtualAlloc(NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 127 | if (!imageBaseForPE){ 128 | std::cout << "[!] Cannot allocate the memory" << std::endl; 129 | return; 130 | } 131 | } 132 | ntHeader->OptionalHeader.ImageBase = (ULONGLONG) imageBaseForPE; 133 | // SizeOfHeaders indicates how much space in the file is used for representing all the file headers, including the MS - DOS header, PE file header, PE optional header, and PE section headers.The section bodies begin at this location in the file. 134 | memcpy(imageBaseForPE, baseAddr, ntHeader->OptionalHeader.SizeOfHeaders); 135 | std::cout << "[+] All headers are copied" << std::endl; 136 | IMAGE_SECTION_HEADER *sectionHeaderCursor = (IMAGE_SECTION_HEADER *)(size_t(ntHeader) + sizeof(IMAGE_NT_HEADERS)); 137 | for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++){ 138 | memcpy(imageBaseForPE + sectionHeaderCursor[i].VirtualAddress, baseAddr + sectionHeaderCursor[i].PointerToRawData, sectionHeaderCursor[i].SizeOfRawData); 139 | } 140 | std::cout << "[+] All sections are copied" << std::endl; 141 | fixImportAddressTable(imageBaseForPE); 142 | if (((ULONGLONG)imageBaseForPE) != preferableAddress) { 143 | if (relocTable) { 144 | fixRelocTable(imageBaseForPE, (BYTE*)preferableAddress, relocTable); 145 | } 146 | else { 147 | std::cout << "[!] No Reloc Table Found" << std::endl; 148 | } 149 | 150 | } 151 | size_t startAddress = (size_t)(imageBaseForPE)+ntHeader->OptionalHeader.AddressOfEntryPoint; 152 | std::cout << "[+] Binary is running" << std::endl; 153 | 154 | ((void(*)())startAddress)(); 155 | } 156 | 157 | int main() { 158 | //Get image base address from struct offsets of PEB and TEB 159 | size_t fileSizeForDebug; 160 | unsigned char encryptKey[16]; 161 | unsigned char IVKey[16]; 162 | int* originalDataLength; 163 | int* encryptedDataLength; 164 | unsigned char* encryptedContent; 165 | unsigned char* originalContent; 166 | char *TEBPtr = (char *) __readgsqword(0x30); 167 | char *PEBPtr = *((char **) (TEBPtr + 0x060)); 168 | char* imageBaseAddress = *(char**)(PEBPtr+0x10); 169 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)imageBaseAddress; 170 | PIMAGE_NT_HEADERS imageNTHeaders = (PIMAGE_NT_HEADERS)(imageBaseAddress + dosHeader->e_lfanew); 171 | PIMAGE_SECTION_HEADER sectionHeadersCursor = (PIMAGE_SECTION_HEADER)(((PBYTE)imageNTHeaders) + sizeof(IMAGE_NT_HEADERS)); 172 | BYTE buf[10]; 173 | for (unsigned int i = 1; i <= imageNTHeaders->FileHeader.NumberOfSections; i++) { 174 | if (strncmp((const char*)sectionHeadersCursor->Name, ".huan", 5) == 0) { 175 | break; 176 | } 177 | sectionHeadersCursor = (PIMAGE_SECTION_HEADER)((PBYTE)sectionHeadersCursor + 0x28); 178 | } 179 | 180 | char* sectionValuePtr = imageBaseAddress + sectionHeadersCursor->VirtualAddress; 181 | //New Section contains 4 byte original length + 4 byte encrypted length + 16 byte Key + 16 byte IV + encrypted data 182 | originalDataLength = (int *) sectionValuePtr; 183 | encryptedDataLength = (int*)(sectionValuePtr + 4); 184 | memcpy(encryptKey,sectionValuePtr + 8, 16); 185 | memcpy(IVKey,sectionValuePtr + 24, 16); 186 | encryptedContent = (unsigned char*) malloc(*encryptedDataLength); 187 | originalContent = (unsigned char*)malloc(*originalDataLength); 188 | memcpy(encryptedContent, sectionValuePtr + 40, *encryptedDataLength); 189 | decryptData(encryptedContent, *encryptedDataLength, encryptKey, IVKey); 190 | memcpy(originalContent, encryptedContent, *originalDataLength); 191 | std::cout << "[+] Data is decrypted! " << std::endl; 192 | peLoader(originalContent); 193 | //Size of raw image shows the required address space (Last section Virtual Address + Last section virtual size 194 | return 0; 195 | } -------------------------------------------------------------------------------- /HuanLoader/HuanLoader.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | JustLoader 10 | Win32 11 | 12 | 13 | JustLoader 14 | x64 15 | 16 | 17 | Release 18 | Win32 19 | 20 | 21 | Debug 22 | x64 23 | 24 | 25 | Release 26 | x64 27 | 28 | 29 | 30 | 16.0 31 | Win32Proj 32 | {2234df93-108c-41fa-a284-25d78fbbbd5e} 33 | HuanLoader 34 | 10.0 35 | 36 | 37 | 38 | Application 39 | true 40 | v142 41 | Unicode 42 | 43 | 44 | Application 45 | false 46 | v142 47 | true 48 | Unicode 49 | 50 | 51 | Application 52 | false 53 | v142 54 | true 55 | Unicode 56 | 57 | 58 | Application 59 | true 60 | v142 61 | Unicode 62 | 63 | 64 | Application 65 | false 66 | v142 67 | true 68 | Unicode 69 | 70 | 71 | Application 72 | false 73 | v142 74 | true 75 | Unicode 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | true 103 | 104 | 105 | false 106 | 107 | 108 | false 109 | 110 | 111 | true 112 | 113 | 114 | false 115 | 116 | 117 | false 118 | 119 | 120 | 121 | Level3 122 | true 123 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | 126 | 127 | Console 128 | true 129 | 130 | 131 | 132 | 133 | Level3 134 | true 135 | true 136 | true 137 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 138 | true 139 | 140 | 141 | Console 142 | true 143 | true 144 | true 145 | 146 | 147 | 148 | 149 | Level3 150 | true 151 | true 152 | true 153 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 154 | true 155 | 156 | 157 | Console 158 | true 159 | true 160 | true 161 | 162 | 163 | 164 | 165 | Level3 166 | true 167 | _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 168 | true 169 | 170 | 171 | Console 172 | true 173 | 174 | 175 | 176 | 177 | Level3 178 | true 179 | true 180 | true 181 | NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 182 | true 183 | 184 | 185 | Console 186 | true 187 | true 188 | true 189 | 190 | 191 | 192 | 193 | Level3 194 | true 195 | true 196 | true 197 | NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 198 | true 199 | MultiThreaded 200 | 201 | 202 | Console 203 | true 204 | true 205 | true 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /Huan.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | JustLoader 10 | Win32 11 | 12 | 13 | JustLoader 14 | x64 15 | 16 | 17 | Release 18 | Win32 19 | 20 | 21 | Debug 22 | x64 23 | 24 | 25 | Release 26 | x64 27 | 28 | 29 | 30 | 16.0 31 | Win32Proj 32 | {db5465f0-98b9-497c-be03-ac3276e7df46} 33 | Huan 34 | 10.0 35 | 36 | 37 | 38 | Application 39 | true 40 | v142 41 | Unicode 42 | 43 | 44 | Application 45 | false 46 | v142 47 | true 48 | Unicode 49 | 50 | 51 | Application 52 | false 53 | v142 54 | true 55 | Unicode 56 | 57 | 58 | Application 59 | true 60 | v142 61 | Unicode 62 | 63 | 64 | Application 65 | false 66 | v142 67 | true 68 | Unicode 69 | 70 | 71 | Application 72 | false 73 | v142 74 | true 75 | Unicode 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | true 103 | 104 | 105 | false 106 | 107 | 108 | false 109 | 110 | 111 | true 112 | 113 | 114 | false 115 | 116 | 117 | false 118 | 119 | 120 | 121 | Level3 122 | true 123 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | 126 | 127 | Console 128 | true 129 | 130 | 131 | 132 | 133 | Level3 134 | true 135 | true 136 | true 137 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 138 | true 139 | 140 | 141 | Console 142 | true 143 | true 144 | true 145 | 146 | 147 | 148 | 149 | Level3 150 | true 151 | true 152 | true 153 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 154 | true 155 | 156 | 157 | Console 158 | true 159 | true 160 | true 161 | 162 | 163 | 164 | 165 | Level3 166 | true 167 | _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;SOLUTIONDIR=R"($(SolutionDir))";%(PreprocessorDefinitions) 168 | true 169 | 170 | 171 | Console 172 | true 173 | 174 | 175 | 176 | 177 | Level3 178 | true 179 | true 180 | true 181 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;SOLUTIONDIR=R"($(SolutionDir))" 182 | true 183 | MultiThreaded 184 | 185 | 186 | Console 187 | true 188 | true 189 | true 190 | 191 | 192 | 193 | 194 | Level3 195 | true 196 | true 197 | true 198 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;SOLUTIONDIR=R"($(SolutionDir))" 199 | true 200 | MultiThreaded 201 | 202 | 203 | Console 204 | true 205 | true 206 | true 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /aes.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. 4 | Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. 5 | 6 | The implementation is verified against the test vectors in: 7 | National Institute of Standards and Technology Special Publication 800-38A 2001 ED 8 | 9 | ECB-AES128 10 | ---------- 11 | 12 | plain-text: 13 | 6bc1bee22e409f96e93d7e117393172a 14 | ae2d8a571e03ac9c9eb76fac45af8e51 15 | 30c81c46a35ce411e5fbc1191a0a52ef 16 | f69f2445df4f9b17ad2b417be66c3710 17 | 18 | key: 19 | 2b7e151628aed2a6abf7158809cf4f3c 20 | 21 | resulting cipher 22 | 3ad77bb40d7a3660a89ecaf32466ef97 23 | f5d3d58503b9699de785895a96fdbaaf 24 | 43b1cd7f598ece23881b00e3ed030688 25 | 7b0c785e27e8ad3f8223207104725dd4 26 | 27 | 28 | NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) 29 | You should pad the end of the string with zeros if this is not the case. 30 | For AES192/256 the key size is proportionally larger. 31 | 32 | */ 33 | 34 | 35 | /*****************************************************************************/ 36 | /* Includes: */ 37 | /*****************************************************************************/ 38 | #include // CBC mode, for memset 39 | #include "aes.h" 40 | 41 | /*****************************************************************************/ 42 | /* Defines: */ 43 | /*****************************************************************************/ 44 | // The number of columns comprising a state in AES. This is a constant in AES. Value=4 45 | #define Nb 4 46 | 47 | #if defined(AES256) && (AES256 == 1) 48 | #define Nk 8 49 | #define Nr 14 50 | #elif defined(AES192) && (AES192 == 1) 51 | #define Nk 6 52 | #define Nr 12 53 | #else 54 | #define Nk 4 // The number of 32 bit words in a key. 55 | #define Nr 10 // The number of rounds in AES Cipher. 56 | #endif 57 | 58 | // jcallan@github points out that declaring Multiply as a function 59 | // reduces code size considerably with the Keil ARM compiler. 60 | // See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 61 | #ifndef MULTIPLY_AS_A_FUNCTION 62 | #define MULTIPLY_AS_A_FUNCTION 0 63 | #endif 64 | 65 | 66 | 67 | 68 | /*****************************************************************************/ 69 | /* Private variables: */ 70 | /*****************************************************************************/ 71 | // state - array holding the intermediate results during decryption. 72 | typedef uint8_t state_t[4][4]; 73 | 74 | 75 | 76 | // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 77 | // The numbers below can be computed dynamically trading ROM for RAM - 78 | // This can be useful in (embedded) bootloader applications, where ROM is often limited. 79 | static const uint8_t sbox[256] = { 80 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 81 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 82 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 83 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 84 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 85 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 86 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 87 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 88 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 89 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 90 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 91 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 92 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 93 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 94 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 95 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 96 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 97 | 98 | #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 99 | static const uint8_t rsbox[256] = { 100 | 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 101 | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 102 | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 103 | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 104 | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 105 | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 106 | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 107 | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 108 | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 109 | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 110 | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 111 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 112 | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 113 | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 114 | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 115 | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; 116 | #endif 117 | 118 | // The round constant word array, Rcon[i], contains the values given by 119 | // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 120 | static const uint8_t Rcon[11] = { 121 | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; 122 | 123 | /* 124 | * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), 125 | * that you can remove most of the elements in the Rcon array, because they are unused. 126 | * 127 | * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon 128 | * 129 | * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), 130 | * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." 131 | */ 132 | 133 | 134 | /*****************************************************************************/ 135 | /* Private functions: */ 136 | /*****************************************************************************/ 137 | /* 138 | static uint8_t getSBoxValue(uint8_t num) 139 | { 140 | return sbox[num]; 141 | } 142 | */ 143 | #define getSBoxValue(num) (sbox[(num)]) 144 | 145 | // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 146 | static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) 147 | { 148 | unsigned i, j, k; 149 | uint8_t tempa[4]; // Used for the column/row operations 150 | 151 | // The first round key is the key itself. 152 | for (i = 0; i < Nk; ++i) 153 | { 154 | RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; 155 | RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; 156 | RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; 157 | RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; 158 | } 159 | 160 | // All other round keys are found from the previous round keys. 161 | for (i = Nk; i < Nb * (Nr + 1); ++i) 162 | { 163 | { 164 | k = (i - 1) * 4; 165 | tempa[0]=RoundKey[k + 0]; 166 | tempa[1]=RoundKey[k + 1]; 167 | tempa[2]=RoundKey[k + 2]; 168 | tempa[3]=RoundKey[k + 3]; 169 | 170 | } 171 | 172 | if (i % Nk == 0) 173 | { 174 | // This function shifts the 4 bytes in a word to the left once. 175 | // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] 176 | 177 | // Function RotWord() 178 | { 179 | const uint8_t u8tmp = tempa[0]; 180 | tempa[0] = tempa[1]; 181 | tempa[1] = tempa[2]; 182 | tempa[2] = tempa[3]; 183 | tempa[3] = u8tmp; 184 | } 185 | 186 | // SubWord() is a function that takes a four-byte input word and 187 | // applies the S-box to each of the four bytes to produce an output word. 188 | 189 | // Function Subword() 190 | { 191 | tempa[0] = getSBoxValue(tempa[0]); 192 | tempa[1] = getSBoxValue(tempa[1]); 193 | tempa[2] = getSBoxValue(tempa[2]); 194 | tempa[3] = getSBoxValue(tempa[3]); 195 | } 196 | 197 | tempa[0] = tempa[0] ^ Rcon[i/Nk]; 198 | } 199 | #if defined(AES256) && (AES256 == 1) 200 | if (i % Nk == 4) 201 | { 202 | // Function Subword() 203 | { 204 | tempa[0] = getSBoxValue(tempa[0]); 205 | tempa[1] = getSBoxValue(tempa[1]); 206 | tempa[2] = getSBoxValue(tempa[2]); 207 | tempa[3] = getSBoxValue(tempa[3]); 208 | } 209 | } 210 | #endif 211 | j = i * 4; k=(i - Nk) * 4; 212 | RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; 213 | RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; 214 | RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; 215 | RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; 216 | } 217 | } 218 | 219 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) 220 | { 221 | KeyExpansion(ctx->RoundKey, key); 222 | } 223 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 224 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) 225 | { 226 | KeyExpansion(ctx->RoundKey, key); 227 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 228 | } 229 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) 230 | { 231 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 232 | } 233 | #endif 234 | 235 | // This function adds the round key to state. 236 | // The round key is added to the state by an XOR function. 237 | static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey) 238 | { 239 | uint8_t i,j; 240 | for (i = 0; i < 4; ++i) 241 | { 242 | for (j = 0; j < 4; ++j) 243 | { 244 | (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; 245 | } 246 | } 247 | } 248 | 249 | // The SubBytes Function Substitutes the values in the 250 | // state matrix with values in an S-box. 251 | static void SubBytes(state_t* state) 252 | { 253 | uint8_t i, j; 254 | for (i = 0; i < 4; ++i) 255 | { 256 | for (j = 0; j < 4; ++j) 257 | { 258 | (*state)[j][i] = getSBoxValue((*state)[j][i]); 259 | } 260 | } 261 | } 262 | 263 | // The ShiftRows() function shifts the rows in the state to the left. 264 | // Each row is shifted with different offset. 265 | // Offset = Row number. So the first row is not shifted. 266 | static void ShiftRows(state_t* state) 267 | { 268 | uint8_t temp; 269 | 270 | // Rotate first row 1 columns to left 271 | temp = (*state)[0][1]; 272 | (*state)[0][1] = (*state)[1][1]; 273 | (*state)[1][1] = (*state)[2][1]; 274 | (*state)[2][1] = (*state)[3][1]; 275 | (*state)[3][1] = temp; 276 | 277 | // Rotate second row 2 columns to left 278 | temp = (*state)[0][2]; 279 | (*state)[0][2] = (*state)[2][2]; 280 | (*state)[2][2] = temp; 281 | 282 | temp = (*state)[1][2]; 283 | (*state)[1][2] = (*state)[3][2]; 284 | (*state)[3][2] = temp; 285 | 286 | // Rotate third row 3 columns to left 287 | temp = (*state)[0][3]; 288 | (*state)[0][3] = (*state)[3][3]; 289 | (*state)[3][3] = (*state)[2][3]; 290 | (*state)[2][3] = (*state)[1][3]; 291 | (*state)[1][3] = temp; 292 | } 293 | 294 | static uint8_t xtime(uint8_t x) 295 | { 296 | return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); 297 | } 298 | 299 | // MixColumns function mixes the columns of the state matrix 300 | static void MixColumns(state_t* state) 301 | { 302 | uint8_t i; 303 | uint8_t Tmp, Tm, t; 304 | for (i = 0; i < 4; ++i) 305 | { 306 | t = (*state)[i][0]; 307 | Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; 308 | Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; 309 | Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; 310 | Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; 311 | Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; 312 | } 313 | } 314 | 315 | // Multiply is used to multiply numbers in the field GF(2^8) 316 | // Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary 317 | // The compiler seems to be able to vectorize the operation better this way. 318 | // See https://github.com/kokke/tiny-AES-c/pull/34 319 | #if MULTIPLY_AS_A_FUNCTION 320 | static uint8_t Multiply(uint8_t x, uint8_t y) 321 | { 322 | return (((y & 1) * x) ^ 323 | ((y>>1 & 1) * xtime(x)) ^ 324 | ((y>>2 & 1) * xtime(xtime(x))) ^ 325 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ 326 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ 327 | } 328 | #else 329 | #define Multiply(x, y) \ 330 | ( ((y & 1) * x) ^ \ 331 | ((y>>1 & 1) * xtime(x)) ^ \ 332 | ((y>>2 & 1) * xtime(xtime(x))) ^ \ 333 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ 334 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ 335 | 336 | #endif 337 | 338 | #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 339 | /* 340 | static uint8_t getSBoxInvert(uint8_t num) 341 | { 342 | return rsbox[num]; 343 | } 344 | */ 345 | #define getSBoxInvert(num) (rsbox[(num)]) 346 | 347 | // MixColumns function mixes the columns of the state matrix. 348 | // The method used to multiply may be difficult to understand for the inexperienced. 349 | // Please use the references to gain more information. 350 | static void InvMixColumns(state_t* state) 351 | { 352 | int i; 353 | uint8_t a, b, c, d; 354 | for (i = 0; i < 4; ++i) 355 | { 356 | a = (*state)[i][0]; 357 | b = (*state)[i][1]; 358 | c = (*state)[i][2]; 359 | d = (*state)[i][3]; 360 | 361 | (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); 362 | (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); 363 | (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); 364 | (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); 365 | } 366 | } 367 | 368 | 369 | // The SubBytes Function Substitutes the values in the 370 | // state matrix with values in an S-box. 371 | static void InvSubBytes(state_t* state) 372 | { 373 | uint8_t i, j; 374 | for (i = 0; i < 4; ++i) 375 | { 376 | for (j = 0; j < 4; ++j) 377 | { 378 | (*state)[j][i] = getSBoxInvert((*state)[j][i]); 379 | } 380 | } 381 | } 382 | 383 | static void InvShiftRows(state_t* state) 384 | { 385 | uint8_t temp; 386 | 387 | // Rotate first row 1 columns to right 388 | temp = (*state)[3][1]; 389 | (*state)[3][1] = (*state)[2][1]; 390 | (*state)[2][1] = (*state)[1][1]; 391 | (*state)[1][1] = (*state)[0][1]; 392 | (*state)[0][1] = temp; 393 | 394 | // Rotate second row 2 columns to right 395 | temp = (*state)[0][2]; 396 | (*state)[0][2] = (*state)[2][2]; 397 | (*state)[2][2] = temp; 398 | 399 | temp = (*state)[1][2]; 400 | (*state)[1][2] = (*state)[3][2]; 401 | (*state)[3][2] = temp; 402 | 403 | // Rotate third row 3 columns to right 404 | temp = (*state)[0][3]; 405 | (*state)[0][3] = (*state)[1][3]; 406 | (*state)[1][3] = (*state)[2][3]; 407 | (*state)[2][3] = (*state)[3][3]; 408 | (*state)[3][3] = temp; 409 | } 410 | #endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 411 | 412 | // Cipher is the main function that encrypts the PlainText. 413 | static void Cipher(state_t* state, const uint8_t* RoundKey) 414 | { 415 | uint8_t round = 0; 416 | 417 | // Add the First round key to the state before starting the rounds. 418 | AddRoundKey(0, state, RoundKey); 419 | 420 | // There will be Nr rounds. 421 | // The first Nr-1 rounds are identical. 422 | // These Nr rounds are executed in the loop below. 423 | // Last one without MixColumns() 424 | for (round = 1; ; ++round) 425 | { 426 | SubBytes(state); 427 | ShiftRows(state); 428 | if (round == Nr) { 429 | break; 430 | } 431 | MixColumns(state); 432 | AddRoundKey(round, state, RoundKey); 433 | } 434 | // Add round key to last round 435 | AddRoundKey(Nr, state, RoundKey); 436 | } 437 | 438 | #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 439 | static void InvCipher(state_t* state, const uint8_t* RoundKey) 440 | { 441 | uint8_t round = 0; 442 | 443 | // Add the First round key to the state before starting the rounds. 444 | AddRoundKey(Nr, state, RoundKey); 445 | 446 | // There will be Nr rounds. 447 | // The first Nr-1 rounds are identical. 448 | // These Nr rounds are executed in the loop below. 449 | // Last one without InvMixColumn() 450 | for (round = (Nr - 1); ; --round) 451 | { 452 | InvShiftRows(state); 453 | InvSubBytes(state); 454 | AddRoundKey(round, state, RoundKey); 455 | if (round == 0) { 456 | break; 457 | } 458 | InvMixColumns(state); 459 | } 460 | 461 | } 462 | #endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 463 | 464 | /*****************************************************************************/ 465 | /* Public functions: */ 466 | /*****************************************************************************/ 467 | #if defined(ECB) && (ECB == 1) 468 | 469 | 470 | void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf) 471 | { 472 | // The next function call encrypts the PlainText with the Key using AES algorithm. 473 | Cipher((state_t*)buf, ctx->RoundKey); 474 | } 475 | 476 | void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf) 477 | { 478 | // The next function call decrypts the PlainText with the Key using AES algorithm. 479 | InvCipher((state_t*)buf, ctx->RoundKey); 480 | } 481 | 482 | 483 | #endif // #if defined(ECB) && (ECB == 1) 484 | 485 | 486 | 487 | 488 | 489 | #if defined(CBC) && (CBC == 1) 490 | 491 | 492 | static void XorWithIv(uint8_t* buf, const uint8_t* Iv) 493 | { 494 | uint8_t i; 495 | for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size 496 | { 497 | buf[i] ^= Iv[i]; 498 | } 499 | } 500 | 501 | void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length) 502 | { 503 | size_t i; 504 | uint8_t *Iv = ctx->Iv; 505 | for (i = 0; i < length; i += AES_BLOCKLEN) 506 | { 507 | XorWithIv(buf, Iv); 508 | Cipher((state_t*)buf, ctx->RoundKey); 509 | Iv = buf; 510 | buf += AES_BLOCKLEN; 511 | } 512 | /* store Iv in ctx for next call */ 513 | memcpy(ctx->Iv, Iv, AES_BLOCKLEN); 514 | } 515 | 516 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) 517 | { 518 | size_t i; 519 | uint8_t storeNextIv[AES_BLOCKLEN]; 520 | for (i = 0; i < length; i += AES_BLOCKLEN) 521 | { 522 | memcpy(storeNextIv, buf, AES_BLOCKLEN); 523 | InvCipher((state_t*)buf, ctx->RoundKey); 524 | XorWithIv(buf, ctx->Iv); 525 | memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); 526 | buf += AES_BLOCKLEN; 527 | } 528 | 529 | } 530 | 531 | #endif // #if defined(CBC) && (CBC == 1) 532 | 533 | 534 | 535 | #if defined(CTR) && (CTR == 1) 536 | 537 | /* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ 538 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) 539 | { 540 | uint8_t buffer[AES_BLOCKLEN]; 541 | 542 | size_t i; 543 | int bi; 544 | for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) 545 | { 546 | if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ 547 | { 548 | 549 | memcpy(buffer, ctx->Iv, AES_BLOCKLEN); 550 | Cipher((state_t*)buffer,ctx->RoundKey); 551 | 552 | /* Increment Iv and handle overflow */ 553 | for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) 554 | { 555 | /* inc will overflow */ 556 | if (ctx->Iv[bi] == 255) 557 | { 558 | ctx->Iv[bi] = 0; 559 | continue; 560 | } 561 | ctx->Iv[bi] += 1; 562 | break; 563 | } 564 | bi = 0; 565 | } 566 | 567 | buf[i] = (buf[i] ^ buffer[bi]); 568 | } 569 | } 570 | 571 | #endif // #if defined(CTR) && (CTR == 1) 572 | 573 | --------------------------------------------------------------------------------