分类 调试 下的文章

本文概述了从生成进程的内存转储、在一台计算机上运行某个版本的CLR到将转储加载到在另一台计算机上运行的WinDbg所涉及的步骤。在WinDbg中,我们希望加载SOS调试器扩展来查询转储。具体来说,即使WinDbg机器没有安装运行转储进程的机器所安装的CLR版本,SOS也应该加载。

步骤1:生成进程转储

要生成示例转储,请导航到Windows任务管理器,右键单击w3wp.exe(我们知道它正在运行.NET代码),然后选择创建转储文件。对于我们希望基于触发器(如CPU或内存利用率超过某个阈值)生成转储的情况,或者当进程抛出某种类型的异常时,ADPlus、DebugDiag和ProcDump提供了更好的转储捕获方法。
在准备将转储复制到WinDbg机器时,我们必须确定w3wp进程中运行的CLR版本及其位。如果对CLR版本有疑问,我们始终可以检查IIS应用程序池设置,在本例中显示的是.NET Framework v2.0.50727。至于位,Task Manager显示w3wp,没有*32后缀,表示64位进程。另一种方法是,我们可以使用Sysinternals流程资源管理器,查找加载到流程中的dll,这些dll来自C:\ Windows\Microsoft.NET子文件夹之一。
在C:\Windows\Microsoft.NET\Framework64中,每个x64版本的.NET都显示为一个子文件夹(v2.0.50727、v4.0.30319等)。文件夹名称版本控制方案缺少的是修补程序版本。查看v2.0.50727文件夹中某个dll的属性,我们可以看到w3wp进程中运行的CLR的确切版本实际上是2.0.50727.4253。
有了内存转储和有关在转储进程中运行托管代码的CLR版本和位的知识,我们就可以将转储和辅助文件复制到WinDbg计算机。

步骤2:将转储和.NET CLR dll复制到其他计算机

要使WinDbg能够加载和检查转储,w3wp中运行的CLR的辅助文件必须可用。这些文件与上面确定的修补程序版本和位匹配,可能已经存在于WinDbg计算机上。但是,我们不是假设,而是将所需的两个文件从w3wp机器的.NET Framework文件夹以及转储文件复制到WinDbg机器:

  • mscordacwks.dll:用于工作站的Microsoft公共对象运行时数据访问组件(COR是CLR的早期名称)公开了API,WinDbg可以通过这些API访问转储进程的内存,从而访问CLR数据结构。组件实际上是从与正在执行进程内的CLR相同的源代码编译的。在死后调试期间,它充当CLR的替身,在转储中及时冻结。在运行的进程中,查询CLR数据结构不仅意味着读取内存结构,还意味着执行本机代码来解释这些结构。在调试的进程之外运行的数据访问组件也有同样的用途。
  • sos.dll:Strike之子的缩写,此库包含WinDbg.NET扩展命令。通过使用mscordacwks.dll(并通过本机检查转储),这些SOS命令查询和解释CLR数据结构,并以可消化的格式呈现这些结构。如果没有mscordacwks.dll和sos.dll,我们将看到CLR数据结构在内存中的分层。

由于CLR内部以及SOS命令可能会随新的运行时版本而更改,因此每个运行时都会附带mscordacwks.dll和SOS.dll的特定版本。因此,从.NET框架文件夹中收集mscordacwks.dll和sos.dll,并与w3wp.dmp一起将这些文件复制到WinDbg计算机上的C:\ debug\w3wp-sp2007文件夹中。

步骤3:首次尝试将w3wp.dmp加载到WinDbg中

打开WinDbg(X64)并转到“文件”菜单,打开崩溃转储。。。并找到C:\ debug\w3wp-sp2007\w3wp.dmp。作为响应,WinDbg打印以下输出:

