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

发布于 2024-11-27 19:27:01 字数 1019 浏览 1 评论 0原文

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

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

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

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

有什么办法可以实现我想要的吗?

I want to encrypt/encode a string at compile time so that the original string does not appear in the compiled executable.

I've seen several examples but they can't take a string literal as argument. See the following example:

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"
}

I don't want to provide each character separately like it does. My goal is to pass a string literal like follows:

EncryptString<"String to encrypt">::value

There's also some examples like this one:

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

But it limits the size of the string.

Is there any way to achieve what I want?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

娇女薄笑 2024-12-04 19:27:01

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

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

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

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

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

以下是使用 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,在编译时随机选择这三个变体之一对字符串字符进行异或运算的算法。

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

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


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

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

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

I think this question deserves an updated answer.

When I asked this question several years ago, I didn't consider the difference between obfuscation and encryption. Had I known this difference then, I'd have included the term Obfuscation in the title before.

C++11 and C++14 have features that make it possible to implement compile-time string obfuscation (and possibly encryption, although I haven't tried that yet) in an effective and reasonably simple way, and it's already been done.

ADVobfuscator is an obfuscation library created by Sebastien Andrivet that uses C++11/14 to generate compile-time obfuscated code without using any external tool, just C++ code. There's no need to create extra build steps, just include it and use it. I don't know a better compile-time string encryption/obfuscation implementation that doesn't use external tools or build steps. If you do, please share.

It not only obuscates strings, but it has other useful things like a compile-time FSM (Finite State Machine) that can randomly obfuscate function calls, and a compile-time pseudo-random number generator, but these are out of the scope of this answer.

Here's a simple string obfuscation example using 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;
}

You can replace the macros DEF_OBFUSCATED and OBFUSCATED with your own macros. Eg.:

#define _OBF(s) OBFUSCATED(s)

...

cout << _OBF("klapaucius");

How does it work?

If you take a look at the definition of these two macros in MetaString.h, you will see:

#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())

Basically, there are three different variants of the MetaString class (the core of the string obfuscation). Each has its own obfuscation algorithm. One of these three variants is chosen randomly at compile-time, using the library's pseudo-random number generator (MetaRandom), along with a random char that is used by the chosen algorithm to xor the string characters.

"Hey, but if we do the math, 3 algorithms * 255 possible char keys (0 is not used) = 765 variants of the obfuscated string"

You're right. The same string can only be obfuscated in 765 different ways. If you have a reason to need something safer (you're paranoid / your application demands increased security) you can extend the library and implement your own algorithms, using stronger obfuscation or even encryption (White-Box cryptography is in the lib's roadmap).


Where / how does it store the obfuscated strings?

One thing I find interesting about this implementation is that it doesn't store the obfuscated string in the data section of the executable.
Instead, it is statically stored into the MetaString object itself (on the stack) and the algorithm decodes it in place at runtime. This approach makes it much harder to find the obfuscated strings, statically or at runtime.

You can dive deeper into the implementation by yourself. That's a very good basic obfuscation solution and can be a starting point to a more complex one.

七七 2024-12-04 19:27:01

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

因此,您可以从以下内容开始:

  1. 加密的字符串.cpp 和加密的字符串.h(空白)
  2. 将文本文件作为输入并覆盖加密的字符串.cpp 和加密的字符串.h 的脚本或独立应用程序

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

Save yourself a heap of trouble down the line with template metaprogramming and just write a stand alone program that encrypts the string and produces a cpp source file which is then compiled in. This program would run before you compile and would produce a cpp and/or header file that would contain the encrypted string for you to use.

So here is what you start with:

  1. encrypted_string.cpp and encrypted_string.h (which are blank)
  2. A script or standalone app that takes a text file as an input and over writes encrypted_string.cpp and encrypted_string.h

If the script fails, your compiling will fail because there will be references in your code to a variable that does not exist. You could get smarter, but that's enough to get you started.

与他有关 2024-12-04 19:27:01

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

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

The reason why the examples you found can't take string literals as template argument is because it's not allowed by the ISO C++ standard. That's because, even though c++ has a string class, a string literal is still a const char *. So, you can't, or shouldn't, alter it (leads to undefined behaviour), even if you can access the characters of such an compile-time string literal.

The only way I see is using defines, as they are handled by the preprocessor before the compiler. Maybe boost will give you a helping hand in that case.

思慕 2024-12-04 19:27:01

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

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

A macro based solution would be to take a variadic argument and pass in each part of the string as a single token. Then stringify the token and encrypt it and concatenate all tokens. The end result would look something like this

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

Where _ is some placeholder for a whitespace character literal. Horribly messy and I would prefer every other solution over this.

Something like this could do it although the Boost.PP Sequence isn't making it any prettier.

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文