2010-05-31 4 views
28

Кто-нибудь знает, как сравнить две сборки .NET, чтобы определить, были ли они созданы из «одинаковых» исходных файлов?Определите, были ли сборки .NET построены из того же источника.

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

Главным препятствием для простого сравнения потоков байтов для двух сборок является то, что .NET включает в себя поле под названием «MVID» (идентификатор версии модуля) сборки. Кажется, что у каждой компиляции другое значение, поэтому, если вы создадите один и тот же код дважды, сборка будет отличаться.

Связанный с этим вопрос: кто-нибудь знает, как заставить MVID быть одинаковым для каждой компиляции? Это позволит нам избежать процесса сравнения, который нечувствителен к различиям в значении MVID. Согласованный MVID будет предпочтительнее, поскольку это означает, что могут использоваться стандартные контрольные суммы.

Основой этого является то, что сторонняя компания несет ответственность за независимое рассмотрение и отключение наших релизов до того, как нам разрешат выпустить в Production. Это включает в себя проверку исходного кода. Они хотят самостоятельно подтвердить, что исходный код, который мы им даем, соответствует двоичным файлам, которые мы ранее построили, протестировали и в настоящее время планируем развернуть. Мы ищем процесс, который позволяет им самостоятельно строить систему из источника, который мы им поставляем, и сравнивать контрольные суммы с контрольными суммами для бинарных файлов, которые мы тестировали.

BTW. Обратите внимание, что мы используем непрерывную интеграцию, автоматические сборки, контроль источника и т. Д. Проблема не связана с внутренним отсутствием контроля над исходными файлами в данной сборке. Проблема в том, что третья сторона несет ответственность за проверку того, что источник, который мы им предоставляем, производит те же бинарные файлы, которые мы тестировали, и планируем ввести в Production. Они не должны доверять ни одной из наших внутренних систем или элементов управления, включая сервер сборки или систему управления исходным кодом. Все, о чем они заботятся, это получение источника, связанного с сборкой, выполнение самой сборки и проверка того, что результаты соответствуют тому, что мы говорим, мы развертываем.

Скорость выполнения сравнения не имеет особого значения.

благодаря

+4

Если единственная разница - это MVID, она всегда будет отображаться в том же положении в потоке байтов, и вы могли бы игнорировать эти разностные алгоритмы? –

+0

Да, это правильно, но мне нужно знать структуру файла, чтобы игнорировать это поле. Вы знаете ссылку на формат? – Clayton

+0

Возможно ли это? Не могли ли разные исходные коды (C#, VB.NET, независимо) вызывать один и тот же двоичный код (или код IL)? Тогда это может не сделать функциональной разницы, но все равно будет разница. EDIT: Упс, извините. Только теперь увидели, что они перестраивают, а затем сравнивают двоичные файлы. –

ответ

3

Есть несколько способов сделать это в зависимости от объема работы, которую вы готовы сделать, и важность выполнения и/или точности. Один из способов, как указал Эрик Дж., - сравнить сборки в двоичном формате, исключая части, которые изменяются при каждой компиляции. Это решение легко и быстро, но может дать вам много ложных негативов. Один лучший способ - развернуть, используя отражение. Если производительность критическая, вы можете начать с сравнения типов и, если они совпадают, перейти к определениям членов. После проверки типов и определений членов, и если все будет равно этой точке, вы можете пойти дальше, исследуя фактический IL каждого метода, пройдя его через метод GetILAsByteArray. Опять же, вы найдете различия, даже если все одно и то же, но скомпилировано с помощью немного разных флагов или другой версии компилятора. Я бы сказал, что лучшим решением является использование инструментов непрерывной интеграции, которые маркируют сборку с номером набора изменений вашего исходного элемента управления (вы используете один, правильно?).

A related article

+0

(Q. отредактировано, чтобы включить дополнительную информацию) Вы и Eric J правильны относительно игнорирования варианта части файла. Это просто, если формат документирован, но я еще не нашел ссылку. Вы знаете об этом? Что касается использования отражения, мы склонны к простейшему решению, потому что внешняя сторона должна будет понять и протестировать утилиту. Если он предоставлен командой разработчиков, это будет большим подозрением, чем если бы программное обеспечение было предоставлено четвертой стороной. Игнорирование нескольких байтов в файле будет проще, чем использование отражения. – Clayton

10

Это не слишком болезненны, чтобы использовать инструменты командной строки, чтобы отфильтровать MVID и дата-штампы времени из текстового представления IL. Предположим, файл file1.exe и file2.exe построены из одних и тех же источников:

c: \ temp> ildasm/all/text file1.exe | find/v "Дата штампа:" | find/v "MVID"> file1.txt

c: \ temp> ildasm/all/text file2.exe | find/v "Дата штампа:" | найти/V "MVID"> file2.txt

C: \ Temp> к file1.txt file2.txt

Сравнение файлов FILE1.TXT и file2.txt

FC: никаких различий не встречается

+1

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

+0

... Похоже, некоторые GUID обновляются. _ ". Field/* 04000027 */static assembly valuetype ' {** A310135E-980F-48EA-A97F-FB0E9C30EA63 **}'/* 0200000F * // '__ StaticArrayInitTypeSize = 6'/* 02000010 */'$$ method0x600001d-1' at I_00002CE0 "_ Наша сборка несколько сложна, объединив CLI C++ с .NET и C# и охватывает около 60 проектов. К сожалению, нет способа исправить идентификаторы, используемые в генерации. – jxramos

0

Другое решение:

Информация о исходном коде сохраняется, когда двоичные файлы скомпилированы в режиме отладки. Затем вы можете проверить, соответствует ли pdb exe, и если строки pdb соответствуют исходному коду.

8

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

Во всяком случае, измененная команда:

ildasm /all /text "assembly.dll" 
| find /v "// Time-date stamp:" 
| find /v "// MVID:" 
| find /v "// Checksum:" 
> assembly.dasm 

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

7

При сравнении библиотек классов с ILDASM v4.0.319.1, кажется, что изображение база не инициализирован. Чтобы избежать несовпадений, используйте пересмотренное решение:

ildasm /all /text assembly.dll 
| find /v "// Time-date stamp:" 
| find /v "// MVID:" 
| find /v "// Checksum:" 
| find /v "// Image base:" 
> assembly.dasm 

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

Заметка о производительности: я взял 8 МБ DLL, которая была построена для AnyCPU, и запускала ILDasm. Результирующий файл был размером 251 МБ и потребовалось несколько минут. Размер был составлен примерно в 32 раза.

1

Вы можете использовать Reflector Diff AddIn here.