接下来,我们发出命令,设置公共Microsoft符号服务器的符号路径,并为加载到w3wp的模块重新加载符号。然后,基于加载到w3wp中的mscorwks.dll的文件夹位置(版本),我们从同一路径加载SOS。.loadby命令实际上是general.load命令的快捷方式,接受要加载的DLL的路径。引入loadby是因为.NET dll往往有一个很长的路径,因此快捷方式根据加载的mscorwks.dll的位置解析路径。在CLR 4.0及更高版本上,命令为.loadby sos CLR,因为CLR现在位于CLR.dll中:

0:000>.symfix0:000>.reload
................................................................
................................................................
................................................................
................................................................
................................................................
................................................................
.
Loading unloaded module list
................
0:000> .loadby sos mscorwks

ba (Break on Access)

ba命令设置处理器断点(通常称为数据断点,不太准确)。此断点在访问指定内存时触发。

用户模式下

[~Thread] ba[ID] Access Size [Options] [Address [Passes]] ["CommandString"]

今天我很高兴向您介绍我的第一个WinDbg扩展lld,目前它只包含一个命令:!inject DLL,它允许您将DLL注入正在调试的进程。sdbgext扩展中有一个类似的命令,但它只适用于32位进程。用法非常简单——只要记住以正确的位加载扩展(32位进程的32位版本)。示例会话可能如下所示:

0:000>.load lld0:000> !injectdll c:\temp\Test.exe
ModLoad:
00000001`3f820000 00000001`3f924000 c:\temp\Test.exe
ModLoad: 000007fe`fd960000 000007fe`fd98e000 C:\Windows\system32\IMM32.DLL
ModLoad: 000007fe`ff410000 000007fe`ff519000 C:\Windows\system32\MSCTF.dll
(bac.5a0): Break instruction exception
- code 80000003(first chance)
ntdll
!LdrpDoDebuggerBreak+0x30:00000000`778c7800 cc int 3

默认情况下,在Wireshark中记录跟踪时,在其中找不到进程id。有时这些信息对于调查你所面临的问题是必要的。我这周碰到了这样的一个问题。我需要在虚拟机(本地地址10.0.2.5)上找到一个进程,该虚拟机仍在使用TLSv1连接到我们的负载平衡器。起初,我只在Wireshark中记录了跟踪并对其进行了过滤(ssl.record.version==”TLS 1.0“):

 

显然,他们的要求是有的。由于整个通信量(握手除外)是加密的,因此无法猜测是谁发送了这些数据包。幸运的是,TLS在底层使用TCP,每个TCP包都有一个端口号,该端口号在给定时间唯一地标识一个进程。因此,如果我们在记录Wireshark跟踪时收集这些信息,我们将能够完成分析。我首选的方法是使用过程监视器。由于进程监视器跟踪可能会快速增长,因此最好删除除TCP/IP类别之外的所有事件(Filter -> Drop Filtered Events):

 

运行procmon后,我们可以在Wireshark中重新记录网络流量。完成后,我们需要将Wireshark中的默认时间格式(View->time Display format->time of Day或只需按Ctrl+Alt+2)更改为Process Monitor中使用的格式。现在,是时候找出其中一个可疑事件并节省时间和源端口:

 

利用这些信息,我们可以在procmon跟踪中找到相应的事件,并通过检查其属性,了解创建给定网络数据包的过程。事件的时间略有不同(Wireshark使用WinPcap/npcap驱动程序,而Process Monitor依赖ETW TCP/IP事件),但通常情况下,这不应该是个问题。如果您查看上面的procmon屏幕截图,您将看到我正在查找的进程是ImageVerifier.exe。
如果需要远程执行此类诊断,并且只能访问远程计算机上的命令行,则可以考虑使用TShark和wtrace(带参数:–filter TCPIP–nosummary)代替Wireshark和Process Monitor。

注意:如果您使用的是Microsoft Message Analyzer,则进程ID在跟踪中。但由于它的内存消耗和缓慢,我坚持使用Wireshark和进程监视器。