2010-04-29 8 views
10

나는 DLL을위한 소스를 가지고 있으며, 어딘가에 그것의 컴파일 된 버전이있다.두 개의 .NET DLL이 같은지 어떻게 알 수 있습니까?

소스를 컴파일하면 이미 컴파일 된 버전과 다른 날짜가됩니다.

사실 그들이 동일하고 다른 시간에 단순히 컴파일되었는지 어떻게 알 수 있습니까?

+0

금액?그게 가장 안전한 방법이 아닌 것 같아요, 확실하지 않은 경우 – Bas

+0

마음에 확실한 변화가 있고 주어진 dll 파일에 있는지 여부를 확인하고 싶다면 항상 .net reflector가 있습니다. 그 이외에, Kangkan의 대답으로 가십시오 –

+0

@Bas : 이것은 아마도 가장 좋은 방법 일 것입니다. @ 데이비드 : 어떻게 리플렉터 도움이됩니다? – CJ7

답변

-1

기본 비교는 DLL의 버전과 크기입니다. 또한 컴파일 된 dll의 날짜 이후에 수정 된 날짜가있는 파일이 있는지 확인할 수 있습니다.

+0

내 질문보기. 소스를 컴파일하면 이미 컴파일 된 버전과 다른 날짜가됩니다. – CJ7

+0

@Craig :주의 깊게 읽으면 소스 코드 파일의 마지막 수정 날짜가 원래 dll 날짜를 초과해서는 안된다고 언급했습니다. 물론 코드가 기능에 포함되지 않은 변경 사항과 함께 저장된 경우 수정 날짜도 변경됩니다. 컴파일하면 새 dll의 날짜가 달라집니다. 그 사소한. – Kangkan

+0

네, 지금 당신의 요지를 봅니다. 네, 원본 파일의 수정 날짜를 살펴 보는 것이 좋습니다. – CJ7

0

.NET Reflector를 사용하여 dll을 disassamble하고 마지막으로 변경 한 코드와 비교하여 동일한 지 확인할 수 있습니다. 그들은 그들이 같은 코드를 기반으로한다는 것을 알고 있습니다.

-1

은 이진 모드의 파일을 비교합니다. 명령 줄에서 다음을 실행 파일이 동일한 경우 사용자에게 알려줍니다,하지만 그들은 정확히 동일한 조건에서 컴파일되지 않는 한 그들은 아마도되지 않습니다

fc /b file1.dll file2.dll 

하는, 당신은 소스 코드를 갖고 있기 때문에 , 가능합니다.

+1

컴파일러는 항상 타임 스탬프와 무작위 MVID를 바이너리에 삽입하기 때문에 사실이 아닙니다. 또한 기본 주소가 변경 될 가능성이 큽니다. –

2

반사경 용 NDependPlugin 모두 어셈블리를 비교할 수 있습니다.

16

두 개의 .dll 파일을 비교하려면 ildasm 또는 IL 코드를 가져 오는 다른 도구를 사용할 수 있습니다. 모든 컴퓨터에서 사용할 수 있도록 dll 파일에 ildasm이 포함 된 샘플을 만들었습니다. 어셈블리를 분해 할 때 ildasm.exe 파일이 실행중인 어셈블리 폴더에 있는지 확인하고 그렇지 않으면 dll 파일에서 파일을 추출합니다. ildasm 파일을 사용하여 IL 코드를 가져 와서 임시 파일에 저장합니다. 그런 다음 우리는 다음과 같은 세 가지 행을 제거해야합니다

MVID -이 모든으로 생성 된 고유 GUID 전에 내가 쓴

이미지 자료 (이미지 기반은 어디 프로그램으로 우리에게 알려줍니다 구축 윈도우 로더에 의해 메모리에로드됩니다.) - 모든뿐만 아니라 구축과 함께이 다른

시간 날짜 스탬프 - ILDASM이 실행되는 시간과 날짜

,

임시 파일 내용을 읽고 regexes를 사용하여이 행을 제거한 다음 파일 내용을 동일한 파일에 저장합니다.

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

지금이 두 IL 코드의 내용을 비교할 수 있습니다 다음은 디스어셈블러 클래스입니다. 다른 옵션은 이러한 파일의 해시 코드를 생성하고 비교하는 것입니다. Hese는 HashCalculator 클래스입니다. using System; using System.IO; using 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); 
       } 
      } 
     } 
    } 
} 

당신은 여기 내 블로그에 전체 응용 프로그램의 소스 코드를 찾을 수 있습니다 - 바이트의 Compare two dll files programmatically

+0

귀하의 모든 게시물은 귀하의 블로그 링크입니다. –

+1

이것이 왜 투표에 부쳐 졌는지 확실하지 않습니다. 여기가 유일한 대답인데 – pappadog

+0

내가 이것을 사용했을 때 동일한 dll이 여전히 guids에 차이가 있다는 것을 알았습니다. guids의 모든 변경 사항을 무시하는 것이 안전합니까? – Daniel

관련 문제