В этом ответе я буду использовать Visual Studio 2017 Community Edition , потому что я хотел убедиться, что среда разработки полностью совместима с Windows.
Я представлю пять методов, от наиболее поддерживаемых до менее. Конечно, фокус этого ответа строго ограничен целью «совместного использования» переменной C ++ с внешним инструментом.
Безопасность такой операции - это отдельная тема и в любом случае тщетная попытка.
Метод 1 - Ресурсы
Windows API 1 и PE 2 поддерживают встраивание ресурсов в исполняемый файл 3 .
Ресурсы обычно представляют собой изображения, значки или локализованные строки, но они могут быть чем угодно - включая необработанные двоичные данные.
С помощью Visual Studio довольно просто добавить ресурс: в Solution Explorer> Файлы ресурсов> Добавить> Новый элемент> Ресурс> Файл ресурса (.rc)
Откроется Представление ресурса , щелкните правой кнопкой мыши Resource.rc и выберите Добавить ресурс ... .
Можно создать стандартные ресурсы, но нам нужен тип Custom ... , который мы можем назвать RAW
.
Это создаст новый двоичный ресурс, даст ему идентификатор и создаст несколько файлов в решении.
Возвращаясь к Solution Explorer , мы можем увидеть эти новые файлы и в конечном итоге отредактировать файл .bin с лучшим шестнадцатеричным редактором, чем встроенный VS.
Особый интерес представляет файл resource.h
, который мы можем включить для определения идентификатора ресурса, в моем случае это было IDR_RAW1
.
После того, как файл bin создан, мы готовы прочитать его в приложении, шаблон, который нужно использовать, является обычным - мне не хочется перебирать эти API еще раз, чтобы найти новый ответ, поэтому я сделаю ссылку Официальная документация и пример кода:
#include <Windows.h>
#include "resource.h"
int WINAPI WinMain(HMODULE hModule, HMODULE hPrevModule, LPSTR lpCmdLine, int showCmd)
{
//Get an handle to our resource
HRSRC hRes = FindResource(hModule, MAKEINTRESOURCE(IDR_RAW1), "RAW");
//Load the resource (Compatibility reasons make this use two APIs)
HGLOBAL hResData = LoadResource(hModule, hRes);
LPVOID ptrData = LockResource(hResData);
/*
ptrData is out binary content. Here is assumed it was a ASCIIZ string
*/
MessageBox(NULL, (LPCSTR)ptrData, "Title", MB_ICONINFORMATION);
return 0;
}
Ресурсы хороши тем, что позволяют легко интегрировать их с другими инструментами автоматической сборки: легко добавить шаг сборки до того, как ресурсы скомпилированы, чтобы генерировать их на лету.
Также очень легко изменять их после создания исполняемого файла - CFF Explorer III - это простой и эффективный инструмент для редактирования ресурсов модуля PE.
Можно даже полностью заменить ресурс, не ограничивая себя тем, что новый ресурс будет иметь тот же размер, что и старый.
Просто откройте модуль в CFF, выберите Редактор ресурсов , найдите необработанный ресурс и отредактируйте / замените его. Затем сохраните.
Метод 2 - Экспорт ПЭ
Исполняемые файлы - это обычный PE-модуль, такой же, как Dlls, разница действительно небольшая.
Как Dlls может экспортировать функции и переменные 4 , так и exes.
С помощью VC ++ можно пометить экспортируемый символ как __declspec(dllexport)
:
#include <Windows.h>
__declspec(dllexport) char var[30] = "Hello, cruel world!";
int WINAPI WinMain(HMODULE hModule, HMODULE hPrevModule, LPSTR lpCmdLine, int showCmd)
{
MessageBox(NULL, var, "Title 2", MB_ICONINFORMATION);
return 0;
}
Ситуация с C ++ мало затронута.
Редактирование модуля PE менее удобно для пользователя, но все еще очень легко для всех.
С CFF откройте каталог экспорта, все экспорты будут перечислены.
Компиляторы C ++ должны манипулировать именами переменных, когда они могут быть разделены из-за поддерживаемых ими функций C ++ - так что вы не найдете в экспорте красивое имя, например var
, но что-то вроде ?var@@3PADA
.
Имя экспорта на самом деле не соответствует какой-либо цели в этом контексте, но вы должны быть в состоянии определить правильный экспорт.
Это должно быть легко, так как очень вероятно, что это будет всего один.
CFF покажет вам функцию RVA, это RVA (относительно базы изображения) переменной, вы можете легко преобразовать ее в смещение файла или просто использовать Address convert встроенный в CFF.
Это откроет шестнадцатеричный редактор и укажет на правильные байты.
Метод 3 - Файлы карт
Если вы не хотите, чтобы экспорт PE указывал прямо на вашу переменную, вы можете указать VS сгенерировать файл MAP .
В файлах карты будут перечислены все символы, экспортированные объектным файлом (примечание: объектный файл , а не модуль PE).
Таким образом, вы должны убедиться, что переменная, в этом случае, экспортируется вашим модулем перевода - это случай по умолчанию для «глобальных» переменных, но не забудьте присоединить модифицированную связь static
к ней и в конечном итоге сделать ее volatile
, чтобы компилятор не устранял его на этапе свертывания констант.
#include "Windows.h"
//extern is redundant, I use it only for documenting the intention
//volatile is a hack to prevent constant folding in this simple case
extern volatile int var2 = 3;
int WINAPI WinMain(HMODULE hModule, HMODULE hPrevModule, LPSTR lpCmdLine, int showCmd)
{
//A simple use of an int
return var2;
}
A MAP файл будет сгенерирован в выходном каталоге вместе с exe, внутри него присутствует строка, подобная этой:
0003:00000018 ?var2@@3HC 00403018 Source.obj
Это дает вам VA переменной (403018
), которую вы можете использовать в CFF Переводчик адресов .
Метод 4 - сканирование ПЭ
Вы можете инициализировать переменную уникальным значением.
Для этого переменный размер должен быть достаточно большим, чтобы вероятность того, что случайная последовательность битов одинакового размера окажется с одним и тем же значением, ничтожна.
Например, если var является QWORD, вероятность нахождения в модуле PE другого QWORD с таким же значением очень мала (один из 2 64 ), но если var является байтом, тогда вероятность только один из 256.
В конце концов, добавьте маркерную переменную (я бы использовал случайный массив из 16 байтов) перед переменной, чтобы пометить ее (то есть действовать как уникальное значение).
Чтобы изменить PE, используйте шестнадцатеричный редактор для поиска этого уникального значения, это даст вам смещение переменной для редактирования.
Метод 5 - Обратный инжиниринг
После каждого выпуска выполняйте обратную обработку приложения (это легко, поскольку вы можете отлаживать его с помощью VS вместе с источниками) и посмотрите, где компилятор выделил переменную.
Обратите внимание на RVA (примечание: RVA не VA, VA является переменным), а затем используйте CFF для редактирования исполняемого файла.
Это требует обратного инженерного анализа каждый раз, когда создается новая версия.
1 Если быть точным, "Win32" API.
2 Я настоятельно советую читателю, по крайней мере, привыкнуть к формату файла PE, как я должен предположить, чтобы этот ответ был кратким и кратким. Отсутствие понимания формата файла PE, скорее всего, приведет к отсутствию понимания вопроса в целом.
3 На самом деле, в любом модуле PE.
4 Символы в целом.