如何修改.NET程序集只读属性?

时间:2013-06-24 14:01:04

标签: .net reflection reverse-engineering

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

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

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

这是只读的。有没有低级别的方法呢?有什么想法吗?

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

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

4 个答案:

答案 0 :(得分:8)

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

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功能而获得hankerin',您可能会对此Q / A感兴趣:
What is [DllImport("QCall")]?
(这可能不言而喻,此时,你是独立的。)

答案 1 :(得分:4)

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

答案 2 :(得分:4)

尽管@ Cyborgx37是正确的 - 您无法更改现有程序集的位置,还有其他方法可以解决此问题。

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

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

在编写自己的加密程序集加载程序时,我必须做很多事情,而且非常成功。

修改

如果你真的死定了,那么这两篇文章应该让你开始研究如何在运行时重写exising方法。但是有一些注意事项:这根本不适用于NGEN或GAC,并且它将有多个难题与async / await / enumerable / exception处理你必须解决。使用风险自负。

MSIL Injection: Rewrite a non dynamic method at runtime

CLR Injection: Runtime Method Replacer

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

  

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

  

微软不支持任何这些hacky东西。如果可能的话,你应该看看另一种方法来实现你的目标。

答案 3 :(得分:1)

答案稍长;

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

  private readonly string location;

相反,它是使用程序集的本机句柄获取的(或者实际上因为该类是抽象的, 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;
  }
}

您可以更轻松地将需要此代码的代码更改为非空,而不是将值注入其中。

- L Lawliet -