如何保护非托管应用程序中的字符串免受进程转储

时间:2018-09-21 06:01:26

标签: c++ security memory aes dump

首先,它不是重复帖子!

请仔细阅读,您会明白!

我正在开发一些应用程序,并且安全性对我来说非常重要...

我搜索了很多东西,做了很多方法,测试了我发现的每种方法,但并没有太大的不同!

◾️我需要保护应用程序中一些非常重要的字符串,例如 AES256的密钥和IV密钥或Base64加密。

我们都面临的两个事实:

  
      
  1. 我们都知道 .NET 的安全性非常低!实际上,一个孩子可以用手机破解它!!!

  2.   
  3. 没有 100%的安全性,我们只会通过使资源越来越难来降低访问资源的速度...

  4.   

但是我现在面临的确实是一个严重的安全问题,它可能会伤害很多客户。

好的,问时间...

我需要知道如何保持我的 AES密钥 AES IV 转储器和内存提取器的安全,即使我不能能够创建非常高的安全性,我需要通过单击一键来保护它!

My application is a Unmanaged C++

我所做的尝试:

A)使用XOR方法:

我使用xor方法创建我的字符串,但是可以通过 string2转储程序一键提取它!

了解更多:https://github.com/Jyang772/XOR_Crypter

B)使用分隔字符串:

这是一个例子:

char Departed_String1[4];
int nmbr1 = 0;
char Departed_String1_dep[4];
int nmbr1_dep = 0;
Departed_String1[nmbr1++] = 'T';
Departed_String1_dep[nmbr1_dep++] = 'U';
Departed_String1[nmbr1++] = 'E';
Departed_String1_dep[nmbr1_dep++] = 'C';
Departed_String1[nmbr1++] = 'S';
Departed_String1_dep[nmbr1_dep++] = 'h';
Departed_String1_dep[nmbr1_dep++] = 'U';
Departed_String1[nmbr1++] = 'T';
Departed_String1_dep[nmbr1_dep++] = 'y';
Departed_String1_dep[nmbr1_dep++] = '8';

也可以使用调试器轻松打开它!

C)我自己的方法:LostChars

const char* teststring_src = "HeVthsNiNVrtTuODkhPgDkmCxQApD:feSCiQDDWePakOTtFcLzTSbKTaZwsUnpeYMlndoYXJyXBpSSSNGsWblpQhUKKCzWUfHnNxQtNsXXnFzXtSGzIBYjCIlSMbEoqwJfArwrqfeLRANEYgjdknHuzSIzgiglRBFEDmFqDBBbgUQD VvDjnQPdFKDYTSxnDXTqKdHtOCayMbACkmQLJqgHtBtTj CoiXxETJwiIkgMgaVaskZtLiWDotsTldTHdBiJiGIfCmjjjBdAbIFiJFFhXPeAjiKbPuktOmiIuhqDkIhMxFBGZevIIjoOuKfddWgUmdFbNfShAIhphPYKhpxtimPhmDatYlOWCXBXQbkFDY QaKyRMhHznNJClQjDmevKUSnfoCXfWplSDzVWxMOkGkntVMmijf QzbalAbRokBAXXfDvevyHbOmAaUIKMBivJVrTxALngQjGShZdzTsZJwIooYLIuqxcTjELFPRFAAzqE fnIznpwtUzEXFBm"; 
std::string teststring = (std::string(1,teststring_src[468])+std::string(1,teststring_src[4])+std::string(1,teststring_src[230])+std::string(1,teststring_src[249])+std::string(1,teststring_src[174])+std::string(1,teststring_src[343])+std::string(1,teststring_src[239])+std::string(1,teststring_src[365])+std::string(1,teststring_src[41])+std::string(1,teststring_src[417])+std::string(1,teststring_src[227])+std::string(1,teststring_src[62])+std::string(1,teststring_src[469])+std::string(1,teststring_src[248])+std::string(1,teststring_src[220])+std::string(1,teststring_src[329])+std::string(1,teststring_src[504])+std::string(1,teststring_src[453])+std::string(1,teststring_src[223])+std::string(1,teststring_src[66])+std::string(1,teststring_src[156])+std::string(1,teststring_src[496])+std::string(1,teststring_src[29])+std::string(1,teststring_src[20]));

不能完美运行仍会转储!

D)使用十六进制代替String:

wchar_t string[7] = { 0x0118, 0x0154, 0x010C, 0x012C, 0x0154, 0x0084, 0x0000 };

for (unsigned int GUEvK = 0, GNsdj = 0; GUEvK < 7; GUEvK++)
{
        GNsdj = string[GUEvK];
        GNsdj = ((GNsdj << 14) | ( (GNsdj & 0xFFFF) >> 2)) & 0xFFFF;
        string[GUEvK] = GNsdj;
}

wprintf(string);

好吧:) This guy开发了一款软件来保护弦乐... ... 哇,棒极了! 50EURO 很划算!但是这里有一个小问题!

通过 Process Explorer

,只需 2单击和1秒即可转储

