├── obfuscate_test_bloat.cpp ├── CMakeLists.txt ├── calculate_bloat.py ├── LICENSE ├── obfuscate_test.cpp ├── README.md └── obfuscate.h /obfuscate_test_bloat.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielkrupinski/Obfuscate/HEAD/obfuscate_test_bloat.cpp -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(Obfuscate) 4 | 5 | set(CMAKE_CXX_STANDARD 14) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | # Functional test 9 | add_executable(obfuscate_test obfuscate.h obfuscate_test.cpp) 10 | 11 | # Bloat test 12 | add_executable(obfuscate_test_bloat_on obfuscate.h obfuscate_test_bloat.cpp) 13 | add_executable(obfuscate_test_bloat_off obfuscate.h obfuscate_test_bloat.cpp) 14 | target_compile_definitions(obfuscate_test_bloat_on PRIVATE USE_AY_OBFUSCATE=1) -------------------------------------------------------------------------------- /calculate_bloat.py: -------------------------------------------------------------------------------- 1 | import os, errno, subprocess, sys 2 | 3 | def callCommand(command): 4 | process = subprocess.Popen(command, stdout=subprocess.PIPE) 5 | process.communicate() 6 | process.wait() 7 | 8 | def main(): 9 | dir = "test_bloat" 10 | try: 11 | os.makedirs(dir) 12 | except OSError as e: 13 | if e.errno != errno.EEXIST: 14 | raise 15 | os.chdir(dir) 16 | callCommand(["cmake", ".."]) 17 | callCommand(["cmake", "--build", ".", "--config", "Debug"]) 18 | callCommand(["cmake", "--build", ".", "--config", "Release"]) 19 | os.chdir("..") 20 | 21 | print ("| Config | Plain string literals | Obfuscated strings | Bloat |") 22 | print ("|:------:|:---------------------:|:------------------:|:-----:|") 23 | for config in ["Release", "Debug"]: 24 | sizeOn = os.stat(os.path.join(dir, config, "obfuscate_test_bloat_on.exe")).st_size 25 | sizeOff = os.stat(os.path.join(dir, config, "obfuscate_test_bloat_off.exe")).st_size 26 | print ("| {} | {} | {} | {} ({:.1f}%) |".format(config, sizeOff, sizeOn, sizeOn - sizeOff, (sizeOn / sizeOff - 1.0) * 100)) 27 | 28 | if __name__ == "__main__": 29 | main() -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /obfuscate_test.cpp: -------------------------------------------------------------------------------- 1 | #include "obfuscate.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // Test AY_OBFUSCATE (main test) 9 | { 10 | // Encrypt a string literal with an XOR cipher at compile time and store it in test. 11 | auto test = AY_OBFUSCATE("Hello World"); 12 | 13 | // The string starts out as encrypted 14 | assert(test.is_encrypted()); 15 | 16 | // Manually decrypt the string. This is useful for pre-processing especially large strings. 17 | test.decrypt(); 18 | assert(!test.is_encrypted()); 19 | 20 | // Output the plain text string to the console (implicitly converts to const char*) 21 | puts(test); 22 | 23 | // Re-encrypt the string so that it cannot be seen in it's plain text form in memory. 24 | test.encrypt(); 25 | assert(test.is_encrypted()); 26 | 27 | // The encrypted string will be automatically decrypted if necessary when implicitly converting to a const char* 28 | puts(test); 29 | 30 | // The string is in a decrypted state 31 | assert(!test.is_encrypted()); 32 | 33 | // Test comparison 34 | assert(std::string("Hello World") == (char*)test); 35 | } 36 | 37 | // Test AY_OBFUSCATE_KEY 38 | { 39 | auto test = AY_OBFUSCATE_KEY("Hello World", '@'); 40 | 41 | puts(test); 42 | 43 | // Test comparison 44 | assert(std::string("Hello World") == (char*)test); 45 | } 46 | 47 | // Test direct API usage 48 | { 49 | constexpr auto obfuscator = ay::makeObfuscator("Hello World"); 50 | auto test = ay::obfuscated_data(obfuscator); 51 | 52 | puts(test); 53 | 54 | // Test comparison 55 | assert(std::string("Hello World") == (char*)test); 56 | } 57 | 58 | return 0; 59 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Obfuscate 2 | Compile-time string literal obfuscation library for C++14. 3 | 4 | ## Whats the problem? 5 | When plain text string literals are used in C++ programs, they will be compiled as-is into the resultant binary. This causes them to be incredibly easy to find. One can simply open up the binary file in a text editor to see all of the embedded string literals in plain view. A special utility called [strings](https://en.wikipedia.org/wiki/Strings_(Unix)) actually exists which can be used to search binary files for plain text strings. 6 | 7 | ## What does this library do? 8 | This header-only library seeks to make it much much more difficult for embedded string literals in binary files to be found by encrypting them at compile-time, forcing the compiler to store the encrypted string literal instead of the plain text version. This will then be decrypted at runtime to be utilised within the program. 9 | 10 | ## How do I use this library? 11 | By simply wrapping your string literal `"My String"` with `AY_OBFUSCATE("My String")` it will be encrypted at compile time and stored in an `ay::obfuscated_data` object which you can manipulate at runtime. For convenience it is also implicitly convertable to a `char*`. 12 | 13 | For example, the following program will not store the string "Hello World" in plain text anywhere in the compiled executable. 14 | ```c++ 15 | int main() 16 | { 17 | std::cout << AY_OBFUSCATE("Hello World") << std::endl; 18 | return 0; 19 | } 20 | ``` 21 | 22 | ## Binary file size overhead 23 | This does come at a small cost. In a very simple program, which only prints out strings the following binary file bloat exists. 24 | 25 | | Config | Plain string literals | Obfuscated strings | Bloat | 26 | |:------:|:---------------------:|:------------------:|:-----:| 27 | | Release | 9216 | 9728 | 512 (5.6%) | 28 | | Debug | 37376 | 48640 | 11264 (30.1%) | 29 | 30 | This output is generated by running calculate_bloat.py 31 | -------------------------------------------------------------------------------- /obfuscate.h: -------------------------------------------------------------------------------- 1 | /* --------------------------- ABOUT --------------------------------- 2 | 3 | Original Author: Adam Yaxley 4 | Website: https://github.com/adamyaxley 5 | License: See end of file 6 | 7 | Obfuscate 8 | Guaranteed compile-time string literal obfuscation library for C++14 9 | 10 | Usage: 11 | Pass string literals into the AY_OBFUSCATE macro to obfuscate them at 12 | compile time. AY_OBFUSCATE returns a temporary ay::obfuscated_data 13 | object that is implicitly convertable to a char*. 14 | 15 | Example: 16 | auto obfuscated_string = AY_OBFUSCATE("Hello World"); 17 | std::cout << obfuscated_string << std::endl; 18 | 19 | ------------------------------------------------------------------- */ 20 | 21 | #include 22 | 23 | namespace ay 24 | { 25 | // Obfuscates a string at compile time 26 | template 27 | class obfuscator 28 | { 29 | public: 30 | // Obfuscates the string 'data' on construction 31 | constexpr obfuscator(const char* data) 32 | { 33 | static_assert(KEY != '\0', "KEY must not be the null character."); 34 | 35 | // On construction each of the characters in the string is obfuscated with an XOR cipher based on KEY 36 | for (std::size_t i = 0; i < N; i++) 37 | { 38 | m_data[i] = data[i] ^ KEY; 39 | } 40 | } 41 | 42 | constexpr const char* getData() const 43 | { 44 | return &m_data[0]; 45 | } 46 | 47 | constexpr std::size_t getSize() const 48 | { 49 | return N; 50 | } 51 | 52 | constexpr char getKey() const 53 | { 54 | return KEY; 55 | } 56 | 57 | private: 58 | 59 | char m_data[N]{}; 60 | }; 61 | 62 | // Handles decryption and re-encryption of an encrypted string at runtime 63 | template 64 | class obfuscated_data 65 | { 66 | public: 67 | obfuscated_data(const obfuscator& obfuscator) 68 | { 69 | for (int i = 0; i < N; i++) 70 | { 71 | m_data[i] = obfuscator.getData()[i]; 72 | } 73 | } 74 | 75 | ~obfuscated_data() 76 | { 77 | // Zero m_data to remove it from memory 78 | for (int i = 0; i < N; i++) 79 | { 80 | m_data[i] = 0; 81 | } 82 | } 83 | 84 | // Returns a pointer to the plain text string, decrypting it if necessary 85 | operator char*() 86 | { 87 | decrypt(); 88 | return m_data; 89 | } 90 | 91 | // Manually decrypt the string 92 | void decrypt() 93 | { 94 | if (is_encrypted()) 95 | { 96 | for (std::size_t i = 0; i < N; i++) 97 | { 98 | m_data[i] ^= KEY; 99 | } 100 | } 101 | } 102 | 103 | // Manually re-encrypt the string 104 | void encrypt() 105 | { 106 | if (!is_encrypted()) 107 | { 108 | for (std::size_t i = 0; i < N; i++) 109 | { 110 | m_data[i] ^= KEY; 111 | } 112 | } 113 | } 114 | 115 | // Returns true if this string is currently encrypted, false otherwise. 116 | bool is_encrypted() const 117 | { 118 | return m_data[N - 1] != '\0'; 119 | } 120 | 121 | private: 122 | 123 | // Local storage for the string. Call is_encrypted() to check whether or not the string is currently obfuscated. 124 | char m_data[N]; 125 | }; 126 | 127 | // This function exists purely to extract the number of elements 'N' in the array 'data' 128 | template 129 | constexpr auto makeObfuscator(const char(&data)[N]) 130 | { 131 | return obfuscator(data); 132 | } 133 | } 134 | 135 | // Obfuscates the string 'data' at compile-time and returns an ay::obfuscated_data object that has 136 | // functions for decrypting the string and is also implicitly convertable to a char* 137 | #define AY_OBFUSCATE(data) AY_OBFUSCATE_KEY(data, '.') 138 | 139 | // Obfuscates the string 'data' with 'key' at compile-time and returns an ay::obfuscated_data object that has 140 | // functions for decrypting the string and is also implicitly convertable to a char* 141 | #define AY_OBFUSCATE_KEY(data, key) \ 142 | [](){ \ 143 | constexpr auto n = sizeof(data)/sizeof(data[0]); \ 144 | static_assert(data[n - 1] == '\0', "String must be null terminated"); \ 145 | constexpr auto obfuscator = ay::makeObfuscator(data); \ 146 | return ay::obfuscated_data(obfuscator); \ 147 | }() 148 | 149 | /* --------------------------- LICENSE ------------------------------- 150 | 151 | Public Domain (http://www.unlicense.org) 152 | 153 | This is free and unencumbered software released into the public domain. 154 | 155 | Anyone is free to copy, modify, publish, use, compile, sell, or 156 | distribute this software, either in source code form or as a compiled 157 | binary, for any purpose, commercial or non-commercial, and by any 158 | means. 159 | 160 | In jurisdictions that recognize copyright laws, the author or authors 161 | of this software dedicate any and all copyright interest in the 162 | software to the public domain. We make this dedication for the benefit 163 | of the public at large and to the detriment of our heirs and 164 | successors. We intend this dedication to be an overt act of 165 | relinquishment in perpetuity of all present and future rights to this 166 | software under copyright law. 167 | 168 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 169 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 170 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 171 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 172 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 173 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 174 | OTHER DEALINGS IN THE SOFTWARE. 175 | 176 | ------------------------------------------------------------------- */ 177 | --------------------------------------------------------------------------------