【问题标题】:How to modify .NET Assembly read-only property?如何修改 .NET 程序集只读属性?
【发布时间】:2013-06-21 01:45:43
【问题描述】:

我想在我的应用程序运行时修改 Assembly.Location 属性。我正在从资源中加载 .NET 程序集,而加载的程序集(从内存中加载)的该字段为空,它会破坏应用程序的功能。

我无法控制他们的源代码。我知道如何在我自己的代码中解决这个错误,但事实并非如此。当他们使用 Assembly.Location 并获得一个空字符串时,问题就开始了。

http://msdn.microsoft.com/en-us/library/system.reflection.assembly.location.aspx

它是只读的。有什么低级的方法吗?有什么想法吗?

我的应用程序是嵌入在资源中的那些应用程序的加载器,因此它不依赖于它们。尝试从内存中加载任何程序集并检查这些已加载程序集的 Assembly.Location 字段,它将为空白。它们没有位置,因为它们是从内存中加载的,但我仍然想通过 .NET 内部修改或任何其他方法来更改它。

压缩的程序集无法解压缩到磁盘。如果您知道如何实现它,我不介意重大问题的危险。

【问题讨论】:

  • 为什么你要这样做?请给我们更多背景信息 - 可能会有更好的方法。
  • 那么它破坏了 什么 功能?这些程序集是您控制的吗?如果是这样,您应该查看是否可以将它们修复为不依赖此属性。
  • 所有这些都是应该在你的问题中出现的上下文。
  • 我不同意票数接近的情况。这个问题不是太本地化......它目前处于一般环境中,并解决了图书馆开发人员真正关心的问题,一个没有很好记录的问题。我相信这个问题是有道理的。
  • 鉴于这与“收官之战”博客文章相关联,并且有一个指向 +11 问题的链接,该问题本质上是在问同样的事情,它看起来像是一个方面“由于个性而投反对票”。

标签: .net reflection reverse-engineering


【解决方案1】:

简单地说,你不能修改属性。在这种情况下,它会通知您从磁盘加载程序集的位置。相反,您需要移动程序集并确保您的应用程序从新位置加载它。

【讨论】:

  • 这不是答案,我只是想通过反射、运行时字节码修改或任何其他必要的方式来修改这个属性。
  • @BartoszWójcik:不,这真的是的答案。如果您使用的程序集依赖于 Assembly.Location 是您无法实现的值,那么这些程序集不适合您使用它们的目的。
  • 仍然不是有效的答案。 .NET 是一个框架,它必须是一种修改它的内部变量和只读字段的方法,我不是在寻找任何 documented 方式。
  • @BartoszWójcik:您可能认为您想通过反射来修改字段,但这会在以后给您带来更多痛苦。您将开始将解决方法堆放在解决方法上。让我们尝试解决最初的问题
  • @BartoszWójcik:这又是错误的做法,您甚至不能通过反射设置该字段,因为它没有内部存储的变量。相反,您需要更改调用 Windows API 的内部函数的结果。
【解决方案2】:

将所需的程序集资源从其容器程序集中复制到容器程序集的目录或子目录中。然后将复制的程序集加载到您的应用程序中。这会容易得多然后尝试更改私有字段(或函数调用的结果)。

Assembly 类公开了一个公共接口 - 他们应该使用它。尝试修改类的内部工作可能会给您带来重大问题,假设它甚至是可能的。一些未来的版本,甚至只是一个定期更新,可能会改变类的内部工作并破坏你的代码。您也无法预测该类的其他部分依赖于该字段。改变其价值可能会产生意想不到的后果。您提议从其定义的行为更改类,这可能会导致其他程序集或程序员进一步感到困惑和沮丧。这就是该字段被实现为只读的原因,也是 .NET 没有提供修改只读值的简单方法的原因。


更新

这是 Location 属性的源代码:

[System.Security.SecurityCritical]  // auto-generated
[ResourceExposure(ResourceScope.Machine)] 
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity] 
private static extern void GetLocation(RuntimeAssembly assembly,
    StringHandleOnStack retString); 

public override String Location 
{
    [System.Security.SecuritySafeCritical]  // auto-generated
    [ResourceExposure(ResourceScope.Machine)]
    [ResourceConsumption(ResourceScope.Machine)] 
    get {
        String location = null; 

        GetLocation(GetNativeHandle(), 
                    JitHelpers.GetStringHandleOnStack(ref location));

        if (location != null)
            new FileIOPermission( FileIOPermissionAccess.PathDiscovery, location ).Demand();

        return location; 
    }
} 

