16

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

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

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

4 回答 4

12

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

几年前我问这个问题时,我没有考虑混淆和加密之间的区别。如果我当时知道这种差异,我之前会在标题中包含术语混淆

C++11 和 C++14 的特性使得以一种有效且相当简单的方式实现编译时字符串混淆(可能还有加密,虽然我还没有尝试过)成为可能,而且它已经完成了。

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

它不仅混淆字符串,而且还有其他有用的东西,比如可以随机混淆函数调用的编译时 FSM(有限状态机)和编译时伪随机数生成器,但这些超出了本文的范围回答。

这是一个使用 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),以及char所选算法对xor字符串字符使用的随机数。

“嘿,但是如果我们算一下,3 个算法 * 255 个可能的字符键(0 未使用)= 765 个混淆字符串的变体”

你是对的。相同的字符串只能以 765 种不同的方式进行混淆。如果您有理由需要更安全的东西(您是偏执狂/您的应用程序需要更高的安全性),您可以扩展库并实施您自己的算法,使用更强的混淆甚至加密(白盒加密在 lib 的路线图中)。


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

我觉得这个实现有趣的一件事是它不会将混淆后的字符串存储在可执行文件的数据部分中。相反,它被静态存储到MetaString对象本身(在堆栈上),并且算法在运行时将其解码到位。这种方法使得在静态或运行时查找混淆字符串变得更加困难。

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

于 2016-02-13T21:50:49.620 回答
8

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

所以这是你开始的:

  1. encrypted_string.cpp 和 encrypted_string.h(空白)
  2. 将文本文件作为输入并重写 encrypted_string.cpp 和 encrypted_string.h 的脚本或独立应用程序

如果脚本失败,您的编译将失败,因为您的代码中将引用不存在的变量。你可以变得更聪明,但这足以让你开始。

于 2011-08-03T23:49:59.547 回答
3

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

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

于 2011-08-03T22:56:09.233 回答
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;
}
于 2011-08-03T23:11:27.690 回答