gpt4 book ai didi

c# - 使用Mono.Cecil替换对类型/ namespace 的引用

转载 作者:太空狗 更新时间:2023-10-29 21:08:42 28 4
gpt4 key购买 nike

背景(不必要,令人困惑,仅出于好奇)

我使用的是Unity3D for Mobile的免费版本,它不允许我在移动设备上使用System.Net.Sockets命名空间。问题是我正在使用引用.dll的已编译System.Net.Sockets库(即IKVM)。我实际上并没有在IKVM中使用引用了System.Net.Sockets的类,因此我没有购买$ 3000的Unity Pro移动许可证,而是制作了一个名为Socketsdudeprgm.Net.Sockets命名空间的 stub 库,该 stub 库仅将所有类和方法替换为 stub (我使用Mono源代码完成了此操作)。

我的问题

我需要将dll中的所有System.Net.Sockets.*引用替换为dudeprgm.Net.Sockets.*我知道这样的事情是可能的并且可以由其他人完成(请参阅页面底部的下面的EDIT)。我想知道自己如何做。

我能够使用Mono.Cecil提出以下代码。
它遍历所有IL指令,检查操作数是否为InlineType,然后检查内联类型是否为System.Net.Sockets的一部分,然后将其重命名为dudeprgm.Net.Sockets并将其写入。 **我不确定这是否是在Mono.Cecil中进行“查找和替换”的正确方法。问题是,这不能捕获所有Sockets用法(请参见下文)。

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.SocketsSystem.Net.Sockets引用。

我很确定会发生这种情况,因为我只更改了 OperandType.InlineType,但是我不确定其他方法。我尝试到处环顾四周,但是在我看来,Mono.Cecil无法将操作数设置为 string,似乎所有操作都只能使用Cecil API( https://stackoverflow.com/a/7215711/837703)完成。

(很抱歉,如果我使用的术语不正确,那么对于IL来说,我通常是新手。)

问题

如何在mono.Cecil中出现 System.Net.Sockets的所有位置而不是仅在操作数是 InlineType的位置的所有位置替换 ?我真的不想遍历Cecil中的每个OperandType,我只是在Cecil中寻找某种查找和替换方法,而我自己不必使用普通的IL。

编辑:(也是不必要的,令人困惑的,并且仅用于好奇)
这个人花了25美元就能做类似的事情:http://www.reddit.com/r/Unity3D/comments/1xq516/good_ol_sockets_net_sockets_for_mobile_without/

Automatic patcher tool that detects and fixes socket usage in scripts and .dll.

    ...

"DLL's are patched using Mono.Cecil. ...



您可以查看https://www.assetstore.unity3d.com/en/#!/content/13166上的第二个屏幕截图,并看到它说它可以替换 namespace 。

该库不符合我的需求,因为1)它没有重命名为我想要的 namespace (dudeprgm.Net.Sockets),2)它重命名的库不支持IKVM所需的所有System.Net.Sockets类,因为IKVM使用了漂亮的每个Sockets类都差不多,并且3)它的价格为25美元,我真的不想买我不打算使用的东西。我只想表明可以替换Mono.Cecil中的 namespace /类型引用。

最佳答案

[01]类似的问题

您用另一个dll(及其中的类型)替换对dll(及其中的类型)的引用的问题在技术上类似于称为“

Google: "c# add strong name to 3rd party assembly"



在此问题中,您希望使用强名称对应用程序进行签名,并可能将其安装到GAC或Ngen-ed中,但是您的应用程序依赖于遗留的第三方库,该库在编译时未添加强名称,这违反了要求强命名程序集只能使用强命名程序集。您没有第三方库的源代码,只有二进制文件,因此您无法重新编译它(==“简化描述”)

可能有几种解决方案,最典型的3种是:

[02]类似问题的解决方案#1

