Как всегда производить байт-в-байт одинаковые.exe при перестроении приложения c#?

сначала я дам вам немного информации о том, почему я задаю этот вопрос:

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

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

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

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

EDIT: это теперь известно чтобы быть 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 ответов


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


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

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

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

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

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


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

обновление: вы могли бы также использовать рефлектор addins для сравнения и разборки.


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

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

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

при создании из консоли, ключ /debug - получить тот же результат.


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

EDIT:

Я на самом деле wantetd перейти по ссылке этой статьи.


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

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


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