From d160c33049d00eaae670dca964a4480f124a8cc4 Mon Sep 17 00:00:00 2001 From: nevergiveupcpp Date: Sun, 19 Apr 2026 10:11:14 +0700 Subject: [PATCH 1/6] wip: refactor obfuscxx template --- CMakeLists.txt | 4 +- include/obfuscxx/obfuscxx.h | 114 ++++++++++++++++++++++++------------ 2 files changed, 80 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d57fe1..152c939 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,4 +43,6 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/obfuscxxConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/obfuscxxConfigVersion.cmake DESTINATION lib/cmake/obfuscxx -) \ No newline at end of file +) + +include(./cmake/local.cmake OPTIONAL) \ No newline at end of file diff --git a/include/obfuscxx/obfuscxx.h b/include/obfuscxx/obfuscxx.h index f8a5514..ac313ee 100644 --- a/include/obfuscxx/obfuscxx.h +++ b/include/obfuscxx/obfuscxx.h @@ -77,8 +77,10 @@ using max_align_t = double; #ifndef OBFUSCXX_DISABLE_WARNS #define OBFUSCXX_RUNTIME_WARNING \ - [[deprecated("OBFUSCXX: Runtime set() uses encrypt method without SIMD obfuscation. For better protection, " \ - "initialize at compile-time.")]] + [[deprecated( \ + "OBFUSCXX: Runtime set() uses encrypt method without SIMD obfuscation. For better protection, " \ + "initialize at compile-time." \ + )]] #else #define OBFUSCXX_RUNTIME_WARNING #endif @@ -128,7 +130,7 @@ namespace ngu { return hash; } - constexpr std::uint64_t rol64(std::uint64_t x, int n) { + OBFUSCXX_FORCEINLINE constexpr std::uint64_t rol64(std::uint64_t x, int n) { n &= 63; if (n == 0) { return x; @@ -136,13 +138,17 @@ namespace ngu { return (x << n) | (x >> (64 - n)); } - constexpr std::uint64_t ror64(std::uint64_t x, int n) { + OBFUSCXX_FORCEINLINE constexpr std::uint64_t ror64(std::uint64_t x, int n) { n &= 63; if (n == 0) { return x; } return (x >> n) | (x << (64 - n)); } + + OBFUSCXX_FORCEINLINE constexpr std::size_t align_up(const std::size_t size, const std::size_t multiple) { + return (size + multiple - 1) & ~(multiple - 1); + } } // namespace detail template struct simd; @@ -234,8 +240,8 @@ namespace ngu { #endif #endif -#define OBFUSCXX_HASH(s) detail::hash_compile_time(s) -#define OBFUSCXX_HASH_RT(s) detail::hash_runtime(s) +#define OBFUSCXX_HASH(s) ngu::detail::hash_compile_time(s) +#define OBFUSCXX_HASH_RT(s) ngu::detail::hash_runtime(s) #if defined(_KERNEL_MODE) || defined(_WIN64_DRIVER) #define OBFUSCXX_ENTROPY \ @@ -245,29 +251,27 @@ namespace ngu { )) #else #define OBFUSCXX_ENTROPY \ - (detail::splitmix64( \ + (ngu::detail::splitmix64( \ OBFUSCXX_HASH(__FILE__) + ((std::uint64_t)__LINE__ * 0x9e3779b97f4a7c15ULL) + \ (OBFUSCXX_HASH(__TIME__) ^ ((std::uint64_t)__COUNTER__ << 32)) \ )) #endif enum class obf_level : std::uint8_t { Low, Medium, High }; - template< - class Type, - std::size_t Size = 1, - obf_level Level = obf_level::Low, - std::uint64_t Entropy = OBFUSCXX_ENTROPY> - class obfuscxx { - static constexpr bool is_single = Size == 1; - static constexpr bool is_array = Size > 1; - static constexpr bool is_single_pointer = std::is_pointer_v && Size == 1; + template class obfuscxx { + using Type = std::remove_extent_t; + static constexpr std::size_t type_size = std::is_array_v ? std::extent_v : 1; + + static constexpr bool is_array = std::is_array_v; + static constexpr bool is_single = !is_array; + static constexpr bool is_single_pointer = std::is_pointer_v && type_size == 1; static constexpr bool is_char = std::is_same_v || std::is_same_v; static constexpr bool is_wchar = std::is_same_v || std::is_same_v; static constexpr std::size_t storage_multiple = OBFUSCXX_HAS_AVX2 ? 8 : 4; static constexpr std::size_t storage_alignment = OBFUSCXX_HAS_AVX2 ? 32 : 16; static constexpr std::size_t storage_size = - is_array ? (Size + storage_multiple - 1) & ~(storage_multiple - 1) : Size; + is_array ? detail::align_up(type_size, storage_multiple) : type_size; struct passkey { explicit passkey() = default; @@ -370,7 +374,7 @@ namespace ngu { static OBFUSCXX_FORCEINLINE void decrypt_vectorized( const volatile std::uint64_t* src, Type* dst, std::size_t count ) { - std::size_t const aligned_count = (count + storage_multiple - 1) & ~(storage_multiple - 1); + std::size_t const aligned_count = detail::align_up(count, storage_multiple); #if defined(__aarch64__) || defined(_M_ARM64) for (std::size_t i{}; i < aligned_count; i += 4) { @@ -542,7 +546,7 @@ namespace ngu { public: explicit consteval obfuscxx(passkey) { - for (std::size_t i{}; i < Size; ++i) { + for (std::size_t i{}; i < type_size; ++i) { storage_[i] = seed ^ iv[i & iv_size]; } } @@ -551,14 +555,14 @@ namespace ngu { storage_[0] = encrypt(val); } - explicit consteval obfuscxx(Type (&arr)[Size]) : obfuscxx(passkey{}) { - for (std::size_t i{}; i < Size; ++i) { + explicit consteval obfuscxx(Type (&arr)[type_size]) : obfuscxx(passkey{}) { + for (std::size_t i{}; i < type_size; ++i) { storage_[i] = encrypt(arr[i]); } } - explicit consteval obfuscxx(const Type (&arr)[Size]) : obfuscxx(passkey{}) { - for (std::size_t i{}; i < Size; ++i) { + explicit consteval obfuscxx(const Type (&arr)[type_size]) : obfuscxx(passkey{}) { + for (std::size_t i{}; i < type_size; ++i) { storage_[i] = encrypt(arr[i]); } } @@ -588,7 +592,7 @@ namespace ngu { OBFUSCXX_FORCEINLINE void copy_to(Type* out, std::size_t count) const requires is_array { - std::size_t effective_count = (count < Size) ? count : Size; + std::size_t effective_count = (count < type_size) ? count : type_size; decrypt_vectorized(storage_, out, effective_count); } @@ -608,7 +612,7 @@ namespace ngu { requires is_array { for (std::size_t i{}; const auto& val : list) { - if (i < Size) { + if (i < type_size) { storage_[i++] = encrypt(val); } } @@ -649,7 +653,7 @@ namespace ngu { OBFUSCXX_FORCEINLINE bool operator==(const obfuscxx& rhs) const requires is_array { - for (std::size_t i{}; i < Size; ++i) { + for (std::size_t i{}; i < type_size; ++i) { if (get(i) != rhs.get(i)) { return false; } @@ -758,10 +762,10 @@ namespace ngu { iterator end() const requires is_array { - return {this, Size}; + return {this, type_size}; } static constexpr std::size_t size() { - return Size; + return type_size; } template struct string_copy { @@ -825,23 +829,23 @@ namespace ngu { ArrayType data[N]; }; - OBFUSCXX_FORCEINLINE string_copy to_string() const + OBFUSCXX_FORCEINLINE string_copy to_string() const requires(is_char || is_wchar) { - string_copy result{}; + string_copy result{}; if constexpr (is_array) { - copy_to(result.data, Size); + copy_to(result.data, type_size); } else { result.data[0] = get(); } return result; } - OBFUSCXX_FORCEINLINE array_copy to_array() const + OBFUSCXX_FORCEINLINE array_copy to_array() const requires(is_array) { - array_copy result{}; - copy_to(result.data, Size); + array_copy result{}; + copy_to(result.data, type_size); return result; } @@ -851,13 +855,49 @@ namespace ngu { } // namespace ngu #if defined(__clang__) || defined(__GNUC__) + +#define OBFUSCXX_LITERAL(str, type, size, level) ngu::obfuscxx(str).to_string() + +/// Obfuscate string literal (Low level) template constexpr auto operator""_obf() { constexpr CharType str[] = {Chars..., '\0'}; - return ngu::obfuscxx(str).to_string(); + return OBFUSCXX_LITERAL(str, CharType, sizeof...(Chars) + 1, ngu::obf_level::Low); } +/// Obfuscate string literal (Medium level) +template constexpr auto operator""_obfm() { + constexpr CharType str[] = {Chars..., '\0'}; + return OBFUSCXX_LITERAL(str, CharType, sizeof...(Chars) + 1, ngu::obf_level::Medium); +} +/// Obfuscate string literal (High level) +template constexpr auto operator""_obfh() { + constexpr CharType str[] = {Chars..., '\0'}; + return OBFUSCXX_LITERAL(str, CharType, sizeof...(Chars) + 1, ngu::obf_level::High); +} + #endif -#define obfusv(val) ngu::obfuscxx(val).get() -#define obfuss(str) ngu::obfuscxx(str).to_string().c_str() +#define OBFUSCXX_VAL_EX(val, level) \ + ngu::obfuscxx, 1, level, OBFUSCXX_ENTROPY>(val).get() +#define OBFUSCXX_STR_EX(str, level) \ + ngu::obfuscxx, sizeof(str) / sizeof(str[0]), level, OBFUSCXX_ENTROPY>(str) \ + .to_string() \ + .c_str() + +/// Obfuscate value (Low level) +#define obfusv(val) OBFUSCXX_VAL_EX(val, ngu::obf_level::Low) +/// Obfuscate string (Low level) +#define obfuss(str) OBFUSCXX_STR_EX(str, ngu::obf_level::Low) + +/// Obfuscate value (Medium level) +#define obfusvm(val) OBFUSCXX_VAL_EX(val, ngu::obf_level::Medium) +/// Obfuscate string (Medium level) +#define obfussm(str) OBFUSCXX_STR_EX(str, ngu::obf_level::Medium) + +/// Obfuscate value (High level) +#define obfusvh(val) OBFUSCXX_VAL_EX(val, ngu::obf_level::High) +/// Obfuscate string (High level) +#define obfussh(str) OBFUSCXX_STR_EX(str, ngu::obf_level::High) + +#define obfusf(type, size, level) ngu::obfuscxx #endif // NGU_OBFUSCXX_H \ No newline at end of file From 15cc34fb6ca4e77e0542a0b8434ecb5ced1d431b Mon Sep 17 00:00:00 2001 From: nevergiveupcpp Date: Thu, 30 Apr 2026 23:18:00 +0700 Subject: [PATCH 2/6] feat!: add macros as primary public interface --- .clang-tidy | 5 +- .gitignore | 61 +- CMakeLists.txt | 2 +- LICENSE | 402 +++---- README.md | 334 +++--- cmake/obfuscxxConfig.cmake.in | 8 +- include/obfuscxx/obfuscxx.h | 1819 +++++++++++++++-------------- tests/CMakePresets.json | 298 ++--- tests/benchmark/CMakeLists.txt | 70 +- tests/benchmark/CMakePresets.json | 10 +- tests/benchmark/benchmark.cpp | 410 +++---- tests/unittest/CMakeLists.txt | 70 +- tests/unittest/CMakePresets.json | 10 +- tests/unittest/unittest.cpp | 194 +-- 14 files changed, 1871 insertions(+), 1822 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 51d9e93..5d1cadd 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -2,7 +2,8 @@ Checks: > readability-identifier-naming, readability-implicit-bool-conversion, misc-const-correctness, - -clang-diagnostic-error + -clang-diagnostic-error, + -clang-diagnostic-gnu-string-literal-operator-template CheckOptions: - key: readability-identifier-naming.ClassCase @@ -38,7 +39,7 @@ CheckOptions: value: '^[A-Z][A-Z0-9_]*$' - key: readability-identifier-naming.EnumConstantCase - value: CamelCase + value: lower_case - key: readability-identifier-naming.EnumCase value: lower_case diff --git a/.gitignore b/.gitignore index 3ab59dc..f823b28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,39 @@ -.vs/ -*.sln.docstates -Debug/ -Release/ -x64/ -x86/ -build/ -install/ -.idea/ - -*.o -*.obj -*.exe -*.dll -*.pdb -*.ilk -*.log -*.tlog -*.user -*.iml -*.xml -*.bat \ No newline at end of file +# IDE +.vs/ +.idea/ + +# Executables +*.exe +*.out +*.app + +# Build directories +build/ +Build/ +build-*/ + +# Development +development/ +cmake/development.cmake + +# CMake generated files +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +Makefile +install_manifest.txt +compile_commands.json + +# Temporary files +*.tmp +*.log +*.bak +*.swp +*.bat + +# vcpkg +vcpkg_installed/ + +# test output & cache +Testing/ +.cache/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 152c939..202cc2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,4 +45,4 @@ install(FILES DESTINATION lib/cmake/obfuscxx ) -include(./cmake/local.cmake OPTIONAL) \ No newline at end of file +include(cmake/development.cmake OPTIONAL) \ No newline at end of file diff --git a/LICENSE b/LICENSE index 9b5e401..7a4a3ea 100644 --- a/LICENSE +++ b/LICENSE @@ -1,202 +1,202 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index e33d422..d97ecb1 100644 --- a/README.md +++ b/README.md @@ -1,167 +1,169 @@ -# obfuscxx - -

- -

- -## Description -Header-only compile-time variables obfuscation library for C++20 and later. - -## How it works -During compilation, data is encrypted via eXtended Tiny Encryption Algorithm (XTEA). Decryption uses SIMD instructions (AVX2/SSE2/NEON) at runtime, making static analysis considerably more complicated. Key entropy is based on the preprocessor macro `__COUNTER__`, the file name(`__FILE__`), and the line number (`__LINE__`) where the variable is defined, and the build time (`__TIME__`) (note: build time is not included when compiling with WDM). - -By selecting different encryption levels (Low, Medium, High), you can control the number of encryption rounds. With Low, there are 2 rounds; Medium uses 6; and High adjusts the number of rounds dynamically based on the key entropy, ranging from 8 to 32. This lets you apply lighter encryption to frequently accessed data, and stronger encryption to data that’s used less often. - -### Why XTEA -XTEA was chosen for several reasons. The primary one is its solid cryptographic strength combined with a minimal algorithm footprint (yes, “cryptographic strength” is somewhat overstated here - the keys and the algorithm itself are visible, the goal here is making analysis more difficult, not cryptographic security). Additionally, XTEA operates on 64-bit blocks, which maps perfectly to a single scalar - one block corresponds to one protected value. - -### SIMD for obfuscation -When working with scalar data, SIMD instructions are used not for vectorization, but to complicate static analysis, data extraction, and emulation-based deobfuscation. For vector data (arrays, strings), SIMD is used to its full potential, enabling parallel decryption without compromising protection quality. - -### SIMD platform support -The library supports multiple SIMD instruction sets depending on the target architecture and compiler flags. On MSVC, SSE2 is used as a fallback by default, this ensures compatibility with both older and newer processors. If support for older processors is not required, define the OBFUSCXX_MSVC_FORCE_AVX2 macro. - -## Decompilation view -The screenshots show only a small portion of the int main() function. In reality, the function can grow to around 250 lines depending on the compiler. - - - - - - - -
-

MSVC, LLVM, GCC compilation (int main(), Level: Low, Arch: x86-64, SIMD Insn: AVX2, SSE2)

