如何在exe或dll中隐藏字符串?

36

我发现从二进制文件中提取硬编码字符串是可能的。
例如,Process Explorer 的属性视图显示了所有长度超过 3 个字符的字符串。

以下是我编写的一个简单可执行文件的代码,用于测试:

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    _TCHAR* hiddenString1 =_T("4537774B-CC80-4eda-B3E4-7A9EE77991F5");
    _TCHAR* hiddenString2 =_T("hidden_password_or_whatever");
    for (int i= 0; i<argc; i++) {
        if (0 == _tcscmp(argv[i],hiddenString1)) {
            _tprintf (_T("The guid argument is correct.\n")); }
        else if (0 == _tcscmp(argv[i],hiddenString2)) {
            _tprintf (_T("Do something here.\n")); }
    }

    _tprintf (_T("This is a visible string.\n"));
    //Keep Running
    Sleep(60000);
    return 0;
}

这些字符串可以从相应的可执行文件中清晰地提取出来:
alt text

我认为这样太容易发现了。

我的问题是:

  1. 如何简单地隐藏可执行文件中的hiddenString1hiddenString2
  2. 有没有比使用一些隐秘输入更安全的“作弊码”使用方式?

1
这个问题是关于 GUID 还是关于字符串的一般性问题? - xtofl
GUID 只是一个例子:我也想隐藏 HTTP 请求的 URL。 - Winz
2
隐藏HTTP请求的URL并不起作用。任何使用嗅探器的人都能够看到您的应用程序正在做什么,而这样做比检查进程的内存要容易得多。 - JoeG
如何在二进制代码中隐藏字符串? - phuclv
您可以参考这里 - 它解释了什么是功能加密和完全同态加密。 - StefanS
9个回答

35

欢迎来到防御性编程的更广阔世界。

有几个选项,但我认为所有选项都依赖于某种形式的混淆;虽然不完美,但至少是一些方法。

  1. 你可以将文本存储在其他二进制形式(十六进制?)中,而不是直接的字符串值。

  2. 你可以加密存储在应用程序中的字符串,然后在运行时解密它们。

  3. 你可以将它们分散在代码中的各个点,并在以后重新组合。

或者采用其中的一些组合。

请记住,有些攻击超出了查看实际二进制的范围。有时候,他们会在程序运行时调查内存地址空间。MS提出了一个称为.Net 2.0中的SecureString的东西。目的是在应用程序运行时保持字符串加密。

第四个想法是不要将字符串存储在应用程序本身中,而是依赖于提交给你控制的服务器的验证代码。在服务器上,你可以验证它是否是合法的“作弊代码”。


2
不错的选择。由于提问者特别关注隐藏Windows GUID字符串(即十六进制数),因此该数据也可以存储为几个整数。 - Drew Dormann
我想在您的好答案中添加加盐密码的可能性。 - AndersK
1
@CyberSpock:我不确定加盐的目的是什么。字符串(无论是否加盐)都存储在二进制中,因此可能会被发现。这意味着只有两个解决方案:混淆以至少使其更难或根本不在应用程序中存储它。 - NotMe
如果密码被加盐,那么它会更难以被发现,因为这需要使用算法来比较密码,但无论如何,这都是徒劳的。 - AndersK
我刚试着将我的字符数组(char str[] = {'H', 'i', '\0'})转换为十六进制字节序列,它们可以以完全相同的方式被发现。 - duckduckgo
@duckduckgo,这是因为在内存中(两种情况下)都是以这种方式存储的。一个简单的解决方法是选择不同的数据类型,如short或int,但仍然可以看到十六进制代码。然后,您可以设计自己的混淆技术,应用于十六进制值,例如位移、添加垃圾等。 - ActiveX

20

有许多方法可以在可执行文件中隐藏数据。其他人已经发布了好的解决方案,有些更强大,有些不太一样。我不会添加到那个列表中。

只需注意:这是一场猫和老鼠的游戏:不可能保证没有人会发现你的“秘密”。

无论你使用多少加密或其他技巧;无论你付出多少努力或金钱。无论有多少“NASA / MIT / CIA / NSA”类型参与其中。

这都归结为简单的物理学:
如果任何用户都无法从可执行文件中提取出你的秘密并“解密”它,则计算机也无法解密它,你的程序将无法使用它。任何具有足够动机的熟练开发人员都会找到揭示秘密的方法。

一旦你将可执行文件交给用户,他们就拥有发现秘密所需的一切。

你能希望的最好结果是使揭示秘密变得困难以至于你从知道秘密中获得的任何好处都变得不值得麻烦。

因此,如果仅仅是不想公开数据,或者成为公开数据的后果只是“不方便”,那么尝试隐藏数据是可以的。但不要想着在你的程序中隐藏“主客户数据库密码”、私钥或其他关键秘密。你做不到。

如果你确实有真正的关键秘密信息,你的程序将以某种方式需要它,但绝不能成为公共信息(例如私钥),那么你需要让你的程序与受你控制的远程服务器通信,应用适当的身份验证和授权控件(也就是说,确保只有批准的人或计算机能够向服务器发出请求),并让该服务器保持秘密并使用它。


1
说得好。而且真正的黑客是非常强大的,你不会用小技巧阻止他们。他们会注意到你处理字符串的热点,并耐心等待应用程序解密它以查看真实内容。 - toto

10

最简单的方法是使用类似异或或rot-13这样的东西对它们进行加密,然后在需要使用时即时解密。这将消除对它们的随意查看,但无法阻止有经验的反向工程师。


