在编译时加密/混淆字符串文字

时间:2011-08-03 22:46:26

标签: c++ string templates metaprogramming compile-time

我想在编译时加密/编码字符串,以便原始字符串不会出现在已编译的可执行文件中。

我见过几个例子,但他们不能把字符串文字作为参数。请参阅以下示例:

template<char c> struct add_three {
    enum { value = c+3 };
};

template <char... Chars> struct EncryptCharsA {
    static const char value[sizeof...(Chars) + 1];
};

template<char... Chars>
char const EncryptCharsA<Chars...>::value[sizeof...(Chars) + 1] = {
    add_three<Chars>::value...
};

int main() {   
    std::cout << EncryptCharsA<'A','B','C'>::value << std::endl;
    // prints "DEF"
}

我不想像它那样单独提供每个角色。我的目标是传递一个字符串文字,如下所示:

EncryptString<"String to encrypt">::value

还有一些像这样的例子:

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

但它限制了字符串的大小。

有没有办法达到我想要的目的?

4 个答案:

答案 0 :(得分:7)

使用模板元编程为自己省去一堆麻烦,只需编写一个独立的程序来加密字符串并生成一个cpp源文件然后编译进来。这个程序将在你编译之前运行并产生一个cpp和/或包含加密字符串的头文件供您使用。

所以这就是你的开始:

  1. encrypted_string.cpp和encrypted_string.h(空白)
  2. 将文本文件作为输入并通过写入encrypted_string.cpp和encrypted_string.h的脚本或独立应用程序
  3. 如果脚本失败,则编译将失败,因为代码中将引用一个不存在的变量。你可以变聪明,但这足以让你开始。

答案 1 :(得分:4)

我认为这个问题值得更新。

几年前我问这个问题时,我没有考虑the difference between obfuscation and encryption。如果我知道这个差异,我之前已经在标题中包含了术语 Obfuscation

C ++ 11和C ++ 14具有的功能可以实现编译时字符串模糊处理(可能还有加密,尽管我还没有尝试过)有效而且相当简单的方式,已经完成了。

ADVobfuscator是由Sebastien Andrivet创建的混淆库,它使用C ++ 11/14生成编译时混淆代码,而不使用任何外部工具,只需要C ++代码。没有必要创建额外的构建步骤,只需包含它并使用它。我不知道更好的编译时字符串加密/混淆实现,它不使用外部工具或构建步骤。如果你这样做,请分享。

它不仅会使字符串变得混乱,而且还有其他有用的东西,比如可以随机混淆函数调用的编译时FSM(Finite State Machine)和编译时伪随机数生成器,但这些都是这个答案的范围。

这是使用ADVobfuscator的简单字符串混淆示例:

#include "MetaString.h"

using namespace std;
using namespace andrivet::ADVobfuscator;

void Example()
{
    /* Example 1 */

    // here, the string is compiled in an obfuscated form, and
    // it's only deobfuscated at runtime, at the very moment of its use
    cout << OBFUSCATED("Now you see me") << endl;

    /* Example 2 */

    // here, we store the obfuscated string into an object to
    // deobfuscate whenever we need to
    auto narrator = DEF_OBFUSCATED("Tyler Durden");

    // note: although the function is named `decrypt()`, it's still deobfuscation
    cout << narrator.decrypt() << endl;
}

您可以使用自己的宏替换宏DEF_OBFUSCATEDOBFUSCATED。例如:

#define _OBF(s) OBFUSCATED(s)

...

cout << _OBF("klapaucius");

它是如何运作的?

如果您在MetaString.h中查看这两个宏的定义,您会看到:

#define DEF_OBFUSCATED(str) MetaString<andrivet::ADVobfuscator::MetaRandom<__COUNTER__, 3>::value, andrivet::ADVobfuscator::MetaRandomChar<__COUNTER__>::value, Make_Indexes<sizeof(str) - 1>::type>(str)

#define OBFUSCATED(str) (DEF_OBFUSCATED(str).decrypt())

基本上,MetaString类有三种不同的变体(字符串混淆的核心)。每个都有自己的混淆算法。在编译时随机选择这三种变体中的一种,使用库的伪随机数生成器(MetaRandom),以及所选算法使用的随机charxor字符串字符。

  

&#34;嘿,但是如果我们进行数学运算,3个算法* 255个可能的char键(0未使用)= 765个混淆字符串的变体&#34;

你是对的。相同的字符串只能以765种不同的方式进行混淆。如果你有理由需要更安全的东西(你是偏执/你的应用程序需要增加安全性),你可以扩展库并实现自己的算法,使用更强的混淆甚至加密(White-Box cryptography在lib& #39;路线图)。

在哪里/如何存储混淆的字符串?

我发现有趣的一点是,它没有将混淆的字符串存储在可执行文件的数据部分中。 相反,它静态存储到MetaString对象本身(在堆栈上),算法在运行时将其解码到位。这种方法使得在静态或运行时更难找到混淆的字符串。

您可以自己深入了解实施情况。这是一个非常好的基本混淆解决方案,可以作为更复杂的解决方案的起点。

答案 2 :(得分:3)

您找到的示例不能将字符串文字作为模板参数的原因是因为ISO C ++标准不允许这样做。那是因为,即使c ++有一个字符串类,字符串文字仍然是一个const char *。因此,即使您可以访问此类编译时字符串文字的字符,也不能或不应该更改它(导致未定义的行为)。

我看到的唯一方法是使用定义,因为它们在编译器之前由预处理器处理。在这种情况下,也许助推会帮助你。

答案 3 :(得分:2)

基于宏的解决方案是采用可变参数并将字符串的每个部分作为单个标记传递。然后对令牌进行字符串化并对其进行加密并连接所有令牌。最终结果看起来像这样

CRYPT(m y _ s t r i n g)

其中_是空格字符文字的占位符。非常混乱,我宁愿选择其他所有解决方案。

这样的事情可以做到这一点虽然Boost.PP序列并没有让它变得更漂亮。

#include <iostream>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

#define GARBLE(x) GARBLE_ ## x
#define GARBLE_a x
#define GARBLE_b y
#define GARBLE_c z

#define SEQ (a)(b)(c)
#define MACRO(r, data, elem) BOOST_PP_STRINGIZE(GARBLE(elem))

int main() {
  const char* foo = BOOST_PP_SEQ_FOR_EACH(MACRO, _, SEQ);
  std::cout << foo << std::endl;
}