- -## Installation - -> To disable the SSE2 fallback on MSVC, define `OBFUSCXX_MSVC_FORCE_AVX2` before including the header or in your CMakeLists.txt. - -### Single header -Just add the `include/` directory to your include path and use `#include ` - -### Package manager -Run `vcpkg install obfuscxx`, then add to your CMakeLists.txt: -```cmake -find_package(obfuscxx CONFIG REQUIRED) -target_link_libraries(your_target PRIVATE obfuscxx::obfuscxx) -``` - -## Examples -### Basic -```cpp -#include - -int main() { - obfuscxx int_value{ 100 }; - std::cout << int_value.get() << '\n'; - int_value = 50; - std::cout << int_value.get() << '\n'; - - obfuscxx float_value{ 1.5f }; - std::cout << float_value.get() << '\n'; - - obfuscxx array{ 1, 2, 3, 4 }; - for (auto val : array) { - std::cout << val << " "; - } - std::cout << '\n'; - - obfuscxx str("Hello, World!"); - std::cout << str.to_string() << '\n'; - - obfuscxx pointer{}; - pointer = new int{101}; - std::cout << pointer.get() << " " << *pointer.get() << '\n'; - delete pointer.get(); -} -``` - -### User-Defined literal (Clang/GCC only) -```cpp -#include - -int main() { - std::cout << "Hello, World!"_obf << '\n'; -} -``` - -### Macros -```cpp -#include - -int main() { - std::cout << obfusv(42) << " " << obfusv(3.14159f) << '\n'; - std::cout << obfuss("Hello, World!") << '\n'; -} -``` - -## Benchmarks - -> Benchmark results may vary depending on compiler flags and the toolchain used. The results below were obtained using the configuration available in [CMakeLists.txt](tests/benchmark/CMakeLists.txt). Note that High-level results may vary between builds, as the number of encryption rounds is dynamically determined based on key entropy. - -### Runtime performance impact -| Operation | MSVC | LLVM | GCC | -|-----------|------|------|-----| -| **Integer Get (Low)** | 4.61 ns | **4.24 ns** | 4.99 ns | -| **Integer Get (Medium)** | 22.5 ns | **13.5 ns** | 15.6 ns | -| **Integer Get (High)** | 57.3 ns | **51.5 ns** | 62.4 ns | -| **Float Get (Low)** | 4.93 ns | **4.08 ns** | 4.35 ns | -| **Float Get (Medium)** | 22.8 ns | **12.9 ns** | 15.1 ns | -| **Float Get (High)** | 56.0 ns | **48.1 ns** | 60.9 ns | -| **String Get (Low)** | 12.0 ns | **7.62 ns** | 13.7 ns | -| **String Get (Medium)** | **24.5 ns** | 28.6 ns | 30.3 ns | -| **String Get (High)** | 68.7 ns | 102 ns | **62.9 ns** | -| **Wide String Get (Low)** | 12.7 ns | **7.57 ns** | 11.7 ns | -| **Wide String Get (Medium)** | **28.3 ns** | 30.3 ns | 31.6 ns | -| **Wide String Get (High)** | 70.2 ns | 101 ns | **63.7 ns** | -| **Array Iteration (100 elem, Low)** | 483 ns | 445 ns | **443 ns** | -| **Array Iteration (100 elem, Medium)** | 2,348 ns | **1,342 ns** | 1,607 ns | -| **Array Iteration (100 elem, High)** | 5,553 ns | **4,640 ns** | 5,887 ns | -| **Array CopyTo (100 elem, Low)** | 91.9 ns | **66.5 ns** | 89.8 ns | -| **Array CopyTo (100 elem, Medium)** | 201 ns | **185 ns** | 225 ns | -| **Array CopyTo (100 elem, High)** | 477 ns | 642 ns | **417 ns** | -| **Array Element Access (Low)** | 4.99 ns | **4.23 ns** | 4.78 ns | -| **Array Element Access (Medium)** | 23.4 ns | **13.5 ns** | 15.8 ns | -| **Array Element Access (High)** | 56.8 ns | **51.8 ns** | 60.4 ns | - -### Test environment -- CPU: 16 cores @ 2496 MHz -- L1 Data Cache: 48 KiB (x8) -- L1 Instruction Cache: 32 KiB (x8) -- L2 Unified Cache: 512 KiB (x8) -- L3 Unified Cache: 16384 KiB (x1) -- Date: 2026-02-22 - -## Building tests and benchmarks -1. Install `vcpkg` and set `VCPKG_ROOT` environment variable -2. Fetch baseline: `cd $VCPKG_ROOT && git fetch origin 34823ada10080ddca99b60e85f80f55e18a44eea` -3. Configure: `cmake --preset ` (MSVC/Clang/GCC) -4. Build: `cmake --build --preset ` (--config Release/Debug) - -## Requirements -- C++20 or later -- Compiler with SIMD support (SSE2/AVX2/NEON) -- CMake 3.15+ (for building tests) -- vcpkg (for dependencies) - -## Platform Support -> Although the library supports multiple architectures and compilers, there are some caveats. NEON instructions on ARM64 are aggressively optimized by the compiler regardless of attempts to prevent it. While constants remain encrypted, the decryption process itself may be simplified by the optimizer into plain scalar operations, reducing the effectiveness of the obfuscation. Due to limited testing on ARM64, the quality of obfuscation on this architecture has not been fully validated. If you are using the library on a NEON-based platform, it is recommended to manually inspect the generated code. - -### Compilers -- MSVC (+ WDM) -- GCC -- Clang - -### Architectures -- x86 (SSE2/AVX2) -- x86-64 (SSE2/AVX2) -- ARM64 (NEON) - -### Operating Systems -- Windows -- Linux -- macOS - -## License +# obfuscxx + +

+ +

+ +## Description +Header-only compile-time variables obfuscation library for C++20 and later. + +## How it works +During compilation, data is encrypted via eXtended Tiny Encryption Algorithm (XTEA). Decryption uses SIMD instructions (AVX2/SSE2/NEON) at runtime, making static analysis considerably more complicated. Key entropy is based on the preprocessor macro `__COUNTER__`, the file name(`__FILE__`), and the line number (`__LINE__`) where the variable is defined, and the build time (`__TIME__`) (note: build time is not included when compiling with WDM). + +By selecting different encryption levels (Low, Medium, High), you can control the number of encryption rounds. With Low, there are 2 rounds; Medium uses 6; and High adjusts the number of rounds dynamically based on the key entropy, ranging from 8 to 32. This lets you apply lighter encryption to frequently accessed data, and stronger encryption to data that’s used less often. + +### Why XTEA +XTEA was chosen for several reasons. The primary one is its solid cryptographic strength combined with a minimal algorithm footprint (yes, “cryptographic strength” is somewhat overstated here - the keys and the algorithm itself are visible, the goal here is making analysis more difficult, not cryptographic security). Additionally, XTEA operates on 64-bit blocks, which maps perfectly to a single scalar - one block corresponds to one protected value. + +### SIMD for obfuscation +When working with scalar data, SIMD instructions are used not for vectorization, but to complicate static analysis, data extraction, and emulation-based deobfuscation. For vector data (arrays, strings), SIMD is used to its full potential, enabling parallel decryption without compromising protection quality. + +### SIMD platform support +The library supports multiple SIMD instruction sets depending on the target architecture and compiler flags. On MSVC, SSE2 is used as a fallback by default, this ensures compatibility with both older and newer processors. If support for older processors is not required, define the OBFUSCXX_MSVC_FORCE_AVX2 macro. + +## Decompilation view +The screenshots show only a small portion of the int main() function. In reality, the function can grow to around 250 lines depending on the compiler. + + + + + + + +
+

MSVC, LLVM, GCC compilation (int main(), Level: Low, Arch: x86-64, SIMD Insn: AVX2, SSE2)

