有时,从二进制(可执行)文件中隐藏字符串很有用.例如,从二进制文件中隐藏加密密钥是有意义的.
当我说"隐藏"时,我的意思是在编译的二进制文件中更难找到字符串.
例如,这段代码:
const char* encryptionKey = "My strong encryption key";
// Using the key
Run Code Online (Sandbox Code Playgroud)
编译后生成一个可执行文件,其数据部分中包含以下内容:
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 |
Run Code Online (Sandbox Code Playgroud)
您可以看到我们的秘密字符串可以轻松找到和/或修改.
我可以隐藏字符串......
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';
Run Code Online (Sandbox Code Playgroud)
......但这不是一个好方法.有更好的想法吗?
PS:我知道仅仅隐藏秘密并不能对付坚定的攻击者,但它总比没有好......
另外,我知道非对称加密,但在这种情况下它是不可接受的.我正在重构现有的应用程序,它使用Blowfish加密并将加密数据传递给服务器(服务器使用相同的密钥解密数据).
我无法更改加密算法,因为我需要提供向后兼容性.我甚至无法更改加密密钥.
Dmi*_*riy 51
对不起,我很抱歉.
你的答案绝对正确,但问题是如何隐藏字符串并做得很好.
我是这样做的:
#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;
}Run Code Online (Sandbox Code Playgroud)
HideString.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;\
}Run Code Online (Sandbox Code Playgroud)
HideString.h中最棘手的一行是:
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ))Run Code Online (Sandbox Code Playgroud)
让我解释一下这条线.代码:
DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t'))Run Code Online (Sandbox Code Playgroud)
BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ)生成序列:
( 'T' ^ ( 0x27 - 0 ) ) ( 'e' ^ ( 0x27 - 1 ) ) ( 's' ^ ( 0x27 - 2 ) ) ( 't' ^ ( 0x27 - 3 ) )Run Code Online (Sandbox Code Playgroud)
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 )Run Code Online (Sandbox Code Playgroud)
最后,
DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t'))Run Code Online (Sandbox Code Playgroud) 生成:
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;
}
Run Code Online (Sandbox Code Playgroud)
"我的强加密密钥"的数据如下所示:
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.........Run Code Online (Sandbox Code Playgroud)
非常感谢您的回答!
Ada*_*iss 47
正如对pavium 答案的评论所指出的,你有两个选择:
不幸的是,如果你必须求助于在代码中嵌入密钥和算法,那么这两者都不是真正的秘密,所以你通过默默无闻的方式留下了(远远弱于)安全性的替代方案.换句话说,正如您所提到的,您需要一种聪明的方法来隐藏可执行文件中的一个或两个.
以下是一些选项,但您需要记住,根据任何加密最佳实践,这些选项都不是真正安全的,并且每个选项都有其缺点:
printf()语句的格式字符串,它往往有数字,字母和标点符号.int,char,等),它的初始化(为非零值,当然)后,从什么地方采取一个字节每个变量中,并在改变之前.请告诉我们您是如何解决问题的!
编辑: 您评论说您正在重构现有代码,因此我假设您不一定自己选择密钥.在这种情况下,请遵循两个步骤:使用上述方法之一加密密钥本身,然后使用该密钥解密用户的数据.
Ste*_*n C 11
在您的代码中隐藏密码是默默无闻的安全措施.这是有害的,因为你认为你有一定程度的保护,而实际上你很少.如果有什么东西值得保障,那么值得保证.
PS:我知道它对真正的黑客不起作用,但它总比没有好......
实际上,在很多情况下,没有什么比弱安全更好.至少你确切地知道你的立场.你不需要成为一个"真正的黑客"来规避嵌入式密码......
编辑:回应此评论:
我知道关键字对,但在这种情况下是不可接受的.我重构了使用Blowfish加密的现有应用程序.传递给服务器和服务器的加密数据解密数据.我不能改变ecryption算法,因为我应该提供向后兼容性.
如果您完全关心安全性,那么保持向后兼容性是一个让您自己容易受到嵌入式密码攻击的绝对不利原因.打破与不安全的安全方案的向后兼容性是一件好事.
就像街头小孩发现你把前门钥匙放在垫子下面一样,但你继续这样做是因为爷爷希望在那里找到它.
对于 C,请查看:https : //github.com/mafonya/c_hide_strings
对于 C++ 这个:
class Alpha : public std::string
{
public:
Alpha(string str)
{
std::string phrase(str.c_str(), str.length());
this->assign(phrase);
}
Alpha c(char c) {
std::string phrase(this->c_str(), this->length());
phrase += c;
this->assign(phrase);
return *this;
}
};
Run Code Online (Sandbox Code Playgroud)
为了使用它,只需包含 Alpha 和:
Alpha str("");
string myStr = str.c('T').c('e').c('s').c('t');
Run Code Online (Sandbox Code Playgroud)
所以 mystr 现在是“测试”,并且该字符串以二进制形式从字符串表中隐藏。
您的示例根本不隐藏字符串; 字符串仍然显示为输出中的一系列字符.
您可以通过多种方式混淆字符串.有简单的替换密码,或者您可以对每个字符(例如,XOR)执行数学运算,其中结果将输入到下一个字符的操作中,等等.
目标是最终得到看起来不像字符串的数据,例如,如果您使用大多数西方语言,大多数字符值将在32-127范围内 - 所以您的目标是为手术主要是把他们大多出该范围的,这样他们就不会引起注意.
这与在荷兰阿姆斯特丹中央火车站附近解锁您的自行车一样安全.(眨眼,它消失了!)
如果您正在尝试为应用程序添加安全性,那么您注定要从一开始就失败,因为任何保护方案都将失败.您所能做的就是让黑客找到所需信息变得更加复杂.还是,一些技巧:
*)确保字符串在二进制文件中以UTF-16格式存储.
*)在字符串中添加数字和特殊字符.
*)使用32位整数数组而不是字符串!将每个转换为字符串并将它们连接起来.
*)使用GUID,将其存储为二进制文件并将其转换为要使用的字符串.
如果您确实需要一些预定义的文本,请对其进行加密并将加密值存储在二进制文件中.在运行时解密它,解密密钥是我之前提到的选项之一.
要意识到黑客会以其他方式破解你的应用程序.即使是密码学专家也无法保证安全.一般而言,唯一可以保护您的是黑客通过黑客攻击代码获得的利润,与黑客攻击的成本相比.(这些费用通常只需要很长时间,但如果需要一周的时间来破解你的应用程序而只需要2天时间来破解别的东西,那么其他东西就更有可能受到攻击.)
如果以其二进制格式存储GUID,而不是文本格式,则使用GUID是实用的.GUID长度为16个字节,可以随机生成.因此,很难猜出用作密码的GUID.但是如果你仍然需要发送纯文本,可以将GUID转换为字符串表示形式,如"3F2504E0-4F89-11D3-9A0C-0305E82C3301".(或Base64编码为"7QDBkvCA1 + B9K/U0vrQx1A ==".)但用户不会在代码中看到任何纯文本,只是一些明显随机的数据.但是,并非GUID中的所有字节都是随机的.GUID中隐藏了版本号.但是,使用GUID不是加密目的的最佳选择.它可以根据您的MAC地址或伪随机数计算,使其合理可预测.不过,它易于创建,易于存储,转换和使用.创建更长的东西不会增加更多的价值,因为黑客会试图找到其他技巧来破解安全性.这只是一个问题,他们愿意花更多的时间来分析二进制文件.
一般而言,保证应用程序安全的最重要因素是对其感兴趣的人数.如果没有人关心您的申请,那么没有人会费心去破解它.如果您是拥有5亿用户的顶级产品,那么您的应用程序将在一小时内被破解.