Как всегда производить байт-в-байт одинаковые.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 и заменив все строки формы "
поток метаданных #Strings-это просто список строк, используемых метаданными, закодированных в UTF-8 и разделенных \0; поэтому поиск и замена имен должны быть легкими, как только вы узнаете, где поток #Strings находится внутри исполняемого файла.
к сожалению, "заголовки потока метаданных", содержащие эту информацию, полностью похоронены внутри формата файла. Вам нужно будет начать с необязательного заголовка NT, найти указатель на заголовок CLI Runtime, разрешите его в положение файла, используя таблицу разделов PE (это RVA, но вам нужна позиция внутри файла), затем перейдите в корень метаданных и прочитайте заголовки потока.
Я не уверен в этом, но просто подумал: используете ли вы какие-либо анонимные типы, для которых компилятор может генерировать имена за кулисами, которые могут отличаться каждый раз, когда компилятор запускается? Просто такая возможность пришла мне в голову. Вероятно, один для Джона Скита ; -)
обновление: вы могли бы также использовать рефлектор addins для сравнения и разборки.
Что касается проблемы GUID PDB, если вы укажете, что PDB не должен генерироваться при компиляции для сборки выпуска, содержит ли двоичный файл GUID файловой системы PDB?
чтобы отключить генерацию PDB:
- щелкните правой кнопкой мыши проект в обозревателе решений и выберите Свойства.
- в меню слева выберите пункт построить.
- убедитесь, что выбрана конфигурация Release (вам все равно понадобится PDB для отладки).
- нажмите на кнопку Дополнительно в правом нижнем углу.
- в разделе Информация о выводе / отладке выберите Нет.
при создании из консоли, ключ /debug - получить тот же результат.
Вы сказали, что после нескольких настроек проекта вы смогли заставить приложения C++ компилироваться повторно с теми же значениями SHA1/MD5. Я нахожусь в той же лодке, что и вы, в отрасли с тестовой лабораторией третьей стороны, которая должна перестроить точно такие же исполняемые файлы повторно.
исследуя, как это сделать в VS2005, я наткнулся на ваш пост здесь. Не могли бы вы поделиться настройками проекта, которые вы сделали, чтобы приложения C++ строились с одинаковыми значениями SHA1/MD5 последовательно? Это окажите большую помощь себе и, возможно, любым другим, кто разделяет это требование.
использовать ildasm.exe, чтобы полностью разобрать обе программы и сравнить IL. Затем вы можете "очистить" код с помощью текстовых методов и (предсказуемо) перекомпилировать его снова.