+ +## Installation + +> To disable the SSE2 fallback on MSVC, define `OBFUSCXX_MSVC_FORCE_AVX2` before including the header or in your CMakeLists.txt. + +### Single header +Just add the `include/` directory to your include path and use `#include ` + +### Package manager +Run `vcpkg install obfuscxx`, then add to your CMakeLists.txt: +```cmake +find_package(obfuscxx CONFIG REQUIRED) +target_link_libraries(your_target PRIVATE obfuscxx::obfuscxx) +``` + +## Examples +### Basic +```cpp +int main() { + using namespace ngu; + + struct asset { + obfxx(std::uint64_t[3], obf_level::high) hashes{ + 0x4c1aab3d, + 0xcacaa7a5, + 0xc6ca594e, + }; + obfxx(char[11]) asset_name{"main-asset"}; + }; + + constexpr asset at{}; + + for (const auto& hash : at.hashes) { + std::cout << hash << std::endl; + } + + std::cout << at.asset_name.to_string() << std::endl; + + //... + + if (constexpr auto true_phrase = obfxxo("nevergiveup"); strcmp(phrase, true_phrase.to_string()) != 0) { + exit(1); + } + + //... + + std::cout << obfxxs("just use it") << std::endl; + std::cout << obfxxs("to", obf_level::medium) << std::endl; + std::cout << obfxxs("protect your data", obf_level::high) << std::endl; + + std::cout << obfxxv(2000) << std::endl; + std::cout << obfxxv(2000, obf_level::high) << std::endl; +} +``` + +### User-Defined literal (Clang/GCC only) +```cpp +#include + +int main() { + std::cout << "Hello, World!"_obf << '\n'; // Low level + std::cout << "It works!"_obfm << '\n'; // Medium level + std::cout << "Goodbye, World!"_obfh << '\n'; // High level +} +``` + +## Benchmarks + +> Benchmark results may vary depending on compiler flags and the toolchain used. The results below were obtained using the configuration available in [CMakeLists.txt](tests/benchmark/CMakeLists.txt). Note that High-level results may vary between builds, as the number of encryption rounds is dynamically determined based on key entropy. + +### Runtime performance impact +| Operation | MSVC | LLVM | GCC | +|-----------|------|------|-----| +| **Integer Get (Low)** | 4.61 ns | **4.24 ns** | 4.99 ns | +| **Integer Get (Medium)** | 22.5 ns | **13.5 ns** | 15.6 ns | +| **Integer Get (High)** | 57.3 ns | **51.5 ns** | 62.4 ns | +| **Float Get (Low)** | 4.93 ns | **4.08 ns** | 4.35 ns | +| **Float Get (Medium)** | 22.8 ns | **12.9 ns** | 15.1 ns | +| **Float Get (High)** | 56.0 ns | **48.1 ns** | 60.9 ns | +| **String Get (Low)** | 12.0 ns | **7.62 ns** | 13.7 ns | +| **String Get (Medium)** | **24.5 ns** | 28.6 ns | 30.3 ns | +| **String Get (High)** | 68.7 ns | 102 ns | **62.9 ns** | +| **Wide String Get (Low)** | 12.7 ns | **7.57 ns** | 11.7 ns | +| **Wide String Get (Medium)** | **28.3 ns** | 30.3 ns | 31.6 ns | +| **Wide String Get (High)** | 70.2 ns | 101 ns | **63.7 ns** | +| **Array Iteration (100 elem, Low)** | 483 ns | 445 ns | **443 ns** | +| **Array Iteration (100 elem, Medium)** | 2,348 ns | **1,342 ns** | 1,607 ns | +| **Array Iteration (100 elem, High)** | 5,553 ns | **4,640 ns** | 5,887 ns | +| **Array CopyTo (100 elem, Low)** | 91.9 ns | **66.5 ns** | 89.8 ns | +| **Array CopyTo (100 elem, Medium)** | 201 ns | **185 ns** | 225 ns | +| **Array CopyTo (100 elem, High)** | 477 ns | 642 ns | **417 ns** | +| **Array Element Access (Low)** | 4.99 ns | **4.23 ns** | 4.78 ns | +| **Array Element Access (Medium)** | 23.4 ns | **13.5 ns** | 15.8 ns | +| **Array Element Access (High)** | 56.8 ns | **51.8 ns** | 60.4 ns | + +### Test environment +- CPU: 16 cores @ 2496 MHz +- L1 Data Cache: 48 KiB (x8) +- L1 Instruction Cache: 32 KiB (x8) +- L2 Unified Cache: 512 KiB (x8) +- L3 Unified Cache: 16384 KiB (x1) +- Date: 2026-02-22 + +## Building tests and benchmarks +1. Install `vcpkg` and set `VCPKG_ROOT` environment variable +2. Fetch baseline: `cd $VCPKG_ROOT && git fetch origin 34823ada10080ddca99b60e85f80f55e18a44eea` +3. Configure: `cmake --preset ` (MSVC/Clang/GCC) +4. Build: `cmake --build --preset ` (--config Release/Debug) + +## Requirements +- C++20 or later +- Compiler with SIMD support (SSE2/AVX2/NEON) +- CMake 3.15+ (for building tests) +- vcpkg (for dependencies) + +## Platform Support +> Although the library supports multiple architectures and compilers, there are some caveats. NEON instructions on ARM64 are aggressively optimized by the compiler regardless of attempts to prevent it. While constants remain encrypted, the decryption process itself may be simplified by the optimizer into plain scalar operations, reducing the effectiveness of the obfuscation. Due to limited testing on ARM64, the quality of obfuscation on this architecture has not been fully validated. If you are using the library on a NEON-based platform, it is recommended to manually inspect the generated code. + +### Compilers +- MSVC (+ WDM) +- GCC +- Clang + +### Architectures +- x86 (SSE2/AVX2) +- x86-64 (SSE2/AVX2) +- ARM64 (NEON) + +### Operating Systems +- Windows +- Linux +- macOS + +## License **obfuscxx** is distributed under the [Apache License 2.0](LICENSE). \ No newline at end of file diff --git a/cmake/obfuscxxConfig.cmake.in b/cmake/obfuscxxConfig.cmake.in index e79ebf3..54cab29 100644 --- a/cmake/obfuscxxConfig.cmake.in +++ b/cmake/obfuscxxConfig.cmake.in @@ -1,5 +1,5 @@ -@PACKAGE_INIT@ - -include("${CMAKE_CURRENT_LIST_DIR}/obfuscxxTargets.cmake") - +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/obfuscxxTargets.cmake") + check_required_components(obfuscxx) \ No newline at end of file diff --git a/include/obfuscxx/obfuscxx.h b/include/obfuscxx/obfuscxx.h index ac313ee..9fb75e0 100644 --- a/include/obfuscxx/obfuscxx.h +++ b/include/obfuscxx/obfuscxx.h @@ -1,903 +1,918 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2025-2026 nevergiveupcpp - -// Copyright 2025-2026 nevergiveupcpp -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef NGU_OBFUSCXX_H -#define NGU_OBFUSCXX_H - -#include -#include - -#if defined(_KERNEL_MODE) || defined(_WIN64_DRIVER) -using byte = std::uint8_t; -using max_align_t = double; -#endif - -#include - -#if defined(__aarch64__) || defined(_M_ARM64) || defined(__ARM_NEON) -#include -#elif defined(_WIN32) -#include -#elif defined(__GNUC__) || defined(__clang__) -#include -#include -#else -#error Unsupported platform -#endif - -#if (defined(__GNUC__) || defined(__clang__)) && defined(__AVX2__) -#define OBFUSCXX_HAS_AVX2 1 -#elif defined(_MSC_VER) && !defined(__clang__) && !defined(_M_ARM64) -#define OBFUSCXX_HAS_AVX2 1 -#else -#define OBFUSCXX_HAS_AVX2 0 -#endif - -#if defined(__clang__) || defined(__GNUC__) -#define OBFUSCXX_VOLATILE -#elif defined(_MSC_VER) -#define OBFUSCXX_VOLATILE volatile -#endif - -#if defined(__clang__) || defined(__GNUC__) -#define OBFUSCXX_FORCEINLINE __attribute__((always_inline)) inline -#else -#define OBFUSCXX_FORCEINLINE __forceinline -#endif - -#if defined(_KERNEL_MODE) || defined(_WIN64_DRIVER) -#define _mm256_extract_epi32(vec, idx) (((int32_t*)&(vec))[(idx)]) -#endif - -#if defined(__clang__) || defined(__GNUC__) -#define OBFUSCXX_MEM_BARRIER(...) __asm__ volatile("" : "+r"(__VA_ARGS__)::"memory"); -#if defined(__aarch64__) || defined(_M_ARM64) -#define OBFUSCXX_MEM_BARRIER_VEC(v0, v1, sum) __asm__ volatile("" : "+w"(v0), "+w"(v1), "+r"(sum)::"memory"); -#else -#define OBFUSCXX_MEM_BARRIER_VEC(v0, v1, sum) __asm__ volatile("" : "+x"(v0), "+x"(v1), "+r"(sum)::"memory"); -#endif -#elif defined(_MSC_VER) -#define OBFUSCXX_MEM_BARRIER(...) _ReadWriteBarrier(); -#define OBFUSCXX_MEM_BARRIER_VEC(v0, v1, sum) _ReadWriteBarrier(); -#endif - -#ifndef OBFUSCXX_DISABLE_WARNS -#define OBFUSCXX_RUNTIME_WARNING \ - [[deprecated( \ - "OBFUSCXX: Runtime set() uses encrypt method without SIMD obfuscation. For better protection, " \ - "initialize at compile-time." \ - )]] -#else -#define OBFUSCXX_RUNTIME_WARNING -#endif - -namespace ngu { - namespace detail { - constexpr std::uint64_t splitmix64(std::uint64_t x) { - x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; - x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; - return x ^ (x >> 31); - } - - template OBFUSCXX_FORCEINLINE consteval std::uint64_t hash_compile_time(char const (&data)[N]) { - std::uint64_t hash = 0; - - for (std::size_t i{}; i < N - 1; ++i) { - hash += data[i] >= 'A' && data[i] <= 'Z' ? data[i] + ('a' - 'A') : data[i]; - hash += hash << 8; - hash ^= hash >> 11; - } - - hash += hash << 5; - hash ^= hash >> 13; - hash += hash << 10; - - return hash; - } - - OBFUSCXX_FORCEINLINE std::uint64_t hash_runtime(char const* str) { - std::size_t length = 0; - while (str[length] != 0) { - ++length; - } - - std::uint64_t hash = 0; - - for (std::size_t i{}; i < length; ++i) { - hash += str[i] >= 'A' && str[i] <= 'Z' ? str[i] + ('a' - 'A') : str[i]; - hash += hash << 8; - hash ^= hash >> 11; - } - - hash += hash << 5; - hash ^= hash >> 13; - hash += hash << 10; - - return hash; - } - - OBFUSCXX_FORCEINLINE constexpr std::uint64_t rol64(std::uint64_t x, int n) { - n &= 63; - if (n == 0) { - return x; - } - return (x << n) | (x >> (64 - n)); - } - - OBFUSCXX_FORCEINLINE constexpr std::uint64_t ror64(std::uint64_t x, int n) { - n &= 63; - if (n == 0) { - return x; - } - return (x >> n) | (x << (64 - n)); - } - - OBFUSCXX_FORCEINLINE constexpr std::size_t align_up(const std::size_t size, const std::size_t multiple) { - return (size + multiple - 1) & ~(multiple - 1); - } - } // namespace detail - - template struct simd; - -#if defined(__aarch64__) || defined(_M_ARM64) - template<> struct simd { - using vec = uint32x4_t; - static OBFUSCXX_FORCEINLINE vec shl4(vec v) { - return vshlq_n_u32(v, 4); - } - static OBFUSCXX_FORCEINLINE vec shr5(vec v) { - return vshrq_n_u32(v, 5); - } - static OBFUSCXX_FORCEINLINE vec vxor(vec a, vec b) { - return veorq_u32(a, b); - } - static OBFUSCXX_FORCEINLINE vec vadd(vec a, vec b) { - return vaddq_u32(a, b); - } - static OBFUSCXX_FORCEINLINE vec vsub(vec a, vec b) { - return vsubq_u32(a, b); - } - static OBFUSCXX_FORCEINLINE vec broadcast(std::uint32_t v) { - return vdupq_n_u32(v); - } - static OBFUSCXX_FORCEINLINE vec from_scalar(std::uint32_t v) { - return vdupq_n_u32(v); - } - static OBFUSCXX_FORCEINLINE std::uint32_t to_scalar(vec v) { - return vgetq_lane_u32(v, 0); - } - }; -#else - template<> struct simd<__m128i> { - using vec = __m128i; - static OBFUSCXX_FORCEINLINE vec shl4(vec v) { - return _mm_slli_epi32(v, 4); - } - static OBFUSCXX_FORCEINLINE vec shr5(vec v) { - return _mm_srli_epi32(v, 5); - } - static OBFUSCXX_FORCEINLINE vec vxor(vec a, vec b) { - return _mm_xor_si128(a, b); - } - static OBFUSCXX_FORCEINLINE vec vadd(vec a, vec b) { - return _mm_add_epi32(a, b); - } - static OBFUSCXX_FORCEINLINE vec vsub(vec a, vec b) { - return _mm_sub_epi32(a, b); - } - static OBFUSCXX_FORCEINLINE vec broadcast(std::uint32_t v) { - return _mm_set1_epi32(v); - } - static OBFUSCXX_FORCEINLINE vec from_scalar(std::uint32_t v) { - return _mm_cvtsi32_si128(v); - } - static OBFUSCXX_FORCEINLINE std::uint32_t to_scalar(vec v) { - return _mm_cvtsi128_si32(v); - } - }; -#if OBFUSCXX_HAS_AVX2 - template<> struct simd<__m256i> { - using vec = __m256i; - static OBFUSCXX_FORCEINLINE vec shl4(vec v) { - return _mm256_slli_epi32(v, 4); - } - static OBFUSCXX_FORCEINLINE vec shr5(vec v) { - return _mm256_srli_epi32(v, 5); - } - static OBFUSCXX_FORCEINLINE vec vxor(vec a, vec b) { - return _mm256_xor_si256(a, b); - } - static OBFUSCXX_FORCEINLINE vec vadd(vec a, vec b) { - return _mm256_add_epi32(a, b); - } - static OBFUSCXX_FORCEINLINE vec vsub(vec a, vec b) { - return _mm256_sub_epi32(a, b); - } - static OBFUSCXX_FORCEINLINE vec broadcast(std::uint32_t v) { - return _mm256_set1_epi32(v); - } - static OBFUSCXX_FORCEINLINE vec from_scalar(std::uint32_t v) { - return _mm256_set1_epi32(v); - } - static OBFUSCXX_FORCEINLINE std::uint32_t to_scalar(vec v) { - return _mm256_extract_epi32(v, 0); - } - }; -#endif -#endif - -#define OBFUSCXX_HASH(s) ngu::detail::hash_compile_time(s) -#define OBFUSCXX_HASH_RT(s) ngu::detail::hash_runtime(s) - -#if defined(_KERNEL_MODE) || defined(_WIN64_DRIVER) -#define OBFUSCXX_ENTROPY \ - (detail::splitmix64( \ - (OBFUSCXX_HASH(__FILE__) * 0x517cc1b727220a95ULL) + ((std::uint64_t)__LINE__ * 0x9e3779b97f4a7c15ULL) + \ - (detail::rol64((std::uint64_t)__COUNTER__, 37) ^ ((std::uint64_t)__LINE__ * 0xff51afd7ed558ccdULL)) \ - )) -#else -#define OBFUSCXX_ENTROPY \ - (ngu::detail::splitmix64( \ - OBFUSCXX_HASH(__FILE__) + ((std::uint64_t)__LINE__ * 0x9e3779b97f4a7c15ULL) + \ - (OBFUSCXX_HASH(__TIME__) ^ ((std::uint64_t)__COUNTER__ << 32)) \ - )) -#endif - - enum class obf_level : std::uint8_t { Low, Medium, High }; - template class obfuscxx { - using Type = std::remove_extent_t; - static constexpr std::size_t type_size = std::is_array_v ? std::extent_v : 1; - - static constexpr bool is_array = std::is_array_v; - static constexpr bool is_single = !is_array; - static constexpr bool is_single_pointer = std::is_pointer_v && type_size == 1; - static constexpr bool is_char = std::is_same_v || std::is_same_v; - static constexpr bool is_wchar = std::is_same_v || std::is_same_v; - - static constexpr std::size_t storage_multiple = OBFUSCXX_HAS_AVX2 ? 8 : 4; - static constexpr std::size_t storage_alignment = OBFUSCXX_HAS_AVX2 ? 32 : 16; - static constexpr std::size_t storage_size = - is_array ? detail::align_up(type_size, storage_multiple) : type_size; - - struct passkey { - explicit passkey() = default; - }; - - static constexpr std::uint64_t seed{Entropy}; - static constexpr std::uint64_t iv[8] = { - 0xcbf43b227a01fe5aULL ^ seed, - 0x32703be7aaa7c38fULL ^ detail::ror64(seed, 13), - 0xb589959b3d854bbcULL ^ detail::rol64(seed, 29), - 0x73b3ef5578a97c8aULL ^ detail::ror64(seed, 41), - 0x92afafd27c6e16e9ULL ^ detail::rol64(seed, 7), - 0xee8291ae3070720aULL ^ detail::ror64(seed, 53), - 0xe2c0d70f73d6c4a0ULL ^ detail::rol64(seed, 19), - 0x82742897b912855bULL ^ detail::ror64(seed, 37), - }; - static constexpr std::uint64_t iv_size = (sizeof(iv) / 8) - 1; - static constexpr std::uint64_t unique_index = seed & iv_size; - static constexpr std::uint64_t unique_value = iv[unique_index]; - - static constexpr std::uint32_t xtea_rounds = (Level == obf_level::Low) ? 2 - : (Level == obf_level::Medium) ? 6 - : (8 + ((unique_value % 13) * 2)); - - static constexpr std::uint32_t xtea_delta = (0x9E3779B9 ^ static_cast(unique_value)) | 1; - - static constexpr std::uint64_t encrypt(Type value) { - std::uint64_t val = to_uint64(value); - - std::uint32_t v0 = static_cast(val); - std::uint32_t v1 = static_cast(val >> 32); - std::uint32_t sum = 0; - - for (std::uint32_t i{}; i < xtea_rounds; ++i) { - v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + static_cast(iv[sum & 3])); - sum += xtea_delta; - v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + static_cast(iv[(sum >> 11) & 3])); - } - - return (static_cast(v1) << 32) | v0; - } - - template static OBFUSCXX_FORCEINLINE Vec xtea_half_round(Vec va, Vec vb, std::uint32_t key_val) { - using S = simd; - auto left = S::shl4(va); - auto right = S::shr5(va); - auto temp = S::vxor(left, right); - temp = S::vadd(temp, va); - auto key = S::broadcast(key_val); - temp = S::vxor(temp, key); - return S::vsub(vb, temp); - } - - template static OBFUSCXX_FORCEINLINE Type decrypt_scalar(std::uint32_t v0, std::uint32_t v1) { - using S = simd; - auto sv0 = S::from_scalar(v0); - auto sv1 = S::from_scalar(v1); - - decrypt_rounds(sv0, sv1); - - v0 = S::to_scalar(sv0); - v1 = S::to_scalar(sv1); - return from_uint64((static_cast(v1) << 32) | v0); - } - - template static OBFUSCXX_FORCEINLINE void decrypt_rounds(Vec& v0_vec, Vec& v1_vec) { - std::uint32_t sum = xtea_delta * xtea_rounds; - - for (std::uint32_t i{}; i < xtea_rounds; ++i) { - OBFUSCXX_MEM_BARRIER_VEC(v0_vec, v1_vec, sum) - v1_vec = xtea_half_round(v0_vec, v1_vec, sum + static_cast(iv[(sum >> 11) & 3])); - sum -= xtea_delta; - OBFUSCXX_MEM_BARRIER_VEC(v0_vec, v1_vec, sum) - v0_vec = xtea_half_round(v1_vec, v0_vec, sum + static_cast(iv[sum & 3])); - } - } - - static OBFUSCXX_FORCEINLINE Type decrypt(std::uint64_t value) { - OBFUSCXX_MEM_BARRIER(value) - - auto const v0 = static_cast(value); - auto const v1 = static_cast(value >> 32); - -#if defined(__aarch64__) || defined(_M_ARM64) - return decrypt_scalar(v0, v1); -#elif OBFUSCXX_HAS_AVX2 -#if defined(_MSC_VER) && !defined(__clang__) && !defined(OBFUSCXX_MSVC_FORCE_AVX2) - if (cpu_has_avx2()) { - return decrypt_scalar<__m256i>(v0, v1); - } - return decrypt_scalar<__m128i>(v0, v1); -#else - return decrypt_scalar<__m256i>(v0, v1); -#endif -#else - return decrypt_scalar<__m128i>(v0, v1); -#endif - } - - static OBFUSCXX_FORCEINLINE void decrypt_vectorized( - const volatile std::uint64_t* src, Type* dst, std::size_t count - ) { - std::size_t const aligned_count = detail::align_up(count, storage_multiple); - -#if defined(__aarch64__) || defined(_M_ARM64) - for (std::size_t i{}; i < aligned_count; i += 4) { - uint32x4x2_t loaded = - vld2q_u32(reinterpret_cast(const_cast(src + i))); - uint32x4_t v0_vec = loaded.val[0]; - uint32x4_t v1_vec = loaded.val[1]; - - decrypt_rounds(v0_vec, v1_vec); - - uint32x4x2_t zipped = vzipq_u32(v0_vec, v1_vec); - alignas(16) std::uint64_t out64[4]; - vst1q_u32(reinterpret_cast(&out64[0]), zipped.val[0]); - vst1q_u32(reinterpret_cast(&out64[2]), zipped.val[1]); - - for (std::size_t j{}; j < 4 && (i + j) < count; ++j) { - dst[i + j] = from_uint64(out64[j]); - } - } - -#elif OBFUSCXX_HAS_AVX2 -#if defined(_MSC_VER) && !defined(__clang__) && !defined(OBFUSCXX_MSVC_FORCE_AVX2) - if (cpu_has_avx2()) { -#endif - for (std::size_t i{}; i < aligned_count; i += 8) { - const auto* p = reinterpret_cast(const_cast(src + i)); - - __m256i d0 = _mm256_load_si256(p); - __m256i d1 = _mm256_load_si256(p + 1); - __m256i perm_even = _mm256_set_epi32(0, 0, 0, 0, 6, 4, 2, 0); - __m256i perm_odd = _mm256_set_epi32(0, 0, 0, 0, 7, 5, 3, 1); - __m256i v0_vec = _mm256_permute2x128_si256( - _mm256_permutevar8x32_epi32(d0, perm_even), _mm256_permutevar8x32_epi32(d1, perm_even), 0x20 - ); - __m256i v1_vec = _mm256_permute2x128_si256( - _mm256_permutevar8x32_epi32(d0, perm_odd), _mm256_permutevar8x32_epi32(d1, perm_odd), 0x20 - ); - - decrypt_rounds(v0_vec, v1_vec); - - __m128i v0_128lo = _mm256_castsi256_si128(v0_vec); - __m128i v1_128lo = _mm256_castsi256_si128(v1_vec); - __m128i v0_128hi = _mm256_extracti128_si256(v0_vec, 1); - __m128i v1_128hi = _mm256_extracti128_si256(v1_vec, 1); - - alignas(32) std::uint64_t out64[8]; - _mm_store_si128(reinterpret_cast<__m128i*>(&out64[0]), _mm_unpacklo_epi32(v0_128lo, v1_128lo)); - _mm_store_si128(reinterpret_cast<__m128i*>(&out64[2]), _mm_unpackhi_epi32(v0_128lo, v1_128lo)); - _mm_store_si128(reinterpret_cast<__m128i*>(&out64[4]), _mm_unpacklo_epi32(v0_128hi, v1_128hi)); - _mm_store_si128(reinterpret_cast<__m128i*>(&out64[6]), _mm_unpackhi_epi32(v0_128hi, v1_128hi)); - - for (std::size_t j{}; j < 8 && (i + j) < count; ++j) { - dst[i + j] = from_uint64(out64[j]); - } - } -#if defined(_MSC_VER) && !defined(__clang__) && !defined(OBFUSCXX_MSVC_FORCE_AVX2) - } else { - for (std::size_t i{}; i < aligned_count; i += 4) { - const auto* p = reinterpret_cast(const_cast(src + i)); - - __m128i d0 = _mm_load_si128(p); - __m128i d1 = _mm_load_si128(p + 1); - __m128i mask = _mm_set1_epi64x(0xFFFFFFFF); - __m128i v0_vec = _mm_castps_si128(_mm_shuffle_ps( - _mm_castsi128_ps(_mm_and_si128(d0, mask)), - _mm_castsi128_ps(_mm_and_si128(d1, mask)), - _MM_SHUFFLE(2, 0, 2, 0) - )); - __m128i v1_vec = _mm_castps_si128(_mm_shuffle_ps( - _mm_castsi128_ps(_mm_srli_epi64(d0, 32)), - _mm_castsi128_ps(_mm_srli_epi64(d1, 32)), - _MM_SHUFFLE(2, 0, 2, 0) - )); - - decrypt_rounds(v0_vec, v1_vec); - - __m128i lo64 = _mm_unpacklo_epi32(v0_vec, v1_vec); - __m128i hi64 = _mm_unpackhi_epi32(v0_vec, v1_vec); - alignas(16) std::uint64_t out64[4]; - _mm_store_si128(reinterpret_cast<__m128i*>(&out64[0]), lo64); - _mm_store_si128(reinterpret_cast<__m128i*>(&out64[2]), hi64); - - for (std::size_t j{}; j < 4 && (i + j) < count; ++j) { - dst[i + j] = from_uint64(out64[j]); - } - } - } -#endif -#else - for (std::size_t i{}; i < aligned_count; i += 4) { - const auto* p = reinterpret_cast(const_cast(src + i)); - - __m128i const d0 = _mm_load_si128(p); - __m128i const d1 = _mm_load_si128(p + 1); - __m128i const mask = _mm_set1_epi64x(0xFFFFFFFF); - __m128i v0_vec = _mm_castps_si128(_mm_shuffle_ps( - _mm_castsi128_ps(_mm_and_si128(d0, mask)), - _mm_castsi128_ps(_mm_and_si128(d1, mask)), - _MM_SHUFFLE(2, 0, 2, 0) - )); - __m128i v1_vec = _mm_castps_si128(_mm_shuffle_ps( - _mm_castsi128_ps(_mm_srli_epi64(d0, 32)), - _mm_castsi128_ps(_mm_srli_epi64(d1, 32)), - _MM_SHUFFLE(2, 0, 2, 0) - )); - - decrypt_rounds(v0_vec, v1_vec); - - __m128i const lo64 = _mm_unpacklo_epi32(v0_vec, v1_vec); - __m128i const hi64 = _mm_unpackhi_epi32(v0_vec, v1_vec); - alignas(16) std::uint64_t out64[4]; - _mm_store_si128(reinterpret_cast<__m128i*>(&out64[0]), lo64); - _mm_store_si128(reinterpret_cast<__m128i*>(&out64[2]), hi64); - - for (std::size_t j{}; j < 4 && (i + j) < count; ++j) { - dst[i + j] = from_uint64(out64[j]); - } - } -#endif - } - - static constexpr std::uint64_t to_uint64(Type value) { - if constexpr (std::is_pointer_v) { - return reinterpret_cast(value); - } else if constexpr (std::is_floating_point_v) { - if constexpr (sizeof(Type) == 4) { - return __builtin_bit_cast(std::uint32_t, value); - } else { - return __builtin_bit_cast(std::uint64_t, value); - } - } else { - return static_cast(value); - } - } - - static OBFUSCXX_FORCEINLINE Type from_uint64(std::uint64_t value) { - if constexpr (std::is_pointer_v) { - return reinterpret_cast(value); - } else if constexpr (std::is_floating_point_v) { - if constexpr (sizeof(Type) == 4) { - return __builtin_bit_cast(Type, static_cast(value)); - } else { - return __builtin_bit_cast(Type, value); - } - } else { - return static_cast(value); - } - } - - static OBFUSCXX_FORCEINLINE bool cpu_has_avx2() { -#if !defined(__aarch64__) && !defined(_M_ARM64) - static const bool cached = []() { -#if defined(_MSC_VER) - int cpu_info[4]{}; - __cpuidex(cpu_info, 7, 0); - return (cpu_info[1] & (1 << 5)) != 0; -#else - unsigned int eax, ebx, ecx, edx; - if (!__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) - return false; - return (ebx & (1 << 5)) != 0; -#endif - }(); - return cached; -#else - return false; -#endif - } - - public: - explicit consteval obfuscxx(passkey) { - for (std::size_t i{}; i < type_size; ++i) { - storage_[i] = seed ^ iv[i & iv_size]; - } - } - - explicit consteval obfuscxx(Type val) : obfuscxx(passkey{}) { - storage_[0] = encrypt(val); - } - - explicit consteval obfuscxx(Type (&arr)[type_size]) : obfuscxx(passkey{}) { - for (std::size_t i{}; i < type_size; ++i) { - storage_[i] = encrypt(arr[i]); - } - } - - explicit consteval obfuscxx(const Type (&arr)[type_size]) : obfuscxx(passkey{}) { - for (std::size_t i{}; i < type_size; ++i) { - storage_[i] = encrypt(arr[i]); - } - } - - consteval obfuscxx(const std::initializer_list& list) : obfuscxx(passkey{}) { - for (std::size_t i{}; const auto& v : list) { - storage_[i++] = encrypt(v); - } - } - - OBFUSCXX_FORCEINLINE Type get() const - requires is_single - { - volatile const std::uint64_t* ptr = &storage_[0]; - std::uint64_t val = *ptr; - return decrypt(val); - } - - OBFUSCXX_FORCEINLINE Type get(std::size_t i) const - requires is_array - { - volatile const std::uint64_t* ptr = &storage_[i]; - std::uint64_t val = *ptr; - return decrypt(val); - } - - OBFUSCXX_FORCEINLINE void copy_to(Type* out, std::size_t count) const - requires is_array - { - std::size_t effective_count = (count < type_size) ? count : type_size; - decrypt_vectorized(storage_, out, effective_count); - } - - OBFUSCXX_RUNTIME_WARNING OBFUSCXX_FORCEINLINE void set(Type val) - requires is_single - { - storage_[0] = encrypt(val); - } - - OBFUSCXX_RUNTIME_WARNING OBFUSCXX_FORCEINLINE void set(Type val, std::size_t i) - requires is_array - { - storage_[i] = encrypt(val); - } - - OBFUSCXX_RUNTIME_WARNING OBFUSCXX_FORCEINLINE void set(const std::initializer_list& list) - requires is_array - { - for (std::size_t i{}; const auto& val : list) { - if (i < type_size) { - storage_[i++] = encrypt(val); - } - } - } - - OBFUSCXX_FORCEINLINE Type operator()() const - requires is_single - { - return get(); - } - - OBFUSCXX_FORCEINLINE Type operator[](std::size_t i) const - requires is_array - { - return get(i); - } - - OBFUSCXX_FORCEINLINE obfuscxx& operator=(Type val) - requires is_single - { - set(val); - return *this; - } - - OBFUSCXX_FORCEINLINE obfuscxx& operator=(const std::initializer_list& list) - requires is_array - { - set(list); - return *this; - } - - OBFUSCXX_FORCEINLINE bool operator==(const obfuscxx& rhs) const - requires is_single - { - return get() == rhs.get(); - } - - OBFUSCXX_FORCEINLINE bool operator==(const obfuscxx& rhs) const - requires is_array - { - for (std::size_t i{}; i < type_size; ++i) { - if (get(i) != rhs.get(i)) { - return false; - } - } - return true; - } - - OBFUSCXX_FORCEINLINE bool operator!=(const obfuscxx& rhs) const { - return !(*this == rhs); - } - - OBFUSCXX_FORCEINLINE operator Type() const - requires is_single - { - return get(); - } - - OBFUSCXX_FORCEINLINE bool operator<(const obfuscxx& rhs) const { - return get() < rhs.get(); - } - - OBFUSCXX_FORCEINLINE bool operator>(const obfuscxx& rhs) const { - return get() > rhs.get(); - } - - OBFUSCXX_FORCEINLINE bool operator<=(const obfuscxx& rhs) const { - return get() <= rhs.get(); - } - - OBFUSCXX_FORCEINLINE bool operator>=(const obfuscxx& rhs) const { - return get() >= rhs.get(); - } - - OBFUSCXX_FORCEINLINE Type operator+(const obfuscxx& rhs) const { - return get() + rhs.get(); - } - - OBFUSCXX_FORCEINLINE Type operator-(const obfuscxx& rhs) const { - return get() - rhs.get(); - } - - OBFUSCXX_FORCEINLINE Type operator*(const obfuscxx& rhs) const - requires(!is_single_pointer) - { - return get() * rhs.get(); - } - - OBFUSCXX_FORCEINLINE Type operator/(const obfuscxx& rhs) const - requires(!is_single_pointer) - { - return get() / rhs.get(); - } - - OBFUSCXX_FORCEINLINE obfuscxx& operator+=(const obfuscxx& rhs) - requires(!is_single_pointer) - { - set(get() + rhs.get()); - return *this; - } - - OBFUSCXX_FORCEINLINE obfuscxx& operator-=(const obfuscxx& rhs) - requires(!is_single_pointer) - { - set(get() - rhs.get()); - return *this; - } - - OBFUSCXX_FORCEINLINE Type operator->() - requires is_single_pointer - { - return get(); - } - - OBFUSCXX_FORCEINLINE Type& operator*() - requires is_single_pointer - { - return *get(); - } - - struct iterator { - const obfuscxx* parent; - std::size_t index; - - Type operator*() const { - return parent->get(index); - } - - iterator& operator++() { - ++index; - return *this; - } - - bool operator!=(const iterator& other) const { - return index != other.index; - } - bool operator==(const iterator& other) const { - return index == other.index; - } - }; - - iterator begin() const - requires is_array - { - return {this, 0}; - } - iterator end() const - requires is_array - { - return {this, type_size}; - } - static constexpr std::size_t size() { - return type_size; - } - - template struct string_copy { - private: - static constexpr bool is_char = std::is_same_v || std::is_same_v; - static constexpr bool is_wchar = - std::is_same_v || std::is_same_v; - - public: - operator const char*() const - requires is_char - { - return data; - } - operator const wchar_t*() const - requires is_wchar - { - return data; - } - - const CharType* c_str() const { - return data; - } - const CharType& operator[](std::size_t i) const { - return data[i]; - } - - const CharType* begin() const { - return data; - } - const CharType* end() const { - return data + N; - } - - constexpr std::size_t size() const { - return N; - } - - CharType data[N]; - }; - - template struct array_copy { - const ArrayType* get() const { - return data; - } - - const ArrayType* begin() const { - return data; - } - const ArrayType* end() const { - return data + N; - } - - constexpr std::size_t size() const { - return N; - } - constexpr std::size_t size_bytes() const { - return N * sizeof(ArrayType); - } - - ArrayType data[N]; - }; - - OBFUSCXX_FORCEINLINE string_copy to_string() const - requires(is_char || is_wchar) - { - string_copy result{}; - if constexpr (is_array) { - copy_to(result.data, type_size); - } else { - result.data[0] = get(); - } - return result; - } - - OBFUSCXX_FORCEINLINE array_copy to_array() const - requires(is_array) - { - array_copy result{}; - copy_to(result.data, type_size); - return result; - } - - private: - alignas(storage_alignment) OBFUSCXX_VOLATILE std::uint64_t storage_[storage_size]{}; - }; -} // namespace ngu - -#if defined(__clang__) || defined(__GNUC__) - -#define OBFUSCXX_LITERAL(str, type, size, level) ngu::obfuscxx(str).to_string() - -/// Obfuscate string literal (Low level) -template constexpr auto operator""_obf() { - constexpr CharType str[] = {Chars..., '\0'}; - return OBFUSCXX_LITERAL(str, CharType, sizeof...(Chars) + 1, ngu::obf_level::Low); -} -/// Obfuscate string literal (Medium level) -template constexpr auto operator""_obfm() { - constexpr CharType str[] = {Chars..., '\0'}; - return OBFUSCXX_LITERAL(str, CharType, sizeof...(Chars) + 1, ngu::obf_level::Medium); -} -/// Obfuscate string literal (High level) -template constexpr auto operator""_obfh() { - constexpr CharType str[] = {Chars..., '\0'}; - return OBFUSCXX_LITERAL(str, CharType, sizeof...(Chars) + 1, ngu::obf_level::High); -} - -#endif - -#define OBFUSCXX_VAL_EX(val, level) \ - ngu::obfuscxx, 1, level, OBFUSCXX_ENTROPY>(val).get() -#define OBFUSCXX_STR_EX(str, level) \ - ngu::obfuscxx, sizeof(str) / sizeof(str[0]), level, OBFUSCXX_ENTROPY>(str) \ - .to_string() \ - .c_str() - -/// Obfuscate value (Low level) -#define obfusv(val) OBFUSCXX_VAL_EX(val, ngu::obf_level::Low) -/// Obfuscate string (Low level) -#define obfuss(str) OBFUSCXX_STR_EX(str, ngu::obf_level::Low) - -/// Obfuscate value (Medium level) -#define obfusvm(val) OBFUSCXX_VAL_EX(val, ngu::obf_level::Medium) -/// Obfuscate string (Medium level) -#define obfussm(str) OBFUSCXX_STR_EX(str, ngu::obf_level::Medium) - -/// Obfuscate value (High level) -#define obfusvh(val) OBFUSCXX_VAL_EX(val, ngu::obf_level::High) -/// Obfuscate string (High level) -#define obfussh(str) OBFUSCXX_STR_EX(str, ngu::obf_level::High) - -#define obfusf(type, size, level) ngu::obfuscxx - +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2025-2026 nevergiveupcpp + +// Copyright 2025-2026 nevergiveupcpp +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NGU_OBFUSCXX_H +#define NGU_OBFUSCXX_H + +#include +#include + +#if defined(_KERNEL_MODE) || defined(_WIN64_DRIVER) +using byte = std::uint8_t; +using max_align_t = double; +#endif + +#include + +#if defined(__aarch64__) || defined(_M_ARM64) || defined(__ARM_NEON) +#include +#elif defined(_WIN32) +#include +#elif defined(__GNUC__) || defined(__clang__) +#include +#include +#else +#error Unsupported platform +#endif + +#if (defined(__GNUC__) || defined(__clang__)) && defined(__AVX2__) +#define OBFUSCXX_HAS_AVX2 1 +#elif defined(_MSC_VER) && !defined(__clang__) && !defined(_M_ARM64) +#define OBFUSCXX_HAS_AVX2 1 +#else +#define OBFUSCXX_HAS_AVX2 0 +#endif + +#if defined(__clang__) || defined(__GNUC__) +#define OBFUSCXX_VOLATILE +#elif defined(_MSC_VER) +#define OBFUSCXX_VOLATILE volatile +#endif + +#if defined(__clang__) || defined(__GNUC__) +#define OBFUSCXX_FORCEINLINE __attribute__((always_inline)) inline +#else +#define OBFUSCXX_FORCEINLINE __forceinline +#endif + +#if defined(_KERNEL_MODE) || defined(_WIN64_DRIVER) +#define _mm256_extract_epi32(vec, idx) (((int32_t*)&(vec))[(idx)]) +#endif + +#if defined(__clang__) || defined(__GNUC__) +#define OBFUSCXX_MEM_BARRIER(...) __asm__ volatile("" : "+r"(__VA_ARGS__)::"memory"); +#if defined(__aarch64__) || defined(_M_ARM64) +#define OBFUSCXX_MEM_BARRIER_VEC(v0, v1, sum) __asm__ volatile("" : "+w"(v0), "+w"(v1), "+r"(sum)::"memory"); +#else +#define OBFUSCXX_MEM_BARRIER_VEC(v0, v1, sum) __asm__ volatile("" : "+x"(v0), "+x"(v1), "+r"(sum)::"memory"); +#endif +#elif defined(_MSC_VER) +#define OBFUSCXX_MEM_BARRIER(...) _ReadWriteBarrier(); +#define OBFUSCXX_MEM_BARRIER_VEC(v0, v1, sum) _ReadWriteBarrier(); +#endif + +#ifndef OBFUSCXX_DISABLE_WARNS +#define OBFUSCXX_RUNTIME_WARNING \ + [[deprecated( \ + "OBFUSCXX: Runtime set() uses encrypt method without SIMD obfuscation. For better protection, " \ + "initialize at compile-time." \ + )]] +#else +#define OBFUSCXX_RUNTIME_WARNING +#endif + +namespace ngu { + namespace detail { + constexpr std::uint64_t splitmix64(std::uint64_t x) { + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; + return x ^ (x >> 31); + } + + template OBFUSCXX_FORCEINLINE consteval std::uint64_t hash_compile_time(char const (&data)[N]) { + std::uint64_t hash = 0; + + for (std::size_t i{}; i < N - 1; ++i) { + hash += data[i] >= 'A' && data[i] <= 'Z' ? data[i] + ('a' - 'A') : data[i]; + hash += hash << 8; + hash ^= hash >> 11; + } + + hash += hash << 5; + hash ^= hash >> 13; + hash += hash << 10; + + return hash; + } + + OBFUSCXX_FORCEINLINE std::uint64_t hash_runtime(char const* str) { + std::size_t length = 0; + while (str[length] != 0) { + ++length; + } + + std::uint64_t hash = 0; + + for (std::size_t i{}; i < length; ++i) { + hash += str[i] >= 'A' && str[i] <= 'Z' ? str[i] + ('a' - 'A') : str[i]; + hash += hash << 8; + hash ^= hash >> 11; + } + + hash += hash << 5; + hash ^= hash >> 13; + hash += hash << 10; + + return hash; + } + + OBFUSCXX_FORCEINLINE constexpr std::uint64_t rol64(const std::uint64_t x, int n) { + n &= 63; + if (n == 0) { + return x; + } + return (x << n) | (x >> (64 - n)); + } + + OBFUSCXX_FORCEINLINE constexpr std::uint64_t ror64(const std::uint64_t x, int n) { + n &= 63; + if (n == 0) { + return x; + } + return (x >> n) | (x << (64 - n)); + } + + OBFUSCXX_FORCEINLINE constexpr std::size_t align_up(const std::size_t size, const std::size_t multiple) { + return (size + multiple - 1) & ~(multiple - 1); + } + } // namespace detail + + template struct simd; + +#if defined(__aarch64__) || defined(_M_ARM64) + template<> struct simd { + using vec = uint32x4_t; + static OBFUSCXX_FORCEINLINE vec shl4(const vec v) { + return vshlq_n_u32(v, 4); + } + static OBFUSCXX_FORCEINLINE vec shr5(const vec v) { + return vshrq_n_u32(v, 5); + } + static OBFUSCXX_FORCEINLINE vec vxor(const vec a, const vec b) { + return veorq_u32(a, b); + } + static OBFUSCXX_FORCEINLINE vec vadd(const vec a, const vec b) { + return vaddq_u32(a, b); + } + static OBFUSCXX_FORCEINLINE vec vsub(const vec a, const vec b) { + return vsubq_u32(a, b); + } + static OBFUSCXX_FORCEINLINE vec broadcast(const std::uint32_t v) { + return vdupq_n_u32(v); + } + static OBFUSCXX_FORCEINLINE vec from_scalar(const std::uint32_t v) { + return vdupq_n_u32(v); + } + static OBFUSCXX_FORCEINLINE std::uint32_t to_scalar(const vec v) { + return vgetq_lane_u32(v, 0); + } + }; +#else + template<> struct simd<__m128i> { + using vec = __m128i; + static OBFUSCXX_FORCEINLINE vec shl4(const vec v) { + return _mm_slli_epi32(v, 4); + } + static OBFUSCXX_FORCEINLINE vec shr5(const vec v) { + return _mm_srli_epi32(v, 5); + } + static OBFUSCXX_FORCEINLINE vec vxor(const vec a, const vec b) { + return _mm_xor_si128(a, b); + } + static OBFUSCXX_FORCEINLINE vec vadd(const vec a, const vec b) { + return _mm_add_epi32(a, b); + } + static OBFUSCXX_FORCEINLINE vec vsub(const vec a, const vec b) { + return _mm_sub_epi32(a, b); + } + static OBFUSCXX_FORCEINLINE vec broadcast(const std::uint32_t v) { + return _mm_set1_epi32(v); + } + static OBFUSCXX_FORCEINLINE vec from_scalar(const std::uint32_t v) { + return _mm_cvtsi32_si128(v); + } + static OBFUSCXX_FORCEINLINE std::uint32_t to_scalar(const vec v) { + return _mm_cvtsi128_si32(v); + } + }; +#if OBFUSCXX_HAS_AVX2 + template<> struct simd<__m256i> { + using vec = __m256i; + static OBFUSCXX_FORCEINLINE vec shl4(const vec v) { + return _mm256_slli_epi32(v, 4); + } + static OBFUSCXX_FORCEINLINE vec shr5(const vec v) { + return _mm256_srli_epi32(v, 5); + } + static OBFUSCXX_FORCEINLINE vec vxor(const vec a, const vec b) { + return _mm256_xor_si256(a, b); + } + static OBFUSCXX_FORCEINLINE vec vadd(const vec a, const vec b) { + return _mm256_add_epi32(a, b); + } + static OBFUSCXX_FORCEINLINE vec vsub(const vec a, const vec b) { + return _mm256_sub_epi32(a, b); + } + static OBFUSCXX_FORCEINLINE vec broadcast(const std::uint32_t v) { + return _mm256_set1_epi32(v); + } + static OBFUSCXX_FORCEINLINE vec from_scalar(const std::uint32_t v) { + return _mm256_set1_epi32(v); + } + static OBFUSCXX_FORCEINLINE std::uint32_t to_scalar(const vec v) { + return _mm256_extract_epi32(v, 0); + } + }; +#endif +#endif + +#define OBFUSCXX_HASH(s) ngu::detail::hash_compile_time(s) +#define OBFUSCXX_HASH_RT(s) ngu::detail::hash_runtime(s) + +#if defined(_KERNEL_MODE) || defined(_WIN64_DRIVER) +#define OBFUSCXX_ENTROPY \ + (detail::splitmix64( \ + (OBFUSCXX_HASH(__FILE__) * 0x517cc1b727220a95ULL) + ((std::uint64_t)__LINE__ * 0x9e3779b97f4a7c15ULL) + \ + (detail::rol64((std::uint64_t)__COUNTER__, 37) ^ ((std::uint64_t)__LINE__ * 0xff51afd7ed558ccdULL)) \ + )) +#else +#define OBFUSCXX_ENTROPY \ + (ngu::detail::splitmix64( \ + OBFUSCXX_HASH(__FILE__) + ((std::uint64_t)__LINE__ * 0x9e3779b97f4a7c15ULL) + \ + (OBFUSCXX_HASH(__TIME__) ^ ((std::uint64_t)__COUNTER__ << 32)) \ + )) +#endif + + /// The obfuscation levels are based on the number of XTEA rounds. The more rounds, the more iterations are + /// performed on the expanded state + enum class obf_level : std::uint8_t { + /// 2 xtea rounds + low, + /// 6 xtea rounds + medium, + /// a random number of xtea rounds, between 8 and 32 + high + }; + + template class obfuscxx { + template using rebind = obfuscxx; + + static_assert(Entropy != 0, "Entropy must not be zero, use the specified macros"); + + using type = std::remove_extent_t; + + static constexpr bool is_array = std::is_array_v; + static constexpr bool is_single = !is_array; + static constexpr bool is_single_pointer = std::is_pointer_v && is_single; + static constexpr bool is_char = std::is_same_v || std::is_same_v; + static constexpr bool is_wchar = std::is_same_v || std::is_same_v; + + static constexpr std::size_t type_size = std::is_array_v ? std::extent_v : 1; + + static constexpr std::size_t simd_alignment = OBFUSCXX_HAS_AVX2 ? 32 : 16; + static constexpr std::size_t storage_multiple = OBFUSCXX_HAS_AVX2 ? 8 : 4; + static constexpr std::size_t storage_alignment = is_array ? simd_alignment : alignof(std::uint64_t); + static constexpr std::size_t storage_size = + is_array ? detail::align_up(type_size, storage_multiple) : type_size; + + struct passkey { + explicit passkey() = default; + }; + + static constexpr std::uint64_t seed{Entropy}; + static constexpr std::uint64_t iv[8] = { + 0xcbf43b227a01fe5aULL ^ seed, + 0x32703be7aaa7c38fULL ^ detail::ror64(seed, 13), + 0xb589959b3d854bbcULL ^ detail::rol64(seed, 29), + 0x73b3ef5578a97c8aULL ^ detail::ror64(seed, 41), + 0x92afafd27c6e16e9ULL ^ detail::rol64(seed, 7), + 0xee8291ae3070720aULL ^ detail::ror64(seed, 53), + 0xe2c0d70f73d6c4a0ULL ^ detail::rol64(seed, 19), + 0x82742897b912855bULL ^ detail::ror64(seed, 37), + }; + static constexpr std::uint64_t iv_size = (sizeof(iv) / 8) - 1; + static constexpr std::uint64_t unique_index = seed & iv_size; + static constexpr std::uint64_t unique_value = iv[unique_index]; + + static constexpr std::uint32_t xtea_rounds = (Level == obf_level::low) ? 2 + : (Level == obf_level::medium) ? 6 + : (8 + ((unique_value % 13) * 2)); + + static constexpr std::uint32_t xtea_delta = (0x9E3779B9 ^ static_cast(unique_value)) | 1; + + static constexpr std::uint64_t encrypt(type value) { + std::uint64_t val = to_uint64(value); + + std::uint32_t v0 = static_cast(val); + std::uint32_t v1 = static_cast(val >> 32); + std::uint32_t sum = 0; + + for (std::uint32_t i{}; i < xtea_rounds; ++i) { + v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + static_cast(iv[sum & 3])); + sum += xtea_delta; + v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + static_cast(iv[(sum >> 11) & 3])); + } + + return (static_cast(v1) << 32) | v0; + } + + template static OBFUSCXX_FORCEINLINE Vec xtea_half_round(Vec va, Vec vb, std::uint32_t key_val) { + using S = simd; + auto left = S::shl4(va); + auto right = S::shr5(va); + auto temp = S::vxor(left, right); + temp = S::vadd(temp, va); + auto key = S::broadcast(key_val); + temp = S::vxor(temp, key); + return S::vsub(vb, temp); + } + + template static OBFUSCXX_FORCEINLINE type decrypt_scalar(std::uint32_t v0, std::uint32_t v1) { + using S = simd; + auto sv0 = S::from_scalar(v0); + auto sv1 = S::from_scalar(v1); + + decrypt_rounds(sv0, sv1); + + v0 = S::to_scalar(sv0); + v1 = S::to_scalar(sv1); + return from_uint64((static_cast(v1) << 32) | v0); + } + + template static OBFUSCXX_FORCEINLINE void decrypt_rounds(Vec& v0_vec, Vec& v1_vec) { + std::uint32_t sum = xtea_delta * xtea_rounds; + + for (std::uint32_t i{}; i < xtea_rounds; ++i) { + OBFUSCXX_MEM_BARRIER_VEC(v0_vec, v1_vec, sum) + v1_vec = xtea_half_round(v0_vec, v1_vec, sum + static_cast(iv[(sum >> 11) & 3])); + sum -= xtea_delta; + OBFUSCXX_MEM_BARRIER_VEC(v0_vec, v1_vec, sum) + v0_vec = xtea_half_round(v1_vec, v0_vec, sum + static_cast(iv[sum & 3])); + } + } + + static OBFUSCXX_FORCEINLINE type decrypt(std::uint64_t value) { + OBFUSCXX_MEM_BARRIER(value) + + auto const v0 = static_cast(value); + auto const v1 = static_cast(value >> 32); + +#if defined(__aarch64__) || defined(_M_ARM64) + return decrypt_scalar(v0, v1); +#elif OBFUSCXX_HAS_AVX2 +#if defined(_MSC_VER) && !defined(__clang__) && !defined(OBFUSCXX_MSVC_FORCE_AVX2) + if (cpu_has_avx2()) { + return decrypt_scalar<__m256i>(v0, v1); + } + return decrypt_scalar<__m128i>(v0, v1); +#else + return decrypt_scalar<__m256i>(v0, v1); +#endif +#else + return decrypt_scalar<__m128i>(v0, v1); +#endif + } + + static OBFUSCXX_FORCEINLINE void decrypt_vectorized( + const volatile std::uint64_t* src, type* dst, std::size_t count + ) { + std::size_t const aligned_count = detail::align_up(count, storage_multiple); + +#if defined(__aarch64__) || defined(_M_ARM64) + for (std::size_t i{}; i < aligned_count; i += 4) { + uint32x4x2_t loaded = + vld2q_u32(reinterpret_cast(const_cast(src + i))); + uint32x4_t v0_vec = loaded.val[0]; + uint32x4_t v1_vec = loaded.val[1]; + + decrypt_rounds(v0_vec, v1_vec); + + uint32x4x2_t zipped = vzipq_u32(v0_vec, v1_vec); + alignas(16) std::uint64_t out64[4]; + vst1q_u32(reinterpret_cast(&out64[0]), zipped.val[0]); + vst1q_u32(reinterpret_cast(&out64[2]), zipped.val[1]); + + for (std::size_t j{}; j < 4 && (i + j) < count; ++j) { + dst[i + j] = from_uint64(out64[j]); + } + } + +#elif OBFUSCXX_HAS_AVX2 +#if defined(_MSC_VER) && !defined(__clang__) && !defined(OBFUSCXX_MSVC_FORCE_AVX2) + if (cpu_has_avx2()) { +#endif + for (std::size_t i{}; i < aligned_count; i += 8) { + const auto* p = reinterpret_cast(const_cast(src + i)); + + __m256i d0 = _mm256_load_si256(p); + __m256i d1 = _mm256_load_si256(p + 1); + __m256i perm_even = _mm256_set_epi32(0, 0, 0, 0, 6, 4, 2, 0); + __m256i perm_odd = _mm256_set_epi32(0, 0, 0, 0, 7, 5, 3, 1); + __m256i v0_vec = _mm256_permute2x128_si256( + _mm256_permutevar8x32_epi32(d0, perm_even), _mm256_permutevar8x32_epi32(d1, perm_even), 0x20 + ); + __m256i v1_vec = _mm256_permute2x128_si256( + _mm256_permutevar8x32_epi32(d0, perm_odd), _mm256_permutevar8x32_epi32(d1, perm_odd), 0x20 + ); + + decrypt_rounds(v0_vec, v1_vec); + + __m128i v0_128lo = _mm256_castsi256_si128(v0_vec); + __m128i v1_128lo = _mm256_castsi256_si128(v1_vec); + __m128i v0_128hi = _mm256_extracti128_si256(v0_vec, 1); + __m128i v1_128hi = _mm256_extracti128_si256(v1_vec, 1); + + alignas(32) std::uint64_t out64[8]; + _mm_store_si128(reinterpret_cast<__m128i*>(&out64[0]), _mm_unpacklo_epi32(v0_128lo, v1_128lo)); + _mm_store_si128(reinterpret_cast<__m128i*>(&out64[2]), _mm_unpackhi_epi32(v0_128lo, v1_128lo)); + _mm_store_si128(reinterpret_cast<__m128i*>(&out64[4]), _mm_unpacklo_epi32(v0_128hi, v1_128hi)); + _mm_store_si128(reinterpret_cast<__m128i*>(&out64[6]), _mm_unpackhi_epi32(v0_128hi, v1_128hi)); + + for (std::size_t j{}; j < 8 && (i + j) < count; ++j) { + dst[i + j] = from_uint64(out64[j]); + } + } +#if defined(_MSC_VER) && !defined(__clang__) && !defined(OBFUSCXX_MSVC_FORCE_AVX2) + } else { + for (std::size_t i{}; i < aligned_count; i += 4) { + const auto* p = reinterpret_cast(const_cast(src + i)); + + __m128i d0 = _mm_load_si128(p); + __m128i d1 = _mm_load_si128(p + 1); + __m128i mask = _mm_set1_epi64x(0xFFFFFFFF); + __m128i v0_vec = _mm_castps_si128(_mm_shuffle_ps( + _mm_castsi128_ps(_mm_and_si128(d0, mask)), + _mm_castsi128_ps(_mm_and_si128(d1, mask)), + _MM_SHUFFLE(2, 0, 2, 0) + )); + __m128i v1_vec = _mm_castps_si128(_mm_shuffle_ps( + _mm_castsi128_ps(_mm_srli_epi64(d0, 32)), + _mm_castsi128_ps(_mm_srli_epi64(d1, 32)), + _MM_SHUFFLE(2, 0, 2, 0) + )); + + decrypt_rounds(v0_vec, v1_vec); + + __m128i lo64 = _mm_unpacklo_epi32(v0_vec, v1_vec); + __m128i hi64 = _mm_unpackhi_epi32(v0_vec, v1_vec); + alignas(16) std::uint64_t out64[4]; + _mm_store_si128(reinterpret_cast<__m128i*>(&out64[0]), lo64); + _mm_store_si128(reinterpret_cast<__m128i*>(&out64[2]), hi64); + + for (std::size_t j{}; j < 4 && (i + j) < count; ++j) { + dst[i + j] = from_uint64(out64[j]); + } + } + } +#endif +#else + for (std::size_t i{}; i < aligned_count; i += 4) { + const auto* p = reinterpret_cast(const_cast(src + i)); + + __m128i const d0 = _mm_load_si128(p); + __m128i const d1 = _mm_load_si128(p + 1); + __m128i const mask = _mm_set1_epi64x(0xFFFFFFFF); + __m128i v0_vec = _mm_castps_si128(_mm_shuffle_ps( + _mm_castsi128_ps(_mm_and_si128(d0, mask)), + _mm_castsi128_ps(_mm_and_si128(d1, mask)), + _MM_SHUFFLE(2, 0, 2, 0) + )); + __m128i v1_vec = _mm_castps_si128(_mm_shuffle_ps( + _mm_castsi128_ps(_mm_srli_epi64(d0, 32)), + _mm_castsi128_ps(_mm_srli_epi64(d1, 32)), + _MM_SHUFFLE(2, 0, 2, 0) + )); + + decrypt_rounds(v0_vec, v1_vec); + + __m128i const lo64 = _mm_unpacklo_epi32(v0_vec, v1_vec); + __m128i const hi64 = _mm_unpackhi_epi32(v0_vec, v1_vec); + alignas(16) std::uint64_t out64[4]; + _mm_store_si128(reinterpret_cast<__m128i*>(&out64[0]), lo64); + _mm_store_si128(reinterpret_cast<__m128i*>(&out64[2]), hi64); + + for (std::size_t j{}; j < 4 && (i + j) < count; ++j) { + dst[i + j] = from_uint64(out64[j]); + } + } +#endif + } + + static constexpr std::uint64_t to_uint64(type value) { + if constexpr (std::is_pointer_v) { + return reinterpret_cast(value); + } else if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(type) == 4) { + return __builtin_bit_cast(std::uint32_t, value); + } else { + return __builtin_bit_cast(std::uint64_t, value); + } + } else { + return static_cast(value); + } + } + + static OBFUSCXX_FORCEINLINE type from_uint64(std::uint64_t value) { + if constexpr (std::is_pointer_v) { + return reinterpret_cast(value); + } else if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(type) == 4) { + return __builtin_bit_cast(type, static_cast(value)); + } else { + return __builtin_bit_cast(type, value); + } + } else { + return static_cast(value); + } + } + + static OBFUSCXX_FORCEINLINE bool cpu_has_avx2() { +#if !defined(__aarch64__) && !defined(_M_ARM64) + static const bool cached = []() { +#if defined(_MSC_VER) + int cpu_info[4]{}; + __cpuidex(cpu_info, 7, 0); + return (cpu_info[1] & (1 << 5)) != 0; +#else + unsigned int eax, ebx, ecx, edx; + if (!__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) + return false; + return (ebx & (1 << 5)) != 0; +#endif + }(); + return cached; +#else + return false; +#endif + } + + public: + explicit consteval obfuscxx(passkey) { + for (std::size_t i{}; i < type_size; ++i) { + storage_[i] = seed ^ iv[i & iv_size]; + } + } + + explicit consteval obfuscxx(type val) : obfuscxx(passkey{}) { + storage_[0] = encrypt(val); + } + + explicit consteval obfuscxx(type (&arr)[type_size]) : obfuscxx(passkey{}) { + for (std::size_t i{}; i < type_size; ++i) { + storage_[i] = encrypt(arr[i]); + } + } + + explicit consteval obfuscxx(const type (&arr)[type_size]) : obfuscxx(passkey{}) { + for (std::size_t i{}; i < type_size; ++i) { + storage_[i] = encrypt(arr[i]); + } + } + + consteval obfuscxx(const std::initializer_list& list) : obfuscxx(passkey{}) { + for (std::size_t i{}; const auto& v : list) { + storage_[i++] = encrypt(v); + } + } + + OBFUSCXX_FORCEINLINE type get() const + requires is_single + { + volatile const std::uint64_t* ptr = &storage_[0]; + std::uint64_t val = *ptr; + return decrypt(val); + } + + OBFUSCXX_FORCEINLINE type get(const std::size_t i) const + requires is_array + { + volatile const std::uint64_t* ptr = &storage_[i]; + std::uint64_t val = *ptr; + return decrypt(val); + } + + OBFUSCXX_FORCEINLINE void copy_to(type* out, const std::size_t count) const + requires is_array + { + std::size_t effective_count = (count < type_size) ? count : type_size; + decrypt_vectorized(storage_, out, effective_count); + } + + OBFUSCXX_RUNTIME_WARNING OBFUSCXX_FORCEINLINE void set(type val) + requires is_single + { + storage_[0] = encrypt(val); + } + + OBFUSCXX_RUNTIME_WARNING OBFUSCXX_FORCEINLINE void set(type val, const std::size_t i) + requires is_array + { + storage_[i] = encrypt(val); + } + + OBFUSCXX_RUNTIME_WARNING OBFUSCXX_FORCEINLINE void set(const std::initializer_list& list) + requires is_array + { + for (std::size_t i{}; const auto& val : list) { + if (i < type_size) { + storage_[i++] = encrypt(val); + } + } + } + + OBFUSCXX_FORCEINLINE type operator()() const + requires is_single + { + return get(); + } + + OBFUSCXX_FORCEINLINE type operator[](const std::size_t i) const + requires is_array + { + return get(i); + } + + OBFUSCXX_FORCEINLINE obfuscxx& operator=(type val) + requires is_single + { + set(val); + return *this; + } + + OBFUSCXX_FORCEINLINE obfuscxx& operator=(const std::initializer_list& list) + requires is_array + { + set(list); + return *this; + } + + template OBFUSCXX_FORCEINLINE bool operator==(const rebind& rhs) const + requires is_single + { + return get() == rhs.get(); + } + + template OBFUSCXX_FORCEINLINE bool operator==(const rebind& rhs) const + requires is_array + { + for (std::size_t i{}; i < type_size; ++i) { + if (get(i) != rhs.get(i)) { + return false; + } + } + return true; + } + + template OBFUSCXX_FORCEINLINE bool operator!=(const rebind& rhs) const { + return !(*this == rhs); + } + + OBFUSCXX_FORCEINLINE operator type() const + requires is_single + { + return get(); + } + + template OBFUSCXX_FORCEINLINE bool operator<(const rebind& rhs) const { + return get() < rhs.get(); + } + + template OBFUSCXX_FORCEINLINE bool operator>(const rebind& rhs) const { + return get() > rhs.get(); + } + + template OBFUSCXX_FORCEINLINE bool operator<=(const rebind& rhs) const { + return get() <= rhs.get(); + } + + template OBFUSCXX_FORCEINLINE bool operator>=(const rebind& rhs) const { + return get() >= rhs.get(); + } + + template OBFUSCXX_FORCEINLINE type operator+(const rebind& rhs) const { + return get() + rhs.get(); + } + + template OBFUSCXX_FORCEINLINE type operator-(const rebind& rhs) const { + return get() - rhs.get(); + } + + template OBFUSCXX_FORCEINLINE type operator*(const rebind& rhs) const + requires(!is_single_pointer) + { + return get() * rhs.get(); + } + + template OBFUSCXX_FORCEINLINE type operator/(const rebind& rhs) const + requires(!is_single_pointer) + { + return get() / rhs.get(); + } + + template OBFUSCXX_FORCEINLINE obfuscxx& operator+=(const rebind& rhs) + requires(!is_single_pointer) + { + set(get() + rhs.get()); + return *this; + } + + template OBFUSCXX_FORCEINLINE obfuscxx& operator-=(const rebind& rhs) + requires(!is_single_pointer) + { + set(get() - rhs.get()); + return *this; + } + + OBFUSCXX_FORCEINLINE type operator->() + requires is_single_pointer + { + return get(); + } + + OBFUSCXX_FORCEINLINE type& operator*() + requires is_single_pointer + { + return *get(); + } + + struct iterator { + const obfuscxx* parent; + std::size_t index; + + type operator*() const { + return parent->get(index); + } + + iterator& operator++() { + ++index; + return *this; + } + + bool operator!=(const iterator& other) const { + return index != other.index; + } + bool operator==(const iterator& other) const { + return index == other.index; + } + }; + + iterator begin() const + requires is_array + { + return {this, 0}; + } + iterator end() const + requires is_array + { + return {this, type_size}; + } + static constexpr std::size_t size() { + return type_size; + } + + template struct string_copy { + private: + static constexpr bool is_char = std::is_same_v || std::is_same_v; + static constexpr bool is_wchar = std::is_same_v || std::is_same_v; + + public: + operator const char*() const + requires is_char + { + return data; + } + operator const wchar_t*() const + requires is_wchar + { + return data; + } + + const CharT* c_str() const { + return data; + } + const CharT& operator[](std::size_t i) const { + return data[i]; + } + + const CharT* begin() const { + return data; + } + const CharT* end() const { + return data + N; + } + + constexpr std::size_t size() const { + return N; + } + + CharT data[N]; + }; + + template struct array_copy { + const ArrayT* get() const { + return data; + } + + const ArrayT* begin() const { + return data; + } + const ArrayT* end() const { + return data + N; + } + + constexpr std::size_t size() const { + return N; + } + constexpr std::size_t size_bytes() const { + return N * sizeof(ArrayT); + } + + ArrayT data[N]; + }; + + OBFUSCXX_FORCEINLINE string_copy to_string() const + requires(is_char || is_wchar) + { + string_copy result{}; + if constexpr (is_array) { + copy_to(result.data, type_size); + } else { + result.data[0] = get(); + } + return result; + } + + OBFUSCXX_FORCEINLINE array_copy to_array() const + requires(is_array) + { + array_copy result{}; + copy_to(result.data, type_size); + return result; + } + + private: + alignas(storage_alignment) OBFUSCXX_VOLATILE std::uint64_t storage_[storage_size]{}; + }; +} // namespace ngu + +#define OBFXX_EXPAND(x) x +#define OBFXX_GET(_1, _2, NAME, ...) NAME + +#define OBFXX_T1(type) ngu::obfuscxx +#define OBFXX_T2(type, level) ngu::obfuscxx + +#define OBFXX_O1(obj) (ngu::obfuscxx, ngu::obf_level::low, OBFUSCXX_ENTROPY>(obj)) +#define OBFXX_O2(obj, level) (ngu::obfuscxx, level, OBFUSCXX_ENTROPY>(obj)) + +#define OBFXX_V1(val) (OBFXX_O1(val).get()) +#define OBFXX_V2(val, level) (OBFXX_O2(val, level).get()) + +#define OBFXX_S1(str) (OBFXX_O1(str).to_string().c_str()) +#define OBFXX_S2(str, level) (OBFXX_O2(str, level).to_string().c_str()) + +/// Obfuscate type (low by default; pass ngu::obf_level::{medium,high} as second arg) +#define obfxx(...) OBFXX_EXPAND(OBFXX_GET(__VA_ARGS__, OBFXX_T2, OBFXX_T1)(__VA_ARGS__)) + +/// Obfuscate value (low by default; pass ngu::obf_level::{medium,high} as second arg) +#define obfxxv(...) OBFXX_EXPAND(OBFXX_GET(__VA_ARGS__, OBFXX_V2, OBFXX_V1)(__VA_ARGS__)) + +/// Obfuscate string (low by default; pass ngu::obf_level::{medium,high} as second arg) +#define obfxxs(...) OBFXX_EXPAND(OBFXX_GET(__VA_ARGS__, OBFXX_S2, OBFXX_S1)(__VA_ARGS__)) + +/// Obfuscate objects (low by default; pass ngu::obf_level::{medium,high} as second arg) +#define obfxxo(...) OBFXX_EXPAND(OBFXX_GET(__VA_ARGS__, OBFXX_O2, OBFXX_O1)(__VA_ARGS__)) + +#if defined(__clang__) || defined(__GNUC__) +#define OBFXX_LITERAL(str, type, level) ngu::obfuscxx(str).to_string() + +/// Obfuscate string literal (low level) +template constexpr auto operator""_obf() { + constexpr CharT str[] = {Chars..., '\0'}; + return OBFXX_LITERAL(str, std::remove_cvref_t, ngu::obf_level::low); +} +/// Obfuscate string literal (medium level) +template constexpr auto operator""_obfm() { + constexpr CharT str[] = {Chars..., '\0'}; + return OBFXX_LITERAL(str, std::remove_cvref_t, ngu::obf_level::medium); +} +/// Obfuscate string literal (high level) +template constexpr auto operator""_obfh() { + constexpr CharT str[] = {Chars..., '\0'}; + return OBFXX_LITERAL(str, std::remove_cvref_t, ngu::obf_level::high); +} +#endif #endif // NGU_OBFUSCXX_H \ No newline at end of file diff --git a/tests/CMakePresets.json b/tests/CMakePresets.json index 94a22bf..a5533dc 100644 --- a/tests/CMakePresets.json +++ b/tests/CMakePresets.json @@ -1,149 +1,149 @@ -{ - "version": 6, - "cmakeMinimumRequired": { - "major": 3, - "minor": 15, - "patch": 0 - }, - "configurePresets": [ - { - "name": "base-release", - "hidden": true, - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" - } - }, - { - "name": "msvc-x64", - "inherits": "base-release", - "displayName": "MSVC x64 Release", - "binaryDir": "${sourceDir}/build/msvc-x64", - "toolset": "v143", - "cacheVariables": { - "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL", - "VCPKG_TARGET_TRIPLET": "x64-windows" - } - }, - { - "name": "msvc-x86", - "inherits": "base-release", - "displayName": "MSVC x86 Release", - "binaryDir": "${sourceDir}/build/msvc-x86", - "toolset": "v143", - "architecture": "Win32", - "cacheVariables": { - "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL", - "VCPKG_TARGET_TRIPLET": "x86-windows" - } - }, - { - "name": "msvc-arm64", - "inherits": "base-release", - "displayName": "MSVC ARM64 Release", - "binaryDir": "${sourceDir}/build/msvc-arm64", - "toolset": "v143", - "architecture": "ARM64", - "cacheVariables": { - "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL", - "VCPKG_TARGET_TRIPLET": "arm64-windows" - } - }, - { - "name": "llvm-x64", - "inherits": "base-release", - "displayName": "LLVM x64 Release", - "binaryDir": "${sourceDir}/build/llvm-x64", - "toolset": "ClangCL", - "cacheVariables": { - "VCPKG_TARGET_TRIPLET": "x64-windows" - } - }, - { - "name": "gcc-mingw-x64", - "inherits": "base-release", - "displayName": "GCC MinGW x64 Release", - "binaryDir": "${sourceDir}/build/gcc-mingw-x64", - "generator": "Ninja", - "cacheVariables": { - "CMAKE_C_COMPILER": "gcc", - "CMAKE_CXX_COMPILER": "g++", - "VCPKG_TARGET_TRIPLET": "x64-mingw-dynamic" - } - }, - { - "name": "gcc-linux-x64", - "inherits": "base-release", - "displayName": "GCC Linux x64 Release", - "binaryDir": "${sourceDir}/build/gcc-linux-x64", - "generator": "Ninja", - "cacheVariables": { - "CMAKE_C_COMPILER": "gcc", - "CMAKE_CXX_COMPILER": "g++", - "VCPKG_TARGET_TRIPLET": "x64-linux-dynamic" - } - }, - { - "name": "gcc-linux-x86", - "inherits": "base-release", - "displayName": "GCC Linux x86 Release", - "binaryDir": "${sourceDir}/build/gcc-linux-x86", - "generator": "Ninja", - "cacheVariables": { - "CMAKE_C_COMPILER": "gcc", - "CMAKE_CXX_COMPILER": "g++", - "CMAKE_C_FLAGS": "-m32 -msse -msse2", - "CMAKE_CXX_FLAGS": "-m32 -msse -msse2", - "VCPKG_TARGET_TRIPLET": "x86-linux" - } - }, - { - "name": "clang-linux-x64", - "inherits": "base-release", - "displayName": "Clang Linux x64 Release", - "binaryDir": "${sourceDir}/build/clang-linux-x64", - "generator": "Ninja", - "cacheVariables": { - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "VCPKG_TARGET_TRIPLET": "x64-linux-dynamic" - } - }, - { - "name": "gcc-linux-arm64", - "inherits": "base-release", - "displayName": "GCC Linux ARM64 Release", - "binaryDir": "${sourceDir}/build/gcc-linux-arm64", - "generator": "Ninja", - "cacheVariables": { - "CMAKE_C_COMPILER": "gcc", - "CMAKE_CXX_COMPILER": "g++", - "VCPKG_TARGET_TRIPLET": "arm64-linux" - } - }, - { - "name": "apple-clang-macos-arm64", - "inherits": "base-release", - "displayName": "Apple Clang macOS ARM64 Release", - "binaryDir": "${sourceDir}/build/apple-clang-macos-arm64", - "generator": "Ninja", - "cacheVariables": { - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", - "VCPKG_TARGET_TRIPLET": "arm64-osx" - } - } - ], - "buildPresets": [ - { "name": "msvc-x64", "configurePreset": "msvc-x64" }, - { "name": "msvc-x86", "configurePreset": "msvc-x86" }, - { "name": "msvc-arm64", "configurePreset": "msvc-arm64" }, - { "name": "llvm-x64", "configurePreset": "llvm-x64" }, - { "name": "gcc-mingw-x64", "configurePreset": "gcc-mingw-x64" }, - { "name": "gcc-linux-x64", "configurePreset": "gcc-linux-x64" }, - { "name": "gcc-linux-x86", "configurePreset": "gcc-linux-x86" }, - { "name": "clang-linux-x64", "configurePreset": "clang-linux-x64" }, - { "name": "gcc-linux-arm64", "configurePreset": "gcc-linux-arm64" }, - { "name": "apple-clang-macos-arm64", "configurePreset": "apple-clang-macos-arm64" } - ] -} +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 15, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base-release", + "hidden": true, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + }, + { + "name": "msvc-x64", + "inherits": "base-release", + "displayName": "MSVC x64 Release", + "binaryDir": "${sourceDir}/build/msvc-x64", + "toolset": "v143", + "cacheVariables": { + "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL", + "VCPKG_TARGET_TRIPLET": "x64-windows" + } + }, + { + "name": "msvc-x86", + "inherits": "base-release", + "displayName": "MSVC x86 Release", + "binaryDir": "${sourceDir}/build/msvc-x86", + "toolset": "v143", + "architecture": "Win32", + "cacheVariables": { + "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL", + "VCPKG_TARGET_TRIPLET": "x86-windows" + } + }, + { + "name": "msvc-arm64", + "inherits": "base-release", + "displayName": "MSVC ARM64 Release", + "binaryDir": "${sourceDir}/build/msvc-arm64", + "toolset": "v143", + "architecture": "ARM64", + "cacheVariables": { + "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL", + "VCPKG_TARGET_TRIPLET": "arm64-windows" + } + }, + { + "name": "llvm-x64", + "inherits": "base-release", + "displayName": "LLVM x64 Release", + "binaryDir": "${sourceDir}/build/llvm-x64", + "toolset": "ClangCL", + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x64-windows" + } + }, + { + "name": "gcc-mingw-x64", + "inherits": "base-release", + "displayName": "GCC MinGW x64 Release", + "binaryDir": "${sourceDir}/build/gcc-mingw-x64", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++", + "VCPKG_TARGET_TRIPLET": "x64-mingw-dynamic" + } + }, + { + "name": "gcc-linux-x64", + "inherits": "base-release", + "displayName": "GCC Linux x64 Release", + "binaryDir": "${sourceDir}/build/gcc-linux-x64", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++", + "VCPKG_TARGET_TRIPLET": "x64-linux-dynamic" + } + }, + { + "name": "gcc-linux-x86", + "inherits": "base-release", + "displayName": "GCC Linux x86 Release", + "binaryDir": "${sourceDir}/build/gcc-linux-x86", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_C_FLAGS": "-m32 -msse -msse2", + "CMAKE_CXX_FLAGS": "-m32 -msse -msse2", + "VCPKG_TARGET_TRIPLET": "x86-linux" + } + }, + { + "name": "clang-linux-x64", + "inherits": "base-release", + "displayName": "Clang Linux x64 Release", + "binaryDir": "${sourceDir}/build/clang-linux-x64", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++", + "VCPKG_TARGET_TRIPLET": "x64-linux-dynamic" + } + }, + { + "name": "gcc-linux-arm64", + "inherits": "base-release", + "displayName": "GCC Linux ARM64 Release", + "binaryDir": "${sourceDir}/build/gcc-linux-arm64", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++", + "VCPKG_TARGET_TRIPLET": "arm64-linux" + } + }, + { + "name": "apple-clang-macos-arm64", + "inherits": "base-release", + "displayName": "Apple Clang macOS ARM64 Release", + "binaryDir": "${sourceDir}/build/apple-clang-macos-arm64", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++", + "VCPKG_TARGET_TRIPLET": "arm64-osx" + } + } + ], + "buildPresets": [ + { "name": "msvc-x64", "configurePreset": "msvc-x64" }, + { "name": "msvc-x86", "configurePreset": "msvc-x86" }, + { "name": "msvc-arm64", "configurePreset": "msvc-arm64" }, + { "name": "llvm-x64", "configurePreset": "llvm-x64" }, + { "name": "gcc-mingw-x64", "configurePreset": "gcc-mingw-x64" }, + { "name": "gcc-linux-x64", "configurePreset": "gcc-linux-x64" }, + { "name": "gcc-linux-x86", "configurePreset": "gcc-linux-x86" }, + { "name": "clang-linux-x64", "configurePreset": "clang-linux-x64" }, + { "name": "gcc-linux-arm64", "configurePreset": "gcc-linux-arm64" }, + { "name": "apple-clang-macos-arm64", "configurePreset": "apple-clang-macos-arm64" } + ] +} diff --git a/tests/benchmark/CMakeLists.txt b/tests/benchmark/CMakeLists.txt index 00433c0..1175113 100644 --- a/tests/benchmark/CMakeLists.txt +++ b/tests/benchmark/CMakeLists.txt @@ -1,36 +1,36 @@ -cmake_minimum_required(VERSION 3.15) -project(benchmark-obfuscxx VERSION 1.0.0 LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -find_package(Threads REQUIRED) -find_package(benchmark CONFIG REQUIRED) - -add_executable(benchmark-obfuscxx - benchmark.cpp -) - -target_include_directories(benchmark-obfuscxx PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../../include -) - -target_link_libraries(benchmark-obfuscxx PRIVATE - Threads::Threads - benchmark::benchmark - benchmark::benchmark_main -) - -target_compile_options(benchmark-obfuscxx PRIVATE - $<$:/constexpr:steps10000000> - $<$,$>>:-mavx2> - $<$,$>>:-mavx2> -) - -target_compile_definitions(benchmark-obfuscxx PRIVATE - $<$:OBFUSCXX_MSVC_FORCE_AVX2> -) - -enable_testing() +cmake_minimum_required(VERSION 3.15) +project(benchmark-obfuscxx VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(Threads REQUIRED) +find_package(benchmark CONFIG REQUIRED) + +add_executable(benchmark-obfuscxx + benchmark.cpp +) + +target_include_directories(benchmark-obfuscxx PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../../include +) + +target_link_libraries(benchmark-obfuscxx PRIVATE + Threads::Threads + benchmark::benchmark + benchmark::benchmark_main +) + +target_compile_options(benchmark-obfuscxx PRIVATE + $<$:/constexpr:steps10000000> + $<$,$>>:-mavx2> + $<$,$>>:-mavx2> +) + +target_compile_definitions(benchmark-obfuscxx PRIVATE + $<$:OBFUSCXX_MSVC_FORCE_AVX2> +) + +enable_testing() add_test(NAME benchmark-obfuscxx COMMAND benchmark-obfuscxx) \ No newline at end of file diff --git a/tests/benchmark/CMakePresets.json b/tests/benchmark/CMakePresets.json index 5b62cde..3eea9db 100644 --- a/tests/benchmark/CMakePresets.json +++ b/tests/benchmark/CMakePresets.json @@ -1,6 +1,6 @@ -{ - "version": 6, - "include": [ - "../CMakePresets.json" - ] +{ + "version": 6, + "include": [ + "../CMakePresets.json" + ] } \ No newline at end of file diff --git a/tests/benchmark/benchmark.cpp b/tests/benchmark/benchmark.cpp index 102e6cf..fdabd8d 100644 --- a/tests/benchmark/benchmark.cpp +++ b/tests/benchmark/benchmark.cpp @@ -1,206 +1,206 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2025-2026 nevergiveupcpp - -// Copyright 2025-2026 nevergiveupcpp -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef NGU_OBFUSCXX_METRICS_BENCHMARK_H -#define NGU_OBFUSCXX_METRICS_BENCHMARK_H - -#include -#include - -using namespace ngu; - -static void BM_IntegerGet_Low(benchmark::State& state) { - obfuscxx int_value{42}; - for (auto _ : state) { - benchmark::DoNotOptimize(int_value.get()); - } -} -BENCHMARK(BM_IntegerGet_Low); - -static void BM_IntegerGet_Medium(benchmark::State& state) { - obfuscxx int_value{42}; - for (auto _ : state) { - benchmark::DoNotOptimize(int_value.get()); - } -} -BENCHMARK(BM_IntegerGet_Medium); - -static void BM_IntegerGet_High(benchmark::State& state) { - obfuscxx int_value{42}; - for (auto _ : state) { - benchmark::DoNotOptimize(int_value.get()); - } -} -BENCHMARK(BM_IntegerGet_High); - -static void BM_FloatGet_Low(benchmark::State& state) { - obfuscxx float_value{42.5f}; - for (auto _ : state) { - benchmark::DoNotOptimize(float_value.get()); - } -} -BENCHMARK(BM_FloatGet_Low); - -static void BM_FloatGet_Medium(benchmark::State& state) { - obfuscxx float_value{42.5f}; - for (auto _ : state) { - benchmark::DoNotOptimize(float_value.get()); - } -} -BENCHMARK(BM_FloatGet_Medium); - -static void BM_FloatGet_High(benchmark::State& state) { - obfuscxx float_value{42.5f}; - for (auto _ : state) { - benchmark::DoNotOptimize(float_value.get()); - } -} -BENCHMARK(BM_FloatGet_High); - -static void BM_StringGet_Low(benchmark::State& state) { - obfuscxx str("benchmark"); - for (auto _ : state) { - benchmark::DoNotOptimize(str.to_string().c_str()); - } -} -BENCHMARK(BM_StringGet_Low); - -static void BM_StringGet_Medium(benchmark::State& state) { - obfuscxx str("benchmark"); - for (auto _ : state) { - benchmark::DoNotOptimize(str.to_string().c_str()); - } -} -BENCHMARK(BM_StringGet_Medium); - -static void BM_StringGet_High(benchmark::State& state) { - obfuscxx str("benchmark"); - for (auto _ : state) { - benchmark::DoNotOptimize(str.to_string().c_str()); - } -} -BENCHMARK(BM_StringGet_High); - -static void BM_WStringGet_Low(benchmark::State& state) { - obfuscxx str(L"benchmark"); - for (auto _ : state) { - benchmark::DoNotOptimize(str.to_string().c_str()); - } -} -BENCHMARK(BM_WStringGet_Low); - -static void BM_WStringGet_Medium(benchmark::State& state) { - obfuscxx str(L"benchmark"); - for (auto _ : state) { - benchmark::DoNotOptimize(str.to_string().c_str()); - } -} -BENCHMARK(BM_WStringGet_Medium); - -static void BM_WStringGet_High(benchmark::State& state) { - obfuscxx str(L"benchmark"); - for (auto _ : state) { - benchmark::DoNotOptimize(str.to_string().c_str()); - } -} -BENCHMARK(BM_WStringGet_High); - -static void BM_ArrayIteration_Low(benchmark::State& state) { - obfuscxx array{}; - for (auto _ : state) { - int sum = 0; - for (auto val : array) { - sum += val; - } - benchmark::DoNotOptimize(sum); - } -} -BENCHMARK(BM_ArrayIteration_Low); - -static void BM_ArrayIteration_Medium(benchmark::State& state) { - obfuscxx array{}; - for (auto _ : state) { - int sum = 0; - for (auto val : array) { - sum += val; - } - benchmark::DoNotOptimize(sum); - } -} -BENCHMARK(BM_ArrayIteration_Medium); - -static void BM_ArrayIteration_High(benchmark::State& state) { - obfuscxx array{}; - for (auto _ : state) { - int sum = 0; - for (auto val : array) { - sum += val; - } - benchmark::DoNotOptimize(sum); - } -} -BENCHMARK(BM_ArrayIteration_High); - -static void BM_ArrayCopyTo_Low(benchmark::State& state) { - obfuscxx array{}; - for (auto _ : state) { - benchmark::DoNotOptimize(array.to_array()); - } -} -BENCHMARK(BM_ArrayCopyTo_Low); - -static void BM_ArrayCopyTo_Medium(benchmark::State& state) { - obfuscxx array{}; - for (auto _ : state) { - benchmark::DoNotOptimize(array.to_array()); - } -} -BENCHMARK(BM_ArrayCopyTo_Medium); - -static void BM_ArrayCopyTo_High(benchmark::State& state) { - obfuscxx array{}; - for (auto _ : state) { - benchmark::DoNotOptimize(array.to_array()); - } -} -BENCHMARK(BM_ArrayCopyTo_High); - -static void BM_ArrayGet_Low(benchmark::State& state) { - obfuscxx array{}; - for (auto _ : state) { - benchmark::DoNotOptimize(array.get(50)); - } -} -BENCHMARK(BM_ArrayGet_Low); - -static void BM_ArrayGet_Medium(benchmark::State& state) { - obfuscxx array{}; - for (auto _ : state) { - benchmark::DoNotOptimize(array.get(50)); - } -} -BENCHMARK(BM_ArrayGet_Medium); - -static void BM_ArrayGet_High(benchmark::State& state) { - obfuscxx array{}; - for (auto _ : state) { - benchmark::DoNotOptimize(array.get(50)); - } -} -BENCHMARK(BM_ArrayGet_High); - +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2025-2026 nevergiveupcpp + +// Copyright 2025-2026 nevergiveupcpp +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NGU_OBFUSCXX_METRICS_BENCHMARK_H +#define NGU_OBFUSCXX_METRICS_BENCHMARK_H + +#include +#include + +using namespace ngu; + +static void BM_IntegerGet_Low(benchmark::State& state) { + obfxx(int) const value{50}; + for (auto _ : state) { + benchmark::DoNotOptimize(value.get()); + } +} +BENCHMARK(BM_IntegerGet_Low); + +static void BM_IntegerGet_Medium(benchmark::State& state) { + obfxx(int, obf_level::medium) const value{50}; + for (auto _ : state) { + benchmark::DoNotOptimize(value.get()); + } +} +BENCHMARK(BM_IntegerGet_Medium); + +static void BM_IntegerGet_High(benchmark::State& state) { + obfxx(int, obf_level::high) const value{50}; + for (auto _ : state) { + benchmark::DoNotOptimize(value.get()); + } +} +BENCHMARK(BM_IntegerGet_High); + +static void BM_FloatGet_Low(benchmark::State& state) { + obfxx(float) const value{42.5f}; + for (auto _ : state) { + benchmark::DoNotOptimize(value.get()); + } +} +BENCHMARK(BM_FloatGet_Low); + +static void BM_FloatGet_Medium(benchmark::State& state) { + obfxx(float, obf_level::medium) const value{42.5f}; + for (auto _ : state) { + benchmark::DoNotOptimize(value.get()); + } +} +BENCHMARK(BM_FloatGet_Medium); + +static void BM_FloatGet_High(benchmark::State& state) { + obfxx(float, obf_level::high) const value{42.5f}; + for (auto _ : state) { + benchmark::DoNotOptimize(value.get()); + } +} +BENCHMARK(BM_FloatGet_High); + +static void BM_StringGet_Low(benchmark::State& state) { + auto constexpr str = obfxxo("benchmark"); + for (auto _ : state) { + benchmark::DoNotOptimize(str.to_string().c_str()); + } +} +BENCHMARK(BM_StringGet_Low); + +static void BM_StringGet_Medium(benchmark::State& state) { + auto constexpr str = obfxxo("benchmark", obf_level::medium); + for (auto _ : state) { + benchmark::DoNotOptimize(str.to_string().c_str()); + } +} +BENCHMARK(BM_StringGet_Medium); + +static void BM_StringGet_High(benchmark::State& state) { + auto constexpr str = obfxxo("benchmark", obf_level::high); + for (auto _ : state) { + benchmark::DoNotOptimize(str.to_string().c_str()); + } +} +BENCHMARK(BM_StringGet_High); + +static void BM_WStringGet_Low(benchmark::State& state) { + auto constexpr str = obfxxo(L"benchmark"); + for (auto _ : state) { + benchmark::DoNotOptimize(str.to_string().c_str()); + } +} +BENCHMARK(BM_WStringGet_Low); + +static void BM_WStringGet_Medium(benchmark::State& state) { + auto constexpr str = obfxxo(L"benchmark", obf_level::medium); + for (auto _ : state) { + benchmark::DoNotOptimize(str.to_string().c_str()); + } +} +BENCHMARK(BM_WStringGet_Medium); + +static void BM_WStringGet_High(benchmark::State& state) { + auto constexpr str = obfxxo(L"benchmark", obf_level::high); + for (auto _ : state) { + benchmark::DoNotOptimize(str.to_string().c_str()); + } +} +BENCHMARK(BM_WStringGet_High); + +static void BM_ArrayIteration_Low(benchmark::State& state) { + obfxx(int[100]) const array{}; + for (auto _ : state) { + int sum = 0; + for (auto const val : array) { + sum += val; + } + benchmark::DoNotOptimize(sum); + } +} +BENCHMARK(BM_ArrayIteration_Low); + +static void BM_ArrayIteration_Medium(benchmark::State& state) { + obfxx(int[100], obf_level::medium) const array{}; + for (auto _ : state) { + int sum = 0; + for (auto const val : array) { + sum += val; + } + benchmark::DoNotOptimize(sum); + } +} +BENCHMARK(BM_ArrayIteration_Medium); + +static void BM_ArrayIteration_High(benchmark::State& state) { + obfxx(int[100], obf_level::high) const array{}; + for (auto _ : state) { + int sum = 0; + for (auto const val : array) { + sum += val; + } + benchmark::DoNotOptimize(sum); + } +} +BENCHMARK(BM_ArrayIteration_High); + +static void BM_ArrayCopyTo_Low(benchmark::State& state) { + obfxx(int[100]) const array{}; + for (auto _ : state) { + benchmark::DoNotOptimize(array.to_array()); + } +} +BENCHMARK(BM_ArrayCopyTo_Low); + +static void BM_ArrayCopyTo_Medium(benchmark::State& state) { + obfxx(int[100], obf_level::medium) const array{}; + for (auto _ : state) { + benchmark::DoNotOptimize(array.to_array()); + } +} +BENCHMARK(BM_ArrayCopyTo_Medium); + +static void BM_ArrayCopyTo_High(benchmark::State& state) { + obfxx(int[100], obf_level::high) const array{}; + for (auto _ : state) { + benchmark::DoNotOptimize(array.to_array()); + } +} +BENCHMARK(BM_ArrayCopyTo_High); + +static void BM_ArrayGet_Low(benchmark::State& state) { + obfxx(int[100]) const array{}; + for (auto _ : state) { + benchmark::DoNotOptimize(array.get(50)); + } +} +BENCHMARK(BM_ArrayGet_Low); + +static void BM_ArrayGet_Medium(benchmark::State& state) { + obfxx(int[100], obf_level::medium) const array{}; + for (auto _ : state) { + benchmark::DoNotOptimize(array.get(50)); + } +} +BENCHMARK(BM_ArrayGet_Medium); + +static void BM_ArrayGet_High(benchmark::State& state) { + obfxx(int[100], obf_level::high) const array{}; + for (auto _ : state) { + benchmark::DoNotOptimize(array.get(50)); + } +} +BENCHMARK(BM_ArrayGet_High); + #endif // NGU_OBFUSCXX_METRICS_BENCHMARK_H \ No newline at end of file diff --git a/tests/unittest/CMakeLists.txt b/tests/unittest/CMakeLists.txt index e0f04dd..0425fc1 100644 --- a/tests/unittest/CMakeLists.txt +++ b/tests/unittest/CMakeLists.txt @@ -1,36 +1,36 @@ -cmake_minimum_required(VERSION 3.15) -project(unittest-obfuscxx VERSION 1.0.0 LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -find_package(Threads REQUIRED) -find_package(GTest CONFIG REQUIRED) - -add_executable(unittest-obfuscxx - unittest.cpp -) - -target_include_directories(unittest-obfuscxx PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../../include -) - -target_link_libraries(unittest-obfuscxx PRIVATE - Threads::Threads - GTest::gtest - GTest::gtest_main -) - -target_compile_options(unittest-obfuscxx PRIVATE - $<$:/constexpr:steps10000000> - $<$,$>>:-mavx2> - $<$,$>>:-mavx2> -) - -target_compile_definitions(unittest-obfuscxx PRIVATE - $<$:OBFUSCXX_MSVC_FORCE_AVX2> -) - -enable_testing() +cmake_minimum_required(VERSION 3.15) +project(unittest-obfuscxx VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(Threads REQUIRED) +find_package(GTest CONFIG REQUIRED) + +add_executable(unittest-obfuscxx + unittest.cpp +) + +target_include_directories(unittest-obfuscxx PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../../include +) + +target_link_libraries(unittest-obfuscxx PRIVATE + Threads::Threads + GTest::gtest + GTest::gtest_main +) + +target_compile_options(unittest-obfuscxx PRIVATE + $<$:/constexpr:steps10000000> + $<$,$>>:-mavx2> + $<$,$>>:-mavx2> +) + +target_compile_definitions(unittest-obfuscxx PRIVATE + $<$:OBFUSCXX_MSVC_FORCE_AVX2> +) + +enable_testing() add_test(NAME unittest-obfuscxx COMMAND unittest-obfuscxx) \ No newline at end of file diff --git a/tests/unittest/CMakePresets.json b/tests/unittest/CMakePresets.json index 5b62cde..3eea9db 100644 --- a/tests/unittest/CMakePresets.json +++ b/tests/unittest/CMakePresets.json @@ -1,6 +1,6 @@ -{ - "version": 6, - "include": [ - "../CMakePresets.json" - ] +{ + "version": 6, + "include": [ + "../CMakePresets.json" + ] } \ No newline at end of file diff --git a/tests/unittest/unittest.cpp b/tests/unittest/unittest.cpp index 73aee60..3245dec 100644 --- a/tests/unittest/unittest.cpp +++ b/tests/unittest/unittest.cpp @@ -26,7 +26,7 @@ using namespace ngu; TEST(ObfuscxxTest, IntegerValue) { - obfuscxx value{100}; + obfxx(int) value{100}; EXPECT_EQ(value.get(), 100); value = 50; @@ -34,39 +34,41 @@ TEST(ObfuscxxTest, IntegerValue) { } TEST(ObfuscxxTest, FloatValue) { - obfuscxx value{1.5f}; + obfxx(float) const value{1.5f}; EXPECT_FLOAT_EQ(value.get(), 1.5f); } TEST(ObfuscxxTest, ArrayIteration) { - obfuscxx array{1, 2, 3, 4}; - int expected[] = {1, 2, 3, 4}; - int i = 0; - for (auto val : array) { + obfxx(int[4]) const array{1, 2, 3, 4}; + constexpr int expected[] = {1, 2, 3, 4}; + + for (int i{}; auto val : array) { EXPECT_EQ(val, expected[i++]); } } TEST(ObfuscxxTest, ToString) { - obfuscxx str("small test string"); - EXPECT_STREQ(str.to_string(), "small test string"); + constexpr auto narrow_str = obfxxo("frost on the morning grass"); + EXPECT_STREQ(narrow_str.to_string(), "frost on the morning grass"); - obfuscxx wstr(L"small test string"); - EXPECT_STREQ(wstr.to_string(), L"small test string"); + constexpr auto wide_str = obfxxo(L"frost on the morning grass"); + EXPECT_STREQ(wide_str.to_string(), L"frost on the morning grass"); } TEST(ObfuscxxTest, PointerValue) { - obfuscxx pointer{}; + obfxx(int*) pointer{}; pointer = new int{101}; + EXPECT_NE(pointer.get(), nullptr); EXPECT_EQ(*pointer.get(), 101); + delete pointer.get(); } TEST(ObfuscxxTest, ComparisonOperators) { - obfuscxx a{100}; - obfuscxx b{100}; - obfuscxx c{50}; + obfxx(int) a{100}; + obfxx(int) b{100}; + obfxx(int) c{50}; EXPECT_TRUE(a == b); EXPECT_FALSE(a == c); @@ -79,8 +81,8 @@ TEST(ObfuscxxTest, ComparisonOperators) { } TEST(ObfuscxxTest, ArithmeticOperators) { - obfuscxx a{10}; - obfuscxx b{5}; + obfxx(int) a{10}; + obfxx(int) b{5}; EXPECT_EQ(a + b, 15); EXPECT_EQ(a - b, 5); @@ -95,20 +97,20 @@ TEST(ObfuscxxTest, ArithmeticOperators) { } TEST(ObfuscxxTest, ObfuscationLevels) { - obfuscxx low{42}; - obfuscxx medium{42}; - obfuscxx high{42}; + obfxx(int, obf_level::low) const low{50}; + obfxx(int, obf_level::medium) const medium{50}; + obfxx(int, obf_level::high) const high{50}; - EXPECT_EQ(low.get(), 42); - EXPECT_EQ(medium.get(), 42); - EXPECT_EQ(high.get(), 42); + EXPECT_EQ(low.get(), 50); + EXPECT_EQ(medium.get(), 50); + EXPECT_EQ(high.get(), 50); } -TEST(ObfuscxxTest, EdgeCases) { - obfuscxx max_int{INT_MAX}; - obfuscxx min_int{INT_MIN}; - obfuscxx zero{0}; - obfuscxx negative{-12345}; +TEST(ObfuscxxTest, IntegerEdgeCases) { + obfxx(int) const max_int{INT_MAX}; + obfxx(int) const min_int{INT_MIN}; + obfxx(int) const zero{0}; + obfxx(int) const negative{-12345}; EXPECT_EQ(max_int.get(), INT_MAX); EXPECT_EQ(min_int.get(), INT_MIN); @@ -117,10 +119,10 @@ TEST(ObfuscxxTest, EdgeCases) { } TEST(ObfuscxxTest, FloatEdgeCases) { - obfuscxx zero{0.0f}; - obfuscxx negative{-3.14f}; - obfuscxx small{0.0001f}; - obfuscxx large{123456.789f}; + obfxx(float) const zero{0.0f}; + obfxx(float) const negative{-3.14f}; + obfxx(float) const small{0.0001f}; + obfxx(float) const large{123456.789f}; EXPECT_FLOAT_EQ(zero.get(), 0.0f); EXPECT_FLOAT_EQ(negative.get(), -3.14f); @@ -129,20 +131,18 @@ TEST(ObfuscxxTest, FloatEdgeCases) { } TEST(ObfuscxxTest, ArrayOperators) { - obfuscxx array{10, 20, 30, 40, 50}; + obfxx(int[5]) const array{10, 20, 30, 40, 50}; EXPECT_EQ(array[0], 10); EXPECT_EQ(array[2], 30); EXPECT_EQ(array[4], 50); - EXPECT_EQ(array.get(1), 20); EXPECT_EQ(array.get(3), 40); - EXPECT_EQ(array.size(), 5); } TEST(ObfuscxxTest, ArraySet) { - obfuscxx array{1, 2, 3}; + obfxx(int[3]) array{1, 2, 3}; array.set(100, 0); array.set(200, 1); @@ -154,22 +154,22 @@ TEST(ObfuscxxTest, ArraySet) { } TEST(ObfuscxxTest, ArrayCopyTo) { - obfuscxx array{1, 2, 3, 4, 5}; - int output[5] = {0}; + obfxx(int[5]) const array{1, 2, 3, 4, 5}; + int buffer[5]{}; - array.copy_to(output, 5); + array.copy_to(buffer, array.size()); for (int i = 0; i < 5; ++i) { - EXPECT_EQ(output[i], i + 1); + EXPECT_EQ(buffer[i], i + 1); } } TEST(ObfuscxxTest, ToArray) { - const obfuscxx array{0, 1, 2, 3}; - auto const deobf_array = array.to_array(); + obfxx(int[4]) const array{0, 1, 2, 3}; + auto const plain_array = array.to_array(); std::array buffer{}; - memcpy(buffer.data(), deobf_array.get(), deobf_array.size_bytes()); + memcpy(buffer.data(), plain_array.get(), plain_array.size_bytes()); EXPECT_EQ(buffer[0], 0); EXPECT_EQ(buffer[1], 1); @@ -178,7 +178,7 @@ TEST(ObfuscxxTest, ToArray) { } TEST(ObfuscxxTest, ArrayAssignment) { - obfuscxx array{1, 2, 3}; + obfxx(int[3]) array{1, 2, 3}; array = {10, 20, 30}; @@ -188,91 +188,90 @@ TEST(ObfuscxxTest, ArrayAssignment) { } TEST(ObfuscxxTest, DataIsEncrypted) { - obfuscxx value{42}; + obfxx(int) const value{50}; - const uint64_t* raw_data = reinterpret_cast(&value); + auto const raw_data = reinterpret_cast(&value); volatile uint64_t encrypted = *raw_data; - EXPECT_NE(encrypted, 42); - - EXPECT_EQ(value.get(), 42); + EXPECT_NE(encrypted, 50); + EXPECT_EQ(value.get(), 50); } TEST(ObfuscxxTest, PointerOperators) { - obfuscxx ptr{}; - ptr = new int{999}; + obfxx(int*) pointer{}; + pointer = new int{999}; - EXPECT_NE(ptr.get(), nullptr); - - EXPECT_EQ(*ptr.get(), 999); + EXPECT_NE(pointer.get(), nullptr); + EXPECT_EQ(*pointer.get(), 999); - *ptr.get() = 111; - EXPECT_EQ(*ptr.get(), 111); + *pointer.get() = 111; + EXPECT_EQ(*pointer.get(), 111); - delete ptr.get(); + delete pointer.get(); } TEST(ObfuscxxTest, EmptyString) { - obfuscxx str(""); + constexpr auto str = obfxxo(""); EXPECT_STREQ(str.to_string(), ""); } TEST(ObfuscxxTest, LongString) { - obfuscxx str("this is a very long test string for obfuscation"); - EXPECT_STREQ(str.to_string(), "this is a very long test string for obfuscation"); -} - -TEST(ObfuscxxTest, ConstCorrectness) { - const obfuscxx const_value{42}; - EXPECT_EQ(const_value.get(), 42); - EXPECT_EQ(const_value(), 42); + constexpr auto str = obfxxo( + "The river ran calmly past the old wooden bridge, where a few fishermen waited in the morning " + "light, watching the wind move through the tall grass on the far bank while a heron stood " + "still among the reeds." + ); + + EXPECT_STREQ( + str.to_string(), + "The river ran calmly past the old wooden bridge, where a few fishermen waited in the morning " + "light, watching the wind move through the tall grass on the far bank while a heron stood " + "still among the reeds." + ); } TEST(ObfuscxxTest, IteratorOperations) { - obfuscxx array{1, 2, 3, 4, 5}; + obfxx(int[5]) const array{1, 2, 3, 4, 5}; auto it = array.begin(); EXPECT_EQ(*it, 1); - ++it; EXPECT_EQ(*it, 2); EXPECT_NE(it, array.end()); - int count = 0; - for (auto it = array.begin(); it != array.end(); ++it) { + int count{}; + for (auto iter = array.begin(); iter != array.end(); ++iter) { count++; } EXPECT_EQ(count, 5); } TEST(ObfuscxxTest, DifferentTypes) { - obfuscxx u64{0xFFFFFFFFFFFFFFFF}; - obfuscxx i8{-127}; - obfuscxx dbl{3.141592653589793}; + obfxx(uint64_t) const uint64_value{0xFFFFFFFFFFFFFFFF}; + obfxx(int8_t) const int8_value{-127}; + obfxx(double) const double_value{3.141592653589793}; - EXPECT_EQ(u64.get(), 0xFFFFFFFFFFFFFFFF); - EXPECT_EQ(i8.get(), -127); - EXPECT_DOUBLE_EQ(dbl.get(), 3.141592653589793); + EXPECT_EQ(uint64_value.get(), 0xFFFFFFFFFFFFFFFF); + EXPECT_EQ(int8_value.get(), -127); + EXPECT_DOUBLE_EQ(double_value.get(), 3.141592653589793); } TEST(ObfuscxxTest, MultipleAssignments) { - obfuscxx value{10}; + obfxx(int) value{10}; value = 20; EXPECT_EQ(value.get(), 20); - value = 30; EXPECT_EQ(value.get(), 30); - value = 40; EXPECT_EQ(value.get(), 40); } TEST(ObfuscxxTest, ArrayEquality) { - obfuscxx a{1, 2, 3}; - obfuscxx b{1, 2, 3}; - obfuscxx c{1, 2, 4}; + obfxx(int[3]) const a{1, 2, 3}; + obfxx(int[3]) const b{1, 2, 3}; + obfxx(int[3]) const c{1, 2, 4}; EXPECT_TRUE(a == b); EXPECT_FALSE(a == c); @@ -280,7 +279,7 @@ TEST(ObfuscxxTest, ArrayEquality) { } TEST(ObfuscxxTest, ImplicitConversion) { - obfuscxx value{42}; + obfxx(int) const value{42}; int x = value; EXPECT_EQ(x, 42); @@ -289,18 +288,33 @@ TEST(ObfuscxxTest, ImplicitConversion) { EXPECT_EQ(result, 52); } -TEST(ObfuscxxTest, RValueDefines) { - EXPECT_STREQ(obfuss("small test string"), "small test string"); - EXPECT_STREQ(obfuss(L"small test string"), L"small test string"); - EXPECT_EQ(obfusv(52), 52); - EXPECT_EQ(obfusv(3.14f), 3.14f); - EXPECT_EQ(obfusv(-3.14f), -3.14f); +TEST(ObfuscxxTest, InlineMacroExpressions) { + EXPECT_STREQ(obfxxs("river by the old bridge"), "river by the old bridge"); + EXPECT_STREQ(obfxxs(L"river by the old bridge"), L"river by the old bridge"); + EXPECT_STREQ(obfxxs("wind across the meadow", obf_level::medium), "wind across the meadow"); + EXPECT_STREQ(obfxxs(L"wind across the meadow", obf_level::medium), L"wind across the meadow"); + EXPECT_STREQ(obfxxs("birds on a wooden fence", obf_level::high), "birds on a wooden fence"); + EXPECT_STREQ(obfxxs(L"birds on a wooden fence", obf_level::high), L"birds on a wooden fence"); + + EXPECT_EQ(obfxxv(52), 52); + EXPECT_EQ(obfxxv(52, obf_level::medium), 52); + EXPECT_EQ(obfxxv(52, obf_level::high), 52); + + EXPECT_FLOAT_EQ(obfxxv(3.14f), 3.14f); + EXPECT_FLOAT_EQ(obfxxv(3.14f, obf_level::medium), 3.14f); + EXPECT_FLOAT_EQ(obfxxv(-3.14f, obf_level::high), -3.14f); } #if defined(__clang__) || defined(__GNUC__) TEST(ObfuscxxTest, UserDefinedLiterals) { - EXPECT_STREQ("small test string"_obf, "small test string"); - EXPECT_STREQ(L"small test string"_obf, L"small test string"); + EXPECT_STREQ("river by the old bridge"_obf, "river by the old bridge"); + EXPECT_STREQ(L"river by the old bridge"_obf, L"river by the old bridge"); + + EXPECT_STREQ("wind across the meadow"_obfm, "wind across the meadow"); + EXPECT_STREQ(L"wind across the meadow"_obfm, L"wind across the meadow"); + + EXPECT_STREQ("birds on a wooden fence"_obfh, "birds on a wooden fence"); + EXPECT_STREQ(L"birds on a wooden fence"_obfh, L"birds on a wooden fence"); } #endif From 23d84ca9f9e5fb58d5388c30b6676d422db24bff Mon Sep 17 00:00:00 2001 From: nevergiveupcpp Date: Fri, 1 May 2026 11:28:38 +0700 Subject: [PATCH 3/6] ci: update clang-format version --- .github/workflows/ci.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 071e60f..663ce6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,10 +113,17 @@ jobs: if: github.event_name == 'pull_request' steps: - uses: actions/checkout@v4 + - name: Install Clang-Format 22 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 22 + sudo apt-get update + sudo apt-get install -y clang-format-22 - name: Check formatting run: | - find include tests -name "*.h" -o -name "*.cpp" | \ - xargs clang-format --dry-run --Werror + find include \( -name "*.h" -o -name "*.cpp" \) | \ + xargs clang-format-22 --dry-run --Werror clang-tidy: name: Clang Tidy @@ -126,8 +133,9 @@ jobs: - uses: actions/checkout@v4 - name: Install Clang-Tidy 19 run: | - wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc - echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main" | sudo tee /etc/apt/sources.list.d/llvm.list + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 19 sudo apt-get update sudo apt-get install -y clang-tidy-19 - name: Check tidy From 19e2c12a14ff6df4d4a33701827691870b533775 Mon Sep 17 00:00:00 2001 From: nevergiveupcpp Date: Fri, 1 May 2026 11:38:12 +0700 Subject: [PATCH 4/6] ci: update clang-tidy config --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 663ce6a..8377ef9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,7 +141,7 @@ jobs: - name: Check tidy run: | find include -name "*.h" -o -name "*.cpp" | \ - xargs -I{} clang-tidy-19 {} -- -x c++ -std=c++20 -I include -include cstdint -include cstddef + xargs -I{} clang-tidy-19 {} -- -x c++ -std=c++20 all-checks: name: All Checks From f96a06a56a49e42b5d3983449a003e58ea2457ed Mon Sep 17 00:00:00 2001 From: nevergiveupcpp Date: Fri, 1 May 2026 11:46:19 +0700 Subject: [PATCH 5/6] ci: update clang-tidy/format config --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8377ef9..d542234 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,7 +122,7 @@ jobs: sudo apt-get install -y clang-format-22 - name: Check formatting run: | - find include \( -name "*.h" -o -name "*.cpp" \) | \ + find include tests \( -name "*.h" -o -name "*.cpp" \) | \ xargs clang-format-22 --dry-run --Werror clang-tidy: @@ -140,8 +140,8 @@ jobs: sudo apt-get install -y clang-tidy-19 - name: Check tidy run: | - find include -name "*.h" -o -name "*.cpp" | \ - xargs -I{} clang-tidy-19 {} -- -x c++ -std=c++20 + find include tests \( -name "*.h" -o -name "*.cpp" \) | \ + xargs -I{} clang-tidy-19 {} --header-filter="./(include|tests)/.*" -- -x c++ -std=c++20 -I include all-checks: name: All Checks From ea82c56a4f22d1cb31952a4bdba106b1dceb7574 Mon Sep 17 00:00:00 2001 From: nevergiveupcpp Date: Fri, 1 May 2026 11:54:10 +0700 Subject: [PATCH 6/6] ci: update clang-tidy/format find scope --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d542234..2b690b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,7 +122,7 @@ jobs: sudo apt-get install -y clang-format-22 - name: Check formatting run: | - find include tests \( -name "*.h" -o -name "*.cpp" \) | \ + find include \( -name "*.h" -o -name "*.cpp" \) | \ xargs clang-format-22 --dry-run --Werror clang-tidy: @@ -140,8 +140,8 @@ jobs: sudo apt-get install -y clang-tidy-19 - name: Check tidy run: | - find include tests \( -name "*.h" -o -name "*.cpp" \) | \ - xargs -I{} clang-tidy-19 {} --header-filter="./(include|tests)/.*" -- -x c++ -std=c++20 -I include + find include \( -name "*.h" -o -name "*.cpp" \) | \ + xargs -I{} clang-tidy-19 {} --header-filter="include/.*" -- -x c++ -std=c++20 -I include all-checks: name: All Checks