我需要在我的appdomain中获取已加载程序集的内存地址。将程序集加载到.Net应用程序时,它们将完全加载到主应用程序内存中。
如果我们在内存中搜索这个字节模式:
byte[] pe_pattern = {
0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0xFF, 0xFF
};
我们找到他们在记忆中放置的地点和地址。但是由于性能的原因,我需要在没有内存扫描的情况下这样做。
我试图通过AppDomain.CurrentDomain.GetAssemblies()
加载程序集并通过垃圾收集器获取它们的地址作为对象,其他一些方法可以在这里找到:Memory address of an object in C#
但我得到的地址不是正确的地址,我没有错误。
在c ++中有一种方法可以通过loadlibrary来获取加载的dll,但在C#中我找不到任何东西。
如何在C#App中获取已加载程序集的内存地址?
我不确定您是否正在寻找(1)映射的汇编文件的虚拟地址,或者(2)加载程序集后JITed代码放置的虚拟地址。
接下来,我将考虑加载一些程序集的主机进程的简单情况。代码可以找到here。让我们关注加载x64_Assembly.dll时会发生什么。
如果我们要查找的是(1)上面定义的(进程'地址空间中映射文件的虚拟地址),那么这意味着下面突出显示的行,显示在VMMap的输出中。这是操作系统加载包含程序集的文件的位置。我不知道你怎么能从你自己的应用程序中以编程方式获得这个。
对于(2),即找到程序集的JITed代码的虚拟地址,如果您使用调试器进入代码,实际上可以看到相应的地址:
正如this thread指出的那样,JITed程序集被放置在堆中,您可以通过再次使用VMMap轻松验证。在我的例子中,可以看到调试器中显示的地址位于具有VMMap的堆块内:
那么您实际定位的是哪个地址?
稍后更新:您可以使用CLR MD获取非常有趣的数据。看看下面的简单代码(摘自Ben Watson的“编写高性能.NET代码”),它得到(1)和可能(2)。您可以在VMMap中看到加载的程序集的图像地址与module.ImageBase
的值匹配,因此您肯定会得到(1)。然而,对于(2),module.Address
的值与我在原始答案中在调试器中看到的m_assembly
变量不同 - 所以其中一个显示了其他内容。但是,如果你考虑一下,并不是所有的代码都是同时进行JIT的 - 而是CLR将JIT编译为(和如果)它们被调用的方法。因此,我认为2个变量包含的虚拟地址指向表示程序集的一些通用结构。
既然你提到你有权检查内存内容,你可以很快找到2个变量中的哪一个是感兴趣的(2)。
你怎么能在实践中这样做?我正在考虑构建一个CLR MD项目,只需输出您在((1)和(2)之后的简单文件中的信息),然后让您的主代码调用此EXE,以便分析您的过程和它加载的程序集并写入数据。当CLR MD进程终止时,您的实际代码可以检索文件中写入的信息并对其检索的虚拟地址进行操作。在上面的示例中,PID只是硬编码(我使用Process Explorer查看分配的PID),但您可以将其作为参数传递给CLR MD项目。
您可以在Visual Studio中使用Manage NuGet Packages for Solution选项来安装CLR MD,并为您的特定项目配置它,然后只需添加一个using Microsoft.Diagnostics.Runtime
。
需要牢记的两件事:
AttachFlag.Passive
方法中使用AttachToProcess
,否则你的原始代码会被无限期暂停。在我采用上面的截图并成功获得module.ImageBase
和module.Address
值之后,我也尝试了这个选项,加上初始代码继续运行得很好。