C ++:如何在编译时加密字符串?

时间:2010-11-04 23:25:54

标签: c++ windows visual-studio-2008

我想在我的.exe中隐藏一些字符串,这样人们就不能简单地打开.exe并查看那里的所有字符串。我不关心加密方法的强度,所以我可能会使用XOR等。

我怎样才能在编译时这样做?这样我的字符串将不会存储在.exe中,但加密版本会存储。然后,我每次只使用我的解密功能在屏幕上显示这些字符串。

10 个答案:

答案 0 :(得分:11)

您可以使用宏对其进行加密或编写自己的预处理器

#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");

答案 1 :(得分:7)

我也认为这是不可能的,即使它非常简单,人们也会编写解决方案,你需要一个自定义工具来扫描构建的文件,然后扫描字符串并对字符串进行加密,这不错但是我想要一个从Visual Studio编译的包,现在可以了!

您需要的是C++ 11(开箱即用的Visual Studio 2015 Update 1)

这个新命令constexpr

会发生魔力

魔术发生在#define

#define XorString( String ) ( CXorString<ConstructIndexList<sizeof( String ) - 1>::Result>( String ).decrypt() )

它不会在编译时解密XorString,仅在运行时解密,但它只会在编译时加密字符串,因此字符串不会出现在可执行文件中

printf(XorString( "this string is hidden!" ));

它将打印出"this string is hidden!",但您不会在可执行文件中找到它作为字符串!,请使用Microsoft Sysinternals Strings程序下载链接自行检查:https://technet.microsoft.com/en-us/sysinternals/strings.aspx

完整的源代码非常大,但可以很容易地包含在一个头文件中。但也非常随机,所以加密的字符串输出将始终改变每个新的编译,种子根据编译时间而改变,非常可靠,完美的解决方案。

创建名为XorString.h

的文件
#pragma once

//-------------------------------------------------------------//
// "Malware related compile-time hacks with C++11" by LeFF   //
// You can use this code however you like, I just don't really //
// give a shit, but if you feel some respect for me, please //
// don't cut off this comment when copy-pasting... ;-)       //
//-------------------------------------------------------------//

////////////////////////////////////////////////////////////////////
template <int X> struct EnsureCompileTime {
    enum : int {
        Value = X
    };
};
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
//Use Compile-Time as seed
#define Seed ((__TIME__[7] - '0') * 1  + (__TIME__[6] - '0') * 10  + \
              (__TIME__[4] - '0') * 60   + (__TIME__[3] - '0') * 600 + \
              (__TIME__[1] - '0') * 3600 + (__TIME__[0] - '0') * 36000)
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
constexpr int LinearCongruentGenerator(int Rounds) {
    return 1013904223 + 1664525 * ((Rounds> 0) ? LinearCongruentGenerator(Rounds - 1) : Seed & 0xFFFFFFFF);
}
#define Random() EnsureCompileTime<LinearCongruentGenerator(10)>::Value //10 Rounds
#define RandomNumber(Min, Max) (Min + (Random() % (Max - Min + 1)))
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
template <int... Pack> struct IndexList {};
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
template <typename IndexList, int Right> struct Append;
template <int... Left, int Right> struct Append<IndexList<Left...>, Right> {
    typedef IndexList<Left..., Right> Result;
};
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
template <int N> struct ConstructIndexList {
    typedef typename Append<typename ConstructIndexList<N - 1>::Result, N - 1>::Result Result;
};
template <> struct ConstructIndexList<0> {
    typedef IndexList<> Result;
};
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
const char XORKEY = static_cast<char>(RandomNumber(0, 0xFF));
constexpr char EncryptCharacter(const char Character, int Index) {
    return Character ^ (XORKEY + Index);
}

template <typename IndexList> class CXorString;
template <int... Index> class CXorString<IndexList<Index...> > {
private:
    char Value[sizeof...(Index) + 1];
public:
    constexpr CXorString(const char* const String)
    : Value{ EncryptCharacter(String[Index], Index)... } {}

    char* decrypt() {
        for(int t = 0; t < sizeof...(Index); t++) {
            Value[t] = Value[t] ^ (XORKEY + t);
        }
        Value[sizeof...(Index)] = '\0';
        return Value;
    }

    char* get() {
        return Value;
    }
};
#define XorS(X, String) CXorString<ConstructIndexList<sizeof(String)-1>::Result> X(String)
#define XorString( String ) ( CXorString<ConstructIndexList<sizeof( String ) - 1>::Result>( String ).decrypt() )
////////////////////////////////////////////////////////////////////