您可以使用 ildasm/ ilasm round trip,将所有二进制文件转换为文本形式,将所有引用更改为等效的强名称(递归),然后将文本转换回代码。示例: 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
  • GACManagerApi.Fusion
  • brutaldev/StrongNameSigner
  • icsharpcode/ILSpy
  • Mono.Cecil.Binary
  • Mono.Cecil.Metadata
  • Mono.ResGen
  • Ricciolo.StylesExplorer.MarkupReflection
  • 和阅读http://referencesource.microsoft.com/

  • [05]您的问题

    尽管您描述的问题只是我上面描述的一个子集,但是如果您想使用GAC安装,而这又需要强力名称签名,那么它很可能是同样的问题。

    我对你的建议是

    [06]您问题的解决方案#1

    尝试使用最简单的解决方案 [02] ,以尽量减少使用Mono包中的ilasm/ildasm工具(而不是Microsoft .NET Framework提供的工具)的麻烦(.NET Framework 4.5中的Microsoft Resgen已损坏,并且无法往返Resx格式,Ildasm输出不能正确处理非ASCII字符等。虽然您不能修复Microsoft损坏的封闭源代码,但可以修复Mono的开源代码,但我不必这样做。)

    [07]您问题的解决方案2

    如果 [06] 不适合您,请研究(调试)→ ILSpy←并研究Mono文档,以了解各种命令行工具的用途以及其来源-您将了解它们如何使用Mono.Cecil库

    如果您需要验证强命名的甚至签名的程序集(篡改它们将使签名无效)或删除签名等。您将花更多的时间来编写代码,这比简单的Stack Overflow答案所能描述的要长。

    [08]您问题的解决方案#3

    潜伏着 ILMerge的作用以及如何为您提供更简单的解决方案

    [09]您问题的解决方案#4

    另一个更简单的解决方案可能是(如果IKVM支持) Hook AssemblyResolve事件,您可以在其中将dll名称重新映射为物理dll,例如来自完全不同的文件或资源流等。如旧的堆栈溢出问题 Embedding DLLs in a compiled executable的几个答案所示

    (编辑#1:评论后)

    [10]您问题的解决方案#5

    如果您或多或少的一般性问题实际上归结为“如何使IKVM.dll使用我的套接字类而不是 namespace System.Net.Sockets中的套接字类”,那么很简单的解决方案可能是:

    使用 http://www.ikvm.net/download.html上提供的源代码编译和部署您自己的IKVM.dll定制版本-不需要二进制Mono.Cecil魔术。

    由于所有代码都是打开的,因此应该可以找到所有指向命名空间 System.Net的引用并将其重定向到 dudeprgm.Net,方法是
  • [10.1]获取IKVM源代码和所有其他先决条件,并确保您可以编译可运行的IKVM.dll。
  • [10.2]将dudeprgm.Net.cs项目添加到解决方案

  • 所有源文件中的
  • [10.3]查找并删除所有类似于using System.Net的内容
  • 所有源文件中的
  • [10.4]全文查找并用System.Net替换所有看起来像dudeprgm.Net的内容
  • [10.5]编译。当编译器提示缺少符号(之前在System.Net namespace 中)时,请将其添加到 stub 文件中。转到[10.5]
  • [10.6]如果上述步骤在2小时后仍未解决为“build ok”,请考虑其他解决方案(或睡一会儿)
  • [10.7]检查IKVM许可证(http://sourceforge.net/p/ikvm/wiki/License/)是否需要更改/声明/确认某些内容,因为原始源代码已被修改

  • (编辑#2:评论后)

    [11]您问题的解决方案#6

    如果您选择track [04] 并使用文本文件和 ilasm/ildasm工具(样式 [02] )似乎没有效果,则以下是我的自动强名称签名器的关键相关部分,该部分使用以下命令将程序集引用更改为其他引用: Mono.Cecil。代码按对我有用的形式按原样粘贴(之前,之后和周围没有代码行)。读取 key : a is Mono.Cecil.AssemblyDefinitionb implements Mono.Cecil.IAssemblyResolverb实例中的key方法是 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]轮到您了

    关于c# - 使用Mono.Cecil替换对类型/ namespace 的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25077830/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com