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程序的重新签名,重新链接相关库的引用


你可能感兴趣的:(C#.NET基础)