答案 2 :(得分:2)

这可能并不适用于问题的古老编译器,但在更现代的C ++实现中,我们可以使用声明为{{{em>字符串 的字符串文字运算符模板 1}}实现编译时混淆。为此,我使用了GCC 7.2.0和constexpr(当然还有一整套警告选项)。

首先,我们定义一个类型来保存我们混淆的字符串数据,使用转换运算符来按需生成明文字符串:

-std=c++17

现在,将源代码文字转换为混淆字符串的文字运算符模板:

#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;
    }
};

演示:

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

我们可以使用#include <iostream> int main() { static const auto message = "squeamish ossifrage"_hidden; std::string plaintext = message; std::cout << plaintext << std::endl; } 程序检查目标代码。二进制文件在任何地方都不包含strings;相反它有squeamish ossifrage。我已经通过一系列优化级别证实了这一点,以证明转换回rptd`lhri!nrrhgs`fd并没有预先计算出来,但我建议您在更改编译器时进行自己的测试和/或设置。

(我故意忽略这是否是可取的要做的事情 - 仅提供技术工作解决方案。)

答案 3 :(得分:1)

您不能通过С++编译器或预处理器加密字符串(字符串文字),但您可以编写一个预构建工具来解析源代码并加密字符串。

或者,您可以尝试使用boost :: mpl :: string。

答案 4 :(得分:1)

关于唯一的方法完全你建议的是写一个真正可怕的宏。但这里有一些替代方案。

  1. 将加密的字符串存储在数据文件中。
  2. 在单个源文件中收集字符串,然后在构建中,在实际编译之前,使用将加密它们的工具(例如sed)对其进行检查。您可以自动执行此步骤。
  3. 使用功能强大的编辑器,以便在工作时可以毫不费力地加密/解密字符串。

答案 5 :(得分:1)

如果您只是想隐藏字符串,那么您可以尝试使用UPX之类的内容来压缩可执行文件。

答案 6 :(得分:0)

无论您的解决方案的细节如何,都将涉及使用某些加密程序加密字符串。

您可以编写一个脚本来加密源代码中的文字。

或者对于Windows exe,您可以加密[.rc]文件中的文字,将字符串作为字符串表资源嵌入到exe中。

但可能最好的解决方案是不要试图隐藏。

当微软尝试在Windows代码上的XOR隐藏技巧时,任意警告非Microsoft DOS不可靠且与此不兼容,它只是对它们适得其反。当然,对于竞争对手说坏话以及用Windows捆绑这些坏话的想法首先是一个真正愚蠢的想法。但是,试图隐藏代码是导致公众尴尬的原因:没有人真正注意到代码产生的警告,但是当人们发现“加密”代码时,他们的好奇心自然而然,他们只需要知道它是什么,并撰写有关它的文章。

干杯&amp;第h。,

答案 7 :(得分:0)

在编译时完成的任何加密也必须在原始EXE中可以撤销(除非你正在做一些真正奇怪的事情)。您的应用程序正在其硬件上运行。注定......禁止DRM,这是(在我看来)邪恶。

答案 8 :(得分:0)

以SSPoke的答案为基础,这是一个更简单,更可靠的解决方案。经过MSVC 2017和gcc 7.3 https://godbolt.org/z/7fc3Zi

的测试

更改:

  • 修复长字符串的整数溢出警告
  • 修复/确保MSVC即使在很长的字符串时(在原始版本中,某些字符串也不会在编译时进行加密),即使在很长的字符串中也要在编译时评估crypto_character()。
  • 支持宽字符串
  • 更简单的代码模板
#include <iostream>

// =============================================================================
namespace crypt {
// =============================================================================

// compile-time seed
#define XSTR_SEED ((__TIME__[7] - '0') * 1ull    + (__TIME__[6] - '0') * 10ull  + \
                   (__TIME__[4] - '0') * 60ull   + (__TIME__[3] - '0') * 600ull + \
                   (__TIME__[1] - '0') * 3600ull + (__TIME__[0] - '0') * 36000ull)