6
对于经验丰富的逆向工程师而言,没有什么能真正阻止他们做到这一点 ;) - n3rd
2
由于他们必须在内存中拥有解密密钥才能运行程序,因此AES也无法阻止他们。 - Martin Beckett

6
除了Chris提到的方法,您还可以使用哈希算法。如果您只想检查是否指定了正确的ID,则实际上不需要在程序中存储整个ID。

  • 创建一个字符串/密码/ID的哈希值(MD5,SHA等),可能会添加“盐”值。将其存储在您的程序中。
  • 运行程序时,在输入的字符串/密码/ID上执行同样的算法,并比较两个哈希值以查看它们是否匹配。
这样,实际文本就不会存储在您的程序中,并且他们无法通过反向工程找出原始文本,因为哈希算法只是单向的。

请注意,某些哈希算法(例如md5)的碰撞现在可以相对容易地找到。 - Josef Pfleger
我不确定这对于这个用途是一个问题。 - Head Geek
1
你可以更进一步,只做公钥加密的部分。这样就能解决碰撞问题。这也是其中一种更安全的方式 - 即使在通过十六进制编辑器挖掘出来之后,它仍然像你使用的加密算法一样安全。 - Ben Barden

5

我也想要隐藏http请求的URL。

如果是你的应用程序在发出请求,那么隐藏这一点毫无意义。运行像fiddler、http analyzer或其他数十种免费和易于获取的方法之一的应用程序将显示你的应用程序正在创建的所有流量。


真的很糟糕。:( - Zeeshan
@ZeeshanMahmood 哪个部分? - CaffGeek
意思是,我们不能通过其他方式在代码中隐藏URL,因为它们将会被揭示出来。 - Zeeshan
你可以将它们隐藏起来,但一旦你访问它,它就会变得可见。 - CaffGeek

4

最好的方法是将您想要隐藏的密码或其他字符串编码为字符数组。例如:

std::string s1 = "Hello";   // This will show up in exe in hex editor
char* s2 = "World";   // this will show up in exe in hex editor
char s3[] = {'G', 'O', 'D'}; // this will not show up in exe in hex editor.

2
在这个简单的C程序中 char s3[] = {'G', 'O', 'D'}; int n = sizeof(s3)/sizeof(s3[0]); printf("%d\n", n);,我在可执行文件中使用十六进制编辑器找到了文本 -> .D$.G.D$.O.D$.D.D$. - Tuksn
我发现如果数据在堆中,可执行文件中的字符串是明文的,如果在栈中,字符串会消失。我知道这非常依赖于编译器/平台(我在win 10上)。 - Zac

3
这是我用于此目的的方法。首先,我使用SysinternalsStrings工具来显示EXE或DLL中的字符串。 然后,我使用文章中的以下小工具将这些字符串替换为作为算术表达式存储的混淆字符数组:例如:而不是字符串: "this is a test" 我将放置以下代码:(由该工具自动生成)
WCHAR T1[28];
 T1[22] = 69;
 T1[15] = 121 - 17;
 T1[9] = L':' + -26;
 T1[12] = L't' - 1;
 T1[6] = 116 - 1;
 T1[17] = 117 - 12;
 T1[3] = 116 - 1;
 T1[14] = L'' - 3;
 T1[13] = L'w' - 3;
 T1[23] = 69;
 T1[26] = L'Y' + 3;
 T1[19] = 111 + 0;
 T1[21] = L'k' - 34;
 T1[27] = L'\\' - 8;
 T1[20] = L'B' + 32;
 T1[4] = 42 + -10;
 T1[25] = L'm' - 17;
 T1[16] = L'H' + 18;
 T1[18] = L'A' + 56;
 T1[24] = 68;
 T1[1] = 105 - 1;
 T1[11] = L'k' - 6;
 T1[10] = 66 + 50;
 T1[2] = 105;
 T1[0] = 117 - 1;
 T1[5] = L'k' - 2;
 T1[8] = 89 + 8;
 T1[7] = 32;

这个问题有很多解决方案,包括我的解决方案在内都不是完美的,但有方法可以混淆、伪装和隐藏敏感字符串。当然你可以在运行时加密和解密它们(参见本文),但我认为更重要的是让这些字符串在可执行文件的位和字节中消失,这样做是有效的。运行我的工具后,你在可执行文件中找不到“this is a test”。


2

如果有特定字符串不想让人看到,可以在运行时加密和解密。

如果不想让人看到您的 GUID,则可以从字节构建,而不是从字符串构建:

const GUID SecretGuid = 
      { 0x4537774B, 0xCC80, 0x4eda, { 0x7A, 0x9E, 0xE7, 0x79, 0x91, 0xF5 } };

2

你所有的秘密代码都是GUID吗?还是这只是一个例子?

或许可以将您的秘密以二进制GUID形式存储:

const GUID SecretGuid =
    { 0x4537774B, 0xCC80, 0x4eda, { 0x7A, 0x9E, 0xE7, 0x79, 0x91, 0xF5 } };

将提供的 GUID 从字符串转换为二进制格式,然后比较这两个二进制 GUID。


GUID仅是一个示例。我也想隐藏HTTP请求的URL。 - Winz
8
有知识的人可以在您的可执行文件中查找字符串,然后启动嗅探器并观察您的应用程序发出的HTTP请求。事实上,在寻找字符串之前,我会首先尝试使用嗅探器。 - Martin Thurau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接