C++: How to encrypt strings at compile time?

前端 未结 10 1256
北荒
北荒 2020-12-18 21:40

I want to hide some strings in my .exe so people can\'t simply just open the .exe and look at all the strings there. I don\'t care about the strength of the encrypting metho

相关标签:
10条回答
  • 2020-12-18 22:37

    This probably doesn't apply to the ancient compiler of the question, but on more more modern C++ implementations, we can use a string literal operator template that's declared constexpr to implement compile-time obfuscation. For this, I've used GCC 7.2.0 with -std=c++17 (and a full set of warning options, of course).

    Firstly, we define a type to hold our obfuscated string data, with a conversion operator to produce a plaintext string on demand:

    #include <array>
    #include <string>
    
    template<typename Char>
    static const Char SECRET = 0x01;
    
    template<typename Char,
             typename std::basic_string<Char>::size_type Length>
    struct obfuscated_string
    {
        using String = std::basic_string<Char>;
    
        const std::array<const Char, Length> storage;
    
        operator String() const
        {
            String s{storage.data(), Length};
            for (auto& c: s)
                c ^= SECRET<Char>;
            return s;
        }
    };
    

    Now, the literal operator template to convert a source-code literal to an obfuscated string:

    template<typename ctype, ctype...STR>
    constexpr obfuscated_string<ctype, sizeof... (STR)> operator ""_hidden()
    {
        return { { (STR ^ SECRET<ctype>)... } };
    }
    

    To demonstrate:

    #include <iostream>
    int main()
    {
        static const auto message = "squeamish ossifrage"_hidden;
        std::string plaintext = message;
        std::cout << plaintext << std::endl;
    }
    

    We can inspect the object code with the strings program. The binary doesn't contain squeamish ossifrage anywhere; instead it has rptd`lhri!nrrhgs`fd. I've confirmed this with a range of optimisation levels to demonstrate that the conversion back to std::string doesn't get pre-computed, but I advise you conduct your own tests whenever you change compiler and/or settings.

    (I'm deliberately ignoring whether this is an advisable thing to do - merely presenting a technical working solution).

    0 讨论(0)
  • 2020-12-18 22:40

    You can't encrypt strings (string literals) by С++ compiler or preprocessor, but you can write a pre-build tool which will parse your source code, and encrypt strings.

    Or, you can try to use boost::mpl::string.

    0 讨论(0)
  • 2020-12-18 22:42

    you can encrypt it using macros or write your own preprocessor

    #define CRYPT8(str) { CRYPT8_(str "\0\0\0\0\0\0\0\0") }
    #define CRYPT8_(str) (str)[0] + 1, (str)[1] + 2, (str)[2] + 3, (str)[3] + 4, (str)[4] + 5, (str)[5] + 6, (str)[6] + 7, (str)[7] + 8, '\0'
    
    // calling it
    const char str[] = CRYPT8("ntdll");
    0 讨论(0)
  • 2020-12-18 22:43

    Here is what I currently use it has hacks to support sprintf functions which spilled plain-text in compiled binary file. You could now use w_sprintf_s instead of sprintf, like so

    char test[256] = { 0 };
    w_sprintf_s(test, 256, XorStr("test test :D %d %+d\n"), 1, 1337);
    

    or

    wchar_t test[256] = { 0 };
    w_sprintf_s(test, 256, XorStrW(L"test test from unicode :D %d %+d\n"), 1, 1337);
    


    May add support for wchar_t wide strings like arkan did.. but I have no use for them right now as I don't write anything in symbols / unicode.

    #pragma once
    #include <string>
    #include <array>
    #include <cstdarg>
    
    #define BEGIN_NAMESPACE( x ) namespace x {
    #define END_NAMESPACE }
    
    BEGIN_NAMESPACE(XorCompileTime)
    
    constexpr auto time = __TIME__;
    constexpr auto seed = static_cast< int >(time[7]) + static_cast< int >(time[6]) * 10 + static_cast< int >(time[4]) * 60 + static_cast< int >(time[3]) * 600 + static_cast< int >(time[1]) * 3600 + static_cast< int >(time[0]) * 36000;
    
    // 1988, Stephen Park and Keith Miller
    // "Random Number Generators: Good Ones Are Hard To Find", considered as "minimal standard"
    // Park-Miller 31 bit pseudo-random number generator, implemented with G. Carta's optimisation:
    // with 32-bit math and without division
    
    template < int N >
    struct RandomGenerator
    {
    private:
        static constexpr unsigned a = 16807; // 7^5
        static constexpr unsigned m = 2147483647; // 2^31 - 1
    
        static constexpr unsigned s = RandomGenerator< N - 1 >::value;
        static constexpr unsigned lo = a * (s & 0xFFFF); // Multiply lower 16 bits by 16807
        static constexpr unsigned hi = a * (s >> 16); // Multiply higher 16 bits by 16807
        static constexpr unsigned lo2 = lo + ((hi & 0x7FFF) << 16); // Combine lower 15 bits of hi with lo's upper bits
        static constexpr unsigned hi2 = hi >> 15; // Discard lower 15 bits of hi
        static constexpr unsigned lo3 = lo2 + hi;
    
    public:
        static constexpr unsigned max = m;
        static constexpr unsigned value = lo3 > m ? lo3 - m : lo3;
    };
    
    template <>
    struct RandomGenerator< 0 >
    {
        static constexpr unsigned value = seed;
    };
    
    template < int N, int M >
    struct RandomInt
    {
        static constexpr auto value = RandomGenerator< N + 1 >::value % M;
    };
    
    template < int N >
    struct RandomChar
    {
        static const char value = static_cast< char >(1 + RandomInt< N, 0x7F - 1 >::value);
    };
    
    template < size_t N, int K, typename Char >
    struct XorString
    {
    private:
        const char _key;
        std::array< Char, N + 1 > _encrypted;
    
        constexpr Char enc(Char c) const
        {
            return c ^ _key;
        }
    
        Char dec(Char c) const
        {
            return c ^ _key;
        }
    
    public:
        template < size_t... Is >
        constexpr __forceinline XorString(const Char* str, std::index_sequence< Is... >) : _key(RandomChar< K >::value), _encrypted{ enc(str[Is])... }
        {
        }
    
        __forceinline decltype(auto) decrypt(void)
        {
            for (size_t i = 0; i < N; ++i) {
                _encrypted[i] = dec(_encrypted[i]);
            }
            _encrypted[N] = '\0';
            return _encrypted.data();
        }
    };
    
    //--------------------------------------------------------------------------------
    //-- Note: XorStr will __NOT__ work directly with functions like printf.
    //         To work with them you need a wrapper function that takes a const char*
    //         as parameter and passes it to printf and alike.
    //
    //         The Microsoft Compiler/Linker is not working correctly with variadic 
    //         templates!
    //  
    //         Use the functions below or use std::cout (and similar)!
    //--------------------------------------------------------------------------------
    
    static auto w_printf = [](const char* fmt, ...) {
        va_list args;
        va_start(args, fmt);
        vprintf_s(fmt, args);
        va_end(args);
    };
    
    static auto w_printf_s = [](const char* fmt, ...) {
        va_list args;
        va_start(args, fmt);
        vprintf_s(fmt, args);
        va_end(args);
    };
    
    static auto w_sprintf = [](char* buf, const char* fmt, ...) {
        va_list args;
        va_start(args, fmt);
        vsprintf(buf, fmt, args);
        va_end(args);
    };
    
    static auto w_sprintf_ret = [](char* buf, const char* fmt, ...) {
        int ret;
        va_list args;
        va_start(args, fmt);
        ret = vsprintf(buf, fmt, args);
        va_end(args);
        return ret;
    };
    
    static auto w_sprintf_s = [](char* buf, size_t buf_size, const char* fmt, ...) {
        va_list args;
        va_start(args, fmt);
        vsprintf_s(buf, buf_size, fmt, args);
        va_end(args);
    };
    
    static auto w_sprintf_s_ret = [](char* buf, size_t buf_size, const char* fmt, ...) {
        int ret;
        va_list args;
        va_start(args, fmt);
        ret = vsprintf_s(buf, buf_size, fmt, args);
        va_end(args);
        return ret;
    };
    
    //Old functions before I found out about wrapper functions.
    //#define XorStr( s ) ( XorCompileTime::XorString< sizeof(s)/sizeof(char) - 1, __COUNTER__, char >( s, std::make_index_sequence< sizeof(s)/sizeof(char) - 1>() ).decrypt() )
    //#define XorStrW( s ) ( XorCompileTime::XorString< sizeof(s)/sizeof(wchar_t) - 1, __COUNTER__, wchar_t >( s, std::make_index_sequence< sizeof(s)/sizeof(wchar_t) - 1>() ).decrypt() )
    
    //Wrapper functions to work in all functions below
    #define XorStr( s ) []{ constexpr XorCompileTime::XorString< sizeof(s)/sizeof(char) - 1, __COUNTER__, char > expr( s, std::make_index_sequence< sizeof(s)/sizeof(char) - 1>() ); return expr; }().decrypt()
    #define XorStrW( s ) []{ constexpr XorCompileTime::XorString< sizeof(s)/sizeof(wchar_t) - 1, __COUNTER__, wchar_t > expr( s, std::make_index_sequence< sizeof(s)/sizeof(wchar_t) - 1>() ); return expr; }().decrypt()
    
    END_NAMESPACE
    
    0 讨论(0)
提交回复
热议问题