2010-04-29 50 views
10

Tengo la fuente de una DLL y tengo una versión compilada de ella en algún lado.¿Cómo puedo saber si dos DLL .NET son iguales?

Si compilo la fuente, tendrá una fecha diferente a la versión ya compilada.

¿Cómo puedo saber si son de hecho iguales y simplemente se han compilado en diferentes momentos?

+0

Cantidad de bytes?aunque ese no es el método más seguro, supongo, no estoy seguro – Bas

+0

si tiene un cierto cambio en mente, y quiere comprobar si está o no en un archivo dll dado, siempre hay un reflector .net; Aparte de eso, vaya con la respuesta de Kangkan –

+0

@Bas: Este es probablemente el mejor método. @David: ¿Cómo podría ayudar el reflector .net? – CJ7

Respuesta

-1

La comparación básica es de la versión y el tamaño de la DLL. Además, puede verificar si algún archivo tiene una fecha de modificación posterior a la fecha del dll compilado.

+0

Mira mi pregunta. Si compilo el origen, tendrá una fecha diferente a la versión ya compilada. – CJ7

+0

@ Craig: Si lees con cuidado, mencioné que la última fecha de modificación de los archivos de código fuente no debería ser más allá de la fecha del dll original. Por supuesto, si el código se ha guardado con cualquier cambio que no esté en la funcionalidad, también cambiará la fecha de modificación. Es seguro que si compila el nuevo dll tendrá una fecha diferente. Es trivial. – Kangkan

+0

Bien, veo tu punto ahora. Sí, sería una buena idea mirar la fecha de modificación de los archivos fuente. – CJ7

0

Puede usar .NET Reflector para desmontar el dll y compararlo con el último cambio de código que realizó para ver si son iguales. Si son ellos, usted sabe que están basados ​​en el mismo código.

-1

compare los archivos en modo binario. Ejecute el siguiente desde la línea de comandos:

fc /b file1.dll file2.dll 

que le permitirá saber si los archivos son idénticos, pero probablemente no será menos que se compilan en exactamente las mismas condiciones, que, desde que tiene el código fuente , es posible.

+1

Esto no es cierto, porque el compilador siempre inserta la marca de tiempo y un MVID aleatorio en el binario. Además, la dirección base es muy probable que cambie. –

2

Tanto NDepend como Plugin para Reflector le permiten comparar conjuntos.

16

Para comparar dos archivos .dll puede usar ildasm o cualquier otra herramienta para calcular el código IL. He creado una muestra con ildasm incrustado en el archivo dll para que pueda usarlo en cada máquina. Cuando desensamblamos un ensamblaje, verificamos si el archivo ildasm.exe existe en la carpeta del ensamblaje en ejecución y, si no, el archivo se extrae de nuestro archivo dll. Usando el archivo ildasm obtenemos el código IL y lo guardamos en un archivo temporal. entonces tenemos que eliminar los siguientes tres filas:

EIMV - como he escrito antes de que este es un GUID único generado con cada construir

Banco de Imágenes (La base de la imagen nos dice en cuanto a donde el programa será cargado en la memoria por el cargador de Windows) -. esto es diferente con cada generación, así

hora-fecha del sello - la fecha y hora cuando se ejecuta el ildasm

Así que leemos el contenido del archivo temporal, eliminamos estas filas usando las expresiones regulares, y luego guardamos el contenido del archivo en el mismo archivo. Aquí es la clase desensamblador:

using System; 
using System.IO; 
using System.Linq; 
using System.Reflection; 
using System.Diagnostics; 
using System.Text.RegularExpressions; 

namespace FileHasher 
{ 
    public class Disassembler 
    { 
     public static Regex regexMVID = new Regex("//\\s*MVID\\:\\s*\\{[a-zA-Z0-9\\-]+\\}", RegexOptions.Multiline | RegexOptions.Compiled); 
     public static Regex regexImageBase = new Regex("//\\s*Image\\s+base\\:\\s0x[0-9A-Fa-f]*", RegexOptions.Multiline | RegexOptions.Compiled); 
     public static Regex regexTimeStamp = new Regex("//\\s*Time-date\\s+stamp\\:\\s*0x[0-9A-Fa-f]*", RegexOptions.Multiline | RegexOptions.Compiled); 

