Tyranid’s Lair: Windows 11 ARM64上的符号解析问题
这是一篇关于我在开发OleViewDotNet工具时遇到的一个问题以及如何解决它的简短博客文章。虽然我不确定是否采用了最佳方法,但如果其他人遇到类似问题,这篇文章可能会有所帮助。
OleViewDotNet能够解析进程中的内部COM结构,并显示重要信息,例如进程当前导出的IPID列表和访问安全描述符。
|
|
为了实现这个任务,我们需要访问COMBASE DLL的符号,以便解析哈希表和其他运行时工件的各种根指针。解析进程信息的大部分代码都在COMProcessParser类中,该类使用DBGHELP库将符号解析为地址。我的代码还支持一种机制,将解析的指针缓存到文本文件中,随后可以在具有相同COMBASE DLL的其他系统上使用,而不需要下载30+ MiB的符号文件。
这在Windows 11 x64上运行良好,但我注意到在ARM64上会得到不正确的结果。过去我遇到过类似的问题,这些问题是由于解析过程中使用的内部结构的变化引起的。微软为COMBASE提供了私有符号,因此很容易检查Windows 11的x64和ARM64版本之间的结构是否有差异。我没有发现任何差异。无论如何,我注意到这也影响了简单的值,例如符号gSecDesc包含指向COM访问安全描述符的指针。然而,在读取该指针时,它总是NULL,即使它应该已经被初始化。
更让我困惑的是,当我在WinDBG中检查该符号时,它显示指针已正确初始化。然而,如果我在WinDBG中使用x命令搜索预期的符号,我发现了一些有趣的事情:
|
|
从输出中我们可以看到有两个gSecDesc符号,而不是一个。第一个具有NULL值,而第二个具有初始化的值。当我检查我的符号解析器返回的地址时,它是第一个,而WinDBG更聪明,会返回第二个。这到底是怎么回事?
这是Windows 11 ARM64上一个新特性的产物,该特性旨在简化x64可执行文件的仿真,即ARM64X。这是一个巧妙(或糟糕)的技巧,以避免在系统上需要单独的ARM64和x64二进制文件。相反,ARM64和x64兼容的代码(称为ARM64EC,即仿真兼容)被合并到单个系统二进制文件中。推测在某些情况下,这意味着全局数据结构需要被复制,一次用于ARM64代码,另一次用于ARM64EC代码。在这种情况下,似乎不应该有两个单独的全局数据值,因为指针就是指针,但我认为可能存在边缘情况,其中情况并非如此,复制值以避免冲突更简单。细节非常有趣,有几个地方对此进行了逆向工程,我至少推荐这篇博客文章。
我的代码使用SymFromName API来查询符号地址,这只会返回它找到的第一个符号,在这种情况下是ARM64EC的符号,在ARM64进程中未初始化。我不知道这是否是DBGHELP中的一个错误,也许它应该尝试返回与二进制文件机器类型匹配的符号,或者也许我使用的方式不对。无论如何,我需要一种获取正确符号的方法,但在浏览DBGHELP库后,没有明显的方法来区分这两个符号。然而,显然WinDBG可以做到,所以一定有办法。
经过一番搜索,我发现调试接口访问(DIA)库有一个IDiaSymbol::get_machineType方法,该方法返回符号的机器类型,要么是ARM64(0xAA64),要么是ARM64EC(0xA641)。不幸的是,我故意使用了DBGHELP,因为它在Windows上默认安装,而DIA需要单独安装。在DBGHELP库中似乎没有等效的方法。
幸运的是,在DBGHELP库中寻找解决方案时,机会出现了。在DBGHELP内部(至少是最近版本),它使用了DIA库的私有副本。这本身并没有多大帮助,但该库导出了几个私有API,允许调用者查询当前的DIA状态。例如,有一个SymGetDiaSession API,它返回一个IDiaSession接口的实例。从该接口,您可以查询IDiaSymbol接口的实例,然后查询机器类型。我不确定DBGHELP内部的DIA版本与公开发布版本的兼容性如何,但对于我的目的来说,它足够兼容。
2024/04/26更新:有人向我指出,机器类型存在于SYMBOL_INFO::Reserved[1]字段中,因此您不需要使用DIA接口进行整个方法。重点仍然是,您需要在ARM64平台上枚举符号,因为可能有多个符号,并且您仍然需要检查机器类型。
为了解决这个问题,OleViewDotNet中的代码在ARM64系统上采取以下步骤:
- 不调用SymFromName,代码枚举名称的所有符号。
- 调用SymGetDiaSession以获取IDiaSession接口的实例。
- 调用IDiaSession::findSymbolByVA方法以获取符号的IDiaSymbol接口实例。
- 调用IDiaSymbol::get_machineType方法以获取符号的机器类型。
- 根据上下文过滤符号,例如,如果解析ARM64进程,则使用ARM64符号。
这比我认为需要的要复杂得多,但我尚未找到替代方法。理想情况下,DBGHELP中的SYMBOL_INFO结构应包含一个机器类型字段,但我想现在很难更改接口。执行机器类型查询的相对简单的代码在这里。如果有人找到了仅使用DBGHELP公共接口的更好方法,我将不胜感激 :)
发布者:tiraniddo
时间:15:09