2010-05-31 6 views
28

¿Alguien sabe de una manera de comparar dos ensamblados .NET para determinar si se crearon a partir de los "mismos" archivos fuente?Determine si los ensamblados .NET se crearon desde la misma fuente

Soy consciente de que hay algunas utilidades de diferenciación disponibles, como el complemento para Reflector, pero no estoy interesado en ver las diferencias en una GUI, solo quiero una forma automática de comparar una colección de binarios para ver si se construyeron a partir de los mismos (o equivalentes) archivos fuente. Entiendo que múltiples archivos de origen diferentes podrían producir el mismo IL, y me doy cuenta de que el proceso solo sería sensible a las diferencias en el IL, no a la fuente original.

El principal obstáculo para simplemente comparar las secuencias de bytes para los dos ensamblajes es que .NET incluye un campo denominado "MVID" (Module Version Identifier) ​​en el ensamblaje. Esto parece tener un valor diferente para cada compilación, por lo que si construye el mismo código dos veces, el ensamblado será diferente.

Una pregunta relacionada es, ¿alguien sabe cómo forzar que el MVID sea el mismo para cada compilación? Esto evitaría que necesitáramos tener un proceso de comparación que no sea sensible a las diferencias en el valor del MVID. Sería preferible un MVID consistente, ya que esto significa que se podrían usar sumas de verificación estándar.

El trasfondo de esto es que una empresa externa es responsable de revisar y firmar independientemente nuestras publicaciones, antes de que se nos permita lanzar a producción. Esto incluye revisar el código fuente. Quieren confirmar de forma independiente que el código fuente que les proporcionamos coincide con los binarios que construimos, probamos y planeamos implementar. Estamos buscando un proceso que les permita construir el sistema de forma independiente desde la fuente que les proporcionamos, y comparar las sumas de comprobación con las sumas de comprobación de los binarios que hemos probado.

BTW. Tenga en cuenta que estamos usando integración continua, compilaciones automáticas, control de fuente, etc. El problema no está relacionado con una falta de control interno sobre qué archivos de origen se incluyeron en una compilación determinada. El problema es que un tercero es responsable de verificar que la fuente que les proporcionamos produce los mismos binarios que hemos probado y planeamos poner en producción. No deberían confiar en ninguno de nuestros sistemas o controles internos, incluido el servidor de compilación o el sistema de control de código fuente. Lo único que les importa es obtener la fuente asociada con la construcción, realizar la construcción ellos mismos y verificar que los resultados coincidan con lo que decimos que estamos implementando.

La velocidad de ejecución de la solución de comparación no es particularmente importante.

gracias

+4

Si la ÚNICA diferencia es el MVID, seguramente siempre aparecería en la misma posición en el flujo de bytes y su algoritmo de diferencia podría ignorar esas posiciones de bytes. –

+0

Sí, eso es correcto, pero necesitaría saber la estructura del archivo para ignorar este campo. ¿Conoces una referencia sobre el formato? – Clayton

+0

