Mono是一个.NET框架类库集(准确来说是一个"非官方的.NET框架"),Mono已经发展得比较成熟了,在LINUX下也可以使用该框架直接运行.NET程序而无需重新编译.关于Mono的其它信息,可以在互联网随便搜索到.

Mono的开源社区:http://groups.google.com/group/mono-cecil

Mono的出现,给分析/修改.NET程序带来很多方便之处,利用其配合.NET的对象映射,可以做很多有趣的东西,比如动态指令注入.....

.NET程序的强签名验证机制,很多人都认为这个是一种加密技术,防止被恶意修改文件.凡事也有善恶之分,很多时候我们可能需要修改一下别人的程序来满足自己的要求,比如出于善意的软件汉化,或者为了方便,需要将未签名的程序注册到全局命名空间(未签名其实也算是一种"签名"的手段,未签名的程序是无法注册到全局命名空间的).

但一旦修改之后,可能会导致整个程序无法使用,.NET的程序加载,会检查程序的完整性,进行HASH比较,被修改过的程序是无法完成加载的.如果是单个独立程序的话还好办,如果是带有其它库的话,那要修改后正常使用,有以下几个办法:

1.将程序在注册表添加"跳过检测程序强签名处理",这个也是最常用的手段了.

2.另外一种做法,就是通过ILDASM,将程序重新反编译为IL汇编文件,然后加入签名,重新编译为新的程序.这个做法是最笨的,也是最多人使用的.如果一个程序关联的库有10个以上的话,那是一个很吃力的工作,某些程序使用过混淆处理,将所有的标识符都以不可打印字符重新命名,在ILASM下面是无法直接编译通过的.

3.在原程序"内容"不变的情况下,实现所有相关的类库重新签名,并且自动RELINK的话,才是最好的解决办法.

针对第二个方法处理,很多人选择了直接移除强签名的方法来替代重新签名.就是将所有相关的库一律移除强签名,嗯,这样运行是可以通过的,但如果这是一个可以重用的第三方类库的话,被移除强签名的类库,是无法在VS里面直接进行打包发布的,如果你的程序打算使用ONECLICK方式发布的话,那就没撤了.

微软为程序集签名提供了一个SN工具,这个工具可以将签名的程序经行重新签名,当然,需要你有公私钥匙对的情况下,钥匙对就肯定没的了.直接让其签名就肯定无望了.被移除公钥的程序也是直接被拒绝签名的.

重新签名也有现成的工具.比如RE-SIGN这个软件:

可以实现单个文件重新签名.但是单纯改了签名,不将相关的库重新连接的话,重新签名就完全没有意义了.

本来打算修改一下RE-SIGN,让其可以在签名的过程中,重新链接所有相关的库,但RE-SIGN这个软件的作者很小气,将RE-SIGN经行了高强度的混淆处理,

面对这样的程序,我不喜欢强人所难,人家没诚意,我不想去搞他.既然免费提供给别人使用,为何.....

到这里的话,Mono可以派上用场了,其实也是牛刀杀鸡了.

用Mono加载一个程序集,拿你自己的钥匙对生成PublicKey替换掉原来程序的PublicKey(当然,如果之前没有的话,也可以直接这样加上去),然后将替换掉的程序集重新保存,再使用微软提供SN工具就可以直接重新签名,PublicKey和PublicKeyToken可是两个不同的东西,这里要理解清楚.代码如下:

        public byte[] GetNewKey(string keyFileName)        {using (FileStream keyPairStream = File.OpenRead(keyFileName))            {return new StrongNameKeyPair(keyPairStream).PublicKey;            }        }

