c++ 编译时std::string混淆

zsohkypk  于 2023-05-24  发布在  其他
关注(0)|答案(1)|浏览(202)

我试着写一个函数,用于编译时隐藏(obfuscate)文本字符串中的二进制代码。目的:禁用容易搜索和修改文本信息与二进制编辑器.
算法很简单:将字符串中的字符转换为deltas,i. e.字符串abcd必须作为a\001\001\001保存在二进制文件中。
我写了下面的代码,它的工作原理:

#include <string>
#include <stdio.h>
#include <string.h>

constexpr std::string str_encrypt(const char *str) {
    std::string rc; 
    unsigned char x = 0, c;
    while(c = *str++) {
         rc.push_back(c - x); 
        x = c;
    }   
    return rc; 
}

std::string str_decrypt(const std::string &str) {
    std::string rc; 
    unsigned char x = 0;
    for(unsigned char c : str)
        rc.push_back(x += c); 
    return rc; 
}

const std::string hello(str_encrypt("Hello, world!"));

int main(int argc, char **argv) {
    for(unsigned char c : hello)
        printf("%d ", c); 
    putchar('\n');
    printf("S=[%s]\n", str_decrypt(hello).c_str());
    return 0;
}

但是字符串在二进制文件中仍然没有“加密”:

$ c++ -std=c++2b -O2 constexpr.cpp  && strings a.out | grep Hello
Hello, world!

当我将constexpr更改为consteval时,我看到编译错误:

$ c++ -std=c++2b -O2 constexpr.cpp 
constexpr.cpp:23:36: error: ‘std::string{std::__cxx11::basic_string<char>::_Alloc_hider{((char*)(&<anonymous>.std::__cxx11::basic_string<char>::<anonymous>.std::__cxx11::basic_string<char>::<unnamed union>::_M_local_buf))}, 13, std::__cxx11::basic_string<char>::<unnamed union>{char [16]{'H', '\035', '\007', '\000', '\003', '\37777777675', '\37777777764', 'W', '\37777777770', '\003', '\37777777772', '\37777777770', '\37777777675', 0}}}’ is not a constant expression
   23 | const std::string hello(str_encrypt("Hello, world!"));

我的问题是:有可能写这样的编译时加密吗?如果有,请建议一下,怎么做。

  • 我的编译器:g++(GCC)12.2.1 20230201*
w8f9ii69

w8f9ii691#

问题是str_encrypt不是在编译时计算的。
当我将constexpr更改为consteval时,我看到编译错误
这是一个提示,当我们切换到consteval时,它要求str_encrypt是一个立即/编译时函数,编译器给出一个错误。
由于C20,std::string可以在编译时上下文中使用,如果它是一个 transient 分配,即字符串必须在退出编译时上下文之前被销毁。
在这个例子中,情况并非如此,所以str_encrypt不能是编译时函数。
如果我们把:
constexpr std::string hello(str_encrypt("Hello, world!"));
然后我们会看到一个类似于在str_encrypt上使用consteval的错误。
但是,数组或std::arrays可以是constexpr。
因此,如果我们返回的不是std::string,而是std::array,那么这个例子可以按预期工作。
这个修改后的例子可以使用g
/ clang++,需要C17。建议使用consteval,以强制编译时求值,但当然需要C20。

#include <string>
#include <stdio.h>
#include <string.h>
#include <array>

template <size_t N>
constexpr std::array<char, N> str_encrypt(const char (&str)[N]) {
    std::array<char, N> output{};
    unsigned char x = 0, c = 0;

    for (int i = 0; i < N; ++i) {
        c = str[i];
        output[i] = (c - x);
        x = c;
    }
    return output;
}

template <size_t N>
std::string str_decrypt(const std::array<char, N> &str) {
    std::string rc;
    unsigned char x = 0;
    for(unsigned char c : str)
        rc.push_back(x += c);
    return rc;
}

constexpr auto hello = str_encrypt("Hello, world!");

int main(int argc, char **argv) {
    for(unsigned char c : hello)
        printf("%d ", c);
    putchar('\n');
    printf("S=[%s]\n", str_decrypt(hello).c_str());
    return 0;
}

输出:

$ ./example
72 29 7 0 3 189 244 87 248 3 250 248 189 223
S=[Hello, world!]

字符串“Hello,world!“不是二进制的,str_encrypt函数也不是。

$ strings example | grep Hello

$ objdump -t -C example | grep str_
0000000000001302  w    F .text  00000000000000a6              std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > str_decrypt<14ul>(std::array<char, 14ul> const&)

Godbolt:https://godbolt.org/z/vPj3bW6Yz

相关问题