这是一个例子:

unsigned char myKey[48] = { 0xCF, 0x34, 0xF8, 0x5F, 0x5C, 0x3D, 0x22, 0x13, 0xB4, 0xF3, 0x63, 0x7E, 0x6B, 0x34, 0x01, 0xB7, 0xDB, 0x89, 0x9A, 0xB5, 0x1B, 0x22, 0xD4, 0x29, 0xE6, 0x7C, 0x43, 0x0B, 0x27, 0x00, 0x91, 0x5F, 0x14, 0x39, 0xED, 0x74, 0x7D, 0x4B, 0x22, 0x04, 0x48, 0x49, 0xF1, 0x88, 0xBE, 0x29, 0x1F, 0x27 };

myKey[30] -= 0x18;
myKey[39] -= 0x8E;
myKey[3] += 0x16;
myKey[1] += 0x45;
myKey[0] ^= 0xA2;
myKey[24] += 0x8C;
myKey[44] ^= 0xDB;
myKey[15] ^= 0xC5;
myKey[7] += 0x60;
myKey[27] ^= 0x63;
myKey[37] += 0x23;
myKey[2] ^= 0x8B;
myKey[25] ^= 0x18;
myKey[12] ^= 0x18;
myKey[14] ^= 0x62;
myKey[11] ^= 0x0C;
myKey[13] += 0x31;
myKey[6] -= 0xB0;
myKey[22] ^= 0xA3;
myKey[43] += 0xED;
myKey[29] -= 0x8C;
myKey[38] ^= 0x47;
myKey[19] -= 0x54;
myKey[33] -= 0xC2;
myKey[40] += 0x1D;
myKey[20] -= 0xA8;
myKey[34] ^= 0x84;
myKey[8] += 0xC1;
myKey[28] -= 0xC6;
myKey[18] -= 0x2A;
myKey[17] -= 0x15;
myKey[4] ^= 0x2C;
myKey[9] -= 0x83;
myKey[26] += 0x31;
myKey[10] ^= 0x06;
myKey[16] += 0x8A;
myKey[42] += 0x76;
myKey[5] ^= 0x58;
myKey[23] ^= 0x46;
myKey[32] += 0x61;
myKey[41] ^= 0x3B;
myKey[31] ^= 0x30;
myKey[46] ^= 0x6C;
myKey[35] -= 0x08;
myKey[36] ^= 0x11;
myKey[45] -= 0xB6;
myKey[21] += 0x51;
myKey[47] += 0xD9;

您只需要运行您的应用程序,然后在Process Explorer中右键单击您的应用程序,然后单击完整转储

...轰!所有的字符串就在这里!

**我尝试了很多其他方法,但是一切都在Full Dump中... **

真的没有办法防止此安全漏洞吗? 感谢您的帮助!

1 个答案:

答案 0 :(得分:3)

首先,请记住,如果攻击者可以将调试器附加到您的进程中,并且您的进程必须是负责解密的进程,那么您就已经失去了定义。您获得的最好的结果就是“默默无闻的安全性”。更安全的方法通常需要将工作的一部分工作转移给攻击者无法访问的参与者-无论是外部防篡改的加密设备还是远程服务。

但是最重要的是,任何方法都容易被转储,因为您迟早需要访问纯文本,并且该方法将存在于内存中,以便任何使用调试器的人都可以读取。

话虽如此,您可以通过仅按需解密字符串并在之后立即将其擦除来减轻最后一个问题;这为攻击者缩小了机会窗口。因此,您不需要一个解密静态缓冲区的函数,而是想要一个用所需的机密填充客户端提供的缓冲区的函数(并确保客户端在释放该缓冲区之前实际上将该缓冲区的内容清零-请使用类似于memset,它使用volatile指针来确保未对擦除进行优化)。

关于将密钥实际存储到可执行文件中,可以使用多种方法来产生混淆。使用普通的初始化程序初始化全局变量会将相关数据放在可执行文件的.rodata节中,这是我首先要查看的地方;任何具有足够高的熵的字符串都是无用的,以便进一步研究它的使用位置(IDA反汇编程序使此操作特别容易)。我想到的一种可能性是实际上是从函数一次初始化一个字节的缓冲区(可能是指向缓冲区volatile的指针,以确保编译器不会产生奇怪的花样)。这应该将您的数据直接放入代码部分,这不太可能令人怀疑,并且由于与操作码的交错,应该将熵保持在较低的水平。

可以使用一些简单的技巧对这些数据进行进一步加密-例如,将其与简单的XorShift PRNG的输出进行XOR运算;这再次加剧了混乱,但是XorShift是在少数指令中实现的,因此您没有额外的依赖项或“可疑”代码。

如果要隐藏解密密钥,则另一个重要点是不要使用操作系统提供的加密原语,而应静态地链接您的实现,并且可能不使用AES-NI或其他明显的赠品。如果我试图提取解密密钥,那么我首先将所有相关的CryptoAPI插入调试器,然后在可执行文件中查找密码指令以找到最有趣的区域。