{"slug": "c-enables-us-to-provide-seamless-data-obfuscation", "title": "C++ enables us to provide seamless data obfuscation", "summary": "PROTEKKT has released a C++ SDK called POL (PROTEKKT Obfuscated Library) that provides seamless data obfuscation to harden applications against reverse engineering, supporting C++17 and automatically upgrading for C++20/23/26. The library offers low-level C APIs for runtime value obfuscation, advanced integer obfuscation with operations on encrypted data, runtime data obfuscation, and static data obfuscation using marker bytes, with higher-level C++ types encapsulating these functions.", "body_md": "## Introduction\n\nIn our previous article ([Hardening Against AI Reverse Engineering: Making Computers Struggle](hardening-against-ai-reverse-engineering.html)), we focused on what most code virtualization products disregarded over the years: Why data obfuscation is just as important as code obfuscation to us and why we want to provide a wider range of possibilities than what code obfuscators usually offer (basic string encryption utilities).\n\nNow we'd like to take you through our SDK, its features and usage examples. We are still finalizing the product so at the time of release certain functionalities may be named or performed differently, but what we are presenting here is pretty much what it will look like.\n\nIn this post we'll go through our C++ implementation named POL (PROTEKKT Obfuscated Library), but we plan on expanding our SDK to other popular programming languages as well.\n\nWe are aware that many projects take longer to upgrade their toolset and C++ standard used, so the library is written to support C++17. If C++20 or C++23 are used, the SDK will automatically switch to a more efficient implementation or enable functionality that is not possible to implement without newer standards. When it becomes widely available, C++26 support in major compilers will allow us to implement even easier interfaces and new features.\n\n## Low-Level API\n\nAt its core, everything is based on a low-level C API. The obfuscation process automatically replaces all instances and references to these functions with randomly generated encryption/decryption stubs implementing the logic behind them. This allows us to implement higher-level concepts in most programming languages that allow interaction with C libraries.\n\nThe API can be split into a few groups:\n\n### 1. Runtime Value Obfuscation\n\nBasic integer obfuscation for runtime values. Suitable for places where performance matters.\n\n```\n//! runtime value obfuscation\nuint8_t  protekkt_rvo_set8(uint8_t);\nuint16_t protekkt_rvo_set16(uint16_t);\nuint32_t protekkt_rvo_set32(uint32_t);\nuint64_t protekkt_rvo_set64(uint64_t);\nuint8_t  protekkt_rvo_get8(uint8_t);\nuint16_t protekkt_rvo_get16(uint16_t);\nuint32_t protekkt_rvo_get32(uint32_t);\nuint64_t protekkt_rvo_get64(uint64_t);\n```\n\n### 2. Advanced Integer Obfuscation\n\nComplex integer obfuscation, supports standard operations without ever decoding complete values in plaintext form.\n\n```\n//! number obfuscation\nvoid protekkt_num_init_u8(void* base);\nvoid protekkt_num_init_u16(void* base);\n// ...\nvoid protekkt_num_add(void* dst, void const* src);\nvoid protekkt_num_sub(void* dst, void const* src);\nvoid protekkt_num_mul(void* dst, void const* src);\nvoid protekkt_num_xor(void* dst, void const* src);\nvoid protekkt_num_and(void* dst, void const* src);\n// ...\n```\n\n### 3. Runtime Data Obfuscation\n\nUsed for obfuscating runtime data.\n\n```\n//! runtime data obfuscation\nvoid protekkt_rdo_init(void* base);\nvoid protekkt_rdo_decode(void const* base, size_t offset, void* dst, size_t size);\nvoid protekkt_rdo_encode(void* base, size_t offset, void const* src, size_t size);\n```\n\n### 4. Static Data Obfuscation\n\nUsed for encrypting compile-time constants and any compile-time initialized data. Data is inserted between marker bytes to allow the obfuscator to locate data that needs to be obfuscated/encrypted.\n\n```\n//! static data obfuscation\nvoid protekkt_sdo_decode(void const* base, size_t offset, void* dst, size_t size);\n\n//! static data obfuscation markers\n#define PTKT_SDO_BEG_MARKER { 'P', 'T', 'K', 'T', 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 'B', 'E', 'G', 0x00 }\n#define PTKT_SDO_END_MARKER { 'P', 'T', 'K', 'T', 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 'E', 'N', 'D', 0x00 }\n\n//! runtime data obfuscation prefix size\n#define PTKT_RDO_MARKER_SIZE 16\n```\n\n## Core C++ Types\n\nTypes encapsulating the low-level C API, providing an intuitive way for initialization, reads and modification of obfuscated data. They are intended to be used directly, but are also the building blocks of more complex data structures.\n\nConstructors, destructors and methods provided by these types are \"force inline\" to ensure unique obfuscations for different variables/objects instead of having a single point in the compiled binary that allows interception of all obfuscated values.\n\n### 1. Runtime Value Obfuscation\n\nA wrapper for integral/enum/boolean types and pointers. Provides overloaded operators to act as a drop-in replacement for any of them (assignment, retrieval, comparisons, arithmetic and bitwise operations, etc). RVO provides data encryption/decryption where performance matters more.\n\n```\n// protekkt/pol/rvo.hpp\n\ntemplate <typename T>\nclass rvo { /* implementation */ };\n```\n\nInstead of:\n\n```\nstd::uint32_t value;\nstd::uint32_t bits;\n\nstd::uint32_t res = value << bits;\n```\n\nAn obfuscated variant would be:\n\n```\npol::rvo<std::uint32_t> value;\npol::rvo<std::uint32_t> bits;\n\npol::rvo<std::uint32_t> res = value << bits;\n```\n\nOr with pointers:\n\n``` php\npol::rvo<my_class*> x;\n\nif (x)\n    x->method();\n```\n\n### 2. Advanced Integer Obfuscation\n\nSimilar to RVO (a simpler and faster obfuscation for integral types), NUM is a significantly more obfuscated variant, a full implementation of integers. It supports all arithmetic and bitwise operations with both plaintext integral values and other instances of NUM, without ever decrypting the hidden values. The exception is the `get()`\n\nmethod which is used when the developer explicitly wants to decrypt the value.\n\n```\n// protekkt/pol/num.hpp\n\ntemplate <typename T>\nclass num { /* implementation */ };\n```\n\nUsage example:\n\n```\n// initialize from runtime values\npol::num<std::uint64_t> score(1000);\npol::num<std::uint64_t> bonus(250);\n\n// perform addition without exposing x, y or the result\nx += y;\n\n// perform multiplication without exposing score, multiplier or the result\npol::num<std::uint64_t> multiplier(2);\npol::num<std::uint64_t> result = score * multiplier;\n\n// operator++ automatically encrypts a literal '1' into a temporary pol::num\n// and performs obfuscated addition\n++result;\n\n// '4' is automatically encrypted into a temporary pol::num\n// and obfuscated bitwise shift is performed\nresult <<= 4;\n\n// pol::num decrypts stored values only if the user explicitly calls get()\n// all operations are performed using obfuscated implementations on internal representation\nauto plaintext = result.get();\n```\n\n### 3. Runtime Data Obfuscation\n\nA class providing encryption/obfuscation of runtime data. The location of encrypted data is configurable through a template parameter depending on whether you want data to be part of the object itself or to be allocated on the heap.\n\n```\n// protekkt/pol/rdo.hpp\n\n// storage policy tags\nstruct inline_t {};\nstruct dynamic_t {};\n\n// reference types allowing access to specific members within\n// complex obfuscated types (e.g. structs, arrays or arrays of structs)\ntemplate <typename U>\nclass const_rdo_ref { /* implementation */ };\ntemplate <typename U>\nclass rdo_ref { /* implementation */ };\n\n// obfuscated wrapper (allowing inline and heap-allocated storage)\n// supports single elements, struct instances and arrays\n// configurable, internal data can be stored as RVO or NUM\ntemplate <typename T, typename Storage = inline_t>\nclass rdo { /* implementation */ };\n```\n\nUsage example:\n\n```\nstruct InnerConfig\n{\n    int   id;\n    float threshold;\n};\n\nstruct OuterConfig\n{\n    int         type;\n    InnerConfig inner;\n};\n\n// initialize the RDO container\npol::rdo<OuterConfig> config({ 1, { 42, 3.14f } });\n\n// retrieving full config in unobfuscated form\nauto full_config = config.read(); \n\n// retrieving threshold without decrypting full object\nfloat threshold = config.ref(&OuterConfig::inner, &InnerConfig::threshold);\n\n// modifying threshold without decrypting/encrypting the full object\nconfig.ref(&OuterConfig::inner, &InnerConfig::threshold) = 9.99f;\n```\n\nOr, if you have functions that normally take a reference to a specific member:\n\n```\nvoid adjust_threshold(pol::rdo_ref<float> threshold_ref)\n{\n    // reading implicitly decrypts just this float\n    float current_val = threshold_ref; \n\n    // writing implicitly encrypts the new value directly into the RDO buffer\n    if (current_val < 10.0f)\n        threshold_ref = 10.0f; \n\n    // the reference also supports standard operators\n    threshold_ref += 2.5f; \n}\n\nvoid example()\n{\n    // initialize the RDO container\n    pol::rdo<OuterConfig> config({ 1, { 42, 3.14f } });\n\n    // pass the generated reference to the function\n    adjust_threshold(config.ref(&OuterConfig::inner, &InnerConfig::threshold));\n}\n```\n\n### 4. Static Data Obfuscation\n\nA class providing access to values known at compile-time. Supports automatic obfuscation of fundamental types as well as compile-time instances of more complex user-defined types (e.g. structs).\n\n```\n// protekkt/pol/sdo.hpp\n\n// reference type allowing reads of specific members within\n// complex obfuscated types (e.g. structs, arrays or arrays of structs)\ntemplate <typename U>\nclass sdo_ref { /* implementation */ };\n\ntemplate <auto V>\nclass sdo\n{\npublic:\n    using T = decltype(V);\n    static_assert(std::is_trivially_copyable_v<T> || is_sdo_array_v<T>, \"T must be trivially copyable for SDO\");\n\n    // embed compile-time data inside markers, the data will be processed by the obfuscator\n    // during obfuscation the process alongside relevant access methods\n    struct layout_type\n    {\n        std::uint8_t beg_m[PTKT_RDO_MARKER_SIZE];\n        T            data_m;\n        std::uint8_t end_m[PTKT_RDO_MARKER_SIZE];\n    };\n\nprivate:\n    static inline PTKT_CONSTINIT layout_type data_m = { PTKT_SDO_BEG_MARKER, V, PTKT_SDO_END_MARKER };\n\npublic:\n    /* implementation */\n};\n```\n\nUsage example:\n\n```\n// constant hidden in compiled binary\n// retrieved into a temporary only upon request\nauto x = pol::sdo<0x12345678>{}.get();\nstruct InnerConfig\n{\n    int   id;\n    float threshold;\n};\n\nstruct OuterConfig\n{\n    int         type;\n    InnerConfig inner;\n};\n\nconstexpr OuterConfig default_config_g = {\n    1, { 42, 3.14f }\n};\n\nvoid f()\n{\n    pol::sdo<default_config_g> config;\n\n    // read just the threshold float without decrypting the entire object\n    float threshold = config.ref(&OuterConfig::inner, &InnerConfig::threshold);\n\n    // decrypts the entire OuterConfig object into a plaintext struct\n    auto full_config = config.get();\n}\n```\n\n## C++ Containers\n\nThe components we went through (RVO, NUM, RDO and SDO) provide us with all functionality required to implement standard container types that allow developers to store data in encrypted/obfuscated form both as part of compile-time data embedded in the binary as well as runtime data. This does not refer only to the actual data being stored by the developer, but also internal book-keeping that containers naturally have to do. The primary focus is on ensuring that data is never decrypted into plaintext form except in cases where it is absolutely needed (usually only temporarily) or explicitly requested by the developer.\n\nAt the time of writing this article, POL implements obfuscated variants of the following standard containers: `array, atomic, list, map, optional, pair, set, string, tuple, unordered_map, unordered_set, vector, unique_ptr and shared_ptr`\n\n.\n\nThe following examples demonstrate functionality and usage of certain containers. Most principles apply to all of them.\n\n```\nvoid string_example()\n{\n    // runtime initialization from a C-string\n    // the string is immediately encrypted into the RDO heap buffer\n    pol::string my_str(\"Hello\");\n\n#if PTKT_HAS_CXX20\n    // C++20 compile-time initialization:\n    pol::string secure_str = pol::make_string<\"Secret\">();\n#else\n    // C++17 compile-time initialization\n    pol::string secure_str = PTKT_SDO_STRING_C17(\"Secret\");\n#endif\n\n    // append standard C-strings directly to the encrypted buffer\n    my_str.append(\", World\");\n    my_str += \"!\";\n\n    // modify a specific character securely using the rdo_ref<char> proxy\n    // this encrypts the 'h' directly into the first index without decrypting the rest\n    my_str[0] = 'h'; \n\n    // operator== provides comparison against plaintext strings\n    // or other instances of pol::string\n    if (my_str == \"hello, World!\")\n        std::print(\"The strings match.\\n\");\n\n    // read a specific character via the implicit decryption of the proxy\n    char fifth_char = my_str[4];\n    std::print(\"The 5th character is: {}\\n\", fifth_char);\n\n    // get the full string as a standard std::string\n    // this decodes the entire string into plaintext in memory\n    auto plaintext = my_str.get();\n    std::print(\"Exported plaintext: {}\\n\", plaintext);\n}\n#include \"protekkt/pol/unordered_map.hpp\"\n\nstruct InnerStats\n{\n    int   health;\n    float stamina;\n};\n\nstruct PlayerData\n{\n    int        level;\n    InnerStats stats;\n};\n\nvoid unordered_map_example()\n{\n    pol::unordered_map<int, PlayerData> players;\n\n    // insert using key and value directly\n    players.insert(1, { 10, { 100, 50.0f } });\n    // insert using pol::make_pair\n    players.insert(pol::make_pair(2, PlayerData{ 15, { 150, 75.0f } }));\n\n    // read via operator[] (implicitly decodes the full struct)\n    PlayerData player_one = players[1];\n\n    // read via find() and iterators\n    auto it = players.find(2);\n    if (it != players.end())\n    {\n        // iterators return a proxy reference pair, so we use (*it).second \n        // which yields an rdo_ref<PlayerData> that implicitly decodes on assignment\n        PlayerData player_two = (*it).second;\n    }\n\n    // operator[] returns an rdo_ref<PlayerData> -- we can use .ref() \n    // to securely target specific nested members in the obfuscated objects\n\n    // subtract 20 from player 1's health directly in the map\n    players[1].ref(&PlayerData::stats, &InnerStats::health) -= 20;\n\n    // assign a completely new value to player 1's stamina\n    players[1].ref(&PlayerData::stats, &InnerStats::stamina) = 100.0f;\n\n    // level up player 1 (modifying a top-level member)\n    players[1].ref(&PlayerData::level) += 1;\n\n    // range-based for loops are supported\n    // 'kv' gets deduced to pol::unordered_map_ref_pair<int, PlayerData>\n    for (auto kv : players)\n    {\n        int index  = kv.first;\n        int health = kv.second.ref(&PlayerData::stats, &InnerStats::health);\n\n        std::print(\"Player Index: {} | Health: {}\\n\", index, health);\n    }\n\n    // erase a specific key\n    players.erase(2); \n}\n```\n\n", "url": "https://wpnews.pro/news/c-enables-us-to-provide-seamless-data-obfuscation", "canonical_source": "https://protekkt.io/data-obfuscation-cpp-sdk.html", "published_at": "2026-07-30 09:15:43+00:00", "updated_at": "2026-07-30 09:22:14.941876+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["PROTEKKT", "POL", "C++", "C++17", "C++20", "C++23", "C++26"], "alternates": {"html": "https://wpnews.pro/news/c-enables-us-to-provide-seamless-data-obfuscation", "markdown": "https://wpnews.pro/news/c-enables-us-to-provide-seamless-data-obfuscation.md", "text": "https://wpnews.pro/news/c-enables-us-to-provide-seamless-data-obfuscation.txt", "jsonld": "https://wpnews.pro/news/c-enables-us-to-provide-seamless-data-obfuscation.jsonld"}}