// -----------------------------------------------------------------------------

// @return a pseudo random number clamped at 0xFFFFFFFF
constexpr unsigned long long linear_congruent_generator(unsigned rounds) {
    return 1013904223ull + (1664525ull * ((rounds> 0) ? linear_congruent_generator(rounds - 1) : (XSTR_SEED) )) % 0xFFFFFFFF;
}

// -----------------------------------------------------------------------------

#define Random() linear_congruent_generator(10)
#define XSTR_RANDOM_NUMBER(Min, Max) (Min + (Random() % (Max - Min + 1)))

// -----------------------------------------------------------------------------

constexpr const unsigned long long XORKEY = XSTR_RANDOM_NUMBER(0, 0xFF);

// -----------------------------------------------------------------------------

template<typename Char >
constexpr Char encrypt_character(const Char character, int index) {
    return character ^ (static_cast<Char>(XORKEY) + index);
}

// -----------------------------------------------------------------------------

template <unsigned size, typename Char>
class Xor_string {
public:
    const unsigned _nb_chars = (size - 1);
    Char _string[size];

    // if every goes alright this constructor should be executed at compile time
    inline constexpr Xor_string(const Char* string)
        : _string{}
    {
        for(unsigned i = 0u; i < size; ++i)
            _string[i] = encrypt_character<Char>(string[i], i);
    }

    // This is executed at runtime.
    // HACK: although decrypt() is const we modify '_string' in place
    const Char* decrypt() const
    {
        Char* string = const_cast<Char*>(_string);
        for(unsigned t = 0; t < _nb_chars; t++) {
            string[t] = string[t] ^ (static_cast<Char>(XORKEY) + t);
        }
        string[_nb_chars] = '\0';
        return string;
    }

};

}// END crypt NAMESPACE ========================================================

#define XorS(name, my_string)    constexpr crypt::Xor_string<(sizeof(my_string)/sizeof(char)), char> name(my_string)
// Because of a limitation/bug in msvc 2017 we need to declare crypt::Xor_string() as a constexpr 
// otherwise the constructor is not evaluated at compile time. The lambda function is here to allow this declaration inside the macro
// because there is no such thing as casting to 'constexpr' (and casting to const does not solve this bug).
#define XorString(my_string) []{ constexpr crypt::Xor_string<(sizeof(my_string)/sizeof(char)), char> expr(my_string); return expr; }().decrypt()

// Crypt normal string char*
#define _c( string ) XorString( string )

#define XorWS(name, my_string)       constexpr crypt::Xor_string<(sizeof(my_string)/sizeof(wchar_t)), wchar_t> name(my_string)
#define XorWideString(my_string) []{ constexpr crypt::Xor_string<(sizeof(my_string)/sizeof(wchar_t)), wchar_t> expr(my_string); return expr; }().decrypt()

// crypt  wide characters
#define _cw( string ) XorWideString( string )


int main(void ) {

    std::cout << _c("0obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze0\n"
                    "1obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze1\n"
                    "2obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze2\n"
                    "3obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze3\n"
                    "4obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze4\n"
                    "5obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze5\n"
                    "6obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze6\n"
                    "7obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze7\n"
                    "8obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze8\n"
                    "9obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze9\n" ) << std::endl;

    std::cout << "Wide strings" << std::endl;

    std::wcout << _cw(L"0obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze0\n"
                       "1obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze1\n"
                       "2obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze2\n"
                       "3obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze3\n"
                       "4obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze4\n"
                       "5obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze5\n"
                       "6obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze6\n"
                       "7obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze7\n"
                       "8obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze8\n"
                       "9obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze9\n")  << std::endl;

    return 0;
}

答案 9 :(得分:0)

这是我目前使用的东西,它具有一些黑客技术来支持sprintf函数,这些函数会将纯文本溢出到已编译的二进制文件中。您现在可以像这样

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

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


可能像arkan一样增加了对wchar_t宽字符串的支持..但是我现在对它们没有用,因为我没有在符号/ 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