Управление версиями исполняемого файла и его модификация во время выполнения

#c #c #binary #io #versioning

#c #c #двоичный #io #управление версиями

Вопрос:

Что я пытаюсь сделать, так это подписать первые 32 байта моего скомпилированного исполняемого файла подписью версии, скажем «1.2.0», и мне нужно изменить эту подпись во времени выполнения, имея в виду, что:

  • это будет сделано самим исполняемым файлом
  • исполняемый файл находится на стороне клиента, что означает, что перекомпиляция невозможна
  • использование внешнего файла для отслеживания версии вместо ее кодирования в самом двоичном коде также не является вариантом
  • решение должно быть независимым от платформы; Я знаю, что Windows / VC позволяет вам создавать версии исполняемого файла с использованием ресурса .rc, но я не знаю эквивалента для Mac (возможно, Info.plist?) и Linux

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

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

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

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

Update2: Спасибо за все ваши ответы и комментарии, на самом деле есть 2 способа сделать это: либо использовать внешний ресурс для отслеживания версии, либо встроить его в сам двоичный файл основного приложения. Я мог выбрать только 1 ответ на SO, поэтому я выбрал тот, с которым собираюсь, хотя это не единственный. 🙂

Комментарии:

1. Вы пытаетесь изменить подпись на диске на в памяти?

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

3. @Seth: как еще он мог бы сохраняться на диске после завершения процесса? @Neil: В общем, именно поэтому я спрашиваю, могу ли я «зарезервировать» эти байты или указать, куда они записываются, когда я их компилирую?

4. То, что вы хотите сделать, невозможно сделать переносимым. Рассмотрим мою ситуацию с использованием Linux: я устанавливаю все игровые лаунчеры под специальной учетной записью пользователя ‘game’. У этой учетной записи нет прав на запись в исполняемую программу запуска. Я не запускаю игру под учетной записью, которую использую для установки.

5. @Heath: Понятно, итак, работая от имени пользователя ‘game’, вы можете изменять все игровые файлы, но не сам исполняемый файл? Значит, вместо этого я должен исправлять исполняемый файл игры, а не программу запуска?

Ответ №1:

Современные версии Windows не позволят вам обновлять установленный программный файл, если вы не работаете с правами администратора. Я полагаю, что все версии Windows полностью блокируют изменения в работающем файле; вот почему вы вынуждены перезагружаться после обновления. Я думаю, вы просите о невозможном.

Комментарии:

1. Итак, можно ли сохранить версию во внешнем файле? Я боюсь, что пользователи подделают файл, и у меня не будет способа обнаружить это, тогда весь процесс исправления будет нарушен.

2. Не могут ли они также подделать exe-файл? Пользователи могут взломать все, если они действительно захотят. Вы можете только усложнить для них задачу, но не сделать ее невозможной, и во многих случаях эта защита от пользователей будет обеспечиваться ценой кросс-платформенной совместимости. Если вы действительно хотите, чтобы с информацией о версии было сложно работать кроссплатформенным способом, криптографически подпишите номер версии вместе с хэшем всех исправленных файлов для этой версии.

3. Определенно. По правде говоря, я могу с таким же успехом сохранить его как обычный текст, в конце концов, если они что-то испортят с ним, клиент не будет проверен, и они в конечном итоге пропустят исправления, а сохранение его в виде обычного текста облегчает исправление на самом деле. Мне просто интересно, как это делают другие, например, лаунчеры Blizzard.

Ответ №2:

Это будет немного непросто по ряду причин. Во-первых, запись в первые N байтов двоичного файла, скорее всего, приведет к нарушению информации заголовка двоичного файла, которая используется загрузчиком программы для определения того, где в файле расположены сегменты кода и данных и т.д. Это будет отличаться на разных платформах (см. Сравнение формата ELF и исполняемого формата) — существует множество различных стандартов двоичного формата.

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

Ответ №3:

Если ваше приложение предназначено для исправления игры, почему бы не встроить туда версию, пока вы этим занимаетесь? Вы можете использовать строку, подобную @Juliano shows, и изменять ее с помощью программы исправления, пока игра не запущена — что должно быть в любом случае, если вы в данный момент производите исправление. 😛


Редактировать: Если вы работаете с Visual Studio, действительно легко встроить такую строку в исполняемый файл с помощью #pragma comment , согласно этой странице MSDN:

 #pragma comment(user, "Version: 1.4.1")
  

Поскольку второй аргумент является простым строковым литералом, его можно объединить, и у меня была бы версия в простом #define :

 // somehwere
#define MY_EXE_VERSION "1.4.1"
// somewhere else
#pragma comment(user, "Version: " MY_EXE_VERSION)
  

Комментарии:

1. Я только что открыл двоичный файл игры World of Warcraft — не лаунчер, в шестнадцатеричном редакторе, и там действительно есть встроенная версия. Таким образом, я считаю, что ваш ответ — это правильный путь, хотя моим требованием было не вмешиваться в сам код приложения, а вместо этого сохранить его в программе запуска.

2. @amireh: Смотрите мою правку, я нашел отличное решение для Visual Studio.

Ответ №4:

Я дам лишь несколько идей о том, как это сделать.

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

 char *Version = "Version: AA.BB.CC";
  

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

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

Версия будет сохранена в вашей двоичной коде в некоторой части. Полезно ли это? Как вы будете извлекать номер версии?

Комментарии:

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