public void ReSign()        {

            AssemblyDefinition asm = AssemblyFactory.GetAssembly(ASSEMBLYNAME);            asm.Name.PublicKey = GetNewKey(KEYFILENAME);            AssemblyFactory.SaveAssembly(asm, ASSEMBLYNAME);

//用KEY文件建立密钥容器                                 byte[] pbKeyBlob = File.ReadAllBytes(KEYFILENAME);string wszKeyContainer = Guid.NewGuid().ToString();            StrongNameKeyInstall(wszKeyContainer, pbKeyBlob, pbKeyBlob.Length);//使用新建的密钥容器对程序集经行签名                      StrongNameSignatureGeneration(ASSEMBLYNAME, wszKeyContainer, IntPtr.Zero, 0, 0, 0);//删除新建的密钥容器                      StrongNameKeyDelete(wszKeyContainer);        }

经过重新保存的程序集,现在可以直接使用微软提供的SN工具经行重新签名了:

sn /Ra RelinkLib.dll mykey.snk

签名可以使用直接启动进程的方式调用SN工具:

        public void RunSnTool()        {//SN工具进程            Process snProcess = new Process                                    {                                        StartInfo =                                            {                                                FileName = "SN.exe",                                                Arguments = string.Format("/Ra {0} {1}", ASSEMBLYNAME, KEYFILENAME)                                            }                                    };            snProcess.Start();            snProcess.WaitForExit();        }

这样,RE-SIGN这个工具所做的工作,也就基本实现了.

你是否觉得这样不好看,有没有办法内部调用SN工具的类似操作啊?答案是有的,

StrongName库作为一项资源包含在 MsCorEE.dll 中,其一系列API包含有:

GetHashFromAssemblyFile 函数使用指定的哈希算法获取指定程序集文件的哈希值。

GetHashFromAssemblyFileW 函数使用指定的哈希算法获取指定为 Unicode 字符串的程序集文件的哈希值。 

GetHashFromBlob 函数使用指定的哈希算法获取指定内存地址处的程序集的哈希值。

GetHashFromFile 函数依据指定文件的内容生成哈希代码。

GetHashFromFileW 函数根据一个 Unicode 字符串指定的文件的内容生成哈希代码。

GetHashFromHandle 函数使用指定的哈希算法并根据具有指定文件句柄的文件的内容生成哈希代码。

StrongNameCompareAssemblies 函数只通过强名称签名确定两个程序集是否不同。

StrongNameErrorInfo 函数获取由某个强名称函数引发的上一个错误代码。

StrongNameFreeBuffer 函数释放上一次调用强名称函数(如 StrongNameGetPublicKey、StrongNameTokenFromPublicKey 或 StrongNameSignatureGeneration)时分配的内存。

StrongNameGetBlob 函数通过位于指定地址的可执行文件的二进制表示形式填充指定的缓冲区。

StrongNameGetBlobFromImage 函数获取位于指定内存地址的程序集映像的二进制表示形式。

StrongNameGetPublicKey 函数从私钥/公钥对中获取公钥。 

StrongNameHashSize 函数使用指定的哈希算法获取哈希值所需要的缓冲区大小。 

StrongNameKeyDelete 函数删除指定的密钥容器。

StrongNameKeyGen 函数创建一个供强名称使用的新公钥/私钥对。

StrongNameKeyGenEx 函数生成具有指定密钥大小的新的公钥/私钥对,以供强名称使用。

StrongNameKeyInstall 函数向容器中导入一个公钥/私钥对。

StrongNameSignatureGeneration 函数生成指定程序集的强名称签名。

StrongNameSignatureGenerationEx 函数基于指定标志为指定的程序集生成强名称签名。

StrongNameSignatureSize 函数返回强名称签名的大小。

StrongNameSignatureVerification 函数获取一个值,该值指示所提供的路径中的程序集清单是否包含强名称签名(根据指定标志进行验证)。

StrongNameSignatureVerificationEx 函数获取一个值,该值指示在提供的路径处的程序集清单是否包含强名称签名。

StrongNameSignatureVerificationFromImage 函数验证已映射到内存的程序集对关联的公钥是否有效。

StrongNameTokenFromAssembly 函数从指定的程序集文件中创建强名称标记。

StrongNameTokenFromAssemblyEx 函数从指定的程序集文件创建强名称标记,并返回公钥。

StrongNameTokenFromPublicKey 函数获取表示公钥的标记。 

这个你可以在头文件StrongName.h里面找到相关的函数原型.

C#的声明如下:

        [DllImport("mscoree.dll", EntryPoint = "StrongNameKeyDelete", CharSet = CharSet.Auto)]public static extern bool StrongNameKeyDelete(string wszKeyContainer);

        [DllImport("mscoree.dll", EntryPoint = "StrongNameKeyInstall", CharSet = CharSet.Auto)]public 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)]public static extern bool StrongNameSignatureGeneration(string wszFilePath, string wszKeyContainer,                                                                IntPtr pbKeyBlob, int cbKeyBlob, int ppbSignatureBlob,int pcbSignatureBlob);

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

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

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

