如何隐藏二进制代码中的字符串?

新手上路,请多包涵

有时,隐藏二进制(可执行)文件中的字符串很有用。例如,从二进制文件中隐藏加密密钥是有意义的。

当我说“隐藏”时,我的意思是让字符串在编译后的二进制文件中更难找到。

例如,这段代码:

 const char* encryptionKey = "My strong encryption key";
// Using the key

编译后生成一个可执行文件,其数据部分包含以下内容:

 4D 79 20 73 74 72 6F 6E-67 20 65 6E 63 72 79 70   |My strong encryp|
74 69 6F 6E 20 6B 65 79                           |tion key        |

您可以看到我们的秘密字符串很容易找到和/或修改。

我可以隐藏字符串…

 char encryptionKey[30];
int n = 0;
encryptionKey[n++] = 'M';
encryptionKey[n++] = 'y';
encryptionKey[n++] = ' ';
encryptionKey[n++] = 's';
encryptionKey[n++] = 't';
encryptionKey[n++] = 'r';
encryptionKey[n++] = 'o';
encryptionKey[n++] = 'n';
encryptionKey[n++] = 'g';
encryptionKey[n++] = ' ';
encryptionKey[n++] = 'e';
encryptionKey[n++] = 'n';
encryptionKey[n++] = 'c';
encryptionKey[n++] = 'r';
encryptionKey[n++] = 'y';
encryptionKey[n++] = 'p';
encryptionKey[n++] = 't';
encryptionKey[n++] = 'i';
encryptionKey[n++] = 'o';
encryptionKey[n++] = 'n';
encryptionKey[n++] = ' ';
encryptionKey[n++] = 'k';
encryptionKey[n++] = 'e';
encryptionKey[n++] = 'y';

…但这不是一个好方法。有更好的想法吗?

PS:我知道仅仅隐藏秘密对坚定的攻击者不起作用,但总比没有好……

另外,我知道不对称加密,但在这种情况下是不可接受的。我正在重构一个使用 Blowfish 加密并将加密数据传递给服务器的现有应用程序(服务器使用相同的密钥解密数据)。

无法 更改加密算法,因为我需要提供向后兼容性。我什至 无法 更改加密密钥。

原文由 Dmitriy 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2k
2 个回答

我很抱歉回答太长了。

你的答案是绝对正确的,但问题是如何隐藏字符串并做得很好。

我是这样做的:

 #include "HideString.h"

DEFINE_HIDDEN_STRING(EncryptionKey, 0x7f, ('M')('y')(' ')('s')('t')('r')('o')('n')('g')(' ')('e')('n')('c')('r')('y')('p')('t')('i')('o')('n')(' ')('k')('e')('y'))
DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t'))

int main()
{
    std::cout << GetEncryptionKey() << std::endl;
    std::cout << GetEncryptionKey2() << std::endl;

    return 0;
}

隐藏字符串.h:

 #include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/enum.hpp>

#define CRYPT_MACRO(r, d, i, elem) ( elem ^ ( d - i ) )

#define DEFINE_HIDDEN_STRING(NAME, SEED, SEQ)\
static const char* BOOST_PP_CAT(Get, NAME)()\
{\
    static char data[] = {\
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ)),\
        '\0'\
    };\
\
    static bool isEncrypted = true;\
    if ( isEncrypted )\
    {\
        for (unsigned i = 0; i < ( sizeof(data) / sizeof(data[0]) ) - 1; ++i)\
        {\
            data[i] = CRYPT_MACRO(_, SEED, i, data[i]);\
        }\
\
        isEncrypted = false;\
    }\
\
    return data;\
}

HideString.h 中最棘手的一行是:

 BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ))

让我解释一下这条线。对于代码:

 DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t'))
 BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ)

生成序列:

 ( 'T'  ^ ( 0x27 - 0 ) ) ( 'e'  ^ ( 0x27 - 1 ) ) ( 's'  ^ ( 0x27 - 2 ) ) ( 't'  ^ ( 0x27 - 3 ) )
 BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ))

产生:

 'T' ^ ( 0x27 - 0 ), 'e' ^ ( 0x27 - 1 ), 's' ^ ( 0x27 - 2 ), 't' ^ ( 0x27 - 3 )

最后,

 DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t'))

产生:

 static const char* GetEncryptionKey2()
{
    static char data[] = {
        'T' ^ ( 0x27 - 0 ), 'e' ^ ( 0x27 - 1 ), 's' ^ ( 0x27 - 2 ), 't' ^ ( 0x27 - 3 ),
        '\0'
    };
    static bool isEncrypted = true;
    if ( isEncrypted )
    {
        for (unsigned i = 0; i < ( sizeof(data) / sizeof(data[0]) ) - 1; ++i)
        {
            data[i] = ( data[i] ^ ( 0x27 - i ) );
        }
        isEncrypted = false;
    }
    return data;
}

“我的强加密密钥”的数据如下所示:

 0x00B0200C  32 07 5d 0f 0f 08 16 16 10 56 10 1a 10 00 08  2.]......V.....
0x00B0201B  00 1b 07 02 02 4b 01 0c 11 00 00 00 00 00 00  .....K.........

非常感谢您的回答!

原文由 Dmitriy 发布,翻译遵循 CC BY-SA 2.5 许可协议

正如其他人提到的那样,混淆字符串并不是实现保护的好方法。如果您不想随意检查二进制可执行文件中的字符串;然后我会做几件事。

我会创建一个大的主字符串(Ms)并将要保护的字符串的各个部分(S1)嵌入到字符串中的不同位置(p1,p2,p3 …等),使这些位置非常随机。在每个位置,我将放置一个 S1 的小子串。每个子字符串可以有不同的长度(l1、l2 等)。然后在代码中,我将动态检索每个部分并将所有部分连接在一起以在代码中重构 S1。因此,代码存储了两个整数数组:一个用于获取每个子字符串位置 [p1,p2,p3…],第二个数组将解释从每个位置读取多少个字符 [l1,l2,l3… ]。

在这种情况下,主字符串 (Ms) 将是可见的,但好奇的人将无法知道如何取回 S1,除非对代码进行逆向工程,检索两个数组并执行相同的过程。当然,主字符串 (Ms) 应该相当大并且相当随机。

在少数情况下,这样的琴弦可以变得更强。如果您有办法将不同的字符串用于字符串帮助执行的同一内部任务。例如,我可能想动态构造一个胡椒粉进行散列…我会将当前的 unix 时间戳连接到上面的(s1)并使用 s1+timestamp 作为字符串。

当然,如果要保护的字符串是一个常量,那么这种基于时间戳的附加方法是不可能的。

这是受阿里巴巴故事中的一个事件启发的:40个小偷中的一个发现了阿里巴巴在村子里的房子。他用粉笔在那间房门上写了一个“x”。幸运的是,阿里巴巴的女朋友看到了——并意识到小偷会和其他 39 人一起回来袭击房子。所以她在村里所有的房子里都画了一个“x”。这就是让我想到的,可以做同样的事情 - 只需切断要混淆的字符串并将每个部分埋入整个“村庄”主字符串中。

希望这与所要求的一致,并且会有所帮助。反馈表示赞赏。

原文由 S. Francis 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题