Как всегда производить побайтовый идентичный .exe при перестройке приложения C #? - PullRequest
18 голосов
/ 15 сентября 2009

Сначала я расскажу вам немного о том, почему я задаю этот вопрос:

В настоящее время я работаю в строго регламентированной отрасли, и поэтому наш кодекс тщательно проверяется официальными тестовыми компаниями. Эти тестовые дома рассчитывают на то, что смогут собрать код и сгенерировать .exe или .dll, которые ТОЧНО одинаковы каждый раз (без явного изменения кода!). Они проверяют MD5 и SHA1 исполняемых файлов, которые они создают, чтобы убедиться в этом.

До этого момента я преимущественно занимался кодированием на C ++, где (после нескольких настроек параметров проекта) мне удавалось заставить проекты последовательно перестраиваться на один и тот же MD5 / SHA1. Сейчас я использую C # в проекте и испытываю большие затруднения, чтобы заставить MD5 соответствовать после перестройки. Я знаю, что в PE-заголовке файла есть «временные метки», и они были очищены до 0. Я также знаю, что для .exe существует GUID, который снова был очищен до 00 00 00 ... и т. д. Однако файлы по-прежнему не совпадают.

Я использую CFF Explorer для просмотра и редактирования заголовка PE, чтобы удалить метки времени и даты. После использования бинарного инструмента сравнения в .exe есть только 2 блока байтов, которые отличаются (оба очень маленькие).

Один из несовместимых блоков появляется просто перед некоторым двоичным кодом, который в ASCII детализирует путь к файлу *Project*\obj\Release\xxx.pdb.

РЕДАКТИРОВАТЬ: Теперь известно, что это GUID файла * .pdb, однако я до сих пор не знаю, смогу ли я изменить его без каких-либо ошибок!?

Другой блок появляется в середине того, что выглядит как имя функции, т.е. (типичный раздел) AssemblyName.GetName.Version.get_Version.System.IO.Ports.SerialPort.Parity.Byte.<PrivateImplementationDetails>{

затем другой кодовый блок:

4A134ACE-D6A0-461B-A47C-3A4232D90816

с последующим:

"}. ValueType .__ StaticArrayInitTypeSize = 7. $$ method0x60000ab-1.RuntimeFieldHandle.InitializeArray` ... и т.д ..

Любые идеи или предложения приветствуются!

Ответы [ 6 ]

5 голосов
/ 24 сентября 2009

Обновление: у Roslyn, похоже, есть флаг компилятора /feature:deterministic для воспроизводимых сборок, хотя пока не работает на 100% .


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

PrivateImplementationDetails немного сложнее - это внутренние вспомогательные классы, генерируемые компилятором для определенных языковых конструкций (инициализаторы массива, операторы switch с использованием строк и т. Д.). Поскольку они используются только для внутреннего использования, имя класса на самом деле не имеет значения, поэтому вы можете просто присвоить им порядковый номер.

Я бы сделал это, пройдя поток метаданных #Strings и заменив все строки в форме " {GUID}" на " {порядковый номер, дополненный до той же длины, что и GUID}".

Поток метаданных #Strings - это просто список строк, используемых метаданными, закодированных в UTF-8 и разделенных \ 0; поэтому поиск и замена имен должны быть простыми, если вы знаете, где находится поток #Strings внутри исполняемого файла.

К сожалению, «заголовки потока метаданных», содержащие эту информацию, довольно глубоко скрыты в формате файла. Вам придется начать с необязательного заголовка NT, найти указатель на заголовок среды выполнения CLI, разрешить его в позицию файла с помощью таблицы разделов PE (это RVA, но вам нужна позиция внутри файла), а затем перейти к корень метаданных и чтение заголовков потока.

2 голосов
/ 22 сентября 2009

Что касается проблемы GUID PDB, если вы указываете, что PDB не должен генерироваться при компиляции для сборок Release, содержит ли двоичный файл GUID файловой системы PDB?

Чтобы отключить генерацию PDB:

  1. Щелкните правой кнопкой мыши свой проект в обозревателе решений и выберите Свойства.
  2. В левом меню выберите Build.
  3. Убедитесь, что выбрана конфигурация Release (вам все еще понадобится PDB для отладки).
  4. Нажмите кнопку «Дополнительно» в правом нижнем углу.
  5. В разделе «Вывод / Отладка» выберите «Нет».

Если вы собираете с консоли, используйте / debug- для получения того же результата.

2 голосов
/ 15 сентября 2009

Я не уверен в этом, но просто мысль: вы используете какие-либо анонимные типы, для которых компилятор может генерировать имена за сценой, которые могут отличаться при каждом запуске компилятора? Просто возможность, которая пришла мне в голову. Вероятно, один для Джона Скита; -)

Обновление: Возможно, вы также можете использовать Reflector addins для сравнения и разборки.

1 голос
/ 15 сентября 2009

Взгляните на ответы из этого вопроса. Особенно по внешней ссылке, представленной в 3-м.

EDIT:

Я действительно хочу сослаться на эту статью.

0 голосов
/ 19 ноября 2013

Используйте ildasm.exe, чтобы полностью разобрать обе программы и сравнить IL. Затем вы можете «очистить» код с помощью текстовых методов и (как и ожидалось) перекомпилировать его снова.

0 голосов
/ 17 марта 2010

Вы сказали, что после нескольких настроек проекта вы смогли получить приложения C ++ для повторной компиляции с одинаковыми значениями SHA1 / MD5. Я нахожусь в той же ситуации, что и вы, находясь в индустрии со сторонней тестовой лабораторией, которой нужно повторять повторное создание точно таких же исполняемых файлов.

Исследуя, как это сделать в VS2005, я наткнулся на ваш пост здесь. Не могли бы вы поделиться настройками проекта, которые вы делали для того, чтобы приложения C ++ собирались с одинаковыми значениями SHA1 / MD5? Это очень помогло бы мне и, возможно, другим людям, которые разделяют это требование.

...