我有一个DLL的源,我有一个编译版本,它躺在某个地方.
如果我编译源代码,它将具有与已编译版本不同的日期.
我怎么知道他们是否相同,只是在不同的时间被编辑?
要比较两个.dll文件,您可以使用ildasm或任何其他工具来交换IL代码.
我在dll文件中创建了一个嵌入式ildasm的示例,以便您可以在每台机器上使用它.当我们拆卸一个程序集时,我们检查执行的程序集文件夹中是否存在ildasm.exe文件,如果没有从我们的dll文件中提取文件.
使用ildasm文件,我们得到IL代码并将其保存到一个临时文件.
然后我们需要删除以下三行:
我在dll文件中创建了一个嵌入式ildasm的示例,以便您可以在每台机器上使用它.当我们拆卸一个程序集时,我们检查执行的程序集文件夹中是否存在ildasm.exe文件,如果没有从我们的dll文件中提取文件.
使用ildasm文件,我们得到IL代码并将其保存到一个临时文件.
然后我们需要删除以下三行:
MVID – 就像我之前写的那样,每个构建都生成一个唯一的GUID
图像基础(图像基础告诉我们,Windows加载程序将在内存中加载程序.) – 这与每个构建都不同
时间戳 – 运行ildasm的时间和日期
因此,我们读取临时文件内容,使用正则表达式删除这些行,然后将文件内容保存到同一个文件.
这是Disassembler类:
- 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,size)) > 0)
- {
- streamToOutputFile.Write(bytes,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类:
使用系统;
使用System.IO;
使用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