获取加载的程序集的内存地址

时间:2019-03-02 14:11:17

标签: c# memory .net-assembly

我需要获取我的appdomain中已加载程序集的内存地址。 将程序集加载到.Net应用程序后,它们将完全加载到主应用程序内存中。

如果我们在内存中搜索此字节模式:

byte[] pe_pattern = {
    0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
    0xFF, 0xFF
};

我们找到它们在内存中放置的位置和地址。 但是由于性能,我需要在没有内存扫描的情况下执行此操作。

我尝试通过AppDomain.CurrentDomain.GetAssemblies()获取已加载的程序集,并通过Garbage Collector将其地址作为对象获取,可以在这里找到其他一些方法:Memory address of an object in C#

但是我得到的地址不是正确的地址,我没有错误。

在c ++中,有一种方法可以通过loadlibrary获取已加载的dll,但在C#中我什么都找不到。

如何获取C#应用程序中已加载程序集的内存地址?

1 个答案:

答案 0 :(得分:3)

我不确定您要查找的是(1)映射程序集文件的虚拟地址,还是(2)加载程序集后放置JITed代码的虚拟地址。

接下来,我将考虑主机进程加载一些程序集的简单情况。可以在here中找到该代码。让我们集中讨论 x64_Assembly.dll 加载时发生的情况。

如果我们要寻找的是上面定义的(1)(进程地址空间内映射文件的虚拟地址),则这意味着下面突出显示的行,如VMMap的输出所示。操作系统在其中加载包含程序集的文件。我不知道如何从自己的应用程序中以编程方式获得这一功能。

enter image description here

对于(2),这是找到程序集的JITed代码的虚拟地址,如果您使用调试器进入代码,则实际上可以看到相应的地址:

enter image description here

正如this thread所指出的那样,JITed程序集被放置在一个堆中,您可以通过再次使用VMMap轻松地进行验证。就我而言,可以通过VMMap看到调试器中显示的地址位于堆块内部:

enter image description here

那么您实际定位的是哪个地址?

最新更新: 您可以使用CLR MD来获取非常有趣的数据。看一下下面的简单代码(摘自Ben Watson的“编写高性能.NET代码”),它得到(1)和可能(2)。您可以在VMMap中看到已加载程序集的映像地址与module.ImageBase的值匹配,因此您肯定会得到(1)。但是,对于(2),module.Address的值与在我的原始答案中的调试器中看到的m_assembly变量不同-因此其中一个正在显示其他内容。但是,如果您考虑一下,并非所有代码都会同时进行JIT处理-CLR会将JIT编译方法(如被调用)进行JIT编译。因此,我相信2个变量包含的虚拟地址指向表示程序集的某种通用结构。

enter image description here

由于您提到您确实有权检查内存内容,因此您可以很快地找出(2)感兴趣的2个变量中的哪个。

您如何在实践中做到这一点?我正在考虑构建CLR MD项目,该项目仅输出您要获取的信息(在一个简单文件中((1)和(2))),然后由您的主代码调用此EXE,以便它分析您的过程并它加载并写入数据的程序集。当CLR MD进程终止时,您的实际代码可以检索写在文件中的信息,并在它检索的那些虚拟地址上进行操作。在上面的示例中,PID只是简单地硬编码(我正在使用Process Explorer查看分配的PID),但是您可以将其作为参数传递给CLR MD项目。

您可以在Visual Studio中使用管理解决方案的NuGet软件包选项来安装CLR MD,并为您的特定项目进行配置,然后只需添加using Microsoft.Diagnostics.Runtime

需要牢记的两件事:

  • 您所使用的CLR MD代码的“位数”必须与您正在分析的过程相匹配(例如,不要为x86构建一个,而为x64构建另一个;有关程序集和交叉位加载的详细信息,请参见在the article I've previously referenced中)
  • 您必须在AttachFlag.Passive方法中使用AttachToProcess,否则原始代码将无限期地暂停。在拍完上面的屏幕截图并成功获得module.ImageBasemodule.Address值之后,我也测试了此选项,并且初始代码继续运行良好。