天天看點

C# 動态修改dll的簽名 以及修改引用該dll檔案的簽名

原文: C# 動态修改dll的簽名 以及修改引用該dll檔案的簽名 讀取RedisSessionStateProvider配置

 提到用mono ceil 來修改程式集以及它的簽名,裡面GetPublicKey 和GetPubliKeyToken 方法裡面那個字元串的擷取 以及後來的簽名 我們都應該 用code來實作,還有應用該dll檔案的簽名也一同需要修改。

是以我這裡實作了一個簡單的helper方法 如下:

namespace ConsoleSession
{
    using Mono.Cecil;
    using System;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;

    public class ChangeAssemblyInfo
    {
        public string FileName { set; get; }
        public string FullName { set; get; }
    }
    public class keyHelper
    {
        static byte[] GetNewKey(string keyFileName)
        {
            using (FileStream keyPairStream = File.OpenRead(keyFileName))
            {
                return new StrongNameKeyPair(keyPairStream).PublicKey;
            }
        }

        public static void ReSign(string keyFileName, string assemblyFileName)
        {
            AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(assemblyFileName);
            asm.Name.PublicKey = GetNewKey(keyFileName);
            asm.Write(assemblyFileName);
            //用KEY檔案建立密鑰容器                     
            byte[] pbKeyBlob = File.ReadAllBytes(keyFileName);
            string wszKeyContainer = Guid.NewGuid().ToString();
            StrongNameKeyInstall(wszKeyContainer, pbKeyBlob, pbKeyBlob.Length);
            //使用建立的密鑰容器對程式集經行簽名          
            StrongNameSignatureGeneration(assemblyFileName, wszKeyContainer, IntPtr.Zero, 0, 0, 0);
            //删除建立的密鑰容器          
            StrongNameKeyDelete(wszKeyContainer);
        }

        private static byte[] tryGetPublicKeyToken(string keyFileName)
        {
            try
            {
                byte[] newPublicKey;
                using (FileStream keyPairStream = File.OpenRead(keyFileName))
                {
                    newPublicKey = new StrongNameKeyPair(keyPairStream).PublicKey;
                }
                int pcbStrongNameToken;
                IntPtr ppbStrongNameToken;
                StrongNameTokenFromPublicKey(newPublicKey, newPublicKey.Length, out ppbStrongNameToken,
                                             out pcbStrongNameToken);
                var token = new byte[pcbStrongNameToken];
                Marshal.Copy(ppbStrongNameToken, token, 0, pcbStrongNameToken);
                StrongNameFreeBuffer(ppbStrongNameToken);
                return token;
            }
            catch (Exception)
            {
                return null;
            }
        }

        public static void ReLink(string keyFileName, ChangeAssemblyInfo[] assemblyInfoList)
        {
            byte[] publicKeyToken = tryGetPublicKeyToken(keyFileName);
            if (publicKeyToken == null)
            {
                return;
            }

            //獲得每個程式集的名稱
            foreach (ChangeAssemblyInfo assemblyInfo in assemblyInfoList)
            {
                assemblyInfo.FullName = AssemblyDefinition.ReadAssembly(assemblyInfo.FileName).Name.FullName;
            }
            //檢查是否被引用,是的話,就替換PublicKeyToken
            foreach (ChangeAssemblyInfo assemblyInfo in assemblyInfoList)
            {
                AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyInfo.FileName);
                foreach (ModuleDefinition module in assembly.Modules)
                    foreach (AssemblyNameReference reference in module.AssemblyReferences)
                        if (assemblyInfoList.Any(a => a.FullName == reference.FullName))
                        {
                            reference.PublicKeyToken = publicKeyToken;
                            assembly.Write(assemblyInfo.FileName);
                        }
            }
        }

        #region StrongName庫作為一項資源包含在 MsCorEE.dll 中,其一系列API包含有
        [DllImport("mscoree.dll", EntryPoint = "StrongNameKeyDelete", CharSet = CharSet.Auto)]
        static extern bool StrongNameKeyDelete(string wszKeyContainer);

        [DllImport("mscoree.dll", EntryPoint = "StrongNameKeyInstall", CharSet = CharSet.Auto)]
        static extern bool StrongNameKeyInstall([MarshalAs(UnmanagedType.LPWStr)] string wszKeyContainer,
                                                       [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2,
                                                           SizeConst = 0)] byte[] pbKeyBlob, int arg0);

        [DllImport("mscoree.dll", EntryPoint = "StrongNameSignatureGeneration", CharSet = CharSet.Auto)]
        static extern bool StrongNameSignatureGeneration(string wszFilePath, string wszKeyContainer,
                                                                IntPtr pbKeyBlob, int cbKeyBlob, int ppbSignatureBlob,
                                                                int pcbSignatureBlob);

        [DllImport("mscoree.dll", EntryPoint = "StrongNameErrorInfo", CharSet = CharSet.Auto)]
        static extern uint StrongNameErrorInfo();

        [DllImport("mscoree.dll", EntryPoint = "StrongNameTokenFromPublicKey", CharSet = CharSet.Auto)]
        static extern bool StrongNameTokenFromPublicKey(byte[] pbPublicKeyBlob, int cbPublicKeyBlob,
                                                               out IntPtr ppbStrongNameToken, out int pcbStrongNameToken);

        [DllImport("mscoree.dll", EntryPoint = "StrongNameFreeBuffer", CharSet = CharSet.Auto)]
        static extern void StrongNameFreeBuffer(IntPtr pbMemory);
        #endregion
    }
}      

調用code 如下:

using System;
    using System.IO;
    using System.Linq;
    using Mono.Cecil;
    class Program
    {
        static void Main(string[] args)
        {
            #region 修改程式集
            string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Microsoft.Web.RedisSessionStateProvider3.dll");
            AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(filePath);
            TypeDefinition[] types = asm.MainModule.Types.ToArray();

            //修改ProviderConfiguration為public
            TypeDefinition typeConfiguration = types.FirstOrDefault(x => x.Name == "ProviderConfiguration");
            typeConfiguration.IsPublic = true;

            //修改ProviderConfiguration的字段為public
            TypeDefinition typeRedisProvide = types.FirstOrDefault(x => x.Name == "RedisSessionStateProvider");
            FieldDefinition filedConfiguration = typeRedisProvide.Fields.ToArray().FirstOrDefault(x => x.Name == "configuration");
            filedConfiguration.IsPublic = true;
            //儲存dll檔案
            filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Microsoft.Web.RedisSessionStateProvider.dll");
            asm.Write(filePath);
            #endregion
            string keyfileName = @"D:\mykey.snk";
            //修改單個dll檔案的簽名
            keyHelper.ReSign(keyfileName,filePath);

            //修改引用該dll檔案的簽名
            keyHelper.ReLink(keyfileName, new ChangeAssemblyInfo[] {
                new ChangeAssemblyInfo { FileName = filePath }
               ,new ChangeAssemblyInfo { FileName=Path.Combine(@"C:\Users\UNIT12\Documents\visual studio 2015\Projects\SessionWebApp\SessionWebApp\bin","SessionWebApp.dll")}
            });
            //Console.ReadLine();
        }
    
    }      

參考資料:

利用Mono-cecil實作.NET程式的重新簽名,重新連結相關庫的引用

繼續閱讀