     private static readonly Lazy<Assembly> currentAssembly = new Lazy<Assembly>(() => 
     { 
      return MethodBase.GetCurrentMethod().DeclaringType.Assembly; 
     }); 

     private static readonly Lazy<string> executingAssemblyPath = new Lazy<string>(() => 
     { 
      return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
     }); 

     private static readonly Lazy<string> currentAssemblyFolder = new Lazy<string>(() => 
     { 
      return Path.GetDirectoryName(currentAssembly.Value.Location); 
     }); 

     private static readonly Lazy<string[]> arrResources = new Lazy<string[]>(() => 
     { 
      return currentAssembly.Value.GetManifestResourceNames(); 
     }); 

     private const string ildasmArguments = "/all /text \"{0}\""; 

     public static string ILDasmFileLocation 
     { 
      get 
      { 
       return Path.Combine(executingAssemblyPath.Value, "ildasm.exe"); 
      } 
     } 

     static Disassembler() 
     { 
      //extract the ildasm file to the executing assembly location 
      ExtractFileToLocation("ildasm.exe", ILDasmFileLocation); 
     } 

     /// <summary> 
     /// Saves the file from embedded resource to a given location. 
     /// </summary> 
     /// <param name="embeddedResourceName">Name of the embedded resource.</param> 
     /// <param name="fileName">Name of the file.</param> 
     protected static void SaveFileFromEmbeddedResource(string embeddedResourceName, string fileName) 
     { 
      if (File.Exists(fileName)) 
      { 
       //the file already exists, we can add deletion here if we want to change the version of the 7zip 
       return; 
      } 
      FileInfo fileInfoOutputFile = new FileInfo(fileName); 

      using (FileStream streamToOutputFile = fileInfoOutputFile.OpenWrite()) 
      using (Stream streamToResourceFile = currentAssembly.Value.GetManifestResourceStream(embeddedResourceName)) 
      { 
       const int size = 4096; 
       byte[] bytes = new byte[4096]; 
       int numBytes; 
       while ((numBytes = streamToResourceFile.Read(bytes, 0, size)) > 0) 
       { 
        streamToOutputFile.Write(bytes, 0, numBytes); 
       } 

       streamToOutputFile.Close(); 
       streamToResourceFile.Close(); 
      } 
     } 

