├── 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 |
--------------------------------------------------------------------------------