¿Eso es posible? ¿No podría el código fuente diferente (C#, VB.NET, lo que sea) dar como resultado el mismo código binario (o código IL para ese caso)? Puede que no haga una diferencia funcional en ese momento, pero seguiría siendo una diferencia. EDITAR: Vaya, disculpe. Acabo de ver ahora que reconstruyen y luego comparan los binarios. –

Respuesta

3

Hay algunas maneras de hacer esto dependiendo de la cantidad de trabajo que usted está dispuesto a hacer y la importancia del desempeño y/o exactitud. Una forma en que señaló Eric J. es comparar los ensamblajes en binario, excluyendo las partes que cambian en cada compilación. Esta solución es fácil y rápida, pero podría darle muchos falsos negativos. Una mejor forma es profundizar utilizando la reflexión. Si el rendimiento es crítico, puede comenzar comparando los tipos y, si coinciden, vaya a las definiciones de los miembros. Después de verificar las definiciones de tipo y miembro y si todo es igual a ese punto, puede ir más allá al examinar el IL real de cada método al obtenerlo a través del método GetILAsByteArray. Nuevamente encontrará diferencias incluso si todo es lo mismo pero compilado con banderas un poco diferentes o una versión diferente del compilador. Diría que la mejor solución es usar una herramienta de integración continua que etiquete la compilación con el número de conjunto de cambios de su control de origen (está utilizando uno, ¿no?).

A related article

+0

(editado para incluir detalles adicionales) Usted y Eric J tienen razón al ignorar la parte variante del archivo. Esto es simple si el formato está documentado, pero aún no encontré una referencia. ¿Conoces uno? En cuanto al uso de la reflexión, nos inclinamos hacia la solución más simple, porque la parte externa tendrá que comprender y probar la utilidad. Si es proporcionado por el equipo de desarrollo, habrá una mayor sospecha de que si el software fuera provisto por una cuarta parte. Ignorar algunos bytes en el archivo será más simple que usar el reflejo. – Clayton

10

No es demasiado doloroso para utilizar las herramientas de línea de comandos para filtrar EIMV y sellos de fecha y hora a partir de una representación de texto de la IL. Supongamos que file1.exe y file2.exe se crean desde las mismas fuentes:

c: \ temp> ildasm/all/text file1.exe | find/v "Sello de fecha y hora:" | find/v "MVID"> archivo1.txt

c: \ temp> ildasm/todo/texto file2.exe | find/v "Sello de fecha y hora:" | find/v "EIMV"> file2.txt

c: \ temp> fc file1.txt file2.txt

Comparación de archivos file1.txt y FILE2.TXT

FC: no hay diferencias encontradas

+1

No creo que este sea el método más completo por razones que todavía no puedo discernir la causa raíz de. Para descubrir esto, básicamente construí mi fuente y copié la carpeta de implementación donde todo se copia. Luego borré el contenido de la carpeta de implementación y reconstruí la fuente. Genere los textos de desmontaje usando su técnica pero encontré diferencias entre los dos más allá de todas las opciones de filtrado que usted y otros proporcionan. – jxramos

+0

... Parece que ciertos GUID se están actualizando. _ ". Field/* 04000027 */static assembly valuetype ' {** A310135E-980F-48EA-A97F-FB0E9C30EA63 **}'/* 0200000F * // '__ StaticArrayInitTypeSize = 6'/* 02000010 */'$$ method0x600001d-1' en I_00002CE0 "_ Nuestra compilación es algo complicada, ya que combina la interoperabilidad CLI C++ con .NET y C# y abarca más de 60 proyectos. Es lamentable que no haya forma de corregir los ID utilizados en la generación. – jxramos

0

Otra solución a tener en cuenta:

La información del código fuente se almacena cuando los binarios se compilan en modo de depuración. Luego puede verificar si pdb coincide con exe y si las líneas de pdb coinciden con el código fuente.

8

He utilizado la solución de Jerry Currry en .Net 4 ensamblados y descubrí que ahora hay un tercer elemento que variará en cada compilación: Checksum. ¿No es sorprendente encontrar una suma de comprobación dentro de un conjunto? Creo que la adición de la suma de comprobación de un archivo dentro de ese archivo cambiará la suma de comprobación ...

De todos modos, el comando modificado es:

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

Tenga en cuenta que también he cambiado las cadenas de búsqueda un poco añadiendo las barras, para evitar coincidencias involuntarias. Las líneas de este comando se deben ejecutar juntas en la misma línea, dividir para la legibilidad. Los nombres de archivo necesitarán comillas dobles a su alrededor si contienen espacios.

7

Al comparar bibliotecas de clases con ILDASM v4.0.319.1, parece que la base de la imagen no se ha inicializado. Para evitar desajustes, utilizar una solución revisado:

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

Punto de entrada (base de imagen) es en realidad la información interesante para los conjuntos ejecutables, y tendrá que ser verificada con cuidado. Inyectar una nueva base de imágenes es una forma común de hacer que un programa haga algo completamente diferente. En mi caso, estoy tratando de verificar la consistencia de compilaciones multiproceso, por lo que es seguro saltear el punto de entrada.

Una nota sobre el rendimiento: Tomé una DLL de 8MB que se creó para AnyCPU y ejecuté ILDasm. El archivo resultante tenía un tamaño de 251 MB y tardó varios minutos en hacerlo. Aproximadamente 32 veces el tamaño fue producido.

1

Puede usar el Reflector Diff AddIn here.

Cuestiones relacionadas