     /// <summary> 
     /// Searches the embedded resource and extracts it to the given location. 
     /// </summary> 
     /// <param name="fileNameInDll">The file name in DLL.</param> 
     /// <param name="outFileName">Name of the out file.</param> 
     protected static void ExtractFileToLocation(string fileNameInDll, string outFileName) 
     { 
      string resourcePath = arrResources.Value.Where(resource => resource.EndsWith(fileNameInDll, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); 
      if (resourcePath == null) 
      { 
       throw new Exception(string.Format("Cannot find {0} in the embedded resources of {1}", fileNameInDll, currentAssembly.Value.FullName)); 
      } 
      SaveFileFromEmbeddedResource(resourcePath, outFileName); 
     } 

     public static string GetDisassembledFile(string assemblyFilePath) 
     { 
      if (!File.Exists(assemblyFilePath)) 
      { 
       throw new InvalidOperationException(string.Format("The file {0} does not exist!", assemblyFilePath)); 
      } 

      string tempFileName = Path.GetTempFileName(); 
      var startInfo = new ProcessStartInfo(ILDasmFileLocation, string.Format(ildasmArguments, assemblyFilePath)); 
      startInfo.WindowStyle = ProcessWindowStyle.Hidden; 
      startInfo.CreateNoWindow = true; 
      startInfo.UseShellExecute = false; 
      startInfo.RedirectStandardOutput = true; 

      using (var process = System.Diagnostics.Process.Start(startInfo)) 
      { 
       string output = process.StandardOutput.ReadToEnd(); 
       process.WaitForExit(); 

       if (process.ExitCode > 0) 
       { 
        throw new InvalidOperationException(
         string.Format("Generating IL code for file {0} failed with exit code - {1}. Log: {2}", 
         assemblyFilePath, process.ExitCode, output)); 
       } 

       File.WriteAllText(tempFileName, output); 
      } 

      RemoveUnnededRows(tempFileName); 
      return tempFileName; 
     } 

     private static void RemoveUnnededRows(string fileName) 
     { 
      string fileContent = File.ReadAllText(fileName); 
      //remove MVID 
      fileContent = regexMVID.Replace(fileContent, string.Empty); 
      //remove Image Base 
      fileContent = regexImageBase.Replace(fileContent, string.Empty); 
      //remove Time Stamp 
      fileContent = regexTimeStamp.Replace(fileContent, string.Empty); 
      File.WriteAllText(fileName, fileContent); 
     } 

     public static string DisassembleFile(string assemblyFilePath) 
     { 
      string disassembledFile = GetDisassembledFile(assemblyFilePath); 
      try 
      { 
       return File.ReadAllText(disassembledFile); 
      } 
      finally 
      { 
       if (File.Exists(disassembledFile)) 
       { 
        File.Delete(disassembledFile); 
       } 
      } 
     } 
    } 
} 

Ahora usted puede comparar el contenido de estos dos códigos IL. Otra opción es generar códigos hash de estos archivos y compararlos. Hese es una clase HashCalculator: usando System; usando System.IO; usando System.Reflection;

namespace FileHasher 
{ 
    public class HashCalculator 
    { 
     public string FileName { get; private set; } 

     public HashCalculator(string fileName) 
     { 
      this.FileName = fileName; 
     } 

     public string CalculateFileHash() 
     { 
      if (Path.GetExtension(this.FileName).Equals(".dll", System.StringComparison.InvariantCultureIgnoreCase) 
       || Path.GetExtension(this.FileName).Equals(".exe", System.StringComparison.InvariantCultureIgnoreCase)) 
      { 
       return GetAssemblyFileHash(); 
      } 
      else 
      { 
       return GetFileHash(); 
      } 
     } 

     private string GetFileHash() 
     { 
      return CalculateHashFromStream(File.OpenRead(this.FileName)); 
     } 

     private string GetAssemblyFileHash() 
     { 
      string tempFileName = null; 
      try 
      { 
       //try to open the assembly to check if this is a .NET one 
       var assembly = Assembly.LoadFile(this.FileName); 
       tempFileName = Disassembler.GetDisassembledFile(this.FileName); 
       return CalculateHashFromStream(File.OpenRead(tempFileName)); 
      } 
      catch(BadImageFormatException) 
      { 
       return GetFileHash(); 
      } 
      finally 
      { 
       if (File.Exists(tempFileName)) 
       { 
        File.Delete(tempFileName); 
       } 
      } 
     } 

     private string CalculateHashFromStream(Stream stream) 
     { 
      using (var readerSource = new System.IO.BufferedStream(stream, 1200000)) 
      { 
       using (var md51 = new System.Security.Cryptography.MD5CryptoServiceProvider()) 
       { 
        md51.ComputeHash(readerSource); 
        return Convert.ToBase64String(md51.Hash); 
       } 
      } 
     } 
    } 
} 

Puede encontrar el código fuente de la aplicación completa en mi blog aquí - Compare two dll files programmatically

+0

Todas tus publicaciones son enlaces a tu blog. –

+1

no estoy seguro de por qué esto fue votado a la baja - esta es la única respuesta aquí que realmente funciona – pappadog

+0

cuando utilicé esto encontré que idéntico dll todavía tenía diferencias en las guías. ¿Es seguro ignorar todos los cambios en las guías? – Daniel

Cuestiones relacionadas