签名过程:首先建立一个容器,使用容器对程序集进行签名.

一个重新签名的过程就这样完成了.对于RELINK的话,其实也简单:

        private 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 void ReLink()        {byte[] publicKeyToken = tryGetPublicKeyToken(ASSEMBLYNAME);if (publicKeyToken == null)            {return;            }//初始化一个要重新签名的程序信息列表            AssemblyInfo[] assemblyInfoList = new[]                                                  {new AssemblyInfo {FileName = @"D:\Test\Test1.dll"},new AssemblyInfo {FileName = @"D:\Test\Test2.dll"},new AssemblyInfo {FileName = @"D:\Test\Test2.dll"}                                                  };//获得每个程序集的名称            foreach (AssemblyInfo assemblyInfo in assemblyInfoList)            {                assemblyInfo.FullName = AssemblyFactory.GetAssembly(assemblyInfo.FileName).Name.FullName;            }//检查是否被引用,是的话,就替换PublicKeyToken            foreach (AssemblyInfo assemblyInfo in assemblyInfoList)            {                AssemblyDefinition assembly = AssemblyFactory.GetAssembly(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;                            AssemblyFactory.SaveAssembly(assembly,assemblyInfo.FileName);                        }            }        }

就是判断相关的一组程序集里,如果有引用当前操作的程序集,就将它的PublicKeyToken改为重新签名后的PublicKeyToken.

从一个KEY文件获取PublicKeyToken有2个方式:

1.使用新KEY对一个程序集签名,然后读取该程序集,获取PublicKeyToken,这个办法不推荐.

2.使用StrongNameTokenFromPublicKey可以直接从KEY文件提取PublicKeyToken,使用完毕别忘记使用StrongNameFreeBuffer释放内存.

写到这里,一个重新签名和重新链接的过程就完毕了.Mono是一个很强大类库,我会继续写一个Mono的方面的文章.

to be continue.....

转载于:https://www.cnblogs.com/flarejune/archive/2008/10/16/1312993.html

利用Mono-cecil实现.NET程序的重新签名,重新链接相关库的引用相关推荐

  1. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

    原文:利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...

  2. 编译时MSIL注入--实践Mono Cecil(1)

    紧接上两篇浅谈.NET编译时注入(C#-->IL)和浅谈VS编译自定义编译任务-MSBuild Task(csproject),在第一篇中我们简单研究了c#语法糖和PostSharp的MSIl注 ...

  3. Mono 把 .NET 应用程序移植到 Linux

    Mono 是基于 .NET 的开放源码开发平台,它让您可以使用各种 .NET 兼容语言创建强大.灵活的 Linux® 应用程序,同时利用跨平台的能力.本文带领您在系统上安装 Mono,并开发第一个用 ...

  4. 运用Mono.Cecil 反射读取.NET程序集元数据

    CLR自带的反射机智和API可以很轻松的读取.NET程序集信息,但是不能对程序集进行修改.CLR提供的是只读的API,但是开源项目Mono.Cecil不仅仅可以读取.NET程序集的元数据,还可以进行修 ...

  5. 使用Mono.Cecil辅助ASP.NET MVC使用dynamic类型Model

    使用Mono.Cecil辅助ASP.NET MVC使用dynamic类型Model 2011-09-06 00:21 by 老赵, 8645 visits 这也是之前在珠三角技术沙龙上的示例之一,解决 ...

  6. Mono.Cecil使用示例之使指定程序集成为UnityEditor.dll的友元程序集

    Mono.Cecil使用示例之使指定程序集成为UnityEditor.dll的友元程序集 Mono.Cecil是一个开源的库,使用Mono.Cecil可以非常方便的在代码中修改C#程序集.在Unity ...

  7. 利用SqlHelper.cs实现Web程序对数据库的增、删、改等操作

    利用SqlHelper.cs实现Web程序对数据库的增.删.改等操作 (2006-08-22 00:50:05) 转载▼ 分类:技术杂谈 在SqlHelper.cs中,封装了所有和SQL数据库相关的操 ...

  8. 利用FRIDA攻击Android应用程序(三)

    利用FRIDA攻击Android应用程序(三) 前言 在我的有关frida的第二篇博客发布不久之后,@muellerberndt决定发布另一个OWASP Android crackme,我很想知道是否 ...

  9. java jmf 视屏监控的核心代码_Java中利用JMF编写摄像头拍照程序_java

    我把程序分为两种,有趣的和无趣的,最近做了几个有趣的项目,其中一个,应当就算是摄像头拍照程序了.用于现场拍照,生成照片,主要用到java Media Framework(JMF). 首先到SUN下载最 ...

  10. python程序实例电话本-利用Python电话本小程序!这波操作你给几分?

    原标题:利用Python电话本小程序!这波操作你给几分? 最近比较忙,只能抽空的写写文章,其实我也是一个上班族,python完全是个人想学然后希望以后对工作有所帮助,2019年了,祝大家新年快乐. 2 ...

最新文章

  1. C语言 读取字符串数组中的字符串并获取字符串的长度
  2. Servlet基础(一) Servlet基础和关键的API介绍
  3. 总结 @ 在 C# string 中的用法
  4. s5-1 CPU调度
  5. Tensorflow卷积神经网络
  6. [paper reading] FCOS
  7. 超市负库存产生的原因及对策
  8. MyEclipse8.5中的可视化界面JTable和JComboBox应用,及常用方法
  9. 不能使用泛型的形参创建对象_泛型就这么简单
  10. 74hc165C语言程序,单片机驱动74hc165程序
  11. [记录]关于电机反转和l298n和我的心里斗争
  12. 《2022谷歌开发者大会》参会之旅
  13. 9大代理服务器软件的比较与分析之校园局域网代理蝴蝶
  14. 坚守普惠金融初心,微众银行持续升级无障碍金融服务
  15. BZOJ[1984]月下“毛景树” 树链剖分+线段树
  16. iOS 全息备份研究
  17. 微软Hyper-V虚拟机复制实现双机备份过程
  18. 阿里云国际站:阿里云服务器遇到了CC攻击怎么处理防护措施?
  19. 搜狗输入法在idea打不了汉字_IDEA下搜狗输入法输入中文时卡着不动的参考解决方法...
  20. Java io流 解压缩多个文件 损坏问题解决

热门文章

  1. 越过山丘才发现无人等候 倒计时84
  2. TS中any与unknwon的区别
  3. 程序猿段子_程序猿的24个段子,笑着笑着眼泪就出来了
  4. spark的数三角形算法_腾讯开源全栈机器学习平台 Angel 3.0,支持三大类型图计算算法...
  5. 京东数据分析工具推荐(京东第三方数据平台)
  6. 远程手机控制开关应用
  7. 收集的一些有意思的网站(持续更新)
  8. .net mvc 文件压缩打包下载
  9. CyanogenMod编译
  10. 特别有趣的spyder运行程序