请注意,此代码实际上位于名为 RuntimeAssembly 的未记录类中,该类在 Assembly 类中定义为 internal。您可以在这里查看完整的源代码:
http://www.dotnetframework.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/Reflection/Assembly@cs/1305376/Assembly@cs

如您所见,这里没有可修改的支持字段。无法根据需要覆盖 Location 属性(无需重写 Windows 操作系统)。

AND... 以防万一您渴望重写 GetLocation 函数,您可能对此 Q/A 感兴趣:
What is [DllImport("QCall")]?
(不用说,在这一点上,你只能靠自己了。)

【讨论】:

  • 对不起,压缩后的程序集无法解压到磁盘。我想要如果你知道如何实现它的话。
  • 这是您第一次提到压缩。我认为如果你能解释你在做什么以及你为什么这样做会有所帮助。你说“如果你不能解决困难的问题,作为一名程序员还有什么意义?”。当当前的方式无法将您带到您想去的地方时,工作的一部分是找到另一种方式。见How to Ask a Question“你的问题的答案可能并不总是你想要的,但这并不意味着它是错误的。”
  • (顺便说一句,Jon Skeet 是 C# 方面最重要的专家之一。如果他说不能做或不应该做,那么给予相当大的关注是明智的听从他的建议。)
  • @Bartosz - 这与netshrink 有什么关系吗?从本质上讲,您的问题是一个很好的问题。我认为如果你能更好地解释事情,它实际上可能会得到一些很好的关注和可靠的答案。也许如果你想重新访问它,它可以重新打开。
  • @Cyborgx37 试图结束这个问题的公平竞争,但不幸的是,OP 显得非常固执,不会接受他正在尝试做的事情并不是使用任何标准都可以真正实现的/可接受的方法(如果有)。成为优秀开发人员的一部分是知道您何时采用错误的方法,了解原因并寻求替代方案 - OP 似乎不愿意这样做,因此 IMO 他不妨留给自己的设备.
【解决方案3】:

虽然@Cyborgx37 是正确的——您不能更改现有程序集的位置,但还有其他方法可以解决此问题。

不要尝试设置已加载程序集的位置,而是使用 Mono.Cecil 之类的库来使用此属性重写程序集。

假设它不是核心 .NET 框架程序集之一,很容易将所有 Assembly.Location 访问替换为您自己可以注入的静态属性。

在编写自己的加密程序集加载器时,我不得不做很多这样的事情,并且非常成功。

编辑

如果您真的下定决心要这样做,那么这两篇文章应该让您开始了解如何在运行时重写现有方法。但是有多个警告:这根本不适用于 NGEN 或 GAC,并且您必须解决异步/等待/可枚举/异常处理的多个难题。使用风险自负。

MSIL Injection: Rewrite a non dynamic method at runtime

CLR Injection: Runtime Method Replacer

我强烈反对它,因为这些解决方案太脆弱而无法在生产代码中使用,作者提到了这一点:

我们需要记住,我们正在以非预期的方式直接操作 CLR 内存。此代码可能不适用于较新版本的 .NET 框架。这是在 Vista x86 上使用 .NET 3.5 测试的,可能无法在您的机器上运行。

Microsoft 不支持任何这种骇人听闻的东西。如果可能,您应该寻找另一种方法来实现您的目标。

【讨论】:

  • 太酷了!它似乎符合 OP 的目标。我会接受这个答案。
【解决方案4】:

答案稍长;

这会有点困难(我相信你已经在怀疑了),难的原因是 Location getter 返回的值没有存储在支持字段中比如

  private readonly string location;

相反,它是使用 Assembly 的本机句柄获取的(或者实际上由于该类是抽象的,RuntimeAssembly 这是您在加载内容时获得的子类从内存中),然后使用外部 dll 调用来读取位置。

看起来像这样(简化);

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void GetLocation(RuntimeAssembly assembly, StringHandleOnStack retString);

public override String Location {
  get {
    String location = null;
    GetLocation(GetNativeHandle(), JitHelpers.GetStringHandleOnStack(ref location));

    return location;
  }
}

更改要求 this 为非空的代码比向其中注入值更容易。

--L Lawliet--

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多