我有一个DLL的源代码,我有一个编译版本,它位于某个地方。
如果我编译源代码,它将与已编译的版本具有不同的日期。
如何判断它们是否实际上是相同的并且仅在不同时间进行编译?
答案 0 :(得分:18)
要比较两个.dll文件,您可以使用ildasm或任何其他工具来生成IL代码。 我在dll文件中创建了一个嵌入了ildasm的示例,以便您可以在每台计算机上使用它。当我们反汇编程序集时,我们检查执行程序集文件夹中是否存在ildasm.exe文件,如果不存在,则从我们的dll文件中提取该文件。 使用ildasm文件,我们获取IL代码并将其保存到临时文件中。 然后我们需要删除以下三行:
MVID - 正如我之前所写,这是每次构建时生成的唯一GUID
Image Base(图像库告诉我们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, 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类: 使用系统; 使用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
答案 1 :(得分:2)
答案 2 :(得分:0)
基本比较是DLL的版本和大小。此外,您可以检查是否有任何文件的修改日期超出了编译后的dll的日期。
答案 3 :(得分:0)
您可以使用.NET Reflector对dll进行Disassamble,并将其与您上次更改的代码进行比较,以查看它们是否相同。如果他们是你,你知道他们是基于相同的代码。
答案 4 :(得分:-1)
比较二进制模式下的文件。从命令行运行以下命令:
fc /b file1.dll file2.dll
这会让你知道文件是否相同,但它们可能不会,除非它们在完全相同的条件下编译,因为你有源代码是可能的。