├── docs
├── how.md
├── reverse-engineering.md
├── images
│ └── Logo-128x128.png
├── index.md
├── obfuscation.md
└── installation.md
├── cmake
└── AdvobfuscatorConfig.cmake.in
├── tests
├── CMakeLists.txt
├── Tests.vcxproj.filters
├── Tests.vcxproj
└── main.cpp
├── Examples
├── CMakeLists.txt
├── Examples.vcxproj.filters
├── demo
│ ├── guessme.cpp
│ ├── guessme_aes.cpp
│ └── demo.cpp
├── example
│ ├── hexdump.h
│ ├── describe.h
│ └── main.cpp
└── Examples.vcxproj
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── CHANGELOG.txt
├── include
└── advobfuscator
│ ├── format.h
│ ├── call.h
│ ├── aes_string.h
│ ├── string.h
│ ├── bytes.h
│ ├── random.h
│ ├── fsm.h
│ ├── obf.h
│ └── aes.h
├── ADVobfuscator.sln
├── README.md
└── ADVobfuscator.xcodeproj
└── project.pbxproj
/docs/how.md:
--------------------------------------------------------------------------------
1 | # How it works
2 |
3 |
--------------------------------------------------------------------------------
/docs/reverse-engineering.md:
--------------------------------------------------------------------------------
1 | # Reverse engineering of an obfuscated executable
2 |
3 |
--------------------------------------------------------------------------------
/docs/images/Logo-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andrivet/ADVobfuscator/HEAD/docs/images/Logo-128x128.png
--------------------------------------------------------------------------------
/cmake/AdvobfuscatorConfig.cmake.in:
--------------------------------------------------------------------------------
1 | @PACKAGE_INIT@
2 |
3 | include("${CMAKE_CURRENT_LIST_DIR}/advobfuscatorTargets.cmake")
4 |
--------------------------------------------------------------------------------
/tests/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_executable(tests main.cpp)
2 | target_link_libraries(tests advobfuscator)
3 | add_test(NAME tests COMMAND tests)
4 |
--------------------------------------------------------------------------------
/Examples/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_executable(example example/main.cpp)
2 | target_link_libraries(example advobfuscator)
3 |
4 | add_executable(demo demo/demo.cpp)
5 | set_target_properties(demo PROPERTIES CXX_VISIBILITY_PRESET hidden)
6 | target_link_libraries(demo advobfuscator)
7 |
8 | add_executable(guessme demo/guessme.cpp)
9 | set_target_properties(guessme PROPERTIES CXX_VISIBILITY_PRESET hidden)
10 | target_link_libraries(guessme advobfuscator)
11 |
12 | add_executable(guessme_aes demo/guessme_aes.cpp)
13 | set_target_properties(guessme_aes PROPERTIES CXX_VISIBILITY_PRESET hidden)
14 | target_link_libraries(guessme_aes advobfuscator)
15 |
--------------------------------------------------------------------------------
/tests/Tests.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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | build/
3 | *.pbxuser
4 | !default.pbxuser
5 | *.mode1v3
6 | !default.mode1v3
7 | *.mode2v3
8 | !default.mode2v3
9 | *.perspectivev3
10 | !default.perspectivev3
11 | *.xcworkspace
12 | !default.xcworkspace
13 | xcuserdata
14 | profile
15 | *.moved-aside
16 | DerivedData
17 | .idea/
18 | Pods
19 |
20 | ## Ignore Visual Studio temporary files, build results, and
21 | ## files generated by popular Visual Studio add-ons.
22 |
23 | # User-specific files
24 | *.suo
25 | *.user
26 | *.userosscache
27 | *.sln.docstates
28 |
29 | # Build results
30 | [Dd]ebug/
31 | [Dd]ebugPublic/
32 | [Rr]elease/
33 | [Rr]eleases/
34 | x64/
35 | x86/
36 | build/
37 | bld/
38 | [Bb]in/
39 | [Oo]bj/
40 |
41 | # Visual Studio 2015 cache/options directory
42 | .vs/
43 |
44 | *_i.c
45 | *_p.c
46 | *_i.h
47 | *.ilk
48 | *.meta
49 | *.obj
50 | *.pch
51 | *.pdb
52 | *.pgc
53 | *.pgd
54 | *.rsp
55 | *.sbr
56 | *.tlb
57 | *.tli
58 | *.tlh
59 | *.tmp
60 | *.tmp_proj
61 | *.log
62 | *.vspscc
63 | *.vssscc
64 | .builds
65 | *.pidb
66 | *.svclog
67 | *.scc
68 |
69 | # Visual C++ cache files
70 | ipch/
71 | *.aps
72 | *.ncb
73 | *.opensdf
74 | *.sdf
75 | *.cachefile
76 |
77 | *.o
78 | Sample
79 |
--------------------------------------------------------------------------------
/Examples/Examples.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 | Header Files
20 |
21 |
22 | Header Files
23 |
24 |
25 | Header Files
26 |
27 |
28 | Header Files
29 |
30 |
31 |
32 |
33 | Source Files
34 |
35 |
36 | Source Files
37 |
38 |
39 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.14)
2 | project(advobfuscator VERSION 2.1.0 LANGUAGES CXX)
3 |
4 | option(BUILD_TESTING "Build unit tests" ON)
5 | option(BUILD_EXAMPLES "Build examples" ON)
6 |
7 | set(CMAKE_CXX_STANDARD 20)
8 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
9 |
10 | add_library(advobfuscator INTERFACE)
11 |
12 | target_include_directories(advobfuscator INTERFACE
13 | $
14 | $
15 | )
16 |
17 | include(GNUInstallDirs)
18 | install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
19 | install(TARGETS advobfuscator EXPORT advobfuscatorTargets)
20 | install(EXPORT advobfuscatorTargets
21 | FILE advobfuscatorTargets.cmake
22 | NAMESPACE advobfuscator::
23 | DESTINATION ${CMAKE_INSTALL_DATADIR}/advobfuscator
24 | )
25 |
26 | include(CMakePackageConfigHelpers)
27 | write_basic_package_version_file(
28 | "${CMAKE_CURRENT_BINARY_DIR}/advobfuscatorConfigVersion.cmake"
29 | VERSION ${PROJECT_VERSION}
30 | COMPATIBILITY SameMajorVersion
31 | ARCH_INDEPENDENT
32 | )
33 | configure_package_config_file(
34 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/AdvobfuscatorConfig.cmake.in"
35 | "${CMAKE_CURRENT_BINARY_DIR}/advobfuscatorConfig.cmake"
36 | INSTALL_DESTINATION ${CMAKE_INSTALL_DATADIR}/advobfuscator
37 | )
38 | install(FILES
39 | "${CMAKE_CURRENT_BINARY_DIR}/advobfuscatorConfig.cmake"
40 | "${CMAKE_CURRENT_BINARY_DIR}/advobfuscatorConfigVersion.cmake"
41 | DESTINATION ${CMAKE_INSTALL_DATADIR}/advobfuscator
42 | )
43 |
44 | if(BUILD_TESTING)
45 | enable_testing()
46 | add_subdirectory(tests)
47 | endif()
48 |
49 | if(BUILD_EXAMPLES)
50 | add_subdirectory(examples)
51 | endif()
52 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The Clear BSD License
2 |
3 | Copyright (c) 2025 Sebastien Andrivet
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted (subject to the limitations in the disclaimer
8 | below) provided that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice,
11 | this list of conditions and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright
14 | notice, this list of conditions and the following disclaimer in the
15 | documentation and/or other materials provided with the distribution.
16 |
17 | * Neither the name of the copyright holder nor the names of its
18 | contributors may be used to endorse or promote products derived from this
19 | software without specific prior written permission.
20 |
21 | NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
22 | THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | POSSIBILITY OF SUCH DAMAGE.
33 |
--------------------------------------------------------------------------------
/Examples/demo/guessme.cpp:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #include
29 | #include
30 |
31 | using namespace andrivet::advobfuscator;
32 |
33 | int main() {
34 | std::string guess;
35 | std::cout << "Guess me if you can: "_obf;
36 | if(std::cin >> guess; guess == "C++rocks"_obf.decode())
37 | std::cout << "Congratulations\n"_obf;
38 | else
39 | std::cout << "Nope\n"_obf;
40 | }
41 |
--------------------------------------------------------------------------------
/Examples/demo/guessme_aes.cpp:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #include
29 | #include
30 |
31 | using namespace andrivet::advobfuscator;
32 |
33 | int main() {
34 | std::string guess;
35 | std::cout << "Guess me if you can: "_aes;
36 | if(std::cin >> guess; guess == "C++rocks"_aes.decrypt())
37 | std::cout << "Congratulations\n"_aes;
38 | else
39 | std::cout << "Nope\n"_aes;
40 | }
41 |
--------------------------------------------------------------------------------
/Examples/example/hexdump.h:
--------------------------------------------------------------------------------
1 | // https://github.com/zmb3/hexdump
2 | // (edited)
3 |
4 | #ifndef HEXDUMP_HPP
5 | #define HEXDUMP_HPP
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | namespace andrivet::advobfuscator {
12 | template
13 | struct ObfuscatedBytes;
14 | }
15 |
16 | namespace hexdump {
17 |
18 | template
19 | struct custom_hexdump {
20 | custom_hexdump(const void *data, std::size_t length)
21 | : data_{static_cast(data)}, length_{length} {}
22 |
23 | template
24 | custom_hexdump(const std::array &block)
25 | : data_(block.data()), length_{block.size()} {}
26 |
27 | template
28 | custom_hexdump(andrivet::advobfuscator::ObfuscatedBytes block)
29 | : data_(block.data()), length_{block.size()} {}
30 |
31 | const std::uint8_t *data_;
32 | const std::size_t length_;
33 | };
34 |
35 | template
36 | std::basic_ostream &operator<<(std::basic_ostream &out, const custom_hexdump &dump) {
37 | out.fill('0');
38 | for(std::size_t i = 0; i < dump.length_; i += row_size) {
39 | out << "0x" << std::setw(6) << std::hex << i << std::dec << ": ";
40 | for(int j = 0; j < row_size; ++j) {
41 | if (i + j < dump.length_)
42 | out << std::hex << std::setw(2) << static_cast(dump.data_[i + j]) << std::dec << " ";
43 | else
44 | out << " ";
45 | }
46 |
47 | out << " ";
48 | if(show_ascii) {
49 | for(std::size_t j = 0; j < row_size; ++j) {
50 | if(i + j < dump.length_) {
51 | if(std::isprint(dump.data_[i + j]))
52 | out << static_cast(dump.data_[i + j]);
53 | else
54 | out << ".";
55 | }
56 | }
57 | }
58 | out << '\n';
59 | }
60 |
61 | return out;
62 | }
63 |
64 | using hexdump8 = custom_hexdump<8, true>;
65 | using hexdump16 = custom_hexdump<16, true>;
66 | using hexdump32 = custom_hexdump<32, true>;
67 | }
68 |
69 | #endif // HEXDUMP_HPP
70 |
--------------------------------------------------------------------------------
/CHANGELOG.txt:
--------------------------------------------------------------------------------
1 | 2.1.1 - December 11, 2025
2 | -------------------------
3 |
4 | - #43 - Fix CMakeFiles.txt
5 |
6 | 2.1.0 - November 29, 2025
7 | -------------------------
8 |
9 | - #40 - Fix an issue when trying to include the head-only file multiple times.
10 |
11 | 2.0.0 - August 29, 2025
12 | ---------------
13 |
14 | - A brand-new implementation based on C++20, UDL (user-defined literals) and more.
15 |
16 | August 2017
17 | ------------
18 |
19 | - Enhance the compatibility with compiler's optimizer (such as `GCC` with `O3`). Previously, the code was sometimes overoptimized by the compiler and as a consequence, non-obfuscated strings remains in the binary code. This was more or less due to a bug in **ADVobfuscator** (I was not using `volatile`).
20 | - Fix a stupid mistake in algorithm #2 (shift). Sometimes, the encryption key is 0.
21 | - Clearly seperate the library code (under `Lib`) from the examples (under `Examples` and `DocCode`).
22 | - Check the compatibility with `C++14`. In the future, I will probably drop the support of `C++11` and keep only `C++14` and `C++17` compatibility.
23 |
24 | August 2016
25 | -----------
26 |
27 | Several enhancement (suppress warnings, fix some errors, enhance portability). In more details:
28 |
29 | - Increase the warning level for Visual Studio and GCC
30 | - Remove several (all) compilation warnings
31 | - Replace `int` by `size_t` when it is appropriate (`sizeof`, `position`)
32 | - Remove unused parameters
33 | - Keys are now of type `char` (were of type `int`, it was wrong)
34 | - Replace the non-portable `##__VA_ARGS__` by the portable `__VA_ARGS__`. As a consequence, new macros (ending with `0`) are defined when there are no parameters
35 | - Remove some (stupid) syntax errors (`;` at the end of some functions). C/C++ is odd: you have to put a `;` when you define a `struct`, but none when you define a function
36 | - A integral type is computed at compile time to store pointers to functions
37 |
38 | August 2015
39 | -----------
40 |
41 | **ADVobfuscator** code has been updated for Visual Studio 2015. **ADVobfuscator** is now compatible with the RTM release of Visual Studio 2015 (previous versions or CTP releases are not). The whitepaper is not yet updated. The code has also been modified in order to avoid problems with `O3` optimization and `GCC`. `GCC` with `O3` defeats obfuscation (because it optimizes too much) and sometimes generates wrong code (not clear yet if it is a bug in `GCC` or in **ADVobfuscator**).
42 |
43 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # ADVobfuscator
2 |
3 | 
4 |
5 | **ADVobfuscator** is a library that uses the `C++20` language to generate, at compile time, obfuscated data and code without using any external tool and without modifying the compiler.
6 | Strings or blocks of data can be obfuscated or encrypted at compile time, and they are decoded at runtime:
7 |
8 | ```c++
9 | std::count << "Obfuscated string"_obf << "\n";
10 | ```
11 |
12 | > Note: This documentation describes the version 2 of **ADVobfuscator**, a complete rewrite of the library that uses C++20 specific features.
13 | > The previous version (legacy) was using C++14.
14 |
15 | ## Table of content
16 |
17 | * [Installation and Usage](installation.md)
18 | * [How to obfuscate or encrypt data and code](obfuscation.md)
19 | * [Reverse engineering of an obfuscated executable](reverse-engineering.md)
20 | * [How it works](how.md)
21 |
22 | ## Copyright and license
23 |
24 | Copyright (c) 2025 Sebastien Andrivet
25 | All rights reserved.
26 |
27 | Redistribution and use in source and binary forms, with or without
28 | modification, are permitted (subject to the limitations in the disclaimer
29 | below) provided that the following conditions are met:
30 |
31 | * Redistributions of source code must retain the above copyright notice,
32 | this list of conditions and the following disclaimer.
33 |
34 | * Redistributions in binary form must reproduce the above copyright
35 | notice, this list of conditions and the following disclaimer in the
36 | documentation and/or other materials provided with the distribution.
37 |
38 | * Neither the name of the copyright holder nor the names of its
39 | contributors may be used to endorse or promote products derived from this
40 | software without specific prior written permission.
41 |
42 | NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
43 | THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
44 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
45 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
46 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
47 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
48 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
49 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
50 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
51 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
52 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
53 | POSSIBILITY OF SUCH DAMAGE.
54 |
55 |
--------------------------------------------------------------------------------
/include/advobfuscator/format.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #ifndef ADVOBFUSCATOR_FORMAT_H
29 | #define ADVOBFUSCATOR_FORMAT_H
30 |
31 | #include
32 | #include "string.h"
33 | #include "aes_string.h"
34 |
35 | /// Formatter for Obfuscated strings
36 | template
37 | struct std::formatter> {
38 | constexpr auto parse(std::format_parse_context& ctx) {
39 | return ctx.begin();
40 | }
41 |
42 | auto format(const andrivet::advobfuscator::ObfuscatedString &s, std::format_context& ctx) const {
43 | return std::format_to(ctx.out(), "{}", s.decode());
44 | }
45 | };
46 |
47 | /// Formatter for encrypted strings (AES)
48 | template
49 | struct std::formatter> {
50 | constexpr auto parse(std::format_parse_context& ctx) {
51 | return ctx.begin();
52 | }
53 |
54 | auto format(const andrivet::advobfuscator::AesString &s, std::format_context& ctx) const {
55 | return std::format_to(ctx.out(), "{}", s.decrypt());
56 | }
57 | };
58 |
59 | #endif //ADVOBFUSCATOR_FORMAT_H
60 |
--------------------------------------------------------------------------------
/ADVobfuscator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.14.36121.58
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Examples", "Examples\Examples.vcxproj", "{0538D995-5491-4772-B435-80DCC996F467}"
7 | EndProject
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tests", "Tests\Tests.vcxproj", "{93014D06-B4A0-45E9-A1FC-D10A51E3286A}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|ARM64 = Debug|ARM64
13 | Debug|x64 = Debug|x64
14 | Debug|x86 = Debug|x86
15 | Release|ARM64 = Release|ARM64
16 | Release|x64 = Release|x64
17 | Release|x86 = Release|x86
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {0538D995-5491-4772-B435-80DCC996F467}.Debug|ARM64.ActiveCfg = Debug|ARM64
21 | {0538D995-5491-4772-B435-80DCC996F467}.Debug|ARM64.Build.0 = Debug|ARM64
22 | {0538D995-5491-4772-B435-80DCC996F467}.Debug|x64.ActiveCfg = Debug|x64
23 | {0538D995-5491-4772-B435-80DCC996F467}.Debug|x64.Build.0 = Debug|x64
24 | {0538D995-5491-4772-B435-80DCC996F467}.Debug|x86.ActiveCfg = Debug|Win32
25 | {0538D995-5491-4772-B435-80DCC996F467}.Debug|x86.Build.0 = Debug|Win32
26 | {0538D995-5491-4772-B435-80DCC996F467}.Release|ARM64.ActiveCfg = Release|ARM64
27 | {0538D995-5491-4772-B435-80DCC996F467}.Release|ARM64.Build.0 = Release|ARM64
28 | {0538D995-5491-4772-B435-80DCC996F467}.Release|x64.ActiveCfg = Release|x64
29 | {0538D995-5491-4772-B435-80DCC996F467}.Release|x64.Build.0 = Release|x64
30 | {0538D995-5491-4772-B435-80DCC996F467}.Release|x86.ActiveCfg = Release|Win32
31 | {0538D995-5491-4772-B435-80DCC996F467}.Release|x86.Build.0 = Release|Win32
32 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Debug|ARM64.ActiveCfg = Debug|ARM64
33 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Debug|ARM64.Build.0 = Debug|ARM64
34 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Debug|x64.ActiveCfg = Debug|x64
35 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Debug|x64.Build.0 = Debug|x64
36 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Debug|x86.ActiveCfg = Debug|Win32
37 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Debug|x86.Build.0 = Debug|Win32
38 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Release|ARM64.ActiveCfg = Release|ARM64
39 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Release|ARM64.Build.0 = Release|ARM64
40 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Release|x64.ActiveCfg = Release|x64
41 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Release|x64.Build.0 = Release|x64
42 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Release|x86.ActiveCfg = Release|Win32
43 | {93014D06-B4A0-45E9-A1FC-D10A51E3286A}.Release|x86.Build.0 = Release|Win32
44 | EndGlobalSection
45 | GlobalSection(SolutionProperties) = preSolution
46 | HideSolutionNode = FALSE
47 | EndGlobalSection
48 | GlobalSection(ExtensibilityGlobals) = postSolution
49 | SolutionGuid = {3684C6E5-84F0-4F95-8678-61569B50F4A7}
50 | EndGlobalSection
51 | EndGlobal
52 |
--------------------------------------------------------------------------------
/docs/obfuscation.md:
--------------------------------------------------------------------------------
1 | # How to obfuscate or encrypt data and code
2 |
3 | ## Obfuscation of strings
4 |
5 | Strings can be obfuscated using ADVobfuscator UDL (user-defined literal) `_obf`:
6 |
7 | ```c++
8 | #include
9 |
10 | std::cout << "abc"_obf << '\n';
11 | ```
12 |
13 | The string is obfuscated at compile time.
14 | The UDL constructs (at compile-time) an instance of `ObfuscatedString`.
15 | At run-time, there is an implicit cast operator to `const char*` so the deobfuscated string can be converted.
16 | This code is thus (almost) equivalent to:
17 |
18 | ```c++
19 | std::cout << ObfuscatedString{"abc"}.decode() << '\n';
20 | ```
21 |
22 | It is also possible to use `std::format`:
23 |
24 | ```c++
25 | #include
26 |
27 | std::cout << std::format("{}\n", "abc"_obf);
28 | ```
29 |
30 | Instances of obfuscated strings can be manipulated like any object.
31 | The implicit cast operator to `const char*` does modify the instance however (to decode the string).
32 | If the instance is immutable, you have to call explicitly `decode()` that returns a `std::string` and does not modify the instance:
33 |
34 | ```c++
35 | static constexpr auto s4 = "An immutable compile-time string"_obf;
36 | std::cout << s4.decode() << '\n';
37 | ```
38 |
39 | ## Obfuscation of data
40 |
41 | Blocks of data (`uint8_t`) can be obfuscated at compile-time using `_obf_bytes`:
42 |
43 | ```c++
44 | #include
45 |
46 | static constexpr auto data = "01 02 04 08 10 20 40 80 1b 36"_obf_bytes;
47 | ```
48 |
49 | The format has to follow these rules:
50 |
51 | * Each byte is represented by two hexadecimal digits.
52 | * These hexadecimal digits can be in lower or upper case.
53 | * Bytes have to be separated by space.
54 |
55 | At compile-time, an instance of `ObfuscatedBytes` is created.
56 | This class provides a subscript operator that decodes, at run-time, the obfuscated data:
57 |
58 | ```c++
59 | auto d = data[0]; // d is an uin8_t
60 | ```
61 |
62 | It is also possible to decode the whole data with `decode()`:
63 |
64 | ```c++
65 | auto decoded = data.decode(); // decoded is an std::array
66 | ```
67 |
68 | There is also a `data()` member function that decodes the data in-place:
69 |
70 | ```c++
71 | auto data = "01 02 04 08 10 20 40 80 1b 36"_obf_bytes;
72 | auto decoded = data.data(); // decoded is an const std::uint8_t
73 | ```
74 |
75 |
76 | ## Encryption of strings with AES
77 |
78 | In this version, it is also possible to encrypt the strings at compile-time using AES.
79 | The usage is however limited because of limitation of compilers (compile-time AES is quite complex for them).
80 | In practice, you can also encrypt strings that are not too long with the `_aes` UDL.
81 | The behaviour is similar to obfuscated strings:
82 |
83 | ```c++
84 | #include
85 |
86 | std::cout << "This is a string containing a secret that has to be hidden with AES"_aes << "\n";
87 | ```
88 |
89 | > Note: The S-box and other well-known data used by AES are obfuscated.
90 |
91 |
92 | ## Obfuscation of function calls
93 |
94 |
--------------------------------------------------------------------------------
/include/advobfuscator/call.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator - Finite state machine generated at compile time
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #ifndef ADVOBFUSCATOR_CALL_H
29 | #define ADVOBFUSCATOR_CALL_H
30 |
31 | #include
32 | #include "fsm.h"
33 |
34 | namespace andrivet::advobfuscator {
35 |
36 | template
37 | struct ObfuscatedCall {
38 | consteval ObfuscatedCall(std::uint32_t recognize, F fn)
39 | : fsm_{recognize, fn} {
40 | }
41 |
42 | template
43 | decltype(auto) operator()(std::uint32_t value, Args... args) const {
44 | auto fn = fsm_.run(value);
45 | if constexpr (std::is_void_v) {
46 | std::invoke(fn, args...);
47 | return;
48 | }
49 | else
50 | return std::invoke(fn, args...);
51 | }
52 |
53 | Fsm fsm_;
54 | };
55 |
56 | template
57 | struct ObfuscatedMethodCall {
58 | consteval ObfuscatedMethodCall(std::uint32_t recognize, F fn)
59 | : fsm_{recognize, fn} {
60 | }
61 |
62 | template
63 | decltype(auto) operator()(std::uint32_t value, O o, Args... args) const {
64 | auto fn = fsm_.run(value);
65 | if constexpr (std::is_void_v) {
66 | std::invoke(fn, o, args...);
67 | return;
68 | }
69 | else
70 | return std::invoke(fn, args...);
71 | }
72 |
73 | Fsm fsm_;
74 | };
75 |
76 | }
77 |
78 | #endif
79 |
--------------------------------------------------------------------------------
/docs/installation.md:
--------------------------------------------------------------------------------
1 | # Installation & Usage
2 |
3 | **ADVobjuscator** is a header-only C++ library that integrates cleanly with CMake.
4 | There are several possibilities to install and use it:
5 |
6 | * Option 0: Manual download
7 | * Option 1: Install & Use via `find_package`
8 | * Option 2: Add as a Git Submodule / Subdirectory
9 | * Option 3: Use with `FetchContent` in CMake
10 |
11 | ## Option 0: Manual download
12 |
13 | If you don’t use CMake or prefer to copy files manually:
14 |
15 | 1. Download
16 |
17 | * Click the green “Code” button on GitHub → Download ZIP
18 | * or download only the `include/` folder
19 |
20 | 2. Copy
21 |
22 | Copy the `include/advobfuscator/` directory into your own project’s `include/` folder.
23 |
24 | 3. Include It in Your Code
25 |
26 | ```c++
27 | #include "advobfuscator/obfuscate.h"
28 | ```
29 |
30 | Make sure your compiler includes the path:
31 |
32 | ```shell
33 | g++ -Iinclude myapp.cpp
34 | ```
35 |
36 | If you are using CMake, here is an example of `CMakeFiles.txt`:
37 |
38 | ```cmake
39 | cmake_minimum_required(VERSION 3.14)
40 | project(myproject LANGUAGES CXX)
41 |
42 | set(CMAKE_CXX_STANDARD 20)
43 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
44 |
45 | add_executable(myapp src/main.cpp)
46 |
47 | # Add the path to the manually downloaded headers
48 | target_include_directories(myapp PRIVATE ${CMAKE_SOURCE_DIR}/include)
49 | ```
50 |
51 |
52 | ## Option 1: Install & Use via `find_package`
53 |
54 | 1. Clone and Install the Library
55 |
56 | ```shell
57 | git clone https://github.com/yourusername/advobfuscator.git
58 | cd advobfuscator
59 | cmake -B build -DCMAKE_INSTALL_PREFIX=/your/install/prefix
60 | cmake --build build --target install
61 | ```
62 |
63 | Replace `/your/install/prefix` with the desired install location (e.g., `/usr/local` or a custom path).
64 |
65 | 2. Link from Your CMake Project
66 |
67 | ```cmake
68 | cmake_minimum_required(VERSION 3.14)
69 | project(myproject)
70 |
71 | # Add path to CMAKE_PREFIX_PATH if not system-installed
72 | list(APPEND CMAKE_PREFIX_PATH "/your/install/prefix")
73 |
74 | find_package(advobfuscator REQUIRED)
75 |
76 | add_executable(myapp main.cpp)
77 | target_link_libraries(myapp PRIVATE advobfuscator::advobfuscator)
78 | ```
79 |
80 | ## Option 2: Add as a Git Submodule / Subdirectory
81 |
82 | 1. Add the Library to Your Project
83 |
84 | ```shell
85 | git submodule add https://github.com/andrivet/advobfuscator.git external/advobfuscator
86 | ```
87 |
88 | 2. Link from Your CMake Project
89 |
90 | ```cmake
91 | add_subdirectory(external/advobfuscator)
92 |
93 | add_executable(myapp main.cpp)
94 | target_link_libraries(myapp PRIVATE advobfuscator::advobfuscator)
95 | ```
96 |
97 | ## Option 3: Use with FetchContent in CMake
98 |
99 | If you want CMake (3.14 or higher) to automatically fetch and integrate **ADVObfuscator**:
100 |
101 | ```cmake
102 | include(FetchContent)
103 |
104 | FetchContent_Declare(
105 | advobfuscator
106 | GIT_REPOSITORY https://github.com/andrivet/advobfuscator.git
107 | GIT_TAG v2.0 # Or use a branch or commit hash
108 | )
109 |
110 | FetchContent_MakeAvailable(advobfuscator)
111 |
112 | add_executable(myapp main.cpp)
113 | target_link_libraries(myapp PRIVATE advobfuscator::advobfuscator)
114 | ```
115 |
--------------------------------------------------------------------------------
/Examples/example/describe.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #include
29 | #include
30 | #include
31 | #include "hexdump.h"
32 |
33 | inline void describe(andrivet::advobfuscator::KeyAlgorithm algo) {
34 | using namespace andrivet::advobfuscator;
35 | switch(algo) {
36 | using enum KeyAlgorithm;
37 | case IDENTITY: std::cout << "Identity"; break;
38 | case INCREMENT: std::cout << "Increment"; break;
39 | case INVERT: std::cout << "Invert"; break;
40 | case SUBSTITUTE: std::cout << "Substitute"; break;
41 | case SWAP: std::cout << "Swap"; break;
42 | default: std::cout << "Unknown"; break;
43 | }
44 | }
45 |
46 | inline void describe(andrivet::advobfuscator::DataAlgorithm algo) {
47 | using namespace andrivet::advobfuscator;
48 | switch(algo) {
49 | using enum DataAlgorithm;
50 | case IDENTITY: std::cout << "Identity"; break;
51 | case CAESAR: std::cout << "Caesar"; break;
52 | case XOR: std::cout << "XOR"; break;
53 | case ROTATE: std::cout << "Rotate bits"; break;
54 | case SUBSTITUTE: std::cout << "Substitute"; break;
55 | default: std::cout << "Unknown"; break;
56 | }
57 | }
58 |
59 | template
60 | void describe(const andrivet::advobfuscator::ObfuscatedString &str, bool raw = true) {
61 | using namespace andrivet::advobfuscator;
62 | std::cout << "Obfuscated: " << (str.obfuscated_ ? "Yes" : "No") << '\n';
63 | std::cout << "Algorithms: ";
64 | for(unsigned i = 0; i < details::MAX_NB_ALGORITHMS; ++i) {
65 | const auto algo = str.algos_[i];
66 | if(algo.key_algo() == KeyAlgorithm::IDENTITY && algo.data_algo() == DataAlgorithm::IDENTITY) continue;
67 | std::cout << "(K=" << static_cast(algo.key());
68 | std::cout << ", KA="; describe(algo.key_algo());
69 | std::cout << ", DA="; describe(algo.data_algo());
70 | std::cout << ") ";
71 | }
72 | std::cout << '\n';
73 | if(raw) {
74 | std::cout << "Raw data:\n";
75 | std::cout << hexdump::hexdump16(str.raw(), N) << "\n";
76 | }
77 | }
78 |
79 | template
80 | void describe(const andrivet::advobfuscator::ObfuscatedBytes &block, bool raw = true) {
81 | using namespace andrivet::advobfuscator;
82 | std::cout << "Algorithms: ";
83 | for(unsigned i = 0; i < details::MAX_NB_ALGORITHMS; ++i) {
84 | const auto algo = block.algos_[i];
85 | if(algo.key_algo() == KeyAlgorithm::IDENTITY && algo.data_algo() == DataAlgorithm::IDENTITY) continue;
86 | std::cout << "(K=" << static_cast(algo.key());
87 | std::cout << ", KA="; describe(algo.key_algo());
88 | std::cout << ", DA="; describe(algo.data_algo());
89 | std::cout << ") ";
90 | }
91 | std::cout << '\n';
92 | if(raw) {
93 | std::cout << "Raw data:\n";
94 | std::cout << hexdump::hexdump32(block.raw(), block.size()) << "\n";
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/include/advobfuscator/aes_string.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator - Comnpile-time strings encrypted with AES-CTR
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #ifndef ADVOBFUSCATOR_AES_STRING_H
29 | #define ADVOBFUSCATOR_AES_STRING_H
30 |
31 | #include
32 | #include
33 | #include "aes.h"
34 | #include "call.h"
35 |
36 | namespace andrivet::advobfuscator {
37 |
38 | /// A compile-time string encrypted with AES-CTR.
39 | template
40 | struct AesString {
41 | /// Construct a compile-time string encrypted with AES-CTR.
42 | /// \lparam str Array of characters to be encrypted at compile-time.
43 | /// \remark A key and a nonce are generated on the fly.
44 | consteval AesString(const char (&str)[N]) noexcept
45 | : key_{generate_random_block<16>(generate_sum(str, 0))},
46 | nonce_{generate_random_block<8>(generate_sum(str, 16))} {
47 | // Compile-time copy of the data
48 | std::copy(str, str + N, data_.begin());
49 | // Compile-time encryption
50 | auto encrypted = encrypt_ctr(data_, key_, nonce_);
51 | // Compile-time copy of the encrypted data
52 | std::copy(encrypted.begin(), encrypted.end(), data_.begin());
53 | }
54 |
55 | /// Destruct the string by first erasing its content.
56 | /// \remark The erasing may be omitted by the compiler.
57 | constexpr ~AesString() noexcept { erase(); }
58 |
59 | /// Implicit conversion to a pointer to (const) characters, like a regular string.
60 | operator const char *() noexcept {
61 | constexpr auto random = call::generate_random(__LINE__);
62 | const ObfuscatedMethodCall call{random, &AesString::decrypt_inplace};
63 | call(random, this);
64 | return reinterpret_cast(data_.data());
65 | }
66 |
67 | /// Descrypt the encrypted string.
68 | [[nodiscard]] constexpr std::string decrypt() const {
69 | std::array buffer;
70 | std::copy(data_.begin(), data_.end(), buffer.begin());
71 | if(encrypted_) decrypt_ctr(buffer.begin(), N, key_, nonce_);
72 | std::string str;
73 | str.resize(N - 1);
74 | std::copy(buffer.begin(), buffer.end() - 1, str.begin());
75 | return str;
76 | }
77 |
78 | /// Get the raw (encrypted) content.
79 | [[nodiscard]] const char *raw() const noexcept { return data_.data(); }
80 |
81 | /// Get the actual length of the string.
82 | [[nodiscard]] constexpr std::size_t size() noexcept { return N - 1; }
83 |
84 | /// Encrypted or decrypted data.
85 | std::array data_{};
86 | /// Is the data encrypted (default) or decrypted (i.e. used)?
87 | bool encrypted_ = true;
88 | /// The nonce used to chain blocks (CTR).
89 | Nonce nonce_{};
90 | /// The key used to encrypt the data.
91 | Key key_{};
92 |
93 | private:
94 | /// Erase the information stored by the string (data, key and nonce)
95 | constexpr void erase() noexcept {
96 | if (encrypted_) return;
97 | std::fill(data_.begin(), data_.end(), 0);
98 | std::fill(key_.begin(), key_.end(), 0);
99 | std::fill(nonce_.begin(), nonce_.end(), 0);
100 | }
101 |
102 | /// Run-time decryption
103 | void decrypt_inplace() noexcept {
104 | if(!encrypted_) return;
105 | decrypt_ctr(reinterpret_cast(data_.data()), N, key_, nonce_);
106 | encrypted_ = false;
107 | }
108 | };
109 |
110 | /// User-defined literal "_aes"
111 | template
112 | consteval auto operator""_aes() { return str; }
113 | }
114 |
115 | #endif
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | What is ADVobfuscator?
2 | ======================
3 |
4 | 
5 |
6 | **ADVobfuscator** is a library that uses the `C++20` language to generate, at compile time, obfuscated data and code without using any external tool and without modifying the compiler.
7 | Strings or blocks of data can be obfuscated or encrypted at compile time, and they are decoded at runtime:
8 |
9 |
10 | How to use it?
11 | ==============
12 |
13 | The [documentation](https://andrivet.github.io/advobfuscator/) explains how to install and use **ADVobfuscator**.
14 |
15 | Look also at the examples in the `Examples` folder.
16 |
17 | Requirements
18 | ------------
19 |
20 | * A `C++20` compatible compiler (i.e. a compiler that is not too old).
21 | * Support of the C++20 Standard Library. It is generally the case if your compiler supports C++20. There are however exceptions, especially for embedded environments.
22 | * CMake 3.14 if you want to build the examples with CMake (this is optional).
23 |
24 | Examples
25 | --------
26 |
27 | ### Linux and macOS (CMake)
28 |
29 | ```
30 | mkdir -p BUILD
31 | cd BUILD
32 | cmake ..
33 | cmake --build .
34 | ```
35 |
36 | Each example is in its subdirectory. For example `./example/demo`.
37 |
38 | ### Windows (Visual Studio 22)
39 |
40 | Open `ADVobfuscator.sln`.
41 |
42 | Debug Builds
43 | -------------
44 |
45 | Debug builds are very special: Compiler do not have (and do not most of the time) respect statements such as `inline` or `constexpr`. All optimizations are also, by default, disabled. Compilers are doing this for a good reason: let you debug, single step, etc.
46 |
47 | As a consequence, **ADVobfuscator** is **not** compatible with Debug builds.
48 | You can compile in Debug but in this case, the strings or data will not be obfuscated.
49 | Obfuscation works only for Release builds.
50 |
51 | Compatibility
52 | =============
53 |
54 | **ADVobfuscator** has been tested with:
55 |
56 | Compiler | Version | OS | CPU | Compatible |
57 | ----------------|----------|-------------|---------|------------|
58 | Apple Clang | 17.0.0 | macOS 15 | AArch64 | YES |
59 | Clang | 19.1.7 | Debian 13 | x86_64 | YES |
60 | Clang | 18.1.8 | Debian 13 | x86_64 | YES |
61 | Clang | 17.0.6 | Debian 13 | x86_64 | YES |
62 | GCC | 15.1.0 | macOS 15 | x86_64 | YES |
63 | GCC | 14.2.0 | macOS 15 | AArch64 | YES |
64 | GCC | 14.2.0 | Debian 13 | x86_64 | YES |
65 | GCC | 13.3.0 | macOS 15 | AArch64 | NO |
66 | Visual Studio | 17.14.13 | Windows 11 | AArch64 | YES |
67 |
68 | Other compilers are probably compatible if they are `C++20` compliant.
69 |
70 |
71 | Files and Folders
72 | =================
73 |
74 | | Files and Folders | Description |
75 | |-----------------------------|-------------------------------------|
76 | | `README.md` | This file |
77 | | `include/advobfuscator` | **ADVobfuscator** library |
78 | | `Examples` | Examples of using **ADVobfuscator** |
79 |
80 | ### Lib
81 |
82 | | Files | Description |
83 | |----------------|----------------------------------------------------------------|
84 | | `aes.h` | Obfuscation using AES-128 compile time encryption |
85 | | `aes_string.h` | Obfuscated strings using AES-128 compile time encryption |
86 | | `bytes.h` | Obfuscated blocks of bytes |
87 | | `fsm.h` | Compile time finite state machine to obfuscate function calls |
88 | | `obj.h` | Obfuscation |
89 | | `random.h` | Generate random numbers at compile time |
90 | | `string.h` | Obfuscated strings |
91 | | `format.h` | std::format Formatting of strings |
92 |
93 |
94 | Copyright and license
95 | =====================
96 |
97 | Copyright (c) 2025 Sebastien Andrivet
98 | All rights reserved.
99 |
100 | Redistribution and use in source and binary forms, with or without
101 | modification, are permitted (subject to the limitations in the disclaimer
102 | below) provided that the following conditions are met:
103 |
104 | * Redistributions of source code must retain the above copyright notice,
105 | this list of conditions and the following disclaimer.
106 |
107 | * Redistributions in binary form must reproduce the above copyright
108 | notice, this list of conditions and the following disclaimer in the
109 | documentation and/or other materials provided with the distribution.
110 |
111 | * Neither the name of the copyright holder nor the names of its
112 | contributors may be used to endorse or promote products derived from this
113 | software without specific prior written permission.
114 |
115 | NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
116 | THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
117 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
118 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
119 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
120 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
121 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
122 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
123 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
124 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
125 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
126 | POSSIBILITY OF SUCH DAMAGE.
127 |
--------------------------------------------------------------------------------
/include/advobfuscator/string.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #ifndef ADVOBFUSCATOR_STRING_H
29 | #define ADVOBFUSCATOR_STRING_H
30 |
31 | #include
32 |
33 | #include "aes_string.h"
34 | #include "obf.h"
35 | #include "call.h"
36 |
37 | namespace andrivet::advobfuscator {
38 |
39 | /// An obfuscated string of characters.
40 | /// \tparam N The number of bytes of the string (including the null terminal byte).
41 | template
42 | struct ObfuscatedString {
43 | /// Construct an obfuscated string of characters.
44 | /// \param str The array of characters (including the null terminal byte).
45 | consteval ObfuscatedString(char const (&str)[N]) noexcept
46 | : algos_{generate_sum(str)} {
47 | encode(str);
48 | };
49 |
50 | /// Construct an obfuscated string of characters.
51 | /// \param str The array of characters (including the null terminal byte).
52 | /// \param params The parameters for the obfuscation (key and algorithms).
53 | consteval ObfuscatedString(char const (&str)[N], const Parameters ¶ms) noexcept
54 | : algos_{params} {
55 | encode(str);
56 | }
57 |
58 | /// Construct an obfuscated string of characters.
59 | /// \param str The array of characters (including the null terminal byte).
60 | /// \param params An array of parameters for the obfuscations (keys and algorithms).
61 | template
62 | consteval ObfuscatedString(char const (&str)[N], const Parameters (¶ms)[A]) noexcept
63 | : algos_{params} {
64 | static_assert(A <= details::MAX_NB_ALGORITHMS, "Maximum number of parameters overflow");
65 | encode(str);
66 | }
67 |
68 | /// Destruct an obfuscated string by first erasing its content.
69 | constexpr ~ObfuscatedString() noexcept { erase(); }
70 |
71 | /// Implicit conversion to a pointer to (const) characters, like a regular string.
72 | operator const char* () noexcept {
73 | constexpr auto random = call::generate_random(__LINE__);
74 | const ObfuscatedMethodCall call{random, &ObfuscatedString::decode_inplace};
75 | call(random, this);
76 | return data_.data();
77 | }
78 |
79 | /// Get the raw (encrypted) content.
80 | [[nodiscard]] const char *raw() const noexcept { return data_.data(); }
81 |
82 | /// Get the actual length of the string.
83 | [[nodiscard]] constexpr std::size_t size() noexcept { return N - 1; }
84 |
85 | /// Decode the obfuscated string
86 | [[nodiscard]] constexpr std::string decode() const {
87 | std::array buffer;
88 | std::copy(data_.begin(), data_.end(), buffer.begin());
89 | if(obfuscated_) algos_.decode(0, buffer.begin(), buffer.end());
90 | std::string str;
91 | str.resize(N - 1);
92 | std::copy(buffer.begin(), buffer.end() - 1, str.begin());
93 | return str;
94 | }
95 |
96 | /// Encoded or decoded data.
97 | std::array data_{};
98 | /// Obfuscations used to encode the data.
99 | Obfuscations algos_;
100 | /// Is the data encoded (default) or decoded (i.e. used)?
101 | bool obfuscated_ = true;
102 |
103 | private:
104 | /// Erase the data of the string.
105 | constexpr void erase() noexcept {
106 | if(!obfuscated_)
107 | std::fill(data_.begin(), data_.end(), 0);
108 | }
109 |
110 | /// Encode an array of characters.
111 | /// \param str The string of characters to be encoded.
112 | consteval void encode(char const (&str)[N]) noexcept {
113 | std::array buffer;
114 | std::copy(str, str + N, buffer.begin());
115 | algos_.encode(0, buffer.begin(), buffer.end());
116 | std::copy(buffer.begin(), buffer.end(), data_.begin());
117 | }
118 |
119 | /// Decode an array of characters in-place.
120 | void decode_inplace() noexcept {
121 | if(!obfuscated_) return;
122 | std::array buffer;
123 | std::copy(data_.begin(), data_.end(), buffer.begin());
124 | algos_.decode(0, buffer.begin(), buffer.end());
125 | std::copy(buffer.begin(), buffer.end(), data_.begin());
126 | obfuscated_ = false;
127 | }
128 | };
129 |
130 | /// User-defined literal "_obf"
131 | template
132 | consteval auto operator ""_obf() { return str; }
133 |
134 | }
135 |
136 | #endif
137 |
--------------------------------------------------------------------------------
/include/advobfuscator/bytes.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator - Obfuscation of a block of bytes
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #ifndef ADVOBFUSCATOR_BYTES_H
29 | #define ADVOBFUSCATOR_BYTES_H
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include "obf.h"
37 |
38 | namespace andrivet::advobfuscator {
39 |
40 | /// A block of obfuscated bytes
41 | template
42 | struct ObfuscatedBytes {
43 | /// Construct a compile-time block of bytes from a string.
44 | /// \lparam str Array of characters representing bytes to be encrypted at compile-time.
45 | /// The format of the string is: two hexadecimal digits seperated by spaces.
46 | /// Example: "01 02 03 1F".
47 | /// \remark A set of obfuscation algorithms are generated on the fly.
48 | consteval ObfuscatedBytes(const char (&str)[N])
49 | : algos_{generate_sum(str)} {
50 | parse(str);
51 | encode();
52 | }
53 |
54 | /// Destruct the block by first erasing its content.
55 | /// \remark The erasing may be omitted by the compiler.
56 | constexpr ~ObfuscatedBytes() noexcept { erase(); }
57 |
58 | /// Get the decoded (deobfuscated) bytes
59 | [[nodiscard]] const std::uint8_t *data() noexcept { decode(); return data_.data(); }
60 |
61 | /// Get the raw (obfuscated) content.
62 | [[nodiscard]] const std::uint8_t *raw() const noexcept { return data_.data(); }
63 |
64 | /// Get the actual size of the block of bytes.
65 | [[nodiscard]] std::size_t size() const noexcept { return N / 3; }
66 |
67 | /// Direct access to a byte in the block.
68 | /// \lparam pos Position of the byte in the block.
69 | /// \return The decoded byte.
70 | [[nodiscard]] constexpr std::uint8_t operator[](std::size_t pos) const {
71 | std::uint8_t b{data_[pos]};
72 | algos_.decode(pos, &b, &b + 1);
73 | return b;
74 | }
75 |
76 | /// Decode (deobfuscate) the block of bytes.
77 | /// \return The decoded bytes.
78 | [[nodiscard]] constexpr std::array decode() const noexcept {
79 | std::array buffer{};
80 | std::copy(data_.begin(), data_.end(), buffer.begin());
81 | algos_.decode(0, buffer.begin(), buffer.end());
82 | return buffer;
83 | }
84 |
85 | /// Obfuscated or decoded data.
86 | std::array data_{};
87 | /// Set of algorithms used for the obfuscation.
88 | Obfuscations algos_;
89 | /// Is the data obfuscated (default) or decoded (i.e. used)?
90 | bool obfuscated_ = true;
91 |
92 | private:
93 | /// Convert an hexadecimal digit to its value.
94 | static consteval std::uint8_t hex_char_value(char c) {
95 | if('0' <= c && c <= '9') return c - '0';
96 | if('a' <= c && c <= 'f') return c - 'a' + 10;
97 | if('A' <= c && c <= 'F') return c - 'A' + 10;
98 | throw std::invalid_argument("Invalid hex character");
99 | }
100 |
101 | /// Parse a string representing bytes in hexadecimal separated by spaces.
102 | consteval void parse(const char (&str)[N]) {
103 | size_t byte_index = 0;
104 | uint8_t high = 0;
105 | bool half = false;
106 |
107 | // For each character (except the terminal null byte)...
108 | for(size_t i = 0; i < N - 1; ++i) {
109 | char c = str[i];
110 | // Is it a space?
111 | if(c == ' ') continue;
112 | // Get the corresponding nibble value
113 | uint8_t value = hex_char_value(c);
114 | // First nibble (half)?
115 | if(!half) {
116 | high = value << 4;
117 | half = true;
118 | }
119 | else {
120 | // We have the two nibbles, store them
121 | data_[byte_index++] = high | value;
122 | half = false;
123 | }
124 | }
125 | }
126 |
127 | /// Erase the information stored by the block (data)
128 | constexpr void erase() noexcept {
129 | if(!obfuscated_)
130 | std::fill(data_.begin(), data_.end(), 0);
131 | }
132 |
133 | /// Encode (obfuscate) the block of data.
134 | consteval void encode() noexcept {
135 | algos_.encode(0, data_.begin(), data_.end());
136 | }
137 |
138 | /// Decode (deobfuscate) the block of data.
139 | void decode() noexcept {
140 | if(!obfuscated_) return;
141 | algos_.decode(0, data_.begin(), data_.end());
142 | obfuscated_ = false;
143 | }
144 | };
145 |
146 | /// User-defined literal "_obf_bytes"
147 | template
148 | consteval auto operator""_obf_bytes() { return block; }
149 |
150 | }
151 |
152 | #endif
153 |
--------------------------------------------------------------------------------
/Examples/demo/demo.cpp:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | using namespace andrivet::advobfuscator;
36 |
37 |
38 | void strings_obfuscation() {
39 | // Obfuscate a string literal
40 | std::cout << "abc"_obf << '\n';
41 |
42 | // Obfuscate a string literal and describe it after deobfuscation
43 | auto s1{"0123456789"_obf};
44 | std::cout << s1 << "\n";
45 |
46 | // Construct explicitly an ObfuscatedString
47 | auto s2 = ObfuscatedString("abcd", {1, KeyAlgorithm::IDENTITY, DataAlgorithm::XOR});
48 | std::cout << s2 << '\n';
49 |
50 | // Construct explicitly an ObfuscatedString with precise obfuscation parameters
51 | static constexpr Parameters params[] = {
52 | {.key=1, .key_algo=KeyAlgorithm::IDENTITY, .data_algo=DataAlgorithm::XOR},
53 | {.key=2, .key_algo=KeyAlgorithm::IDENTITY, .data_algo=DataAlgorithm::XOR}
54 | };
55 | auto s3 = ObfuscatedString("abcde", params);
56 | std::cout << s3 << '\n';
57 |
58 | static constexpr auto s4 = "An immutable compile-time string"_obf;
59 | // It is not possible to use directly s4 since it is immutable:
60 | // std::cout << s4 << '\n'; // Compilation failure
61 | // So use decode() instead:
62 | std::cout << s4.decode() << '\n';
63 | }
64 |
65 | void blocks_obfuscation() {
66 |
67 | static constexpr auto rcon = "01 02 04 08 10 20 40 80 1b 36"_obf_bytes;
68 |
69 | for(std::size_t i = 0; i < rcon.size(); ++i)
70 | std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)rcon[i] << ' ';
71 | std::cout << std::dec << '\n';
72 |
73 | // Note that it is a static constexpr
74 | static constexpr ObfuscatedBytes<16 * 3> sbox[16] = {
75 | "63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76"_obf_bytes,
76 | "CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0"_obf_bytes,
77 | "B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15"_obf_bytes,
78 | "04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75"_obf_bytes,
79 | "09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84"_obf_bytes,
80 | "53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF"_obf_bytes,
81 | "D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8"_obf_bytes,
82 | "51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2"_obf_bytes,
83 | "CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73"_obf_bytes,
84 | "60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB"_obf_bytes,
85 | "E0 32 3A 0A 49 06 24 5C C2 D3 AC 62 91 95 E4 79"_obf_bytes,
86 | "E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08"_obf_bytes,
87 | "BA 78 25 2E 1C A6 B4 C6 E8 DD 74 1F 4B BD 8B 8A"_obf_bytes,
88 | "70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E"_obf_bytes,
89 | "E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF"_obf_bytes,
90 | "8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16"_obf_bytes};
91 |
92 | for(const auto &box : sbox) {
93 | // This is not permitted since data() modifies the object (non-const)
94 | // std::cout << hexdump32(box.data(), 16);
95 | // So use decode() instead:
96 | const auto &decoded = box.decode();
97 | for(const auto &i : decoded)
98 | std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)i << ' ';
99 | std::cout << std::dec << '\n';
100 | }
101 | }
102 |
103 | void aes_encryption_certificate() {
104 | auto s1 = R"(-----BEGIN CERTIFICATE-----
105 | MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
106 | MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
107 | VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
108 | NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
109 | TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
110 | ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
111 | V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
112 | gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
113 | FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
114 | CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
115 | BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
116 | BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
117 | Wm7DCfrPNGVwFWUQOmsPue9rZBgO
118 | -----END CERTIFICATE-----)"_obf;
119 |
120 | std::cout << s1 << '\n';
121 | }
122 |
123 | void aes_encryption_strings() {
124 | std::cout << "This is a string containing a secret that has to be hidden with AES"_aes << "\n";
125 | }
126 |
127 | int main() {
128 | strings_obfuscation();
129 | blocks_obfuscation();
130 | aes_encryption_certificate();
131 | aes_encryption_strings();
132 | return 0;
133 | };
--------------------------------------------------------------------------------
/include/advobfuscator/random.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #include
29 |
30 | #ifndef ADVOBFUSCATOR_RANDOM_H
31 | #define ADVOBFUSCATOR_RANDOM_H
32 |
33 | namespace andrivet::advobfuscator {
34 |
35 | namespace details {
36 |
37 | /// Use current (compile time) as a seed
38 | static constexpr char time[] = __TIME__; // __TIME__ has the following format: hh:mm:ss in 24-hour time
39 |
40 | /// Convert a digit into the corresponding number
41 | constexpr int digit_to_int(char c) { return c - '0'; }
42 |
43 | /// Convert time string (hh:mm:ss) into a number
44 | static constexpr unsigned seed =
45 | digit_to_int(time[7]) +
46 | digit_to_int(time[6]) * 10 +
47 | digit_to_int(time[4]) * 60 +
48 | digit_to_int(time[3]) * 600 +
49 | digit_to_int(time[1]) * 3600 +
50 | digit_to_int(time[0]) * 36000;
51 |
52 | /// Generate a (pseudo) random number.
53 | /// \tparam T Type of the number to generate (std::size_t by default).
54 | /// \param count The count for the generation of random numbers.
55 | /// \param max The maximum value of the number generated (excluded).
56 | /// \return A number generated randomly.
57 | /// \remarks Inspired by 1988, Stephen Park and Keith Miller
58 | /// "Random Number Generators: Good Ones Are Hard To Find", considered as "minimal standard"
59 | /// Park-Miller 31 bit pseudo-random number generator, implemented with G. Carta's optimisation:
60 | /// with 32-bit math and without division
61 | template
62 | consteval T generate_random(std::size_t count, T max) {
63 | const uint32_t a = 16807; // 7^5
64 | const uint32_t m = 2147483647; // 2^31 - 1
65 |
66 | auto s = seed;
67 | while(count-- > 0) {
68 | uint32_t lo = a * (s & 0xFFFF); // Multiply lower 16 bits by 16807
69 | uint32_t hi = a * (s >> 16); // Multiply higher 16 bits by 16807
70 | uint32_t lo2 = lo + ((hi & 0x7FFF) << 16); // Combine lower 15 bits of hi with lo's upper bits
71 | uint32_t lo3 = lo2 + hi;
72 | s = lo3 > m ? lo3 - m : lo3;
73 | }
74 |
75 | // Note: A bias is introduced by the modulo operation.
76 | // However, I do believe it is negligible in this case (M is far lower than 2^31 - 1)
77 | return static_cast(s % static_cast(max));
78 | }
79 | }
80 |
81 | /// Generate a (pseudo) random number strictly greater than 0.
82 | /// \tparam T Type of the number to generate (std::size_t by default).
83 | /// \param count Randomization counter.
84 | /// \param max The maximum value of the number generated (excluded).
85 | template
86 | consteval T generate_random_not_0(std::size_t count, T max) {
87 | return static_cast(details::generate_random(count, static_cast(max) - 1) + 1);
88 | }
89 |
90 | /// Generate a (pseudo) random number in a range.
91 | /// \tparam T Type of the number to generate (std::size_t by default).
92 | /// \param count Randomization counter.
93 | /// \param max The maximum value of the number generated (excluded).
94 | template
95 | consteval T generate_random(std::size_t count, T min, T max) {
96 | return static_cast(details::generate_random(count, static_cast(max) - static_cast(min)) + static_cast(min));
97 | }
98 |
99 | /// Generate a (pseudo) random number in a range (0 included).
100 | /// \tparam T Type of the number to generate (std::size_t by default).
101 | /// \param count Randomization counter.
102 | /// \param max The maximum value of the number generated (excluded).
103 | template
104 | consteval T generate_random(std::size_t count, T max) {
105 | return static_cast(details::generate_random(count, static_cast(max)));
106 | }
107 |
108 | /// Generate block of (pseudo) random numbers.
109 | /// \tparam N The size of the block of numbers.
110 | /// \param count Randomization counter.
111 | /// \return An array of (pseudo) random numbers.
112 | template
113 | consteval std::array generate_random_block(std::size_t count) {
114 | std::array block;
115 | for(std::size_t i = 0; i < N; ++i) block[i] = details::generate_random(count + i, 256);
116 | return block;
117 | }
118 |
119 | /// Compute the sum of a initial number of of the values of characters.
120 | /// \tparam N The number of characters.
121 | /// \param str The string of characters.
122 | /// \param initial The initial value of the sum (0 by default).
123 | template
124 | consteval std::size_t generate_sum(char const (&str)[N], size_t initial = 0) {
125 | std::size_t sum = initial;
126 | for(std::size_t i = 0; i < N; ++i) sum = (sum + str[i]) % 1000;
127 | return sum;
128 | }
129 |
130 | }
131 |
132 | #endif
133 |
134 |
--------------------------------------------------------------------------------
/include/advobfuscator/fsm.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator - Finite state machine generated at compile time
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #ifndef ADVOBFUSCATOR_FSM_H
29 | #define ADVOBFUSCATOR_FSM_H
30 |
31 | #include
32 | #include
33 | #include
34 |
35 | namespace andrivet::advobfuscator {
36 |
37 | constexpr size_t NB_BITS = 32; ///< Number of bits recognized.
38 | constexpr size_t TRANSITIONS_PER_BIT = 8; ///< Number of transitions per bit.
39 | constexpr size_t MAX_TRANSITIONS = NB_BITS * TRANSITIONS_PER_BIT; ///< Total number of transitions.
40 |
41 | // For each bit to recognize, we create a small FSM of 4 states and 8 transitions.
42 | // - The first transition moves the recognizer to the next state.
43 | // - The 3 other states are in an infinite loop.
44 |
45 | /// A transition between two states
46 | namespace details {
47 | template
48 | struct Transition {
49 | bool input; ///< Input value (bit: 0 or 1).
50 | int from; ///< From this state.
51 | int to; ///< To this state.
52 | O o; ///< Object to return.
53 | };
54 |
55 | /// Compute the number of bits to represent a value.
56 | constexpr int num_bits(auto value) {
57 | int num = 0;
58 | for(auto v = value; v > 0; v >>= 1) ++num;
59 | if(num == 0) num = 1; // edge case for 0
60 | return num;
61 | }
62 | }
63 |
64 | namespace call {
65 | /// Generate a random number for the recognizer.
66 | consteval std::uint32_t generate_random(std::size_t count) {
67 | return andrivet::advobfuscator::generate_random(count, UINT32_MAX / 10, UINT32_MAX);
68 | };
69 | }
70 |
71 | /// A finite state machine that recognize a number bit per bit,
72 | template
73 | struct Fsm {
74 | /// Construct a new finite state machine that recognize a number and store an object.
75 | /// \param recognize The number to be recognized by this finite state machine.
76 | /// \param o The object stored in one of the transition (the active one).
77 | consteval Fsm(std::uint32_t recognize, O o) {
78 | // Get a random number for the activate transition of the recognizer.
79 | // The activate transition is the transition that stores the object.
80 | const std::uint32_t activate = generate_random_not_0(recognize % 1000, NB_BITS - 1);
81 | auto bits = details::num_bits(recognize);
82 |
83 | // For each bit...
84 | for(int i = 0; i < bits; ++i) {
85 | // Get the bit's value
86 | bool bit = (recognize >> (bits - 1 - i)) & 0x01;
87 | // Transition to the next state of the recognizer and store (or not) the object
88 | add_transition(bit, 4 * i, 4 * i + 4, i + 1 == activate ? o : O{});
89 | // Transition to states in an infinite loop
90 | add_transition(!bit, 4 * i, 4 * i + 1, O{});
91 | add_transition(0, 4 * i + 1, 4 * i + 2, O{});
92 | add_transition(1, 4 * i + 1, 4 * i + 3, O{});
93 | add_transition(0, 4 * i + 2, 4 * i + 3, O{});
94 | add_transition(1, 4 * i + 2, 4 * i + 1, O{});
95 | add_transition(0, 4 * i + 3, 4 * i + 1, O{});
96 | add_transition(0, 4 * i + 3, 4 * i + 2, O{});
97 | }
98 | }
99 |
100 | /// Add a transition to the finite state machine.
101 | /// \param input Input value.
102 | /// \param from From state.
103 | /// \param to To state.
104 | /// \param o Object to be stored in transition.
105 | consteval void add_transition(bool input, int from, int to, O o) {
106 | if(nb_transition_ >= MAX_TRANSITIONS) throw std::exception(); // MAX_TRANSITIONS is too small
107 | transitions_[nb_transition_++] = {.input = input, .from = from, .to = to, .o = o};
108 | }
109 |
110 | /// Find a transition from a state and with an input value.
111 | /// \param state The from state of the transition.
112 | /// \param input The input value.
113 | /// \return The transition found.
114 | /// \exception The FSM is supposed to cover all the cases and this member function will always find
115 | /// a transition. If it is not the case, there is a bug in the generation of the FSM.
116 | /// In this case, an exception is raised.
117 | [[nodiscard]] constexpr const details::Transition &find(int state, bool input) const {
118 | for(int i = 0; i < nb_transition_; ++i) {
119 | if(transitions_[i].from == state && transitions_[i].input == input)
120 | return transitions_[i];
121 | }
122 | throw std::exception(); // Missing transition in the FSM (i.e. a bug)
123 | }
124 |
125 | /// Run the finite state machine on a number.
126 | /// \param value The value to recognize.
127 | /// \remark The FSM will never return (infinite loop) if the number is wrong.
128 | /// This is by design to annoy reverse-engineering.
129 | decltype(auto) run(std::uint32_t value) const {
130 | auto bits = details::num_bits(value);
131 | int state = 0;
132 |
133 | // For each bit in reverse...
134 | for(int i = bits - 1; i >= 0; --i) {
135 | // Get the value of the bit.
136 | bool bit = (value >> i) & 1;
137 | // Find the transition
138 | auto &transition = find(state, bit);
139 | // Update the state.
140 | state = transition.to;
141 | // Treat the active transition as a final state.
142 | if(transition.o != O{}) return transition.o;
143 | }
144 | throw std::exception(); // Invalid FSM (i.e.bug);
145 | }
146 |
147 | /// Transitions of the finite state machine
148 | std::array, MAX_TRANSITIONS> transitions_{};
149 | /// Number of transitions
150 | std::size_t nb_transition_{};
151 | };
152 | }
153 |
154 | #endif
155 |
--------------------------------------------------------------------------------
/Examples/example/main.cpp:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include "describe.h"
36 |
37 | using namespace andrivet::advobfuscator;
38 | using namespace hexdump;
39 |
40 |
41 | void strings_obfuscation() {
42 | // Obfuscate a string literal
43 | std::cout << "abc"_obf << '\n';
44 | describe("abc"_obf);
45 |
46 | // Using std::format
47 | std::cout << std::format("{}\n", "abc"_obf);
48 |
49 | // Obfuscate a string literal and describe it after deobfuscation
50 | auto s1{"0123456789"_obf};
51 | describe(s1);
52 | std::cout << s1 << "\n";
53 | describe(s1);
54 |
55 | // Construct explicitly an ObfuscatedString
56 | auto s2 = ObfuscatedString("abcd", {1, KeyAlgorithm::IDENTITY, DataAlgorithm::XOR});
57 | std::cout << s2 << '\n';
58 | describe(s2);
59 |
60 | // Construct explicitly an ObfuscatedString with precise obfuscation parameters
61 | static constexpr Parameters params[] = {
62 | {.key=1, .key_algo=KeyAlgorithm::IDENTITY, .data_algo=DataAlgorithm::XOR},
63 | {.key=2, .key_algo=KeyAlgorithm::IDENTITY, .data_algo=DataAlgorithm::XOR}
64 | };
65 | auto s3 = ObfuscatedString("abcde", params);
66 | describe(s3);
67 | std::cout << s3 << '\n';
68 | describe(s3);
69 |
70 | static constexpr auto s4 = "An immutable compile-time string"_obf;
71 | describe(s4);
72 | // It is not possible to use directly s4 since it is immutable:
73 | // std::cout << s4 << '\n'; // Compilation failure
74 | // So use decode() instead:
75 | std::cout << s4.decode() << '\n';
76 | }
77 |
78 | void blocks_obfuscation() {
79 | static constexpr auto rcon0 = ObfuscatedBytes{"01 02 04 08 10 20 40 80 1b 36"};
80 | describe(rcon0);
81 | std::cout << hexdump32(rcon0.decode()) << '\n';
82 |
83 | static constexpr auto rcon = "01 02 04 08 10 20 40 80 1b 36"_obf_bytes;
84 | describe(rcon);
85 | std::cout << hexdump32(rcon.decode()) << '\n';
86 |
87 | for(std::size_t i = 0; i < rcon.size(); ++i)
88 | std::cout << std::hex << std::setw(2) << (int)rcon[i] << ' ';
89 | std::cout << std::dec << '\n';
90 |
91 | // Note that it is a static constexpr
92 | static constexpr ObfuscatedBytes<16 * 3> sbox[16] = {
93 | "63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76"_obf_bytes,
94 | "CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0"_obf_bytes,
95 | "B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15"_obf_bytes,
96 | "04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75"_obf_bytes,
97 | "09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84"_obf_bytes,
98 | "53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF"_obf_bytes,
99 | "D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8"_obf_bytes,
100 | "51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2"_obf_bytes,
101 | "CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73"_obf_bytes,
102 | "60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB"_obf_bytes,
103 | "E0 32 3A 0A 49 06 24 5C C2 D3 AC 62 91 95 E4 79"_obf_bytes,
104 | "E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08"_obf_bytes,
105 | "BA 78 25 2E 1C A6 B4 C6 E8 DD 74 1F 4B BD 8B 8A"_obf_bytes,
106 | "70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E"_obf_bytes,
107 | "E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF"_obf_bytes,
108 | "8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16"_obf_bytes};
109 |
110 | for(const auto &i : sbox)
111 | std::cout << hexdump16(i.raw(), 16);
112 |
113 | for(const auto &i : sbox) {
114 | // This is not permitted since data() modifies the object (non-const)
115 | // std::cout << hexdump32(i.data(), 16);
116 | // So use decode() instead:
117 | std::cout << hexdump16(i.decode());
118 | }
119 | }
120 |
121 | void certificate_obfuscation() {
122 | auto s1 = R"(-----BEGIN CERTIFICATE-----
123 | MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
124 | MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
125 | VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
126 | NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
127 | TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
128 | ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
129 | V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
130 | gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
131 | FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
132 | CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
133 | BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
134 | BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
135 | Wm7DCfrPNGVwFWUQOmsPue9rZBgO
136 | -----END CERTIFICATE-----)"_obf;
137 |
138 | std::cout << s1 << '\n';
139 |
140 | // With std::format
141 | std::cout << std::format("{}\n", s1);
142 | }
143 |
144 | void aes_encryption_strings() {
145 | std::cout << "This is a string containing a secret that has to be hidden with AES"_aes << "\n";
146 | }
147 |
148 | int to_protect1(const std::string &arg1, int arg2) {
149 | std::cout << "Protected "_obf << arg1 << '\n';
150 | return arg2;
151 | }
152 |
153 | void to_protect2() {
154 | std::cout << "Protected2 "_obf;
155 | }
156 |
157 | void obfuscate_call1() {
158 | using namespace std::string_literals;
159 |
160 | constexpr auto random = call::generate_random(42);
161 | const ObfuscatedCall call{random, &to_protect1};
162 | const auto ret = call(random, "hello"s, 42);
163 | std::cout << "Returned value: " << ret << '\n';
164 | }
165 |
166 | void obfuscate_call2() {
167 | constexpr auto random = call::generate_random(43);
168 | const ObfuscatedCall call{random, &to_protect2};
169 | call(random);
170 | }
171 |
172 | struct S1 {
173 | void method1() {}
174 | };
175 |
176 | void obfuscate_method_call() {
177 | S1 s1{};
178 |
179 | constexpr auto random = call::generate_random(44);
180 | const ObfuscatedMethodCall call{random, &S1::method1};
181 | call(random, s1);
182 | }
183 |
184 | int main() {
185 | strings_obfuscation();
186 | blocks_obfuscation();
187 | certificate_obfuscation();
188 | aes_encryption_strings();
189 | obfuscate_call1();
190 | obfuscate_call2();
191 | obfuscate_method_call();
192 | return 0;
193 | };
--------------------------------------------------------------------------------
/tests/Tests.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 | Debug
22 | ARM64
23 |
24 |
25 | Release
26 | ARM64
27 |
28 |
29 |
30 | 17.0
31 | Win32Proj
32 | {93014d06-b4a0-45e9-a1fc-d10a51e3286a}
33 | Tests
34 | 10.0
35 |
36 |
37 |
38 | Application
39 | true
40 | v143
41 | Unicode
42 |
43 |
44 | Application
45 | false
46 | v143
47 | true
48 | Unicode
49 |
50 |
51 | Application
52 | true
53 | v143
54 | Unicode
55 |
56 |
57 | Application
58 | false
59 | v143
60 | true
61 | Unicode
62 |
63 |
64 | Application
65 | true
66 | v143
67 | Unicode
68 |
69 |
70 | Application
71 | false
72 | v143
73 | true
74 | Unicode
75 |
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 | ..\include;$(IncludePath)
102 |
103 |
104 | ..\include;$(IncludePath)
105 |
106 |
107 | ..\include;$(IncludePath)
108 |
109 |
110 | ..\include;$(IncludePath)
111 |
112 |
113 | ..\include;$(IncludePath)
114 |
115 |
116 | ..\include;$(IncludePath)
117 |
118 |
119 |
120 | Level3
121 | true
122 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
123 | true
124 | stdcpp20
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 | stdcpp20
140 |
141 |
142 | Console
143 | true
144 |
145 |
146 |
147 |
148 | Level3
149 | true
150 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
151 | true
152 | stdcpp20
153 |
154 |
155 | Console
156 | true
157 |
158 |
159 |
160 |
161 | Level3
162 | true
163 | true
164 | true
165 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
166 | true
167 | stdcpp20
168 |
169 |
170 | Console
171 | true
172 |
173 |
174 |
175 |
176 | Level3
177 | true
178 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
179 | true
180 | stdcpp20
181 |
182 |
183 | Console
184 | true
185 |
186 |
187 |
188 |
189 | Level3
190 | true
191 | true
192 | true
193 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
194 | true
195 | stdcpp20
196 |
197 |
198 | Console
199 | true
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/Examples/Examples.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 | Debug
22 | ARM64
23 |
24 |
25 | Release
26 | ARM64
27 |
28 |
29 |
30 | 17.0
31 | Win32Proj
32 | {0538d995-5491-4772-b435-80dcc996f467}
33 | ADVobfuscator
34 | 10.0
35 | Example
36 |
37 |
38 |
39 | Application
40 | true
41 | v143
42 | Unicode
43 |
44 |
45 | Application
46 | false
47 | v143
48 | true
49 | Unicode
50 |
51 |
52 | Application
53 | true
54 | v143
55 | Unicode
56 |
57 |
58 | Application
59 | false
60 | v143
61 | true
62 | Unicode
63 |
64 |
65 | Application
66 | true
67 | v143
68 | Unicode
69 |
70 |
71 | Application
72 | false
73 | v143
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 | ../include;$(IncludePath)
103 |
104 |
105 | ../include;$(IncludePath)
106 |
107 |
108 | ../include;$(IncludePath)
109 |
110 |
111 | ../include;$(IncludePath)
112 |
113 |
114 | ../include;$(IncludePath)
115 |
116 |
117 | ../include;$(IncludePath)
118 |
119 |
120 |
121 | Level3
122 | true
123 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
124 | true
125 | stdcpp20
126 |
127 |
128 | Console
129 | true
130 |
131 |
132 |
133 |
134 | Level3
135 | true
136 | true
137 | true
138 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
139 | true
140 | stdcpp20
141 |
142 |
143 | Console
144 | true
145 |
146 |
147 |
148 |
149 | Level3
150 | true
151 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
152 | true
153 | stdcpp20
154 |
155 |
156 | Console
157 | true
158 |
159 |
160 |
161 |
162 | Level3
163 | true
164 | true
165 | true
166 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
167 | true
168 | stdcpp20
169 |
170 |
171 | Console
172 | true
173 |
174 |
175 |
176 |
177 | Level3
178 | true
179 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
180 | true
181 | stdcpp20
182 |
183 |
184 | Console
185 | true
186 |
187 |
188 |
189 |
190 | Level3
191 | true
192 | true
193 | true
194 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
195 | true
196 | stdcpp20
197 |
198 |
199 | Console
200 | true
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
--------------------------------------------------------------------------------
/include/advobfuscator/obf.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #ifndef ADVOBFUSCATOR_OBF_H
29 | #define ADVOBFUSCATOR_OBF_H
30 |
31 | #include
32 | #include
33 | #include "random.h"
34 |
35 | namespace andrivet::advobfuscator {
36 |
37 | /// Algorithms to encode data
38 | enum class DataAlgorithm {
39 | IDENTITY, ///< Identity function, i.e. no change.
40 | CAESAR, ///< Caesar algorithm, key is the displacement.
41 | XOR, ///< XOR with the key.
42 | ROTATE, ///< Bits rotation, key is the displacement.
43 | SUBSTITUTE, ///< Substitute bits, key % 8 is the displacement.
44 | NB_VALUES ///< Number of values in this enum.
45 | };
46 |
47 | /// Algorithms to encode a key from a previous one
48 | enum class KeyAlgorithm {
49 | IDENTITY, ///< Identity function, i.e. no change.
50 | INCREMENT, ///< Key is incremented at each step.
51 | INVERT, ///< Key is inverted at each step.
52 | SUBSTITUTE, ///< Substitute bits (0 becomes 7, 7 becomes 0, ...) at each step.
53 | SWAP, ///< Swap high and low nibbles at each step.
54 | NB_VALUES ///< Number of values in this enum.
55 | };
56 |
57 | /// Parameters of an obfuscation algorithm.
58 | struct Parameters {
59 | std::uint8_t key = 0; ///< Key to be used.
60 | KeyAlgorithm key_algo = KeyAlgorithm::IDENTITY; ///< Algorithm to compute the next key.
61 | DataAlgorithm data_algo = DataAlgorithm::IDENTITY; ///< Algorithm to encode data.
62 | };
63 |
64 | // ------------------------------------------------------------------
65 | // Internal details
66 | // ------------------------------------------------------------------
67 |
68 | namespace details {
69 | /// Minimal number of algorithms
70 | static const std::size_t MIN_NB_ALGORITHMS = 2;
71 | /// Maximal number of algorithms
72 | static const std::size_t MAX_NB_ALGORITHMS = 4;
73 |
74 | /// Substitute bits in a byte.
75 | /// \param b Input byte.
76 | /// \param d Number of bits for the substitution.
77 | /// \remark If d = 7, bits 0 and 7 are exchanged, bits 1 and 6 are exchanged, etc.
78 | /// If d = 6, bits 0 and 6 are exchanged, bits 1 and 5 are exchanged, etc.
79 | /// \result The result of the substitution.
80 | constexpr uint8_t substitute(uint8_t b, uint8_t d) {
81 | d %= 8;
82 | uint8_t result = 0;
83 | for(uint8_t i = 0; i < 8; ++i) {
84 | auto bit = (b >> i) & 0x01;
85 | result |= bit << (i <= d ? d - i : 8 - i + d);
86 | }
87 | return result;
88 | }
89 |
90 | /// Generalized Caesar cypher (ROT).
91 | /// \param b Input byte.
92 | /// \param d Displacement for the rotation.
93 | /// \result The result of the rotation.
94 | constexpr uint8_t caesar(uint8_t b, uint8_t d) {
95 | return static_cast((d >= 128 ? b + d - 256 : b + d) % 256);
96 | }
97 |
98 | /// Generalized Inverted Caesar cypher.
99 | /// \param b Input byte.
100 | /// \param d Displacement for the inverted rotation.
101 | /// \result The result of the inverted rotation.
102 | constexpr uint8_t caesar_inverted(uint8_t b, uint8_t d) {
103 | return static_cast((d >= 128 ? b - d + 256 : b - d) % 256);
104 | }
105 |
106 | /// XOR between a byte and a key.
107 | /// \param b Input byte.
108 | /// \param key Key to be used to XOR the byte.
109 | /// \result The result of the XOR operation.
110 | constexpr uint8_t x0r(uint8_t b, uint8_t key) {
111 | return static_cast(b ^ key);
112 | }
113 |
114 | /// Rotate bits in a byte left (positive d) or right (negative d).
115 | /// \param b Input byte.
116 | /// \param d Number of bits for the rotation.
117 | /// \remark If d = 1, bits 0 becomes bit 1, bits 1 become bit 2, etc.
118 | /// \result The result of the rotation.
119 | constexpr uint8_t rotate(uint8_t b, uint8_t d) {
120 | return static_cast(
121 | d >= 128
122 | ? std::rotr(static_cast(b), (256 - d) % 8)
123 | : std::rotl(static_cast(b), d % 8));
124 | }
125 |
126 | /// Rotate bits in a byte left (negative d) or right (positive d).
127 | /// \param b Input byte.
128 | /// \param d Number of bits for the rotation.
129 | /// \remark If d = 1, bits 0 becomes bit 1, bits 1 become bit 2, etc.
130 | /// \result The result of the rotation.
131 | constexpr uint8_t rotate_inverted(uint8_t b, uint8_t d) {
132 | return static_cast(
133 | d >= 128
134 | ? std::rotl(static_cast(b), (256 - d) % 8)
135 | : std::rotr(static_cast(b), d % 8));
136 | }
137 |
138 | /// Swap nibbles in a byte.
139 | /// \result The result of the swapping.
140 | constexpr uint8_t swap(uint8_t b) {
141 | return ((b & 0xF0) >> 4) | ((b & 0x0F) << 4);
142 | }
143 | }
144 |
145 | // ------------------------------------------------------------------
146 | // Public interface
147 | // ------------------------------------------------------------------
148 |
149 | /// An obfuscation algorithm
150 | struct Obfuscation {
151 | /// Construct an obfuscation with identity algorithms.
152 | consteval Obfuscation() = default;
153 |
154 | /// Construct an obfuscation with on the fly algorithms.
155 | /// \param counter Randomization counter.
156 | consteval explicit Obfuscation(std::size_t counter) noexcept
157 | : parameters_{
158 | .key = generate_random_not_0(counter, 0x7F),
159 | .key_algo = generate_random(counter + 2, KeyAlgorithm::NB_VALUES), // Identity is acceptable here
160 | .data_algo = generate_random_not_0(counter + 1, DataAlgorithm::NB_VALUES)
161 | } {}
162 |
163 | /// Construct an obfuscation with explicit algorithms.
164 | /// \param params Parameters for the obfuscation (key and algorithms).
165 | consteval explicit Obfuscation(const Parameters ¶ms) noexcept : parameters_{params} {}
166 |
167 | /// Encode a byte.
168 | /// \param key Key to be used for the encoding.
169 | /// \return The encoded byte.
170 | [[nodiscard]] consteval std::uint8_t encode(std::uint8_t c, std::uint8_t key) const {
171 | switch(parameters_.data_algo) {
172 | using enum DataAlgorithm;
173 | case IDENTITY: break;
174 | case CAESAR: return details::caesar(c, key);
175 | case XOR: return details::x0r(c, key);
176 | case ROTATE: return details::rotate(c, key);
177 | case SUBSTITUTE: return details::substitute(c, key);
178 | case NB_VALUES: throw std::exception(); // Invalid data encoding
179 | }
180 | return c;
181 | }
182 |
183 | /// Decode a byte.
184 | /// \param key Key to be used for the decoding.
185 | /// \return The decoded byte.
186 | [[nodiscard]] constexpr std::uint8_t decode(std::uint8_t c, std::uint8_t key) const {
187 | switch(parameters_.data_algo) {
188 | using enum DataAlgorithm;
189 | case IDENTITY: break;
190 | case CAESAR: return details::caesar_inverted(c, key);
191 | case XOR: return static_cast(c ^ key);
192 | case ROTATE: return details::rotate_inverted(c, key);
193 | case SUBSTITUTE: return details::substitute(c, key);
194 | case NB_VALUES: throw std::exception(); // Invalid data encoding
195 | }
196 | return c;
197 | }
198 |
199 | /// Compute the next key from the current one.
200 | /// \param key The current key.
201 | /// \return The new key computed from the given key.
202 | [[nodiscard]] constexpr std::uint8_t next_key(std::uint8_t key) const {
203 | switch(parameters_.key_algo) {
204 | using enum KeyAlgorithm;
205 | case IDENTITY: break; // This is acceptable here
206 | case INCREMENT: return static_cast((key + 1) % 256);
207 | case INVERT: return details::x0r(key, 0xFF);
208 | case SUBSTITUTE: return details::substitute(key, 7);
209 | case SWAP: return details::swap(key);
210 | default: throw std::exception(); // Invalid key encoding;
211 | }
212 | return key;
213 | }
214 |
215 | /// Encode a range of data.
216 | /// \param begin_pos Relative position of the beginning of the range from the whole data.
217 | /// \param begin Pointer to the first byte to encode.
218 | /// \param end Pointer past the last byte to encode.
219 | template
220 | consteval void encode(std::size_t begin_pos, It begin, It end) const noexcept {
221 | auto key = parameters_.key;
222 | while(begin_pos-- > 0) key = next_key(key);
223 | for(auto current = begin; current < end; key = next_key(key), ++current)
224 | *current = encode(*current, key);
225 | }
226 |
227 | /// Decode a range of data.
228 | /// \param begin_pos Relative position of the beginning of the range from the whole data.
229 | /// \param begin Pointer to the first byte to decode.
230 | /// \param end Pointer past the last byte to decode.
231 | template
232 | constexpr void decode(std::size_t begin_pos, It begin, It end) const noexcept {
233 | auto key = parameters_.key;
234 | while(begin_pos-- > 0) key = next_key(key);
235 | for(auto current = begin; current < end; key = next_key(key), ++current)
236 | *current = decode(*current, key);
237 | }
238 |
239 | /// Get the current key for the obfuscation
240 | [[nodiscard]] constexpr std::uint8_t key() const noexcept { return parameters_.key; }
241 |
242 | /// Get the algorithm to compute the next key
243 | [[nodiscard]] constexpr KeyAlgorithm key_algo() const noexcept { return parameters_.key_algo; }
244 |
245 | /// Get the algorithm to encode data
246 | [[nodiscard]] constexpr DataAlgorithm data_algo() const noexcept { return parameters_.data_algo; }
247 |
248 | /// Parameters for the obfuscation (key and algorithms).
249 | Parameters parameters_;
250 | };
251 |
252 | // ------------------------------------------------------------------
253 | // Internal details
254 | // ------------------------------------------------------------------
255 |
256 | namespace details {
257 |
258 | /// Construct an Identity obfuscation (i.e. no obfuscation)
259 | consteval Obfuscation
260 | make_algorithm() { return Obfuscation{}; }
261 |
262 | /// Construct an random generated obfuscation.
263 | /// \param counter Randomization counter.
264 | consteval Obfuscation
265 | make_algorithm(std::size_t counter) { return Obfuscation{counter}; }
266 |
267 | /// Construct an obfuscation with explicit algorithms.
268 | /// \param params Parameters for the obfuscation (key and algorithms).
269 | consteval Obfuscation
270 | make_algorithm(const Parameters ¶ms) { return Obfuscation{params}; }
271 |
272 | /// Construct a set of obfuscations.
273 | /// \param counter Randomization counter.
274 | /// \param nb_algorithms Number of obfuscations to generate.
275 | template
276 | consteval std::array
277 | make_algorithms(std::size_t counter, std::size_t nb_algorithms, std::index_sequence) {
278 | return {I >= nb_algorithms ? make_algorithm() : make_algorithm(counter + 3 * I)...};
279 | }
280 |
281 | /// Construct a set of obfuscations.
282 | /// \param params Array of parameters for the obfuscation (key and algorithms).
283 | template
284 | consteval std::array
285 | make_algorithms(const Parameters (¶ms)[A], std::index_sequence) {
286 | return {I >= A ? make_algorithm() : make_algorithm(params[I])...};
287 | }
288 | }
289 |
290 | /// A set of obfuscations
291 | struct Obfuscations {
292 | /// Construct a set of random generated obfuscations.
293 | /// \param counter Randomization counter.
294 | consteval explicit Obfuscations(std::size_t counter) noexcept
295 | : algos_{details::make_algorithms(
296 | counter,
297 | generate_random(counter, details::MIN_NB_ALGORITHMS, details::MAX_NB_ALGORITHMS),
298 | std::make_index_sequence{}
299 | )} {}
300 |
301 | /// Construct a set of obfuscations with explicit parameters.
302 | /// \param params Parameters for the obfuscation (key and algorithms).
303 | consteval explicit Obfuscations(const Parameters ¶ms) noexcept
304 | : algos_{details::make_algorithm(params)} {}
305 |
306 | /// Construct a set of obfuscations with explicit parameters.
307 | /// \param params Array of parameters for the obfuscation (key and algorithms).
308 | template
309 | consteval explicit Obfuscations(const Parameters (¶ms)[A]) noexcept
310 | : algos_{details::make_algorithms(
311 | params,
312 | std::make_index_sequence{})} {}
313 |
314 | /// Encode a range of data.
315 | /// \param begin_pos Relative position of the beginning of the range from the whole data.
316 | /// \param begin Pointer to the first byte to encode.
317 | /// \param end Pointer past the last byte to encode.
318 | template
319 | consteval void encode(std::size_t begin_pos, It begin, It end) const {
320 | for(std::size_t i = 0; i < details::MAX_NB_ALGORITHMS; ++i)
321 | algos_[i].encode(begin_pos, begin, end);
322 | }
323 |
324 | /// Decode a range of data.
325 | /// \param begin_pos Relative position of the beginning of the range from the whole data.
326 | /// \param begin Pointer to the first byte to decode.
327 | /// \param end Pointer past the last byte to decode.
328 | template
329 | constexpr void decode(std::size_t begin_pos, It begin, It end) const noexcept {
330 | for(std::size_t i = 0; i < details::MAX_NB_ALGORITHMS; ++i)
331 | algos_[details::MAX_NB_ALGORITHMS - i - 1].decode(begin_pos, begin, end);
332 | }
333 |
334 | /// Get a decoded element.
335 | /// \param index Position of the element to decode and return.
336 | constexpr Obfuscation &operator[](std::size_t index) noexcept { return algos_[index]; }
337 |
338 | /// Get a decoded element.
339 | /// \param index Position of the element to decode and return.
340 | constexpr const Obfuscation &operator[](std::size_t index) const noexcept { return algos_[index]; }
341 |
342 | /// A set of obfuscations
343 | std::array algos_;
344 | };
345 | }
346 |
347 | #endif
348 |
--------------------------------------------------------------------------------
/tests/main.cpp:
--------------------------------------------------------------------------------
1 | // ADVobfuscator
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | using namespace andrivet::advobfuscator;
35 |
36 | void test_strings_obfuscation() {
37 | auto s0 = "abc"_obf;
38 |
39 | assert(
40 | s0.raw()[0] != 'a' ||
41 | s0.raw()[1] != 'b' ||
42 | s0.raw()[2] != 'c' ||
43 | s0.raw()[3] != 0
44 | );
45 | assert(s0[0] == 'a');
46 | assert(s0[1] == 'b');
47 | assert(s0[2] == 'c');
48 | assert(s0[3] == 0);
49 |
50 | auto s1 = "ABCDEFGHIJKLMNOP"_obf;
51 | assert(
52 | s1.raw()[0] != 'A' ||
53 | s1.raw()[1] != 'B' ||
54 | s1.raw()[2] != 'C' ||
55 | s1.raw()[3] != 'D' ||
56 | s1.raw()[4] != 'E' ||
57 | s1.raw()[5] != 'F' ||
58 | s1.raw()[6] != 'G' ||
59 | s1.raw()[7] != 'H' ||
60 | s1.raw()[8] != 'I' ||
61 | s1.raw()[9] != 'J' ||
62 | s1.raw()[10] != 'K' ||
63 | s1.raw()[11] != 'L' ||
64 | s1.raw()[12] != 'M' ||
65 | s1.raw()[13] != 'N' ||
66 | s1.raw()[14] != 'O' ||
67 | s1.raw()[15] != 'P' ||
68 | s1.raw()[16] != 0
69 | );
70 | assert(s1[0] == 'A');
71 | assert(s1[1] == 'B');
72 | assert(s1[2] == 'C');
73 | assert(s1[3] == 'D');
74 | assert(s1[4] == 'E');
75 | assert(s1[5] == 'F');
76 | assert(s1[6] == 'G');
77 | assert(s1[7] == 'H');
78 | assert(s1[8] == 'I');
79 | assert(s1[9] == 'J');
80 | assert(s1[10] == 'K');
81 | assert(s1[11] == 'L');
82 | assert(s1[12] == 'M');
83 | assert(s1[13] == 'N');
84 | assert(s1[14] == 'O');
85 | assert(s1[15] == 'P');
86 | assert(s1[16] == 0);
87 |
88 | auto s2{"0123456789"_obf};
89 | assert(
90 | s2.raw()[0] != '0' ||
91 | s2.raw()[1] != '1' ||
92 | s2.raw()[2] != '2' ||
93 | s2.raw()[3] != '3' ||
94 | s2.raw()[4] != '4' ||
95 | s2.raw()[5] != '5' ||
96 | s2.raw()[6] != '6' ||
97 | s2.raw()[7] != '7' ||
98 | s2.raw()[8] != '8' ||
99 | s2.raw()[9] != '9' ||
100 | s2.raw()[10] != 0
101 | );
102 | assert(s2[0] == '0');
103 | assert(s2[1] == '1');
104 | assert(s2[2] == '2');
105 | assert(s2[3] == '3');
106 | assert(s2[4] == '4');
107 | assert(s2[5] == '5');
108 | assert(s2[6] == '6');
109 | assert(s2[7] == '7');
110 | assert(s2[8] == '8');
111 | assert(s2[9] == '9');
112 | assert(s2[10] == 0);
113 |
114 | auto s3 = ObfuscatedString("abcd", {1, KeyAlgorithm::IDENTITY, DataAlgorithm::XOR});
115 | assert(
116 | s3.raw()[0] != 'a' ||
117 | s3.raw()[1] != 'b' ||
118 | s3.raw()[2] != 'c' ||
119 | s3.raw()[3] != 'd' ||
120 | s3.raw()[4] != 0
121 | );
122 | assert(s3[0] == 'a');
123 | assert(s3[1] == 'b');
124 | assert(s3[2] == 'c');
125 | assert(s3[3] == 'd');
126 | assert(s3[4] == 0);
127 |
128 | static constexpr Parameters params[] = {
129 | {.key=1, .key_algo=KeyAlgorithm::IDENTITY, .data_algo=DataAlgorithm::XOR},
130 | {.key=2, .key_algo=KeyAlgorithm::IDENTITY, .data_algo=DataAlgorithm::XOR}
131 | };
132 | auto s4 = ObfuscatedString("abcde", params);
133 | assert(
134 | s4.raw()[0] != 'a' ||
135 | s4.raw()[1] != 'b' ||
136 | s4.raw()[2] != 'c' ||
137 | s4.raw()[3] != 'd' ||
138 | s4.raw()[4] != 'e' ||
139 | s4.raw()[5] != 0
140 | );
141 | assert(s4[0] == 'a');
142 | assert(s4[1] == 'b');
143 | assert(s4[2] == 'c');
144 | assert(s4[3] == 'd');
145 | assert(s4[4] == 'e');
146 | assert(s4[5] == 0);
147 |
148 | static constexpr auto s5 = "An immutable compile-time string"_obf;
149 | assert(s5.decode() == "An immutable compile-time string");
150 | }
151 |
152 | void test_block_obfuscation() {
153 | static constexpr auto rcon = "01 02 04 08 10 20 40 80 1b 36"_obf_bytes;
154 | auto decoded = rcon.decode();
155 |
156 | assert(
157 | rcon.raw()[0] != 0x01 ||
158 | rcon.raw()[1] != 0x02 ||
159 | rcon.raw()[2] != 0x04 ||
160 | rcon.raw()[3] != 0x08 ||
161 | rcon.raw()[4] != 0x10 ||
162 | rcon.raw()[5] != 0x10 ||
163 | rcon.raw()[6] != 0x40 ||
164 | rcon.raw()[7] != 0x80 ||
165 | rcon.raw()[8] != 0x1b ||
166 | rcon.raw()[9] != 0x36
167 | );
168 |
169 | assert(decoded[0] == 0x01);
170 | assert(decoded[1] == 0x02);
171 | assert(decoded[2] == 0x04);
172 | assert(decoded[3] == 0x08);
173 | assert(decoded[4] == 0x10);
174 | assert(decoded[5] == 0x20);
175 | assert(decoded[6] == 0x40);
176 | assert(decoded[7] == 0x80);
177 | assert(decoded[8] == 0x1b);
178 | assert(decoded[9] == 0x36);
179 |
180 | assert(rcon[0] == 0x01);
181 | assert(rcon[1] == 0x02);
182 | assert(rcon[2] == 0x04);
183 | assert(rcon[3] == 0x08);
184 | assert(rcon[4] == 0x10);
185 | assert(rcon[5] == 0x20);
186 | assert(rcon[6] == 0x40);
187 | assert(rcon[7] == 0x80);
188 | assert(rcon[8] == 0x1b);
189 | assert(rcon[9] == 0x36);
190 |
191 | }
192 |
193 | void test_aes_key_expansion() {
194 | // https://csrc.nist.gov/files/pubs/fips/197/final/docs/fips-197.pdf
195 | // Appendix A - Key Expansion Examples
196 |
197 | static constexpr Key key = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
198 |
199 | const auto expanded = details::key_expansion(key);
200 |
201 | uint32_t w00 = expanded[ 0][0] << 24 | expanded[ 0][1] << 16 | expanded[ 0][2] << 8 | expanded[ 0][3];
202 | uint32_t w01 = expanded[ 1][0] << 24 | expanded[ 1][1] << 16 | expanded[ 1][2] << 8 | expanded[ 1][3];
203 | uint32_t w02 = expanded[ 2][0] << 24 | expanded[ 2][1] << 16 | expanded[ 2][2] << 8 | expanded[ 2][3];
204 | uint32_t w03 = expanded[ 3][0] << 24 | expanded[ 3][1] << 16 | expanded[ 3][2] << 8 | expanded[ 3][3];
205 | uint32_t w04 = expanded[ 4][0] << 24 | expanded[ 4][1] << 16 | expanded[ 4][2] << 8 | expanded[ 4][3];
206 | uint32_t w05 = expanded[ 5][0] << 24 | expanded[ 5][1] << 16 | expanded[ 5][2] << 8 | expanded[ 5][3];
207 | uint32_t w06 = expanded[ 6][0] << 24 | expanded[ 6][1] << 16 | expanded[ 6][2] << 8 | expanded[ 6][3];
208 | uint32_t w07 = expanded[ 7][0] << 24 | expanded[ 7][1] << 16 | expanded[ 7][2] << 8 | expanded[ 7][3];
209 | uint32_t w08 = expanded[ 8][0] << 24 | expanded[ 8][1] << 16 | expanded[ 8][2] << 8 | expanded[ 8][3];
210 | uint32_t w09 = expanded[ 9][0] << 24 | expanded[ 9][1] << 16 | expanded[ 9][2] << 8 | expanded[ 9][3];
211 | uint32_t w10 = expanded[10][0] << 24 | expanded[10][1] << 16 | expanded[10][2] << 8 | expanded[10][3];
212 | uint32_t w11 = expanded[11][0] << 24 | expanded[11][1] << 16 | expanded[11][2] << 8 | expanded[11][3];
213 | uint32_t w12 = expanded[12][0] << 24 | expanded[12][1] << 16 | expanded[12][2] << 8 | expanded[12][3];
214 | uint32_t w13 = expanded[13][0] << 24 | expanded[13][1] << 16 | expanded[13][2] << 8 | expanded[13][3];
215 | uint32_t w14 = expanded[14][0] << 24 | expanded[14][1] << 16 | expanded[14][2] << 8 | expanded[14][3];
216 | uint32_t w15 = expanded[15][0] << 24 | expanded[15][1] << 16 | expanded[15][2] << 8 | expanded[15][3];
217 | uint32_t w16 = expanded[16][0] << 24 | expanded[16][1] << 16 | expanded[16][2] << 8 | expanded[16][3];
218 | uint32_t w17 = expanded[17][0] << 24 | expanded[17][1] << 16 | expanded[17][2] << 8 | expanded[17][3];
219 | uint32_t w18 = expanded[18][0] << 24 | expanded[18][1] << 16 | expanded[18][2] << 8 | expanded[18][3];
220 | uint32_t w19 = expanded[19][0] << 24 | expanded[19][1] << 16 | expanded[19][2] << 8 | expanded[19][3];
221 | uint32_t w20 = expanded[20][0] << 24 | expanded[20][1] << 16 | expanded[20][2] << 8 | expanded[20][3];
222 | uint32_t w21 = expanded[21][0] << 24 | expanded[21][1] << 16 | expanded[21][2] << 8 | expanded[21][3];
223 | uint32_t w22 = expanded[22][0] << 24 | expanded[22][1] << 16 | expanded[22][2] << 8 | expanded[22][3];
224 | uint32_t w23 = expanded[23][0] << 24 | expanded[23][1] << 16 | expanded[23][2] << 8 | expanded[23][3];
225 | uint32_t w24 = expanded[24][0] << 24 | expanded[24][1] << 16 | expanded[24][2] << 8 | expanded[24][3];
226 | uint32_t w25 = expanded[25][0] << 24 | expanded[25][1] << 16 | expanded[25][2] << 8 | expanded[25][3];
227 | uint32_t w26 = expanded[26][0] << 24 | expanded[26][1] << 16 | expanded[26][2] << 8 | expanded[26][3];
228 | uint32_t w27 = expanded[27][0] << 24 | expanded[27][1] << 16 | expanded[27][2] << 8 | expanded[27][3];
229 | uint32_t w28 = expanded[28][0] << 24 | expanded[28][1] << 16 | expanded[28][2] << 8 | expanded[28][3];
230 | uint32_t w29 = expanded[29][0] << 24 | expanded[29][1] << 16 | expanded[29][2] << 8 | expanded[29][3];
231 | uint32_t w30 = expanded[30][0] << 24 | expanded[30][1] << 16 | expanded[30][2] << 8 | expanded[30][3];
232 | uint32_t w31 = expanded[31][0] << 24 | expanded[31][1] << 16 | expanded[31][2] << 8 | expanded[31][3];
233 | uint32_t w32 = expanded[32][0] << 24 | expanded[32][1] << 16 | expanded[32][2] << 8 | expanded[32][3];
234 | uint32_t w33 = expanded[33][0] << 24 | expanded[33][1] << 16 | expanded[33][2] << 8 | expanded[33][3];
235 | uint32_t w34 = expanded[34][0] << 24 | expanded[34][1] << 16 | expanded[34][2] << 8 | expanded[34][3];
236 | uint32_t w35 = expanded[35][0] << 24 | expanded[35][1] << 16 | expanded[35][2] << 8 | expanded[35][3];
237 | uint32_t w36 = expanded[36][0] << 24 | expanded[36][1] << 16 | expanded[36][2] << 8 | expanded[36][3];
238 | uint32_t w37 = expanded[37][0] << 24 | expanded[37][1] << 16 | expanded[37][2] << 8 | expanded[37][3];
239 | uint32_t w38 = expanded[38][0] << 24 | expanded[38][1] << 16 | expanded[38][2] << 8 | expanded[38][3];
240 | uint32_t w39 = expanded[39][0] << 24 | expanded[39][1] << 16 | expanded[39][2] << 8 | expanded[39][3];
241 | uint32_t w40 = expanded[40][0] << 24 | expanded[40][1] << 16 | expanded[40][2] << 8 | expanded[40][3];
242 | uint32_t w41 = expanded[41][0] << 24 | expanded[41][1] << 16 | expanded[41][2] << 8 | expanded[41][3];
243 | uint32_t w42 = expanded[42][0] << 24 | expanded[42][1] << 16 | expanded[42][2] << 8 | expanded[42][3];
244 | uint32_t w43 = expanded[43][0] << 24 | expanded[43][1] << 16 | expanded[43][2] << 8 | expanded[43][3];
245 |
246 | assert(w00 == 0x2b7e1516);
247 | assert(w01 == 0x28aed2a6);
248 | assert(w02 == 0xabf71588);
249 | assert(w03 == 0x09cf4f3c);
250 |
251 | assert(w04 == 0xa0fafe17);
252 | assert(w05 == 0x88542cb1);
253 | assert(w06 == 0x23a33939);
254 | assert(w07 == 0x2a6c7605);
255 |
256 | assert(w08 == 0xf2c295f2);
257 | assert(w09 == 0x7a96b943);
258 | assert(w10 == 0x5935807a);
259 | assert(w11 == 0x7359f67f);
260 |
261 | assert(w12 == 0x3d80477d);
262 | assert(w13 == 0x4716fe3e);
263 | assert(w14 == 0x1e237e44);
264 | assert(w15 == 0x6d7a883b);
265 |
266 | assert(w16 == 0xef44a541);
267 | assert(w17 == 0xa8525b7f);
268 | assert(w18 == 0xb671253b);
269 | assert(w19 == 0xdb0bad00);
270 |
271 | assert(w20 == 0xd4d1c6f8);
272 | assert(w21 == 0x7c839d87);
273 | assert(w22 == 0xcaf2b8bc);
274 | assert(w23 == 0x11f915bc);
275 |
276 | assert(w24 == 0x6d88a37a);
277 | assert(w25 == 0x110b3efd);
278 | assert(w26 == 0xdbf98641);
279 | assert(w27 == 0xca0093fd);
280 |
281 | assert(w28 == 0x4e54f70e);
282 | assert(w29 == 0x5f5fc9f3);
283 | assert(w30 == 0x84a64fb2);
284 | assert(w31 == 0x4ea6dc4f);
285 |
286 | assert(w32 == 0xead27321);
287 | assert(w33 == 0xb58dbad2);
288 | assert(w34 == 0x312bf560);
289 | assert(w35 == 0x7f8d292f);
290 |
291 | assert(w36 == 0xac7766f3);
292 | assert(w37 == 0x19fadc21);
293 | assert(w38 == 0x28d12941);
294 | assert(w39 == 0x575c006e);
295 |
296 | assert(w40 == 0xd014f9a8);
297 | assert(w41 == 0xc9ee2589);
298 | assert(w42 == 0xe13f0cc8);
299 | assert(w43 == 0xb6630ca6);
300 | }
301 |
302 | void test_aes_cipher() {
303 | // https://csrc.nist.gov/files/pubs/fips/197/final/docs/fips-197.pdf
304 | // Appendix B - Cipher Example
305 |
306 | static constexpr Block input = {0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34};
307 | static constexpr Key key = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
308 |
309 | const auto encrypted = encrypt(input, key);
310 | assert(encrypted[ 0] == 0x39); assert(encrypted[ 1] == 0x25); assert(encrypted[ 2] == 0x84); assert(encrypted[ 3] == 0x1d);
311 | assert(encrypted[ 4] == 0x02); assert(encrypted[ 5] == 0xdc); assert(encrypted[ 6] == 0x09); assert(encrypted[ 7] == 0xfb);
312 | assert(encrypted[ 8] == 0xdc); assert(encrypted[ 9] == 0x11); assert(encrypted[10] == 0x85); assert(encrypted[11] == 0x97);
313 | assert(encrypted[12] == 0x19); assert(encrypted[13] == 0x6a); assert(encrypted[14] == 0x0b); assert(encrypted[15] == 0x32);
314 |
315 | const auto decrypted = decrypt(encrypted, key);
316 | assert(decrypted[ 0] == 0x32); assert(decrypted[ 1] == 0x43); assert(decrypted[ 2] == 0xf6); assert(decrypted[ 3] == 0xa8);
317 | assert(decrypted[ 4] == 0x88); assert(decrypted[ 5] == 0x5a); assert(decrypted[ 6] == 0x30); assert(decrypted[ 7] == 0x8d);
318 | assert(decrypted[ 8] == 0x31); assert(decrypted[ 9] == 0x31); assert(decrypted[10] == 0x98); assert(decrypted[11] == 0xa2);
319 | assert(decrypted[12] == 0xe0); assert(decrypted[13] == 0x37); assert(decrypted[14] == 0x07); assert(decrypted[15] == 0x34);
320 | }
321 |
322 | void test_aes_ctr_cipher() {
323 | static constexpr Byte input[] = {0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34, 0x69, 0x65, 0xfa, 0x98, 0x18, 0xad, 0x58};
324 | static constexpr Key key = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
325 | static constexpr Nonce nonce = {0xb2, 0x96, 0x75, 0x8f, 0x1b, 0x06, 0x5d, 0x3e};
326 |
327 | auto encrypted = encrypt_ctr(input, key, nonce);
328 | decrypt_ctr(encrypted.data(), encrypted.size(), key, nonce);
329 |
330 | auto decrypted = encrypted.data();
331 | assert(decrypted[ 0] == input[ 0]); assert(decrypted[ 1] == input[ 1]); assert(decrypted[ 2] == input[ 2]); assert(decrypted[ 3] == input[ 3]);
332 | assert(decrypted[ 4] == input[ 4]); assert(decrypted[ 5] == input[ 5]); assert(decrypted[ 6] == input[ 6]); assert(decrypted[ 7] == input[ 7]);
333 | assert(decrypted[ 8] == input[ 8]); assert(decrypted[ 9] == input[ 9]); assert(decrypted[10] == input[10]); assert(decrypted[11] == input[11]);
334 | assert(decrypted[12] == input[12]); assert(decrypted[13] == input[13]); assert(decrypted[14] == input[14]); assert(decrypted[15] == input[15]);
335 | assert(decrypted[16] == input[16]); assert(decrypted[17] == input[17]); assert(decrypted[18] == input[18]); assert(decrypted[19] == input[19]);
336 | assert(decrypted[20] == input[20]); assert(decrypted[21] == input[21]); assert(decrypted[22] == input[22]);
337 | }
338 |
339 |
340 | int main() {
341 | test_strings_obfuscation();
342 | test_block_obfuscation();
343 | test_aes_key_expansion();
344 | test_aes_cipher();
345 | test_aes_ctr_cipher();
346 | return 0;
347 | }
348 |
--------------------------------------------------------------------------------
/ADVobfuscator.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 77;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | D1AA05142E12A8C90014B686 /* guessme.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D1A045AF2E1276CC00488F69 /* guessme.cpp */; };
11 | D1AA05152E12A8CF0014B686 /* demo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D1A045AE2E1276CC00488F69 /* demo.cpp */; };
12 | D1AA05162E12A8E20014B686 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D1A045B42E1276CC00488F69 /* main.cpp */; };
13 | /* End PBXBuildFile section */
14 |
15 | /* Begin PBXCopyFilesBuildPhase section */
16 | D1A045A22E12762700488F69 /* CopyFiles */ = {
17 | isa = PBXCopyFilesBuildPhase;
18 | buildActionMask = 2147483647;
19 | dstPath = /usr/share/man/man1/;
20 | dstSubfolderSpec = 0;
21 | files = (
22 | );
23 | runOnlyForDeploymentPostprocessing = 1;
24 | };
25 | D1AA04FF2E12A7D90014B686 /* CopyFiles */ = {
26 | isa = PBXCopyFilesBuildPhase;
27 | buildActionMask = 2147483647;
28 | dstPath = /usr/share/man/man1/;
29 | dstSubfolderSpec = 0;
30 | files = (
31 | );
32 | runOnlyForDeploymentPostprocessing = 1;
33 | };
34 | D1AA050A2E12A8050014B686 /* CopyFiles */ = {
35 | isa = PBXCopyFilesBuildPhase;
36 | buildActionMask = 2147483647;
37 | dstPath = /usr/share/man/man1/;
38 | dstSubfolderSpec = 0;
39 | files = (
40 | );
41 | runOnlyForDeploymentPostprocessing = 1;
42 | };
43 | /* End PBXCopyFilesBuildPhase section */
44 |
45 | /* Begin PBXFileReference section */
46 | D1A045A42E12762700488F69 /* example */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example; sourceTree = BUILT_PRODUCTS_DIR; };
47 | D1A045AE2E1276CC00488F69 /* demo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = demo.cpp; sourceTree = ""; };
48 | D1A045AF2E1276CC00488F69 /* guessme.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = guessme.cpp; sourceTree = ""; };
49 | D1A045B02E1276CC00488F69 /* guessme_aes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = guessme_aes.cpp; sourceTree = ""; };
50 | D1A045B22E1276CC00488F69 /* describe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = describe.h; sourceTree = ""; };
51 | D1A045B32E1276CC00488F69 /* hexdump.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hexdump.h; sourceTree = ""; };
52 | D1A045B42E1276CC00488F69 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; };
53 | D1AA05012E12A7D90014B686 /* demo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = demo; sourceTree = BUILT_PRODUCTS_DIR; };
54 | D1AA050C2E12A8050014B686 /* guessme */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = guessme; sourceTree = BUILT_PRODUCTS_DIR; };
55 | /* End PBXFileReference section */
56 |
57 | /* Begin PBXFrameworksBuildPhase section */
58 | D1A045A12E12762700488F69 /* Frameworks */ = {
59 | isa = PBXFrameworksBuildPhase;
60 | buildActionMask = 2147483647;
61 | files = (
62 | );
63 | runOnlyForDeploymentPostprocessing = 0;
64 | };
65 | D1AA04FE2E12A7D90014B686 /* Frameworks */ = {
66 | isa = PBXFrameworksBuildPhase;
67 | buildActionMask = 2147483647;
68 | files = (
69 | );
70 | runOnlyForDeploymentPostprocessing = 0;
71 | };
72 | D1AA05092E12A8050014B686 /* Frameworks */ = {
73 | isa = PBXFrameworksBuildPhase;
74 | buildActionMask = 2147483647;
75 | files = (
76 | );
77 | runOnlyForDeploymentPostprocessing = 0;
78 | };
79 | /* End PBXFrameworksBuildPhase section */
80 |
81 | /* Begin PBXGroup section */
82 | D1A0459B2E12762700488F69 = {
83 | isa = PBXGroup;
84 | children = (
85 | D1A045A52E12762700488F69 /* Products */,
86 | D1A045B72E1276CC00488F69 /* Examples */,
87 | );
88 | sourceTree = "";
89 | };
90 | D1A045A52E12762700488F69 /* Products */ = {
91 | isa = PBXGroup;
92 | children = (
93 | D1A045A42E12762700488F69 /* example */,
94 | D1AA05012E12A7D90014B686 /* demo */,
95 | D1AA050C2E12A8050014B686 /* guessme */,
96 | );
97 | name = Products;
98 | sourceTree = "";
99 | };
100 | D1A045B12E1276CC00488F69 /* demo */ = {
101 | isa = PBXGroup;
102 | children = (
103 | D1A045AE2E1276CC00488F69 /* demo.cpp */,
104 | D1A045AF2E1276CC00488F69 /* guessme.cpp */,
105 | D1A045B02E1276CC00488F69 /* guessme_aes.cpp */,
106 | );
107 | path = demo;
108 | sourceTree = "";
109 | };
110 | D1A045B52E1276CC00488F69 /* example */ = {
111 | isa = PBXGroup;
112 | children = (
113 | D1A045B22E1276CC00488F69 /* describe.h */,
114 | D1A045B32E1276CC00488F69 /* hexdump.h */,
115 | D1A045B42E1276CC00488F69 /* main.cpp */,
116 | );
117 | path = example;
118 | sourceTree = "";
119 | };
120 | D1A045B72E1276CC00488F69 /* Examples */ = {
121 | isa = PBXGroup;
122 | children = (
123 | D1A045B12E1276CC00488F69 /* demo */,
124 | D1A045B52E1276CC00488F69 /* example */,
125 | );
126 | path = Examples;
127 | sourceTree = "";
128 | };
129 | /* End PBXGroup section */
130 |
131 | /* Begin PBXNativeTarget section */
132 | D1A045A32E12762700488F69 /* example */ = {
133 | isa = PBXNativeTarget;
134 | buildConfigurationList = D1A045AB2E12762700488F69 /* Build configuration list for PBXNativeTarget "example" */;
135 | buildPhases = (
136 | D1A045A02E12762700488F69 /* Sources */,
137 | D1A045A12E12762700488F69 /* Frameworks */,
138 | D1A045A22E12762700488F69 /* CopyFiles */,
139 | );
140 | buildRules = (
141 | );
142 | dependencies = (
143 | );
144 | name = example;
145 | packageProductDependencies = (
146 | );
147 | productName = ADVobfuscator;
148 | productReference = D1A045A42E12762700488F69 /* example */;
149 | productType = "com.apple.product-type.tool";
150 | };
151 | D1AA05002E12A7D90014B686 /* demo */ = {
152 | isa = PBXNativeTarget;
153 | buildConfigurationList = D1AA05052E12A7D90014B686 /* Build configuration list for PBXNativeTarget "demo" */;
154 | buildPhases = (
155 | D1AA04FD2E12A7D90014B686 /* Sources */,
156 | D1AA04FE2E12A7D90014B686 /* Frameworks */,
157 | D1AA04FF2E12A7D90014B686 /* CopyFiles */,
158 | );
159 | buildRules = (
160 | );
161 | dependencies = (
162 | );
163 | name = demo;
164 | packageProductDependencies = (
165 | );
166 | productName = demo;
167 | productReference = D1AA05012E12A7D90014B686 /* demo */;
168 | productType = "com.apple.product-type.tool";
169 | };
170 | D1AA050B2E12A8050014B686 /* guessme */ = {
171 | isa = PBXNativeTarget;
172 | buildConfigurationList = D1AA05102E12A8050014B686 /* Build configuration list for PBXNativeTarget "guessme" */;
173 | buildPhases = (
174 | D1AA05082E12A8050014B686 /* Sources */,
175 | D1AA05092E12A8050014B686 /* Frameworks */,
176 | D1AA050A2E12A8050014B686 /* CopyFiles */,
177 | );
178 | buildRules = (
179 | );
180 | dependencies = (
181 | );
182 | name = guessme;
183 | packageProductDependencies = (
184 | );
185 | productName = guessme;
186 | productReference = D1AA050C2E12A8050014B686 /* guessme */;
187 | productType = "com.apple.product-type.tool";
188 | };
189 | /* End PBXNativeTarget section */
190 |
191 | /* Begin PBXProject section */
192 | D1A0459C2E12762700488F69 /* Project object */ = {
193 | isa = PBXProject;
194 | attributes = {
195 | BuildIndependentTargetsInParallel = 1;
196 | LastUpgradeCheck = 1640;
197 | TargetAttributes = {
198 | D1A045A32E12762700488F69 = {
199 | CreatedOnToolsVersion = 16.4;
200 | };
201 | D1AA05002E12A7D90014B686 = {
202 | CreatedOnToolsVersion = 16.4;
203 | };
204 | D1AA050B2E12A8050014B686 = {
205 | CreatedOnToolsVersion = 16.4;
206 | };
207 | };
208 | };
209 | buildConfigurationList = D1A0459F2E12762700488F69 /* Build configuration list for PBXProject "ADVobfuscator" */;
210 | developmentRegion = en;
211 | hasScannedForEncodings = 0;
212 | knownRegions = (
213 | en,
214 | Base,
215 | );
216 | mainGroup = D1A0459B2E12762700488F69;
217 | minimizedProjectReferenceProxies = 1;
218 | preferredProjectObjectVersion = 77;
219 | productRefGroup = D1A045A52E12762700488F69 /* Products */;
220 | projectDirPath = "";
221 | projectRoot = "";
222 | targets = (
223 | D1A045A32E12762700488F69 /* example */,
224 | D1AA05002E12A7D90014B686 /* demo */,
225 | D1AA050B2E12A8050014B686 /* guessme */,
226 | );
227 | };
228 | /* End PBXProject section */
229 |
230 | /* Begin PBXSourcesBuildPhase section */
231 | D1A045A02E12762700488F69 /* Sources */ = {
232 | isa = PBXSourcesBuildPhase;
233 | buildActionMask = 2147483647;
234 | files = (
235 | D1AA05162E12A8E20014B686 /* main.cpp in Sources */,
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | };
239 | D1AA04FD2E12A7D90014B686 /* Sources */ = {
240 | isa = PBXSourcesBuildPhase;
241 | buildActionMask = 2147483647;
242 | files = (
243 | D1AA05152E12A8CF0014B686 /* demo.cpp in Sources */,
244 | );
245 | runOnlyForDeploymentPostprocessing = 0;
246 | };
247 | D1AA05082E12A8050014B686 /* Sources */ = {
248 | isa = PBXSourcesBuildPhase;
249 | buildActionMask = 2147483647;
250 | files = (
251 | D1AA05142E12A8C90014B686 /* guessme.cpp in Sources */,
252 | );
253 | runOnlyForDeploymentPostprocessing = 0;
254 | };
255 | /* End PBXSourcesBuildPhase section */
256 |
257 | /* Begin XCBuildConfiguration section */
258 | D1A045A92E12762700488F69 /* Debug */ = {
259 | isa = XCBuildConfiguration;
260 | buildSettings = {
261 | ALWAYS_SEARCH_USER_PATHS = NO;
262 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
263 | CLANG_ANALYZER_NONNULL = YES;
264 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
265 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
266 | CLANG_ENABLE_MODULES = YES;
267 | CLANG_ENABLE_OBJC_ARC = YES;
268 | CLANG_ENABLE_OBJC_WEAK = YES;
269 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
270 | CLANG_WARN_BOOL_CONVERSION = YES;
271 | CLANG_WARN_COMMA = YES;
272 | CLANG_WARN_CONSTANT_CONVERSION = YES;
273 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
274 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
275 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
276 | CLANG_WARN_EMPTY_BODY = YES;
277 | CLANG_WARN_ENUM_CONVERSION = YES;
278 | CLANG_WARN_INFINITE_RECURSION = YES;
279 | CLANG_WARN_INT_CONVERSION = YES;
280 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
281 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
282 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
284 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
285 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
286 | CLANG_WARN_STRICT_PROTOTYPES = YES;
287 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
288 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
289 | CLANG_WARN_UNREACHABLE_CODE = YES;
290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
291 | COPY_PHASE_STRIP = NO;
292 | DEBUG_INFORMATION_FORMAT = dwarf;
293 | ENABLE_STRICT_OBJC_MSGSEND = YES;
294 | ENABLE_TESTABILITY = YES;
295 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
296 | GCC_C_LANGUAGE_STANDARD = gnu17;
297 | GCC_DYNAMIC_NO_PIC = NO;
298 | GCC_NO_COMMON_BLOCKS = YES;
299 | GCC_OPTIMIZATION_LEVEL = 0;
300 | GCC_PREPROCESSOR_DEFINITIONS = (
301 | "DEBUG=1",
302 | "$(inherited)",
303 | );
304 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
305 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
306 | GCC_WARN_UNDECLARED_SELECTOR = YES;
307 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
308 | GCC_WARN_UNUSED_FUNCTION = YES;
309 | GCC_WARN_UNUSED_VARIABLE = YES;
310 | HEADER_SEARCH_PATHS = include;
311 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
312 | MACOSX_DEPLOYMENT_TARGET = 15.5;
313 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
314 | MTL_FAST_MATH = YES;
315 | ONLY_ACTIVE_ARCH = YES;
316 | SDKROOT = macosx;
317 | };
318 | name = Debug;
319 | };
320 | D1A045AA2E12762700488F69 /* Release */ = {
321 | isa = XCBuildConfiguration;
322 | buildSettings = {
323 | ALWAYS_SEARCH_USER_PATHS = NO;
324 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
325 | CLANG_ANALYZER_NONNULL = YES;
326 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
328 | CLANG_ENABLE_MODULES = YES;
329 | CLANG_ENABLE_OBJC_ARC = YES;
330 | CLANG_ENABLE_OBJC_WEAK = YES;
331 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
332 | CLANG_WARN_BOOL_CONVERSION = YES;
333 | CLANG_WARN_COMMA = YES;
334 | CLANG_WARN_CONSTANT_CONVERSION = YES;
335 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
336 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
337 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
338 | CLANG_WARN_EMPTY_BODY = YES;
339 | CLANG_WARN_ENUM_CONVERSION = YES;
340 | CLANG_WARN_INFINITE_RECURSION = YES;
341 | CLANG_WARN_INT_CONVERSION = YES;
342 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
343 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
344 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
345 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
346 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
347 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
348 | CLANG_WARN_STRICT_PROTOTYPES = YES;
349 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
350 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
351 | CLANG_WARN_UNREACHABLE_CODE = YES;
352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
353 | COPY_PHASE_STRIP = NO;
354 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
355 | ENABLE_NS_ASSERTIONS = NO;
356 | ENABLE_STRICT_OBJC_MSGSEND = YES;
357 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
358 | GCC_C_LANGUAGE_STANDARD = gnu17;
359 | GCC_NO_COMMON_BLOCKS = YES;
360 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
361 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
362 | GCC_WARN_UNDECLARED_SELECTOR = YES;
363 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
364 | GCC_WARN_UNUSED_FUNCTION = YES;
365 | GCC_WARN_UNUSED_VARIABLE = YES;
366 | HEADER_SEARCH_PATHS = include;
367 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
368 | MACOSX_DEPLOYMENT_TARGET = 15.5;
369 | MTL_ENABLE_DEBUG_INFO = NO;
370 | MTL_FAST_MATH = YES;
371 | SDKROOT = macosx;
372 | };
373 | name = Release;
374 | };
375 | D1A045AC2E12762700488F69 /* Debug */ = {
376 | isa = XCBuildConfiguration;
377 | buildSettings = {
378 | CODE_SIGN_STYLE = Automatic;
379 | MACOSX_DEPLOYMENT_TARGET = 11.5;
380 | PRODUCT_NAME = "$(TARGET_NAME)";
381 | };
382 | name = Debug;
383 | };
384 | D1A045AD2E12762700488F69 /* Release */ = {
385 | isa = XCBuildConfiguration;
386 | buildSettings = {
387 | CODE_SIGN_STYLE = Automatic;
388 | MACOSX_DEPLOYMENT_TARGET = 11.5;
389 | PRODUCT_NAME = "$(TARGET_NAME)";
390 | };
391 | name = Release;
392 | };
393 | D1AA05062E12A7D90014B686 /* Debug */ = {
394 | isa = XCBuildConfiguration;
395 | buildSettings = {
396 | CODE_SIGN_STYLE = Automatic;
397 | MACOSX_DEPLOYMENT_TARGET = 11.5;
398 | PRODUCT_NAME = "$(TARGET_NAME)";
399 | };
400 | name = Debug;
401 | };
402 | D1AA05072E12A7D90014B686 /* Release */ = {
403 | isa = XCBuildConfiguration;
404 | buildSettings = {
405 | CODE_SIGN_STYLE = Automatic;
406 | MACOSX_DEPLOYMENT_TARGET = 11.5;
407 | PRODUCT_NAME = "$(TARGET_NAME)";
408 | };
409 | name = Release;
410 | };
411 | D1AA05112E12A8050014B686 /* Debug */ = {
412 | isa = XCBuildConfiguration;
413 | buildSettings = {
414 | CODE_SIGN_STYLE = Automatic;
415 | MACOSX_DEPLOYMENT_TARGET = 11.5;
416 | PRODUCT_NAME = "$(TARGET_NAME)";
417 | };
418 | name = Debug;
419 | };
420 | D1AA05122E12A8050014B686 /* Release */ = {
421 | isa = XCBuildConfiguration;
422 | buildSettings = {
423 | CODE_SIGN_STYLE = Automatic;
424 | MACOSX_DEPLOYMENT_TARGET = 11.5;
425 | PRODUCT_NAME = "$(TARGET_NAME)";
426 | };
427 | name = Release;
428 | };
429 | /* End XCBuildConfiguration section */
430 |
431 | /* Begin XCConfigurationList section */
432 | D1A0459F2E12762700488F69 /* Build configuration list for PBXProject "ADVobfuscator" */ = {
433 | isa = XCConfigurationList;
434 | buildConfigurations = (
435 | D1A045A92E12762700488F69 /* Debug */,
436 | D1A045AA2E12762700488F69 /* Release */,
437 | );
438 | defaultConfigurationIsVisible = 0;
439 | defaultConfigurationName = Release;
440 | };
441 | D1A045AB2E12762700488F69 /* Build configuration list for PBXNativeTarget "example" */ = {
442 | isa = XCConfigurationList;
443 | buildConfigurations = (
444 | D1A045AC2E12762700488F69 /* Debug */,
445 | D1A045AD2E12762700488F69 /* Release */,
446 | );
447 | defaultConfigurationIsVisible = 0;
448 | defaultConfigurationName = Release;
449 | };
450 | D1AA05052E12A7D90014B686 /* Build configuration list for PBXNativeTarget "demo" */ = {
451 | isa = XCConfigurationList;
452 | buildConfigurations = (
453 | D1AA05062E12A7D90014B686 /* Debug */,
454 | D1AA05072E12A7D90014B686 /* Release */,
455 | );
456 | defaultConfigurationIsVisible = 0;
457 | defaultConfigurationName = Release;
458 | };
459 | D1AA05102E12A8050014B686 /* Build configuration list for PBXNativeTarget "guessme" */ = {
460 | isa = XCConfigurationList;
461 | buildConfigurations = (
462 | D1AA05112E12A8050014B686 /* Debug */,
463 | D1AA05122E12A8050014B686 /* Release */,
464 | );
465 | defaultConfigurationIsVisible = 0;
466 | defaultConfigurationName = Release;
467 | };
468 | /* End XCConfigurationList section */
469 | };
470 | rootObject = D1A0459C2E12762700488F69 /* Project object */;
471 | }
472 |
--------------------------------------------------------------------------------
/include/advobfuscator/aes.h:
--------------------------------------------------------------------------------
1 | // ADVobfuscator - A compile-time implementation of AES
2 | //
3 | // Copyright (c) 2025, Sebastien Andrivet
4 | // All rights reserved.
5 | //
6 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
7 | // following conditions are met:
8 | //
9 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10 | // disclaimer.
11 | //
12 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
13 | // following disclaimer in the documentation and/or other materials provided with the distribution.
14 | //
15 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
16 | // products derived from this software without specific prior written permission.
17 | //
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 | // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | //
26 | // Get latest version on https://github.com/andrivet/ADVobfuscator
27 |
28 | #ifndef ADVOBFUSCATOR_AES_H
29 | #define ADVOBFUSCATOR_AES_H
30 |
31 | // References:
32 | // * https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/aes-development/rijndael-ammended.pdf#page=19
33 | // * https://csrc.nist.gov/csrc/media/publications/fips/197/final/documents/fips-197.pdf
34 | // * https://cr.yp.to/aes-speed.html
35 | // * https://stackoverflow.com/questions/69066821/rijndael-s-box-in-c
36 | // * https://link.springer.com/content/pdf/10.1007/3-540-36400-5_13.pdf
37 |
38 | #include
39 | #include
40 | #include
41 | #include "random.h"
42 | #include "bytes.h"
43 |
44 | namespace andrivet::advobfuscator {
45 | /// Length of the cypher key
46 | static constexpr std::size_t n_key{128}; // 128-bit or 192-bit or 256-bit
47 |
48 | // I prefer to use std::uin8_t instead of std::byte for the implicit conversions (from int)
49 | using Byte = std::uint8_t;
50 | using Block = std::array;
51 | using Key = std::array;
52 | using Nonce = std::array;
53 |
54 | // ------------------------------------------------------------------
55 | // Internal details
56 | // ------------------------------------------------------------------
57 |
58 | namespace details {
59 | // A 32-bit word
60 | using Word = std::array;
61 | // An array of 4 columns, each column has 4 rows (s[column][row]).
62 | using State = std::array;
63 |
64 | /// Number of rounds
65 | consteval std::size_t n_rounds() {
66 | static_assert(n_key == 128 || n_key == 192 || n_key == 256);
67 | return n_key == 128 ? 10 : n_key == 192 ? 12 : 14;
68 | }
69 |
70 | // Expanded key
71 | using EKey = std::array;
72 |
73 | // Rijndael S-Box (obfuscated)
74 | static constexpr ObfuscatedBytes<16 * 3> sbox[16] = {
75 | "63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76"_obf_bytes,
76 | "CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0"_obf_bytes,
77 | "B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15"_obf_bytes,
78 | "04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75"_obf_bytes,
79 | "09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84"_obf_bytes,
80 | "53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF"_obf_bytes,
81 | "D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8"_obf_bytes,
82 | "51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2"_obf_bytes,
83 | "CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73"_obf_bytes,
84 | "60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB"_obf_bytes,
85 | "E0 32 3A 0A 49 06 24 5C C2 D3 AC 62 91 95 E4 79"_obf_bytes,
86 | "E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08"_obf_bytes,
87 | "BA 78 25 2E 1C A6 B4 C6 E8 DD 74 1F 4B BD 8B 8A"_obf_bytes,
88 | "70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E"_obf_bytes,
89 | "E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF"_obf_bytes,
90 | "8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16"_obf_bytes};
91 |
92 | // Rijndael Inverse S-Box (obfuscated)
93 | static constexpr ObfuscatedBytes<16 * 3> inv_sbox[16] = {
94 | "52 09 6a d5 30 36 a5 38 bf 40 a3 9e 81 f3 d7 fb"_obf_bytes,
95 | "7c e3 39 82 9b 2f ff 87 34 8e 43 44 c4 de e9 cb"_obf_bytes,
96 | "54 7b 94 32 a6 c2 23 3d ee 4c 95 0b 42 fa c3 4e"_obf_bytes,
97 | "08 2e a1 66 28 d9 24 b2 76 5b a2 49 6d 8b d1 25"_obf_bytes,
98 | "72 f8 f6 64 86 68 98 16 d4 a4 5c cc 5d 65 b6 92"_obf_bytes,
99 | "6c 70 48 50 fd ed b9 da 5e 15 46 57 a7 8d 9d 84"_obf_bytes,
100 | "90 d8 ab 00 8c bc d3 0a f7 e4 58 05 b8 b3 45 06"_obf_bytes,
101 | "d0 2c 1e 8f ca 3f 0f 02 c1 af bd 03 01 13 8a 6b"_obf_bytes,
102 | "3a 91 11 41 4f 67 dc ea 97 f2 cf ce f0 b4 e6 73"_obf_bytes,
103 | "96 ac 74 22 e7 ad 35 85 e2 f9 37 e8 1c 75 df 6e"_obf_bytes,
104 | "47 f1 1a 71 1d 29 c5 89 6f b7 62 0e aa 18 be 1b"_obf_bytes,
105 | "fc 56 3e 4b c6 d2 79 20 9a db c0 fe 78 cd 5a f4"_obf_bytes,
106 | "1f dd a8 33 88 07 c7 31 b1 12 10 59 27 80 ec 5f"_obf_bytes,
107 | "60 51 7f a9 19 b5 4a 0d 2d e5 7a 9f 93 c9 9c ef"_obf_bytes,
108 | "a0 e0 3b 4d ae 2a f5 b0 c8 eb bb 3c 83 53 99 61"_obf_bytes,
109 | "17 2b 04 7e ba 77 d6 26 e1 69 14 63 55 21 0c 7d"_obf_bytes};
110 |
111 | // Rijndael round constants (obfuscated)
112 | static constexpr auto rcon = "01 02 04 08 10 20 40 80 1b 36"_obf_bytes;
113 |
114 | /// Get high nibble
115 | /// \param b Byte to which we want to extract the high nibble.
116 | /// \return The high nibble
117 | [[nodiscard]] constexpr Byte high(Byte b) { return b >> 4; }
118 |
119 | /// Get low nibble
120 | /// \param b Byte to which we want to extract the low nibble.
121 | /// \return The low nibble
122 | [[nodiscard]] constexpr Byte low(Byte b) { return b & 0x0F; }
123 |
124 | /// Multiplication in GF(2^8) of two bytes.
125 | /// \param v0 First argument
126 | /// \param v1 Second argument
127 | /// \return Result of the multiplication of v0 and v1 in GF(2^8)
128 | /// \remark https://en.wikipedia.org/wiki/Rijndael_MixColumns
129 | [[nodiscard]] constexpr Byte gmul(Byte v0, Byte v1) {
130 | Byte product = 0; // Initial product value
131 | // For each bit...
132 | for(std::size_t i = 0; i < 8; ++i) {
133 | // If least significant bit is set, add (xor) v0 to product
134 | if(v1 & 1) product ^= v0;
135 | // Set high_bit to the x^7 term of v0
136 | const bool high_bit = v0 & 0x80;
137 | // Shift v0 to the left to multiply it by x (v0 = v0 * x)
138 | v0 <<= 1;
139 | // Turn x^8 into x^4+x^3+x+1
140 | if(high_bit) v0 ^= 0x1B;
141 | // Right shift v1
142 | v1 >>= 1;
143 | }
144 | return product;
145 | }
146 |
147 | /// SubWord Transformation - non-linear byte substitution using sbox.
148 | /// \param word Word to transform.
149 | /// \return Transformed Word.
150 | [[nodiscard]] constexpr Word sub_word(const Word &word) {
151 | return Word{
152 | sbox[high(word[0])][low(word[0])],
153 | sbox[high(word[1])][low(word[1])],
154 | sbox[high(word[2])][low(word[2])],
155 | sbox[high(word[3])][low(word[3])]
156 | };
157 | }
158 |
159 | /// SubBytes Transformation - non-linear byte substitution using sbox.
160 | /// \param state State to transform.
161 | /// \return Transformed state.
162 | [[nodiscard]] constexpr State sub_bytes(const State &state) {
163 | return State{
164 | sub_word(state[0]),
165 | sub_word(state[1]),
166 | sub_word(state[2]),
167 | sub_word(state[3])};
168 | }
169 |
170 | /// InvSubWord Transformation -Inverse of SubWord.
171 | /// \param word Word to transform.
172 | /// \return Transformed Word.
173 | [[nodiscard]] constexpr Word inv_sub_word(const Word &word) {
174 | return Word{
175 | inv_sbox[high(word[0])][low(word[0])],
176 | inv_sbox[high(word[1])][low(word[1])],
177 | inv_sbox[high(word[2])][low(word[2])],
178 | inv_sbox[high(word[3])][low(word[3])]
179 | };
180 | }
181 |
182 | /// InvSubBytes Transformation - Inverse of SubBytes.
183 | /// \param state State to transform.
184 | /// \return Transformed state.
185 | [[nodiscard]] constexpr State inv_sub_bytes(const State &state) {
186 | return State{
187 | inv_sub_word(state[0]),
188 | inv_sub_word(state[1]),
189 | inv_sub_word(state[2]),
190 | inv_sub_word(state[3])};
191 | }
192 |
193 | /// ShiftRows Transformation - bytes in the last three rows are cyclically shifted.
194 | /// \param state State to transform.
195 | /// \return Transformed state.
196 | /// \remark Section 5.1.2
197 | [[nodiscard]] constexpr State shift_rows(const State &state) {
198 | return State{
199 | Word{state[0][0], state[1][1], state[2][2], state[3][3]}, // c0
200 | Word{state[1][0], state[2][1], state[3][2], state[0][3]}, // c1
201 | Word{state[2][0], state[3][1], state[0][2], state[1][3]}, // c2
202 | Word{state[3][0], state[0][1], state[1][2], state[2][3]} // c3
203 | };
204 | }
205 |
206 | /// InvShiftRows Transformation - Inverse of ShiftRows.
207 | /// \param state State to transform.
208 | /// \return Transformed state.
209 | /// \remark Section 5.1.2
210 | [[nodiscard]] constexpr State inv_shift_rows(const State &state) {
211 | return State{
212 | Word{state[0][0], state[3][1], state[2][2], state[1][3]}, // c0
213 | Word{state[1][0], state[0][1], state[3][2], state[2][3]}, // c1
214 | Word{state[2][0], state[1][1], state[0][2], state[3][3]}, // c2
215 | Word{state[3][0], state[2][1], state[1][2], state[0][3]} // c3
216 | };
217 | }
218 |
219 | /// MixColumns Transformation - Multiply a column by a fixed polynomial.
220 | /// \param c The column to transform.
221 | /// \return The transformed column.
222 | /// \remark Section 5.1.3
223 | [[nodiscard]] constexpr Word mix_column(const Word &c) {
224 | // 4.2.3 - The MixColumn transformation
225 | // c(x) = 3 * x^3 + 1 * x^2 + 1 * x + 2 modulo x^4 + 1
226 | const auto v0{gmul(c[0], 0x02) ^ gmul(c[1], 0x03) ^ c[2] ^ c[3]};
227 | const auto v1{ c[0] ^ gmul(c[1], 0x02) ^ gmul(c[2], 0x03) ^ c[3]};
228 | const auto v2{ c[0] ^ c[1] ^ gmul(c[2], 0x02) ^ gmul(c[3], 0x03)};
229 | const auto v3{gmul(c[0], 0x03) ^ c[1] ^ c[2] ^ gmul(c[3], 0x02)};
230 | return Word{
231 | static_cast(v0),
232 | static_cast(v1),
233 | static_cast(v2),
234 | static_cast(v3)};
235 | }
236 |
237 | /// InvMixColumns Transformation - Inverse of MixColumns.
238 | /// \param c The column to transform.
239 | /// \return The transformed column.
240 | /// \remark Section 5.3.3
241 | [[nodiscard]] constexpr Word inv_mix_column(const Word &c) {
242 | // 4.2.3 - The MixColumn transformation
243 | // c(x) = 3 * x^3 + 1 * x^2 + 1 * x + 2 modulo x^4 + 1
244 | const auto v0{gmul(c[0], 0x0e) ^ gmul(c[1], 0x0b) ^ gmul(c[2], 0x0d) ^ gmul(c[3], 0x09)};
245 | const auto v1{gmul(c[0], 0x09) ^ gmul(c[1], 0x0e) ^ gmul(c[2], 0x0b) ^ gmul(c[3], 0x0d)};
246 | const auto v2{gmul(c[0], 0x0d) ^ gmul(c[1], 0x09) ^ gmul(c[2], 0x0e) ^ gmul(c[3], 0x0b)};
247 | const auto v3{gmul(c[0], 0x0b) ^ gmul(c[1], 0x0d) ^ gmul(c[2], 0x09) ^ gmul(c[3], 0x0e)};
248 | return Word{
249 | static_cast(v0),
250 | static_cast(v1),
251 | static_cast(v2),
252 | static_cast(v3)};
253 | }
254 |
255 | /// MixColumns Transformation - Multiply columns by a fixed polynomial.
256 | /// \param state The state to transform.
257 | /// \return The transformed state.
258 | [[nodiscard]] constexpr State mix_columns(const State &state) {
259 | return State{mix_column(state[0]), mix_column(state[1]), mix_column(state[2]), mix_column(state[3])};
260 | }
261 |
262 | /// InvMixColumns Transformation - Inverse of MixColumns.
263 | /// \param state The state to transform.
264 | /// \return The transformed state.
265 | [[nodiscard]] constexpr State inv_mix_columns(const State &state) {
266 | return State{inv_mix_column(state[0]), inv_mix_column(state[1]), inv_mix_column(state[2]), inv_mix_column(state[3])};
267 | }
268 |
269 | /// AddRoundKey Transformation - Add a Round Key to the State.
270 | /// \param state The current state.
271 | /// \param ekey The round key.
272 | /// \return The transformed state.
273 | /// \remark Section 5.1.4
274 | [[nodiscard]] constexpr State add_round_key(const State &state, const EKey &ekey, std::size_t round) {
275 | State new_state;
276 | for(std::size_t c = 0; c < 4; ++c)
277 | for(std::size_t r = 0; r < 4; ++r)
278 | new_state[c][r] = state[c][r] ^ ekey[round * 4 + c][r];
279 | return new_state;
280 | }
281 |
282 | /// RotWord Transformation - Cyclic permutation
283 | /// \param w The Word to transform.
284 | /// \returnThe transformed Word.
285 | [[nodiscard]] constexpr Word rot_word(const Word &w) {
286 | return Word{w[1], w[2], w[3], w[0]};
287 | }
288 |
289 | /// Addition (XOR) in GF(2^8) of two words.
290 | /// \param w0 The left operand.
291 | /// \param w1 The right operand.
292 | /// \return The result of the addition in GF(2^8) of each byte.
293 | [[nodiscard]] constexpr Word operator^(const Word &w0, const Word &w1) {
294 | return Word{
295 | static_cast(w0[0] ^ w1[0]),
296 | static_cast(w0[1] ^ w1[1]),
297 | static_cast(w0[2] ^ w1[2]),
298 | static_cast(w0[3] ^ w1[3])
299 | };
300 | }
301 |
302 | /// Addition (XOR) in GF(2^8) of a word and a byte.
303 | /// \param w0 The left operand.
304 | /// \param b The right operand.
305 | /// \return The result of the addition in in GF(2^8) of each byte of the left operand with the right operand.
306 | [[nodiscard]] constexpr Word operator^(const Word &w0, Byte b) {
307 | return Word{
308 | static_cast(w0[0] ^ b),
309 | static_cast(w0[1] ^ b),
310 | static_cast(w0[2] ^ b),
311 | static_cast(w0[3] ^ b)
312 | };
313 | }
314 |
315 | /// Key Expansion - Generate a key schedule.
316 | /// \param key The key to be expanded.
317 | /// \return The expanded key.
318 | /// \remark Section 5.2
319 | [[nodiscard]] constexpr EKey key_expansion(const Key &key) {
320 | EKey ekey;
321 | const auto nk = n_key / 32;
322 |
323 | // First 4 words: copy of the encryption key
324 | for(std::size_t i = 0; i < nk; ++i)
325 | ekey[i] = {key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]};
326 |
327 | const auto n_r = n_rounds();
328 | for(std::size_t i = nk; i < 4 * (n_r + 1); ++i) {
329 | Word temp = ekey[i - 1];
330 | if(i % nk == 0) {
331 | temp = sub_word(rot_word(temp));
332 | temp[0] ^= rcon[i / nk - 1];
333 | }
334 | else if(nk > 6 and i % nk == 4)
335 | temp = sub_word(temp);
336 | ekey[i] = ekey[i - 4] ^ temp;
337 | }
338 |
339 | return ekey;
340 | }
341 |
342 | /// Create a State from a Block.
343 | /// \param block The Block used to create the State.
344 | /// \return The State created from the Block.
345 | [[nodiscard]] constexpr State to_state(const Block &block) {
346 | State state;
347 | for(std::size_t i = 0; i < 4*4; ++i) state[i / 4][i % 4] = block[i];
348 | return state;
349 | }
350 |
351 | /// Create a Block from a State.
352 | /// \param state The State used to create the Block.
353 | /// \return The Block created from the State.
354 | [[nodiscard]] constexpr Block to_block(const State &state) {
355 | Block block;
356 | for(std::size_t i = 0; i < 4*4; ++i) block[i] = state[i / 4][i % 4];
357 | return block;
358 | }
359 |
360 | }
361 |
362 | // ------------------------------------------------------------------
363 | // Public functions
364 | // ------------------------------------------------------------------
365 |
366 | /// Encrypt a block (128-bit) with a key.
367 | /// \param block Block to be encrypted with AES.
368 | /// \param key AES key.
369 | /// \return The encrypted block.
370 | [[nodiscard]] constexpr Block encrypt(const Block &block, const Key &key) {
371 | using namespace details;
372 |
373 | const auto ekey = key_expansion(key);
374 | State state = add_round_key(to_state(block), ekey, 0);
375 | for(std::size_t round = 1; round < n_rounds(); ++round)
376 | state = add_round_key(mix_columns(shift_rows(sub_bytes(state))), ekey, round);
377 | state = add_round_key(shift_rows(sub_bytes(state)), ekey, n_rounds());
378 | return to_block(state);
379 | }
380 |
381 | /// Encrypt an array of bytes (128-bit) with a key.
382 | /// \param block bytes to be encrypted with AES.
383 | /// \param key AES key.
384 | /// \return The encrypted block.
385 | [[nodiscard]] consteval Block encrypt(const Byte (&block)[16], const Byte (&key)[n_key / 8]) {
386 | return encrypt(std::to_array(block), std::to_array(key));
387 | }
388 |
389 | /// Decrypt (at runtime) a block of bytes with a key.
390 | /// \param block bytes to be decrypted with AES.
391 | /// \param key AES key.
392 | /// \return The decrypted block.
393 | [[nodiscard]] inline Block decrypt(const Block &block, const Key &key) {
394 | using namespace details;
395 | const auto ekey = key_expansion(key);
396 |
397 | State state = add_round_key(to_state(block), ekey, n_rounds());
398 | for(std::size_t round = n_rounds() - 1; round >= 1; --round)
399 | state = inv_mix_columns(add_round_key(inv_sub_bytes(inv_shift_rows(state)), ekey, round));
400 | state = add_round_key(inv_sub_bytes(inv_shift_rows(state)), ekey, 0);
401 | return to_block(state);
402 | }
403 |
404 | /// Encrypt in-place a string with a key using CTR (Counter) code (using a nonce)
405 | /// \param block bytes to be encrypted with AES. The number of bytes does not need to be a multiple of 128.
406 | /// \param key AES key.
407 | /// \param nonce The random nonce to initialize the stream.
408 | template
409 | [[nodiscard]] consteval std::array encrypt_ctr(const std::array &block, const Key &key, const Nonce &nonce) {
410 | Block ctr{
411 | nonce[0], nonce[1], nonce[2], nonce[3], nonce[4], nonce[5], nonce[6], nonce[7],
412 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
413 | };
414 |
415 | std::array encrypted;
416 | const auto nb_whole_blocks = N / 16;
417 | const auto nb_bytes_last_block = N % 16;
418 | for(std::size_t i = 0; i < nb_whole_blocks; ++i) {
419 | auto encrypted_ctr = encrypt(ctr, key);
420 | // Combine the cipher and the plain bytes
421 | for(std::size_t j = 0; j < 16; ++j) encrypted[i * 16 + j] = block[i * 16 + j] ^ encrypted_ctr[j];
422 | // Update the counter
423 | for(std::size_t j = 0; j < 8; ++j) ctr[8 + j] = static_cast((i >> j * 8) & 0x00000000000000FF);
424 | }
425 |
426 | const auto encrypted_ctr = encrypt(ctr, key);
427 | for(std::size_t j = 0; j < nb_bytes_last_block; ++j)
428 | encrypted[nb_whole_blocks * 16 + j] = block[nb_whole_blocks * 16 + j] ^ encrypted_ctr[j];
429 |
430 | return encrypted;
431 | }
432 |
433 | /// Decrypt in-place a string with a key using CTR (Counter) code (using a nonce)
434 | /// \param data bytes to be decrypted with AES. The number of bytes does not need to be a multiple of 128.
435 | /// \param key AES key.
436 | /// \param nonce The random nonce to initialize the stream.
437 | inline void decrypt_ctr(Byte *data, size_t size, const Key &key, const Nonce &nonce) {
438 | Block ctr{
439 | nonce[0], nonce[1], nonce[2], nonce[3], nonce[4], nonce[5], nonce[6], nonce[7],
440 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
441 | };
442 |
443 | const auto nb_whole_blocks = size / 16;
444 | const auto nb_bytes_last_block = size % 16;
445 | for(std::size_t i = 0; i < nb_whole_blocks; ++i) {
446 | auto encrypted_ctr = encrypt(ctr, key);
447 | // Combine the cipher and the plain bytes
448 | for(std::size_t j = 0; j < 16; ++j) data[i * 16 + j] = data[i * 16 + j] ^ encrypted_ctr[j];
449 | // Update the counter
450 | for(std::size_t j = 0; j < 8; ++j) ctr[8 + j] = static_cast((i >> j * 8) & 0x00000000000000FF);
451 | }
452 |
453 | const auto encrypted_ctr = encrypt(ctr, key);
454 | for(std::size_t j = 0; j < nb_bytes_last_block; ++j)
455 | data[nb_whole_blocks * 16 + j] = data[nb_whole_blocks * 16 + j] ^ encrypted_ctr[j];
456 | }
457 |
458 | /// Decrypt out-of-place a string with a key using CTR (Counter) code (using a nonce)
459 | /// \param data bytes to be decrypted with AES. The number of bytes does not need to be a multiple of 128.
460 | /// \param key AES key.
461 | /// \param nonce The random nonce to initialize the stream.
462 | /// \return The decrypted bytes
463 | template
464 | [[nodiscard]] consteval std::array encrypt_ctr(const Byte (&data)[N], const Key &key, const Nonce &nonce) {
465 | std::array buffer{};
466 | std::copy(data, data + N, buffer.begin());
467 | return encrypt_ctr(buffer, key, nonce);
468 | }
469 | }
470 |
471 | #endif
472 |
--------------------------------------------------------------------------------