如何判断两个.NET DLL是否相同? [英] How can I tell whether two .NET DLLs are the same?

查看:135
本文介绍了如何判断两个.NET DLL是否相同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个DLL的源代码,我的编译版本位于某处。



如果我编译源代码,将有一个不同的日期已经编译的版本。



我如何知道他们是否相同,只是在不同的时间被编译?

解决方案

要比较两个.dll文件,您可以使用ildasm或任何其他工具来获取IL代码。
我已经在dll文件中创建了一个嵌入式ildasm的示例,以便您可以在每个机器上使用它。当我们拆卸一个程序集时,我们检查执行的程序集文件夹中是否存在ildasm.exe文件,如果没有从我们的dll文件中提取文件。
使用ildasm文件,我们得到IL代码并将其保存到一个临时文件。
然后我们需要删除以下三行:



MVID - 正如我之前写的,是每个构建生成的唯一GUID



Image Base(图片基础告诉我们Windows加载程序在内存中加载程序的位置。) - 这与每个构建都不同以及



时间戳 - 运行ildasm的时间和日期



所以我们读取临时文件内容,使用正则表达式删除这些行,然后将文件内容保存到同一个文件。
这是Disassembler类:

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

命名空间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& lt; Assembly& gt currentAssembly = new Lazy& lt; Assembly& gt;(()=& gt;
{
return MethodBase.GetCurrentMethod()。DeclaringType.Assembly;
});

private static readonly Lazy& lt; string& gt executionAssemblyPath = new Lazy& lt; string& gt;(()=& gt;
{
return Path.GetDirectoryName(Assembly.GetExecutingAssembly()。Location);
});

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

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

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

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

static Disassembler()
{
//将ildasm文件解压缩到执行的程序集位置
ExtractFileToLocation(ildasm .exe,ILDasmFileLocation);
}

///& lt; summary& gt
///将文件从嵌入式资源保存到给定位置。
///& lt; / summary& gt
///& lt; param name =embeddedResourceName& gt;嵌入资源的名称& lt / / param& gt;
///& lt; param name =fileName& gt。文件的名称。& lt; / param& gt;
protected static void SaveFileFromEmbeddedResource(string embeddedResourceName,string fileName)
{
if(File.Exists(fileName))
{
//文件已存在,我们如果我们要更改7zip
返回的版本,可以在这里添加删除;
}
FileInfo fileInfoOutputFile = new FileInfo(fileName);

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

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

///& lt; summary& gt
///搜索嵌入式资源并将其提取到给定位置。
///& lt; / summary& gt
///& lt; param name =fileNameInDll& gt; DLL中的文件名& lt; / param& gt;
///& lt; param name =outFileName& gt。out文件的名称& lt; / param& gt
protected static void ExtractFileToLocation(string fileNameInDll,string outFileName)
{
string resourcePath = arrResources.Value.Where(resource =& gt; resource.EndsWith(fileNameInDll,StringComparison.InvariantCultureIgnoreCase)) .FirstOrDefault();
if(resourcePath == null)
{
throw new异常(string.Format({1}嵌入式资源中找不到{0},fileNameInDll,currentAssembly.Value。全名));
}
SaveFileFromEmbeddedResource(resourcePath,outFileName);
}

public static string GetDisassembledFile(string assemblyFilePath)
{
if(!File.Exists(assemblyFilePath))
{
throw new InvalidOperationException(string.Format(文件{0}不存在!,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;

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

if(process.ExitCode& gt; 0)
{
throw new InvalidOperationException(
string.Format(生成文件{0}的IL代码失败具有退出代码 - {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);
//删除MVID
fileContent = regexMVID.Replace(fileContent,string.Empty);
//删除Image Base
fileContent = regexImageBase.Replace(fileContent,string.Empty);
//删除时间戳
fileContent = regexTimeStamp.Replace(fileContent,string.Empty);
File.WriteAllText(fileName,fileContent);
}

public static string DisassembleFile(string assemblyFilePath)
{
string反汇编文件= GetDisassembledFile(assemblyFilePath);
try
{
return File.ReadAllText(decompembledFile);
}
finally
{
if(File.Exists(decompembledFile))
{
File.Delete(反汇编文件);
}
}
}
}
}

现在您可以比较这两个IL代码的内容。另一个选择是生成这些文件的哈希码并进行比较。 Hese是一个HashCalculator类:
使用System;
使用System.IO;
使用System.Reflection;

 命名空间FileHasher 
{
public class HashCalculator
{
public string FileName {get;私人集合

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

私有字符串GetFileHash()
{
返回CalculateHashFromStream(File.OpenRead(this.FileName));
}

私有字符串GetAssemblyFileHash()
{
string tempFileName = null;
尝试
{
//尝试打开程序集以检查这是否为.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);
}
}
}

私有字符串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);
}
}
}
}
}

您可以在我的博客上找到完整的应用程序源代码 - 以编程方式比较两个dll文件


I have the source for a DLL and I have a compiled version of it lying around somewhere.

If I compile the source it will have a different date to the already-compiled version.

How can I tell whether they are in fact the same and have merely been compiled at different times?

解决方案

To compare two .dll files you can use ildasm or any other tool for geting IL code. I have created a sample with embedded ildasm in the dll file so that you can use it on every machine. When we disassemble an assembly we check if the ildasm.exe file exists in the executing assembly folder and if not the file is extracted there from our dll file. Using the ildasm file we get the IL code and save it to a temporary file. Then we need to remove the following three rows:

MVID - as I wrote before this is a unique GUID generated with every build

Image Base (The image base tells us as to where the program will be loaded in memory by the Windows loader.) - this is different with every build as well

Time-date stamp - the time and date when the ildasm is run

So we read the temp file content, remove these rows using the regexes, and then save the file content to the same file. Here is the Disassembler class:

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

Now you can compare the contents of these two IL codes. Another option is to generate hash codes of these files and compare them. Hese is a HashCalculator class: 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);
                }
            }
        }
    }
}

You can find the full application source code on my blog here - Compare two dll files programmatically

这篇关于如何判断两个.NET DLL是否相同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