Как скрыть строки в exe или dll?

Я обнаружил, что можно извлечь жестко закодированные строки из двоичного.
Например, представление свойств Процесс 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. как просто скрыть hiddenString1 или hiddenString2 в исполняемые?
  2. есть ли более безопасное способ использования "чит-кода", чем с какой-то неясный скрытый вход?

9 ответов


Добро пожаловать в мир защитного программирования.

есть несколько вариантов, но я считаю, что все они зависят от какой-то формы обфускации; которая, хотя и не идеальна, по крайней мере, что-то.

  1. вместо прямого строкового значения вы можете сохранить текст в какой-либо другой двоичной форме (hex?).

  2. вы можете зашифровать строки, которые хранятся в вашем приложении, а затем расшифровать их в время.

  3. вы можете разделить их по различным точкам в коде и восстановить позже.

или какая-то их комбинация.

имейте в виду, что некоторые атаки пойти дальше, чем в двоичном. Иногда они будут исследовать адресное пространство памяти программы во время ее работы. MS придумал что-то под названием SecureString в .Net 2.0. Цель состоит в том, чтобы сохранить строки зашифрованными пока приложение работает.

четвертая идея-не хранить строку в самом приложении, а полагаться на код проверки, который будет отправлен на сервер, которым вы управляете. На сервере вы можете проверить, является ли это законным "чит-кодом" или нет.


есть много способов, чтобы непонятных данные в исполняемый файл. Другие здесь опубликовали хорошие решения - некоторые сильнее других. Я не буду добавлять к этому списку.

просто имейте в виду: это все игра в кошки-мышки: это невозможно to гарантия что никто не узнает ваш "секрет".

независимо от того, сколько шифрования или других уловок, которые вы используете, независимо от того, сколько усилий и денег вы вкладываете в него. Сколько бы их ни было Типы" NASA/MIT/CIA/NSA " участвуют в его сокрытии.

все сводится к простой физике:
Если бы это было невозможно любой пользователь, чтобы вытащить свой секрет из исполняемого файла и" отобразить " его, тогда компьютер также не сможет отобразить его, и ваша программа не сможет его использовать. Любой умеренно квалифицированный разработчик с достаточным стимулом найдет способ раскрыть секрет.

момент, когда вы передали свой исполняемый файл пользователь, у них есть все, что нужно, чтобы узнать секрет.

лучшее, на что вы можете надеяться, это сделать это так сложно чтобы раскрыть секрет, что любые преимущества, которые вы можете получить от знания секрета, не стоят хлопот.

таким образом, это нормально, чтобы попытаться скрыть данные, если это просто "нехорошо" для него, чтобы быть публичным, или если последствия его обнародования будут просто "неудобными". Но даже не думайте скрывать в своей программе "пароль к вашему master client database", закрытый ключ или какой-либо другой важный секрет. Ты просто не можешь.--3-->

Если у вас есть действительно критически секретная информация, которая так или иначе понадобится вашей программе, но никогда не должна стать общедоступной информацией (например, закрытый ключ), вам нужно будет поговорить с удаленным сервером под вашим контролем, применить соответствующие средства аутентификации и авторизации (то есть, убедитесь, что только утвержденные людей или компьютеры могут сделать запрос сервер), и пусть этот сервер хранит секрет и использует его.


самый простой способ-зашифровать их с помощью чего-то тривиального, например xor или rot-13, а затем расшифровать их на лету, когда они используются. Это устранит случайный просмотр их, но это не остановит никого с большим опытом обращения вспять.


в дополнение к этим методам Крис упоминает, что вы также можете использовать алгоритм хэширования. Если все, что вы хотите сделать, это проверить, был ли указан правильный идентификатор, вам не нужно хранить весь идентификатор в вашей программе.

  • создайте хэш (MD5, SHA и т. д.) строки/пароля/идентификатора, с которым вы хотите сравнить, возможно, добавьте к нему значение "соль". Сохраните это в своей программе
  • при запуске программы выполните тот же алгоритм на входной строке / пароле / id и сравните два хэша, чтобы посмотреть, совпадают ли они.

таким образом, фактический текст никогда не сохраняется в вашей программе, и они не могут перепроектировать вашу программу, чтобы узнать, что был исходный текст, потому что хэш-алгоритмы являются односторонними.


есть URL-адреса для http-запросов, которые я тоже хотел бы скрыть.

Если ваше приложение делает запрос, нет смысла скрывать это. Запуск приложения, такого как fiddler, http analyzer или один из десятков других бесплатных и доступных методов, покажет весь трафик, создаваемый вашим приложением.


будут ли все ваши секретные коды GUID или это просто пример?

возможно, сохраните свой секрет как двоичный guid:

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

затем преобразуйте предоставленный guid из строки в двоичный формат и сравните два двоичных GUID.


Если есть определенная строка, которую вы не хотите, чтобы люди могли видеть, зашифруйте ее и расшифруйте во время выполнения.

Если вы не хотите, чтобы люди видели ваш GUID, тогда постройте его из байтов, а не из строки:

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

лучшее, что вы можете сделать, это закодировать пароль или другую строку, которую вы хотите скрыть как массив символов. Например:

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.

вот метод я использую для этой цели. Во-первых, я использую строки на Sysinternals для отображения строк в EXE или DLL. Затем я использую после небольшой инструмент (см. статьи) для замены этих строк скремблированным массивом символов, хранящихся в виде арифметического выражения: например: вместо строки: "это тест" Я поместите следующий код: (который автоматически генерируется инструмент)

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;

существует много решений этой проблемы, и ни одно из них (включая мое) не идеально, однако есть способы скремблировать, замаскировать и скрыть чувствительные строки. Вы можете, конечно, зашифровать их и расшифровать во время выполнения (см. Эту статью), но я считаю более важным, чтобы эти строки исчезли среди битов и байтов исполняемого файла, и он работает. После запуска моего инструмента вы не найдете "это тест" в исполняемом файле.