c# – 使用Mono.Cecil替换对类型/命名空间的引用
|
背景(不必要,令人困惑,只为好奇)
我正在使用Unity3D for Mobile的免费版本,它不允许我在移动设备上使用System.Net.Sockets命名空间.问题是我正在使用引用System.Net.Sockets的已编译的.dll库(即IKVM).我实际上并没有使用引用System.Net.Sockets的IKVM中的类,因此我没有购买3000美元的Unity Pro移动许可证,而是创建了一个名为dudeprgm.Net.Sockets的Sockets命名空间的存根库,它只替换了所有存根的类和方法(我使用Mono源代码完成此操作). 我的问题 我需要将dll中的所有System.Net.Sockets.*引用替换为dudeprgm.Net.Sockets.*.我知道这样的事情是可能的并且由其他人完成(参见下面的编辑,在页面底部).我想知道自己该怎么做. 我能够使用Mono.Cecil提出以下代码. private static AssemblyDefinition stubsAssembly;
static void Main(string[] args) {
AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(args[0]);
stubsAssembly = AssemblyDefinition.ReadAssembly("Socket Stubs.dll");
// ...
// Call ProcessSockets on everything
// ...
asm.Write(args[1]);
}
/*
* This will be run on every property,constructor and method in the entire dll given
*/
private static void ProcessSockets(MethodDefinition method) {
if (method.HasBody) {
Mono.Collections.Generic.Collection<Instruction> instructions = method.Body.Instructions;
for (int i = 0; i < instructions.Count; i++) {
Instruction instruction = instructions[i];
if (instruction.OpCode.OperandType == OperandType.InlineType) {
string operand = instruction.Operand.ToString();
if (operand.StartsWith("System.Net.Sockets")) {
Console.WriteLine(method.DeclaringType + "." + method.Name + "(...) uses type " + operand);
Console.WriteLine("t(Instruction: " + instruction.OpCode.ToString() + " " + instruction.Operand.ToString() + ")");
instruction.Operand = method.Module.Import(stubsAssembly.MainModule.GetType("dudeprgm.Net.Sockets",operand.Substring(19)));
Console.WriteLine("tReplaced with type " + "dudeprgm.Net.Sockets" + operand.Substring(18));
}
}
}
}
}
它工作正常,但只捕获“简单”指令.用ildasm反编译,我可以看到它取代了这里的类型: box ['Socket Stubs'/*23000009*/]dudeprgm.Net.Sockets.SocketOptionLevel/*01000058*/ 但它没有抓住这些“复杂”的指示: callvirt instance void [System/*23000003*/]System.Net.Sockets.Socket/*0100003F*/::SetSocketOption(valuetype [System/*23000003*/]System.Net.Sockets.SocketOptionLevel/*01000055*/,valuetype [System/*23000003*/]System.Net.Sockets.SocketOptionName/*01000056*/,int32) /* 0A000094 */ 现在.dll是dudeprgm.Net.Sockets和System.Net.Sockets参考的混乱. 我很确定这种情况正在发生,因为我只是在改变OperandType.InlineTypes,但我不确定如何做到这一点.我试过到处寻找,但在我看来,像Mono.Cecil无法将操作数设置为字符串,似乎只需要使用Cecil API(https://stackoverflow.com/a/7215711/837703)完成所有操作. (对不起,如果我使用的是不正确的条款,我对IL一般都是新手.) 题 如何替换System.Net.Sockets出现在Mono.Cecil中的所有位置,而不仅仅是操作数是InlineType的位置?我真的不想查看Cecil中的每一个OperandType,我只是在Cecil中寻找一些查找和替换方法,我不必自己使用普通的IL. 编辑:(也没必要,只为好奇)
您可以在https://www.assetstore.unity3d.com/en/#!/content/13166查看第二个屏幕截图,看看它是否可以替换名称空间. 该库不符合我的需求,因为1)它不会重命名为我想要的命名空间(dudeprgm.Net.Sockets),2)它重命名的库不支持所有的System.Net.Sockets类IKVM需要,因为IKVM几乎使用每个Sockets类而且3)它的成本是25美元而且我真的不想购买我不会使用的东西.我只想表明在Mono.Cecil中替换名称空间/类型引用是可能的. 解决方法
[01]类似的问题
用另一个dll(和其中的类型)替换对dll(和其中的类型)的引用的问题在技术上类似于已知的问题
在这个问题中,您希望您的应用程序通过强名称签名并可能已安装到GAC或Ngen-ed中,但您的应用程序依赖于在编译时未添加强名称的旧版第三方库,这违反了要求一个强大的命名程序集只能使用强名称程序集.您没有第三方库的源代码,只有二进制文件,因此您无法重新编译它(==“简化描述”) 有几种可能的解决方案,最常见的是3种: [02]类似问题的解决方案#1 您可以使用ildasm / ilasm往返,将所有二进制文件转换为文本形式,将所有引用更改为强名称等效(递归)并将文本转换回代码.示例:http://buffered.io/posts/net-fu-signing-an-unsigned-assembly-without-delay-signing/和https://stackoverflow.com/a/6546134/2626313 [03]类似问题的解决方案#2 您可以使用已编写的工具来解决此问题,例如:http://brutaldev.com/post/2013/10/18/NET-Assembly-Strong-Name-Signer [04]类似问题的解决方案#3 您可以创建一个精心设计的工具来满足您的确切需求.有可能,我已经完成了,花了几个星期,代码重达几千行代码.对于我已经重复使用的最脏的工作(稍作修改)主要来自(无序)源代码: > ApiChange.Api.Introspection.CorFlagsReader.cs [05]你的问题 虽然您所描述的问题看起来只是我上面描述的问题的一部分,但如果您想使用GAC安装,则可能会出现同样的问题,而GAC安装又需要强名称签名. 我给你的建议是 [06]你的问题的解决方案#1 提供最简单的解决方案[02]尝试使用Mono软件包中的ilasm / ildasm工具,而不是Microsoft .NET Framework提供的工具(.NET Framework 4.5中的Microsoft Resgen已损坏且不能往返resx格式,Ildasm输出不能正确处理非ASCII字符等.虽然你无法修复微软破解的闭源,你可以修复Mono的开源,但我不需要.) [07]你的问题的解决方案#2 如果[06]不适合你,那么学习(调试)→ILSpy←并研究各种命令行工具的Mono文档,做你需要的东西及其来源 – 你会看到他们如何使用Mono.Cecil库 如果您需要验证强名称或甚至签名的程序集(篡改它们将使签名无效)或删除签名等.您将深入研究代码的时间超过简单的Stack Overflow答案可以描述的内容. [08]你的问题的解决方案#3 潜伏在ILMerge周围,以及如何为您提供更简单的解决方案 [09]你的问题的解决方案#4 另一个更简单的解决方案可能是(如果IKVM支持它)挂钩AssemblyResolve事件,您可以将dll名称重新映射到物理dll,例如,从完全不同的文件或资源流等.如旧的Stack Overflow问题Embedding DLLs in a compiled executable的几个答案中所示 (编辑#1:评论后) [10]你的问题的解决方案#5 如果您或多或少的一般性问题实际上归结为“如何使IKVM.dll使用我的套接字类而不是命名空间System.Net.Sockets中的那些”,那么非常简单的解决方案可能是: 使用http://www.ikvm.net/download.html提供的源代码编译和部署您自己的自定义版本的IKVM.dll – 无需使用二进制Mono.Cecil魔法. 由于所有代码都是开放的,因此应该可以找到并将指向命名空间System.Net的所有引用重定向到dudeprgm.Net中 > [10.1]获取IKVM源代码和所有其他先决条件,并确保可以编译工作的IKVM.dll (编辑#2:评论后) [11]你的问题的解决方案#6 如果您选择track [04]并使用文本文件和ilasm / ildasm工具(样式[02])似乎效率不高,那么下面是我的自动强名称签名者的关键相关部分,它使用Mono将程序集引用更改为其他引用.塞西尔.代码按原样粘贴(不含代码行,之前,之后和所有代码),以适合我的形式.读取键:a是Mono.Cecil.AssemblyDefinition,b实现Mono.Cecil.IAssemblyResolver,b实例中的键方法是AssemblyDefinition Resolve(AssemblyNameReference name)方法,它将所需的DLL名称转换为对AssemblyDefinition.ReadAssembly(..)的调用.我不需要解析指令流,重新映射汇编引用就足够了(如果需要,我可以在这里粘贴我的代码中的其他几个部分) /// <summary>
/// Fixes references in assembly pointing to other assemblies to make their PublicKeyToken-s compatible. Returns true if some changes were made.
/// <para>Inspiration comes from https://github.com/brutaldev/StrongNameSigner/blob/master/src/Brutal.Dev.StrongNameSigner.UI/MainForm.cs
/// see call to SigningHelper.FixAssemblyReference
/// </para>
/// </summary>
public static bool FixStrongNameReferences(IEngine engine,string assemblyFile,string keyFile,string password)
{
var modified = false;
assemblyFile = Path.GetFullPath(assemblyFile);
var assemblyHasStrongName = GetAssemblyInfo(assemblyFile,AssemblyInfoFlags.Read_StrongNameStatus)
.StrongNameStatus == StrongNameStatus.Present;
using (var handle = new AssemblyHandle(engine,assemblyFile))
{
AssemblyDefinition a;
var resolver = handle.GetAssemblyResolver();
a = handle.AssemblyDefinition;
foreach (var reference in a.MainModule.AssemblyReferences)
{
var b = resolver.Resolve(reference);
if (b != null)
{
// Found a matching reference,let's set the public key token.
if (BitConverter.ToString(reference.PublicKeyToken) != BitConverter.ToString(b.Name.PublicKeyToken))
{
reference.PublicKeyToken = b.Name.PublicKeyToken ?? new byte[0];
modified = true;
}
}
}
foreach (var resource in a.MainModule.Resources.ToList())
{
var er = resource as EmbeddedResource;
if (er != null && er.Name.EndsWith(".resources",StringComparison.OrdinalIgnoreCase))
{
using (var targetStream = new MemoryStream())
{
bool resourceModified = false;
using (var sourceStream = er.GetResourceStream())
{
using (System.Resources.IResourceReader reader = new System.Resources.ResourceReader(sourceStream))
{
using (var writer = new System.Resources.ResourceWriter(targetStream))
{
foreach (DictionaryEntry entry in reader)
{
var key = (string)entry.Key;
if (entry.Value is string)
{
writer.AddResource(key,(string)entry.Value);
}
else
{
if (key.EndsWith(".baml",StringComparison.OrdinalIgnoreCase) && entry.Value is Stream)
{
Stream newBamlStream = null;
if (FixStrongNameReferences(handle,(Stream)entry.Value,ref newBamlStream))
{
writer.AddResource(key,newBamlStream,closeAfterWrite: true);
resourceModified = true;
}
else
{
writer.AddResource(key,entry.Value);
}
}
else
{
writer.AddResource(key,entry.Value);
}
}
}
}
}
if (resourceModified)
{
targetStream.Flush();
// I'll swap new resource instead of the old one
a.MainModule.Resources.Remove(resource);
a.MainModule.Resources.Add(new EmbeddedResource(er.Name,resource.Attributes,targetStream.ToArray()));
modified = true;
}
}
}
}
}
if (modified)
{
string backupFile = SigningHelper.GetTemporaryFile(assemblyFile,1);
// Make a backup before overwriting.
File.Copy(assemblyFile,backupFile,true);
try
{
try
{
AssemblyResolver.RunDefaultAssemblyResolver(Path.GetDirectoryName(assemblyFile),() => {
// remove previous strong name https://groups.google.com/forum/#!topic/mono-cecil/5If6OnZCpWo
a.Name.HasPublicKey = false;
a.Name.PublicKey = new byte[0];
a.MainModule.Attributes &= ~ModuleAttributes.StrongNameSigned;
a.Write(assemblyFile);
});
if (assemblyHasStrongName)
{
SigningHelper.SignAssembly(assemblyFile,keyFile,null,password);
}
}
catch (Exception)
{
// Restore the backup if something goes wrong.
File.Copy(backupFile,assemblyFile,true);
throw;
}
}
finally
{
File.Delete(backupFile);
}
}
}
return modified;
}
[12]轮到你了 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
