2010-04-29 4 views
10

J'ai la source d'une DLL et j'en ai une version compilée qui traîne quelque part.Comment puis-je savoir si deux DLL .NET sont identiques?

Si je compile la source, la date déjà compilée sera différente.

Comment puis-je savoir si elles sont en fait les mêmes et ont simplement été compilées à des moments différents?

+0

quantité d'octets?bien que ce ne soit pas la méthode la plus sûre je suppose, pas sûr – Bas

+0

si vous avez un certain changement dans l'esprit, et que vous voulez vérifier si oui ou non dans un fichier dll donné, il y a toujours .net reflector; à part ça, allez avec la réponse de Kangkan –

+0

@Bas: C'est probablement la meilleure méthode. @David: Comment l'aide du réflecteur .net? – CJ7

Répondre

-1

La comparaison de base concerne la version et la taille de la DLL. En outre, vous pouvez vérifier si un fichier a une date de modification au-delà de la date de la DLL compilée.

+0

Voir ma question. Si je compile la source, la date déjà compilée sera différente. – CJ7

+0

@Craig: Si vous lisez attentivement, j'ai mentionné que la date de dernière modification du (des) fichier (s) de code source ne devrait pas dépasser la date d'origine de la DLL. Bien sûr, si le code a été sauvegardé avec des modifications qui ne sont pas dans la fonctionnalité, la date de modification changera également. Il est sûr que si vous compilez la nouvelle DLL aura une date différente. C'est trivial. – Kangkan

+0

OK, je vois votre point maintenant. Oui, ce serait une bonne idée de regarder la date de modification des fichiers source. – CJ7

0

Vous pouvez utiliser .NET Reflector pour désassembler la DLL et la comparer au dernier changement de code que vous avez effectué pour voir s'ils sont identiques. S'ils le sont, vous savez qu'ils sont basés sur le même code.

-1

comparez les fichiers en mode binaire. Exécutez la commande suivante à partir de la ligne de commande:

fc /b file1.dll file2.dll 

Cela vous permettra de savoir si les fichiers sont identiques, mais ils ne seront probablement pas à moins qu'ils ne sont compilés dans exactement les mêmes conditions, ce qui, étant donné que vous avez le code source , est possible.

+1

Cela n'est pas vrai, car le compilateur insère toujours l'horodatage et un MVID aléatoire dans le binaire. De plus, l'adresse de base est très susceptible de changer. –

2

Les deux NDepend et pour Reflector vous permettent de comparer les assemblages.

16

Pour comparer deux fichiers .dll, vous pouvez utiliser ildasm ou tout autre outil pour obtenir le code IL. J'ai créé un exemple avec ildasm incorporé dans le fichier dll afin que vous puissiez l'utiliser sur chaque machine. Lorsque nous démontons un assembly, nous vérifions si le fichier ildasm.exe existe dans le dossier de l'assembly en cours d'exécution et si ce n'est pas le cas, le fichier est extrait de notre fichier dll. En utilisant le fichier ildasm nous obtenons le code IL et l'enregistrons dans un fichier temporaire. Ensuite, nous devons enlever les trois lignes suivantes:

MVID - comme je l'ai écrit avant que ce soit un GUID unique généré avec tous les construire

base d'image (La base d'image nous dit que l'endroit où le programme sera chargé en mémoire par le chargeur Windows) -. ce qui est différent avec chaque construction, ainsi

Horodatage-date - l'heure et date à laquelle le ildasm est exécuté

Nous lisons donc le contenu du fichier temporaire, supprimons ces lignes en utilisant les expressions rationnelles, puis sauvegardons le contenu du fichier dans le même fichier. Voici la classe désassembleur:

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); 
       } 
      } 
     } 
    } 
} 

Maintenant, vous pouvez comparer le contenu de ces deux codes IL. Une autre option consiste à générer des codes de hachage de ces fichiers et à les comparer. Voici une classe HashCalculator: using System; en utilisant System.IO; en utilisant 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); 
       } 
      } 
     } 
    } 
} 

Vous pouvez trouver le code source de l'application complète sur mon blog ici - Compare two dll files programmatically

+0

Tous vos messages sont des liens vers votre blog. –

+1

pas sûr pourquoi cela a été voté vers le bas - c'est la seule réponse ici qui fonctionne réellement – pappadog

+0

quand je l'ai utilisé, j'ai trouvé que DLL identique avait encore des différences dans les guids. Est-il sûr d'ignorer tous les changements de guids? – Daniel

Questions connexes