cc682/NetRoc
http://netroc682.spaces.live.com/
符号语法和符号匹配
符号用来直接操作被调试程序的助记符(tokens)。例如,可以通过命令bp main 来在main函数上设置断点,或者使用dd MyInt L1命令来显示整数变量MyInt 的值。
很多情况下,符号可以作为调试器命令的参数。可以支持大多数数字参数,以及一些文本参数。除了常规的符号语法之外,也有一些适用于各种单独情况的语法规则。
常规符号语法规则
符号名由一个或多个字符组成,但是总是以字母、下划线(_)、问号(?)或美元符号($)开头。
符号名可以用模块名来进行限制。使用一个感叹号(!)来分隔模块名和符号(例如,mymodule!main)。没有使用模块名的时候,符号也可以用感叹号作为前缀。
符号名是完全不区分大小写的。这意味着如果程序中同时存在myInt 和MyInt ,不能被调试器正确识别;不管怎么写,访问它们的命令都有可能访问到另外一个。
数值表达式中的符号语法
调试器可以识别两种不同的表达式:Microsoft宏汇编(MASM)表达式和C++表达式。由于和符号相关,这两种语法形式有以下区别:
- 在MASM表达式中,每个符号都被解释成地址。根据该符号引用的内容不同,它可以是全局变量、局部变量、函数、段、模块的地址或者其他任何可识别的标志。
- 在C++ 表达式中,符号根据它的类型来解释。根据引用内容的不同,可能被解释为整数、数据结构、函数指针或任何其他数据类型。不符合C++数据类型的符号(例如未更改的模块名)都会产生语法错误。
关于如何使用每一种语法的说明,查看表达式求值。
如果使用MSAM表达式语法,任何可以被解释为16进制数或寄存器的符号(例如,BadFeed, ebX)都必须用感叹号作为前缀。这能确保调试器将它识别为符号。
ss (Set Symbol Suffix)命令可以用来设置符号后缀。这使得调试器在找不到符号的情况下自动在符号名后添加"A" 或者"W"。
很多Win32函数存在ASCII和Unicode版本。这些函数的名字后被分别添加了"A" 或"W"。使用符号后缀可以帮助调试器搜索这些符号。
后缀匹配默认没有激活。
文本表达式中的符号语法
符号可以用于一些命令的文本参数——例如bm (Set Breakpoint) 和x (Examine Symbols)。
这些文本参数支持很多通配符和说明。查看字符串通配符语法获取详细信息。除标准字符串通配符之外,用来指定符号的文本表达式也可以使用一个下划线作为前缀。当对这样的符号进行匹配时,调试器把它当作任意数量的下划线,包括零。
匹配文本表达式中的符号时,不会使用符号后缀。
符号状态缩写
符号文件的类型和它们的加载状态可以通过lm (List Loaded Modules)命令、 !lmi 扩展命令或 WinDbg的Debug | Modules菜单命令获取。
它们都可以显示已加载的模块和它们的符号的信息。
下面这些缩写是这些命令产生的输出中使用的:
缩写
|
含义
|
deferred
|
模块已经加载,但是调试器还没有尝试加载它的符号。符号将在需要的时候被加载。查看延迟符号加载获取详细信息。
|
#
|
符号文件和可执行文件的时间戳或者校验和有一些不匹配。
|
T
|
时间戳丢失、不能访问或者等于0。
|
C
|
校验和丢失、不可访问或者等于0。
|
DIA
|
符号文件通过调试接口访问(Debug Interface Access (DIA))被加载。
|
Export
|
没有找到实际的符号文件,所以符号信息是从二进制文件的导出表中获得的。
|
M
|
符号文件和可执行文件得时间戳或校验和有些不匹配。但是,因为符号选项的原因符号文件仍然被加载了。
|
PERF
|
该二进制文件包含性能优化后的代码。标准的地址计算方法可能产生不正确的结果。
|
Stripped
|
调试信息已经从映像文件中剥离出来了。
|
PDB
|
符号是.pdb格式的。
|
COFF
|
符号是通用对象文件格式(common object file format (COFF))的。
|
性能优化后的代码
Microsoft有一些技术用于重新调整编译和链接之后的代码,以使得它们执行得更高效。这些技术会根据情况优化内存结构。
这样的优化减少了分页(和页面错误),并且增加代码和数据之间的空间位置。它会定位原始代码位置不好造成的性能瓶颈。经过优化的组件中可能有一些代码或数据块被移动到另外的地方。
在经过这些技术优化后的模块中,代码和数据块的位置经常不在按照通常的编译和链接之后所应该在的地方。另外,函数可能会被分割为很多不相邻的块,以使得最常使用的代码路径能放到相同的页面中。
因此,函数(或任何符号)加上偏移的意义不像在非优化代码中一样。
调试优化后的代码
调试中可以对任何已经加载了符号的模块使用!lmi扩展命令来查看是否经过了性能优化:
0:000> !lmi ntdll
Loaded Module Info: [ntdll]
Module: ntdll
Base Address: 77f80000
Image Name: ntdll.dll
Machine Type: 332 (I386)
Time Stamp: 394193d2 Fri Jun 09 18:03:14 2000
CheckSum: 861b1
Characteristics: 230e stripped perf
Debug Data Dirs: Type Size VA Pointer
MISC 110, 0, 76c00 [Data not mapped]
Image Type: DBG - Image read successfully from symbol server.
c:\symbols\dll\ntdll.dbg
Symbol Type: DIA PDB - Symbols loaded successfully from symbol server.
c:\symbols\dll\ntdll.pdb
在这个输出重,注意"Characteristics" 这一行上的perf 术语。何表明ntdll.dll是经过了性能优化的。
调试器可以识别没有偏移的寒暑或其他符号,这使得可以成功在函数或其他标签上设置断点。但是,反汇编操作的结果可能令人困惑,因为它会反映出优化器进行过的改变。
由于调试器会尝试靠近原始代码,所以可能会看到一些有趣的结果。调试性能优化后的代码的首要规则是,不能对优化后的代码进行可靠的地址计算。
这里是一个示例:
kd> bl
0 e f8640ca6 0001 (0001) tcpip!IPTransmit
1 e f8672660 0001 (0001) tcpip!IPFragment
kd> u f864b4cb
tcpip!IPTransmit+e48:
f864b4cb f3a4 rep movsb
f864b4cd 8b75cc mov esi,[ebp-0x34]
f864b4d0 8b4d10 mov ecx,[ebp+0x10]
f864b4d3 8b7da4 mov edi,[ebp-0x5c]
f864b4d6 8bc6 mov eax,esi
f864b4d8 6a10 push 0x10
f864b4da 034114 add eax,[ecx+0x14]
f864b4dd 57 push edi
可以从断点列表中看到IPTransmit 的地址是0xF8640CA6。
当反汇编该函数中0xF864B4CB 开始的一段代码时,输出显示这是从函数开始之后的第0xE48的字节。但是,如果用该地址减去函数的基地址,会发现实际的偏移是0xA825。
发生的事情是这样的:调试器确实从 0xF864B4CB 开始的指令反汇编二进制机器码。但是不是使用简单的减法计算偏移,调试器尽可能好的显示出了优化之前该代码相对于函数入口点的偏移。这个值为0xE48。
另一方面,如果想在IPTransmit+0xE48查看,会看到这些内容:
kd> u tcpip!iptransmit+e48
tcpip!ARPTransmit+d8:
f8641aee 0856ff or [esi-0x1],dl
f8641af1 75fc jnz tcpip!ARPTransmit+0xd9 (f8641aef)
f8641af3 57 push edi
f8641af4 e828eeffff call tcpip!ARPSendData (f8640921)
f8641af9 5f pop edi
f8641afa 5e pop esi
f8641afb 5b pop ebx
f8641afc c9 leave
调试器识别出来IPTransmit 等于地址0xF8640CA6,并且命令解析器简单的进行加法,发现0xF8640CA6 + 0xE48 = 0xF8641AEE 。这个地址作为了u (Unassemble)命令的参数。但是当分析了该位置后,调试器发现它不是IPTransmit 加上偏移0xE48。实际上,这根本不是该函数的一部分。所以它修改为函数ARPTransmit 加上偏移0xD8。
这样的原因在于优化对于地址计算来说是不可逆的。调试器可以拿到一个地址并推断出它原始的符号和偏移,但是由于没有足够信息,不能通过符号和偏移来计算出正确的地址。因此,这些情况下反汇编不是那么有用。
cc682/NetRoc
http://netroc682.spaces.live.com/
使用源码服务器
SrcSrv (srcsrv.dll)是一个以和符号服务器SymSrv类似的方式工作的源码服务器。它是Windows调试工具包的一部分。
要使用源码服务器,调试的二进制文件必须按源码进行索引。当二进制文件在构建时已经被索引,并且源码文件位置包含在PDB文件中时,调试器可以自动寻找到适合的源文件。
在调试器中使用SrcSrv
SrcSrv可以在WinDbg、KD、NTSD或CDB中使用。
要在调试器中使用源码服务器,只需要在源码路径中简单的包含SRV* 文本。关于控制该路径的详细信息,查看源码路径。例如:
.srcpath srv*;c:\mysource
这个例子中也包含一个传统的源码路径成员。SrcSrv可以和本地源码路径一起使用。不管SRV*出现在开头还是后面,源码服务器总是在其他路径之前被搜索。如果SrcSrv不能访问或者找不到源文件,调试器会搜索其他路径。
任何从源码服务器下载的源文件在调试会话结束之后,都会保存在硬盘中。这很类似于符号服务器的流式存储(即downstream store,貌似这样翻译比较贴切一点)功能。源文件在本地被保存到主目录的src子目录中。默认的主目录是调试器的安装目录;但是可以通过!homedir 扩展命令修改。如果该子目录不存在,则会自动被创建。
如果使用.open (Open Source File)命令来通过SrcSrv打开新的源文件,必须包含-m Address 参数。
使用SgeStore来控制缓存大小
AgeStore和源码服务器一起工作的方式和与符号服务器一起的时候完全一样:它可以删除比指定日期早的缓存文件,或者将缓存内容的大小控制到指定值之下。这在流式存储(downstream store)变得过大时非常有用。详细信息查看AgeStore。
源码索引
关于如何索引源码的信息,查看srcsrv.doc中的文档。该文档和SrcSrv本身一样,是Windows调试工具包的一部分。必须进行自定义安装才能获得该文件,查看调试器安装获得详细信息。
延迟符号加载
默认情况下,目标模块加载时并没有实际加载符号信息。符号是在调试器需要使用时才加载的。这称为延迟符号加载(deferred symbol loading 或 lazy symbol loading)。当该选项启用时,调试器在遇到不认识的符号时才会进行符号加载。
当延迟符号加载被禁用是,进程的启动可能变得慢很多,因为所有符号在模块加载时就会被加载起来。
在WinDbg中,对于没有模块前缀的符号,延迟符号加载的特性可以使用Debug菜单的Resolve Unqualified Symbols选项来更改。
通过使用ld (Load Symbols) 命令或.reload (Reload Module)命令和/f选项,可以忽略延迟符号加载 。他们强制指定符号被立即加载起来,即使其他符号的加载是延迟的。如果符号路径改变了,符号不会自动重新加载。
默认情况下,延迟符号加载是启用的。在CDB和KD中, -s 命令行选项可以关掉它。在CDB中也可以通过tools.ini 文件的LazyLoad选项来关掉。当调试器运行起来之后,可以通过.symopt+0x4 或.symopt-0x4命令来打开或关闭。
设置符号选项
有很多选项用于控制符号如何被加载和使用。这些选项可以通过各种办法设置。
下表列出了这些符号选项:
改变符号选项的设置
.symopt (Set Symbol Options)命令可以用来修改或显示符号选项的设置。另外,很多命令行参数和命令也可以修改这些设置;他们分别在SYMOPT_XXX 节中列出。
也可以在-sflags 命令行选项中一次控制所有设置。 该选项可以后跟一个10进制数字或带0x前缀的16进制数字。建议使用16进制数,因为这些符号标志也是按这样排列的。注意使用这种方法时,由于设置了所有位,所以所有符号设置都会被覆盖掉。例如,-sflags 0x401不单是打开SYMOPT_EXACT_SYMBOLS 和SYMOPT_CASE_INSENSITIVE ,还会将其他默认打开的选项全部关闭!
当程序以不带任何符号相关的命令行选项启动时,所有标志位的默认值在WinDbg中为0x30237,在CDB和KD中为0xB0227,在DBH工具中为0x10C13。
该选项使得所有对符号名的搜索区分大小写。
所有调试器中该选项默认打开。调试器运行之后,可以通过.symopt+0x1 或.symopt-0x1打开或关闭。
DBH中该选项默认打开。DBH运行时,可以通过symopt +1 或symopt -1来打开或关闭。
该选项使得公有符号名被显示或搜索时都忽略修饰符。私有符号不管该选项是否激活都永远不会被修饰。关于符号名修饰符的更多信息,查看公有和私有符号。
所有调试器中该选项默认打开。调试器运行后,可以通过.symopt+0x2 或.symopt-0x2打开或关闭。
DBH中该选项默认打开。可以通过-d命令行选项关闭。DBH运行时,可以通过symopt +2 或symopt -2打开或关闭。
该选项称为延迟符号加载(deferred symbol loading 或lazy symbol loading)。激活时,当目标模块加载时并不实际加载符号,而是等到调试器用到的时候才加载。查看延迟符号加载获取详细信息。
所有调试器默认打开该选项。在CDB和KD中,-s命令行选项可以关闭它。对于CDB也可以通过使用tools.ini 文件的LazyLoad变量来关闭。调试器运行时,通过.symopt+0x4 或.symopt-0x4,来打开或关闭。
DBH中该选项默认关闭。DBH运行时可通过symopt +4 或symopt -4来打开或关闭。
该选项关闭C++转换。设置了这个选项时,所有符号中的::都被__ 替代。
所有调试器默认关闭该选项。它可以通过-snc命令行选项打开。调试器运行时,通过.symopt+0x8 或.symopt-0x8来打开关闭。
DBH默认关闭该选项。运行时通过symopt +8 或symopt -8来打开关闭。
该选项允许从源码文件中读取行号信息。必须打开才能使得源码调试正确工作。
在KD和CDB中该选项默认关闭;在WinDbg中默认打开。对于CDB和KD可以通过-lines 命令行选项来打开。调试器运行时,可以通过.symopt+0x10 或.symopt-0x10来打开关闭。也可以通过 .lines (Toggle Source Line Support)命令来打开或关闭。
DBH中默认打开该选项。运行时可以通过symopt +10 或symopt -10来打开关闭。
当代码被优化,并且期望的位置没有符号时,启用该选项会用最靠近的符号来替代。
所有调试器默认打开该选项。运行时,通过.symopt+0x20 或.symopt-0x20来打开关闭。
DBH中默认打开。运行时可以通过symopt +20 或symopt -20来打开关闭。
该选项降低符号处理器尝试匹配符号时的精确性。
所有调试器默认关闭。运行时可以通过.symopt+0x40 或.symopt-0x40来打开关闭。
DBH中默认关闭。运行时通过symopt +40 或symopt -40来打开关闭。
该选项使得符号处理器在搜索符号时,忽略已加载模块映像头中的CV记录。
所有调试器中默认关闭。可以通过-sicv 命令行选项打开。运行时通过.symopt+0x80 或.symopt-0x80打开关闭。
DBH中默认关闭。运行时通过symopt +80 或symopt -80打开关闭。
该选项禁止符号处理器自动为模块进行加载。设置这个选项并且调试器尝试匹配某个符号时,它只会在已加载模块中搜索。
这个选项可以用来避免误输入符号名的问题。通常,误输入符号使得调试器搜索所有卸载的符号文件时暂停。使用该选项,误输入的符号在已加载模块中找不到,所以搜索就会被中止。
所有调试器默认关闭该选项。可以通过-snul命令行选项激活。运行时通过.symopt+0x100 或.symopt-0x100打开关闭。
DBH中默认关闭。运行时通过symopt +100 或symopt -100打开关闭。
这个选项使得文件访问错误对话框被取消掉。
如果该选项关闭,再符号加载时遇到的类似"驱动器未准备好"这样的文件访问错误,会出现一个对话框。如果该选项打开,这些对话框不会出现,并且所有访问错误都会收到一条"fail"的响应。
所有调试器默认打开该选项。可以通过-sdce命令行选项来关闭。运行时通过.symopt+0x200 或.symopt-0x200来打开关闭。
DBH默认关闭。运行时,通过symopt +200 或symopt -200来打开关闭。
这个选项使得调试器对所有符号文件进行严格的匹配。
打开时,即使符号文件和符号处理器的要求有很小的差异,这些符号也会被忽略。
调试器默认关闭该选项。可以通过-ses来激活。运行时,通过.symopt+0x400 或.symopt-0x400来打开关闭。
-failinc命令行选项也打开SYMOPT_EXACT_SYMBOLS。另外,如果调试用户模式minidump或内核模式minidump, -failinc将防止调试器加载任何映像不能被映射的模块。
DBH中默认打开。运行时通过symopt +400 或symopt -400来打开关闭。
该选项使得DbgHelp可以读取保存在内存某个绝对地址中的符号。绝大多数情况下不需要该选项。
所有调试器中默认关闭。运行时通过.symopt+0x800 或.symopt-0x800来打开关闭。
DBH中默认打开。运行时通过symopt +800 或symopt -800来打开关闭。
该选项使得调试器忽略环境变量中设置的符号路径和可执行映像路径。
所有调试器默认关闭该选项。可以通过-sins命令行选项激活。但是,运行时不能通过.symopt 命令来控制,因为环境变量仅在启动时会被读取。
DBH中默认关闭,并且任何情况下都会被DBH忽略。
这个选项强制DbgHelp和ImageHlp在枚举Intel Itanium处理器上的32位模块。
所有调试器默认关闭该选项。运行时通过.symopt+0x2000 或.symopt-0x2000来打开关闭。
DBH中默认关闭。运行时通过symopt +2000 或symopt -2000打开关闭。
这个选项使得DbgHelp忽略私有符号数据,仅在公有符号表中搜索符号信息。这是模拟DbgHelp加入对这些类型的支持之前的行为。查看公有和私有符号。
所有调试器中都默认关闭。运行时通过.symopt+0x4000 或.symopt-0x4000来打开关闭。
DBH 中默认关闭。可以通过-d命令行选项打开。运行时通过symopt +4000 或symopt -4000打开关闭。
该选项阻止DbgHelp搜索公有符号表。这使得符号枚举和搜索变得更快。如果比较关心搜索速度,那么SYMOPT_AUTO_PUBLICS 选项比较适合。关于公有符号表的信息,查看公有和私有符号。
所有调试器中默认关闭。运行时,通过.symopt+0x8000 或.symopt-0x8000打开关闭。
DBH中默认关闭。运行时通过symopt +8000 或symopt -8000打开关闭。
这个选项使得DbgHelp将在.pdb中搜索公有符号表作为最后的选择。如果在私有符号中找到了任何匹配,公有符号就不会再被搜索。这样会提高搜索速度。
所有调试器中默认打开。可以通过-sup命令行选项关闭。运行时通过.symopt+0x10000 或.symopt-0x10000打开关闭。
DBH中默认打开。使用-d命令行选项时会被关掉。运行时,使用symopt +10000 或symopt -10000打开关闭。
这个选项避免在加载符号时DbgHelp搜索磁盘查找映象文件的拷贝。
所有调试器中默认打开。运行时通过.symopt+0x20000 或.symopt-0x20000打开关闭。
DBH中默认关闭。运行时通过symopt +20000 或symopt -20000打开关闭。
(仅内核模式) 该选项指示是否激活了安全模式。
所有调试器默认关闭。可以通过-secure命令行选项激活。如果调试器正在运行、在静止模式下,并且没有建立任何调试服务器,可以通过.symopt+0x40000 或.secure (Activate Secure Mode)来打开安全模式。
DBH中默认关闭。运行时通过symopt +40000 或symopt -40000来打开关闭。
安全模式一旦激活之后就不能再关闭。
这个选项阻止来自代理服务器的验证对话框。这可能使得SymSrv 不能访问internet上的符号存储。
详细信息,查看防火墙和代理服务器。
在KD和CDB中,该选项默认打开;WinDbg中默认关闭。调试器运行时可以通过.symopt+0x80000 或.symopt-0x80000,后跟 .reload (Reload Module) 命令来打开或关闭。也可以使用!sym prompts off 和!sym prompts 扩展命令,后跟 .reload (Reload Module) 命令了来打开关闭。
DBH中默认关闭。运行时通过symopt +80000 或symopt -80000来打开关闭。
该选项打开详细符号加载(noisy symbol loading)。他使得调试器搜索符号时显示信息。
所有符号文件加载时会显示名字。如果调试器不能加载某个符号文件,会显示错误信息。.pdb文件的错误信息以文本方式显示,.dbg文件的错误信息以错误码方式显示。这些错误码在winerror.h文件中说明。
如果某个映像文件被重新加载以获取符号化的头信息,也会被显示出来。
所有调试器默认关闭该选项。可以通过-n命令行选项激活。运行时通过.symopt+0x80000000 或.symopt-0x80000000来打开关闭。也可以通过!sym noisy 和 !sym quiet命令来打开或关闭。
注意 这个选项不能和详细源码加载(noisy source loading)混淆——它是用.srcnoisy (Noisy Source Loading)命令来控制的。
DBH中默认关闭。可以使用-n命令行选项激活。DBH运行时,可以通过symopt +80000000 或symopt -80000000来打开或关闭。也可以通过verbose on 和verbose off 命令来打开或关闭。
cc682/NetRoc
http://netroc682.spaces.live.com/
防火墙和代理服务器
如果使用SymSrv来访问符号,并且计算机在一个使用代理服务器的网络中或符号存储在防火墙之外,数据传输可能需要认证。
当SymSrv接收到认证请求时,调试器根据不同的配置,可以显示认证请求或者自动拒绝该请求。
SymSrv中整合了对代理服务器的支持。它可以使用默认代理服务器或自己指定的其他代理服务器。
认证请求
调试器可以配置为允许认证请求。当防火墙或代理服务器要求认证时,会出现一个对话框。在调试器能够下载符号之前,需要先输入一些信息(通常是用户名和密码)。如果输入的信息错误,该对话框会再次出现。如果选择了Cancel按钮,对话框会关闭并且不会获得任何符号信息。
如果调试器配置为拒绝任何认证请求,则不会出现任何对话框,并且当需要认证时,不会有任何符号被传输过来。
如果手动或调试器自动拒绝了认证请求,SymSrv之后不会再尝试联系该符号存储。如果希望重新连接,必须重起调试会话或使用!symsrv close。
注意 使用KD或CDB时,认证对话框可能出现在打开的窗口之后。这时需要最小化或者移动一些窗口来找到它。
在WinDbg中,默认允许认证请求。在KD和CDB中,默认拒绝认证请求。
要允许认证请求,可以使用!sym prompts 或.symopt-0x80000。要拒绝所有请求,使用!sym prompts off 或.symopt+0x80000。使用!sym显示当前设置。
对认证许可状态进行任何改变之后,必须使用.reload (Reload Module) 命令。
选择代理服务器
要选择Windows的默认代理服务器,打开控制面板中的Internet选项(Internet Options),选择连接(Connections)选项卡,再选择局域网设置(LAN Settings)按钮。这时可以输入代理服务器名字和端口号,或者选择高级(Advanced)来配置多个代理服务器。详细信息,查看Internet Explorer的帮助文件。
要指定symsrv使用的代理服务器,可以将_NT_SYMBOL_PROXY 环境变量设置为该代理服务器的名字或IP,后跟一个冒号和端口号。例如:
set _NT_SYMBOL_PROXY=myproxyserver:80
使用该方法选择代理服务器后,所有使用SymSrv来访问符号服务器的Windows调试器都会使用它。也会被任何使用DbgHelo作为符号处理器的其他调试器使用。这个设置不会影响其他程序。
使用其他符号服务器
如果想为符号搜索使用不同的方法,可以提供自己的符号服务器DLL,而不是使用SymSrv。
设置符号路径
使用不是SymSrv的其他符号服务器时,调试器的符号路径设置方法和SymSrv一样。查看使用SymSrv获得符号路径语法的说明。唯一的不同是将字符串symsrv.dll 改为自己的符号服务器DLL的名字。
如果希望,可以在这个参数中自由的用其他语法来指定使用不同的技术,如UNC路径、SQL数据库标识符或Internet规范。
使用自己的符号服务器
服务器的核心部分是和DbgHelp通信来查找符号的代码。每次DbgHelp 为新加载的模块请求符号时,会调用符号服务器来定位适合的符号文件。符号服务器通过时间戳或映像大小这样的唯一参数来定位每个文件。服务器为请求的文件返回一个可用的路径。要实现这些,服务器必须导出SymbolServer 函数。
服务器还需要支持SymbolServerSetOptions 和SymbolServerGetOptions函数。并且如果导出了SymbolServerClose,DbgHelp也会调用。查看符号服务器API 获得这些例程文档的位置。
符号服务器不能改变实际的符号文件名。DbgHelp 在多个位置保存了符号文件的名字。因此,服务器返回的文件名必须和请求符号时指定的相同。该限制用于确保符号加载时显示的符号名是程序员能够理解的。
多符号服务器的限制
DbgHelp只支持一次使用一个符号服务器。 符号路径可以包含同一个符号服务器DLL的多个示例,但是不能包含多个不同的符号服务器DLL。这并不会造成很大的限制——因为仍然可以在符号路径中包含单个符号服务器的多个示例,每个都指向不同的符号存储。但是如果想在两个不同的符号服务器DLL之间进行切换,每次都需要重设符号路径。
安装自己的符号服务器
安装符号服务器的细节由不同的情况决定。可能希望设置一个安装过程来自动复制符号服务器DLL和设置_NT_SYMBOL_PATH环境变量。
根据自己的服务器使用的不同技术,可能还需要安装或访问符号数据本身。
使用SymStore
SymStore (symstore.exe) 是用于创建符号存储的工具。它被包含在Windows调试工具包中。
SymStore按照某种格式存储符号,使得调试器可以通过时间戳、映像大小(对于.dbg或可执行文件)、签名和寿命(.pdb文件)来查找符号。 使用符号存储而不是常规的符号存储格式的好处是,所有符号都可以在同一个服务器上进行存储或引用,而调试器不需要知道具有哪些产品对应的符号。
注意,不同版本的.pdb符号文件(例如共有和私有符号)不能保存在相同的符号服务器中,因为他们具有相同的签名和寿命。
SymStore事务
每个对SymStore的调用都被记录为事务。有两种不同的事务:添加和删除。
当创建符号存储时,一个称为"000admin"的目录会在服务器根目录下被创建。000admin 为每个事务都包含一个文件,和日志文件server.txt和history.txt一样。server.txt 文件包含当前服务器上的所有事务的列表。history.txt 文件包含所有历史事务,按时间排序。
每次SymStore存储或移除符号文件时,都会创建一个新的事务号。然后,一个以事务号为名字的文件会在000admin中创建。文件中包含所有在该事务中添加到符号存储的文件或指针地列表。如果某个事务被删除,SymStore会读取它的事务文件来决定哪些文件和指针需要删除。
add 和del 选项用来指定是否进行事务添加或删除。在添加操作中包含/p 选项表示添加一个指针;忽略掉/p指定添加实际的符号文件。
也可以通过分来的两个步骤来创建符号存储。第一步,使用SymStore 和/x 选项来创建一个索引文件。第二步,使用SymStore 和/y选项通过索引文件中的信息来实际的存储文件或指针。
由于各种原因,这是一种很有用的技术。例如,这样使得符号存储由于某些原因丢失的时候能够方便的重建,只要索引文件还存在。或者包含符号文件的计算机和创建符号存储的计算机之间网络连接速度很慢时。这种情况下,可以在符号文件所在机器上创建索引文件,并将该文件传输到第二台机器,在第二台机器上创建符号存储。
SymStore参数的完整列表,查看SymStore命令行选项。
注意 SymStore不支持多个用户同时发起事务。建议指派一个用户作为符号存储的"管理员"并处理所有的add 和del事务。
事务示例
这里有两个SymStore在\\MyDir\symsrv 中添加Windows 2000 build 2195符号指针的示例:
symstore add /r /p /f \\BuildServer\BuildShare\2195free\symbols\*.* /s \\MyDir\symsrv /t "Windows 2000" /v "Build 2195 x86 free" /c "Sample add"
symstore add /r /p /f \\BuildServer\BuildShare\2195free\symbols\*.* /s \\MyDir\symsrv /t "Windows 2000" /v "Build 2195 x86 checked" /c "Sample add"
下面的示例中,SymStore将\\largeapp\appserver\bins 中的项目的实际的符号文件添加到\\MyDir\symsrv:
symstore add /r /f \\largeapp\appserver\bins\*.* /s \\MyDir\symsrv /t "Large Application" /v "Build 432" /c "Sample add"
这是使用索引文件的示例。首先创建\\largeapp\appserver\bins\上的符号文件的索引文件。这种情况下,索引文件放在第三台计算机\\hubserver\hubshare上。 使用/g 选项来指定前缀"\\largeapp\appserver"可能会在未来改变:
symstore add /r /p /g \\largeapp\appserver /f \\largeapp\appserver\bins\*.* /x \\hubserver\hubshare\myindex.txt
假设现在将\\largeapp\appserver 机器上的所有符号文件移动到\\myarchive\appserver 上。可以使用如下方法从\\hubserver\hubshare\myindex.txt 上的索引文件创建符号存储本身:
symstore add /y \\hubserver\hubshare\myindex.txt /g \\myarchive\appserver /s \\MyDir\symsrv /p /t "Large Application" /v "Build 432" /c "Sample Add from Index"
最后,这是一个SymStore删除之前的事务中添加的文件的示例。查看后面的小节获得关于事务ID的说明。(本例中为0000000096)。
symstore del /i 0000000096 /s \\MyDir\symsrv
压缩文件
SymStore可以使用两种方法使用压缩文件:
- 使用SymStore和/p选项来保存符号文件的指针。SymStore 完成之后,压缩指针指向的文件。
- 使用SymStore和/x选项来创建索引文件。SymStore完成之后,压缩索引文件中列出的文件。然后,使用SymStore和/y选项(如果愿意,也可以使用/p)来将这些文件或指针保存到符号存储。 (SymStore在这个操作中不需要解压文件。)
符号服务器会负责在适当的时候解压这些文件。
如果使用SymSrv作为符号服务器,必须使用随Microsoft Windows SDK发布的 compress.exe 工具来进行压缩。压缩文件扩展名的最后一个字符必须是下划线 (例如module1.pd_ 或module2.db_)。详细信息,查看使用SymSrv。
server.txt 和history.txt 文件
添加事务之后,会在server.txt 和history.txt 中添加一些信息为未来使用。下面是一次添加事务在server.txt 和history.txt 中产生的行的示例:
0000000096,add,ptr,10/09/99,00:08:32,Windows NT 4.0 SP 4,x86 fre 1.156c-RTM-2,Added from \\mybuilds\symbols,
这是用逗号分隔的行。下面是每个部分的说明:
字段 | 说明 |
0000000096 | 事务ID号,由SymStore创建 |
add | 事务类型,这个字段可以是add或del。 |
ptr | 添加的是文件或指针。该字段可以是file或ptr。 |
10/09/99 | 事务发生的日期。 |
00:08:32 | 事务开始的时间。 |
Windows NT | 产品 |
x86 fre | 版本(可选)。 |
Added from | 注释(可选)。 |
Unused | (保留为以后使用)。 |
这是事务文件0000000096中的一些示例行。每行都记录了添加到目录中的文件或指针的目录和位置。
canon800.dbg\35d9fd51b000,\\mybuilds\symbols\sp4\dll\canon800.dbg
canonlbp.dbg\35d9fd521c000,\\mybuilds\symbols\sp4\dll\canonlbp.dbg
certadm.dbg\352bf2f48000,\\mybuilds\symbols\sp4\dll\certadm.dbg
certcli.dbg\352bf2f1b000,\\mybuilds\symbols\sp4\dll\certcli.dbg
certcrpt.dbg\352bf04911000,\\mybuilds\symbols\sp4\dll\certcrpt.dbg
certenc.dbg\352bf2f7f000,\\mybuilds\symbols\sp4\dll\certenc.dbg
如果使用del事务来撤销以前的add事务,这些行会从server.txt中删除,并添加下面这行到history.txt中:
0000000105,del,0000000096
删除事务的字段说明如下。
字段 | 说明 |
0000000105 | 事物ID号,SymStore创建。 |
del | 事务类型。该字段可以是add或del。 |
0000000096 | 被删除的事务。 |
符号存储格式
SymStore使用文件系统本身作为数据库。它创建一个大的目录树,目录名基于符号文件的时间戳、签名、寿命和其他数据。
例如一些不同的acpi.dbg添加到服务器之后,目录可能像下面一样:
Directory of \\mybuilds\symsrv\acpi.dbg
10/06/1999 05:46p <DIR> .
10/06/1999 05:46p <DIR> ..
10/04/1999 01:54p <DIR> 37cdb03962040
10/04/1999 01:49p <DIR> 37cdb04027740
10/04/1999 12:56p <DIR> 37e3eb1c62060
10/04/1999 12:51p <DIR> 37e3ebcc27760
10/04/1999 12:45p <DIR> 37ed151662060
10/04/1999 12:39p <DIR> 37ed15dd27760
10/04/1999 11:33a <DIR> 37f03ce962020
10/04/1999 11:21a <DIR> 37f03cf7277c0
10/06/1999 05:38p <DIR> 37fa7f00277e0
10/06/1999 05:46p <DIR> 37fa7f01620a0
这个例子中,acpi.dbg的查找路径可能像这样:\\mybuilds\symsrv\acpi.dbg\37cdb03962040。
在查找目录中可能有三个文件:
- acpi.dbg,如果保存了该文件的话
- 如果保存的是指针,file.ptr 包含实际的符号文件路径。
- refs.ptr,包含当前被添加到符号存储中的,具有该时间戳和映像大小的acpi.dbg的当前位置的完整列表。
显示\\mybuilds\symsrv\acpi.dbg\37cdb03962040 中的目录列表如下:
10/04/1999 01:54p 52 file.ptr
10/04/1999 01:54p 67 refs.ptr
file.ptr 文件包含字符串"\\mybuilds\symbols\x86\2128.chk\symbols\sys\acpi.dbg"。因为该目录中没有acpi.dbg 文件,所以调试器会试图从\\mybuilds\symbols\x86\2128.chk\symbols\sys\acpi.dbg中去查找。
refs.ptr 中的内容仅被SymStore使用,而不会被调试器使用。这个文件记录该目录中发生过的所有事务的记录。refs.ptr 中的一个示例行可能像这样:
0000000026,ptr,\\mybuilds\symbols\x86\2128.chk\symbols\sys\acpi.dbg
这表明指向\\mybuilds\symbols\x86\2128.chk\symbols\sys\acpi.dbg 的指针由事务"0000000026"添加。
一些符号文件在各种产品、版本或特殊产品中保持不变。这样的一个例子是Windows 2000的文件msvcrt.pdb。列举\\mybuilds\symsrv\msvcrt.pdb 目录显示只有两个版本的msvcrt.pdb 被添加到了符号服务器上:
Directory of \\mybuilds\symsrv\msvcrt.pdb
10/06/1999 05:37p <DIR> .
10/06/1999 05:37p <DIR> ..
10/04/1999 11:19a <DIR> 37a8f40e2
10/06/1999 05:37p <DIR> 37f2c2272
但是,列举\\mybuilds\symsrv\msvcrt.pdb\37a8f40e2 目录表明refs.ptr 中有一些指针。
Directory of \\mybuilds\symsrv\msvcrt.pdb\37a8f40e2
10/05/1999 02:50p 54 file.ptr
10/05/1999 02:50p 2,039 refs.ptr
\\mybuilds\symsrv\msvcrt.pdb\37a8f40e2\refs.ptr的内容如下:
0000000001,ptr,\\mybuilds\symbols\x86\2137\symbols\dll\msvcrt.pdb
0000000002,ptr,\\mybuilds\symbols\x86\2137.chk\symbols\dll\msvcrt.pdb
0000000003,ptr,\\mybuilds\symbols\x86\2138\symbols\dll\msvcrt.pdb
0000000004,ptr,\\mybuilds\symbols\x86\2138.chk\symbols\dll\msvcrt.pdb
0000000005,ptr,\\mybuilds\symbols\x86\2139\symbols\dll\msvcrt.pdb
0000000006,ptr,\\mybuilds\symbols\x86\2139.chk\symbols\dll\msvcrt.pdb
0000000007,ptr,\\mybuilds\symbols\x86\2140\symbols\dll\msvcrt.pdb
0000000008,ptr,\\mybuilds\symbols\x86\2140.chk\symbols\dll\msvcrt.pdb
0000000009,ptr,\\mybuilds\symbols\x86\2136\symbols\dll\msvcrt.pdb
0000000010,ptr,\\mybuilds\symbols\x86\2136.chk\symbols\dll\msvcrt.pdb
0000000011,ptr,\\mybuilds\symbols\x86\2135\symbols\dll\msvcrt.pdb
0000000012,ptr,\\mybuilds\symbols\x86\2135.chk\symbols\dll\msvcrt.pdb
0000000013,ptr,\\mybuilds\symbols\x86\2134\symbols\dll\msvcrt.pdb
0000000014,ptr,\\mybuilds\symbols\x86\2134.chk\symbols\dll\msvcrt.pdb
0000000015,ptr,\\mybuilds\symbols\x86\2133\symbols\dll\msvcrt.pdb
0000000016,ptr,\\mybuilds\symbols\x86\2133.chk\symbols\dll\msvcrt.pdb
0000000017,ptr,\\mybuilds\symbols\x86\2132\symbols\dll\msvcrt.pdb
0000000018,ptr,\\mybuilds\symbols\x86\2132.chk\symbols\dll\msvcrt.pdb
0000000019,ptr,\\mybuilds\symbols\x86\2131\symbols\dll\msvcrt.pdb
0000000020,ptr,\\mybuilds\symbols\x86\2131.chk\symbols\dll\msvcrt.pdb
0000000021,ptr,\\mybuilds\symbols\x86\2130\symbols\dll\msvcrt.pdb
0000000022,ptr,\\mybuilds\symbols\x86\2130.chk\symbols\dll\msvcrt.pdb
0000000023,ptr,\\mybuilds\symbols\x86\2129\symbols\dll\msvcrt.pdb
0000000024,ptr,\\mybuilds\symbols\x86\2129.chk\symbols\dll\msvcrt.pdb
0000000025,ptr,\\mybuilds\symbols\x86\2128\symbols\dll\msvcrt.pdb
0000000026,ptr,\\mybuilds\symbols\x86\2128.chk\symbols\dll\msvcrt.pdb
0000000027,ptr,\\mybuilds\symbols\x86\2141\symbols\dll\msvcrt.pdb
0000000028,ptr,\\mybuilds\symbols\x86\2141.chk\symbols\dll\msvcrt.pdb
0000000029,ptr,\\mybuilds\symbols\x86\2142\symbols\dll\msvcrt.pdb
0000000030,ptr,\\mybuilds\symbols\x86\2142.chk\symbols\dll\msvcrt.pdb
这表明相同的msvcrt.pdb 在保存在\\mybuilds\symsrv 上的几个Windows 2000符号版本中都使用了。
这是同时包含了文件和附加指针的目录示例:
Directory of E:\symsrv\dbghelp.dbg\38039ff439000
10/12/1999 01:54p 141,232 dbghelp.dbg
10/13/1999 04:57p 49 file.ptr
10/13/1999 04:57p 306 refs.ptr
这种情况下, refs.ptr 有下面这样的内容:
0000000043,file,e:\binaries\symbols\retail\dll\dbghelp.dbg
0000000044,file,f:\binaries\symbols\retail\dll\dbghelp.dbg
0000000045,file,g:\binaries\symbols\retail\dll\dbghelp.dbg
0000000046,ptr,\\MyDir\bin\symbols\retail\dll\dbghelp.dbg
0000000047,ptr,\\foo2\bin\symbols\retail\dll\dbghelp.dbg
这样,事务43、44和45向服务器添加了同样的文件,事务46和47添加了指针。如果事务43、44和45被删除,dbghelp.dbg 文件也会从目录中删除掉。这时,目录具有如下内容:
Directory of e:\symsrv\dbghelp.dbg\38039ff439000
10/13/1999 05:01p 49 file.ptr
10/13/1999 05:01p 130 refs.ptr
新的file.ptr 包含"\\foo2\bin\symbols\retail\dll\dbghelp.dbg",而refs.ptr 包含:
0000000046,ptr,\\MyDir\bin\symbols\retail\dll\dbghelp.dbg
0000000047,ptr,\\foo2\bin\symbols\retail\dll\dbghelp.dbg
只要refs.ptr 的最后一项为指针,就会存在file.ptr文件,并且包含相关文件的路径。当refs.ptr 最后一项为文件时,目录中不会存在file.ptr。因此,任何对refs.ptr 中最后一项的删除操作都可能使得file.ptr 被创建、删除或修改。
使用其它符号存储
除了使用SymStore之外,还可以编写自己的符号存储创建程序。
由于SymStore事务都是按CSV 格式文本文件记录日志的,所以可以在自己的数据库程序中使用已存在的任何SymStore日止文件。
如果使用Windows调试工具包中提供的SymSrv程序,建议还是使用SymStore。这两个程序总是会被同时升级,并且他们的版本会一直匹配。
cc682/NetRoc
http://netroc682.spaces.live.com/
使用符号服务器和符号存储
要正确地设置符号是一件有挑战性的工作,特别是对于内核调试。这经常需要知道计算机上所有产品的名字和版本。调试器必须能定位每个产品和Service pack对应的符号文件。
如果目录列表很长,可能产生非常长的符号路径。
要想简单的整理这些符号文件,可以使用符号服务器。符号服务器使得调试器可以在不需要产品名字、版本或build号的情况下自动获得正确的符号文件。Windows调试工具包包含的符号服务器称为SymSrv。
符号服务器通过在符号路径中包含特定的文本来使用。每次调试器需要为新加载的模块加载符号时,都会调用符号服务器来定位适合的符号文件。符号服务器在符号存储中定位这些文件。这是符号文件的集合,管理员可以使用索引和工具来添加和删除文件。文件通过例如时间戳和映像大小这样的唯一参数来进行索引。Windows调试工具包包含一个称为SymStore的符号存储。
本节包含:
使用Symsrv
使用其它符号服务器
使用SymStore
使用其它符号存储
使用SymSrv
SymSrv (symsrv.dll)是一个包含在Windows调试工具包中的符号服务器。
很多Windows调试工具包的用户使用Microsoft网站上的公用符号存储来访问Microsoft产品的符号。如果采用这种方法,只需要阅读下面列出的第一个主题。
本节包含:
Microsoft公共符号
SymSrv高级使用方式
防火墙和代理服务器
Microsoft公共符号
Microsoft有一个网站用于公开的访问Windows符号。可以使用下面的方式在符号路径中直接引用这个站点:
set _NT_SYMBOL_PATH=srv*DownstreamStore*http://msdl.microsoft.com/download/symbols
DownstreamStore 必须指定本地计算机或网络上的其它计算机用来缓存符号的目录。符号缓存(downstream store)保存调试器曾访问过的符号; 而绝大多数未曾访问过的符号仍然保存在Microsoft的符号存储。这使得符号缓存相对较小,而每个文件仅下载一次使得符号服务器可以快速工作。
要避免键入很长的符号路径,可以使用.symfix (Set Symbol Store Path)命令。下面的命令将公共符号存储加入到现有的符号路径中:
.symfix+ DownstreamStore
注意 要成功访问Microsoft公共符号存储,需要快速的internet连接。如果internet连接仅有56 Kps或更低,最好将Windows符号直接安装在硬盘中。详细信息,查看 安装Windows符号文件。
关于公共符号存储的更多信息,查看
Windows Symbols网站。
SymSrv 的高级使用方式
SymSrv可以从一个集中的符号存储来交付符号文件。该存储可以包含对应于任意多个程序或操作系统的任意数量的符号文件。也可以包含二进制文件(这在调试小转储(minidumps)文件时非常有用)。
符号存储可以包含实际的符号和二进制文件,也可以仅包含符号文件的指针。这时,SymSrv会从它们的源直接获得实际的文件。
SymSrv也可以用来将大的符号存储分割为适合各种不同调试任务的小的子集。
最后,SymSrv可以使用操作系统提供的登陆信息从HTTP或HTTPS源获得符号文件。SymSrv支持由智能卡(smartcards)、证书、常规登陆和密码(regular logins and passwords)保护的HTTPS站点。
设置符号路径
要使用符号服务器,symsrv.dll必须和调试器安装在同一个目录下。必须按下面的方法之一设置符号路径:
set _NT_SYMBOL_PATH = symsrv*ServerDLL*DownstreamStore*\\Server\Share
set _NT_SYMBOL_PATH = symsrv*ServerDLL*\\Server\Share
set _NT_SYMBOL_PATH = srv*DownstreamStore*\\Server\Share
set _NT_SYMBOL_PATH = srv*\\Server\Share
语法中的各部分说明如下:
symsrv
这个关键字必须首先出现。它指示调试器该对象是一个符号服务器,而不是普通的符号目录。
ServerDLL
指定符号服务器DLL的名字。如果使用SymSrv符号服务器,则总是symsrv.dll。
srv
这是symsrv*symsrv.dll的缩写。
DownstreamStore
指定符号缓存。这是用于缓存各个符号文件的本地或网络共享目录。
可以指定多于一个的缓存,以星号分开。多个缓存的情况在本页后面的层叠符号缓中描述。
如果在一般用于指定符号缓存的行中包含两个星号,则使用默认的符号缓存。这是位于主目录下的sym子目录。主目录默认是调试器的安装目录;但是可以通过!homedir扩展命令改变。
如果DownstreamStore 指定的是不存在的目录,SymStore会尝试创建它。
如果DownstreamStore 参数省略,并且没有包含其它的星号——即srv加上一个星号或者symsrv加上两个星号——则不会创建符号缓存。调试器会直接从服务器加载符号文件,而不会在本地进行缓存。
注意 如果从HTTP或HTTPS站点访问符号,或符号存储使用压缩文件,则总是会使用符号缓存。如果没有指定符号缓存,在主目录下的sym子目录中会创建一个。
\\Server\Share
指定服务器和远程符号存储的共享。
如果使用了符号缓存,调试器首先会在里面查找符号文件。在没有找到的情况下,调试器才从指定的服务器和共享中定位符号文件,然后在符号缓存中保存一个副本。文件副本会保存在DownstreamStore 下和\\Server\Share目录树对应的子目录中。
符号服务器不一定要是符号路径中的唯一入口。如果符号路径包含多个入口,调试器会按照它们出现的顺序(从左到右)来查找需要的符号文件,不管它是符号服务器或者真实的目录。
这里有一些示例。要使用SymSrv和\\mybuilds\mysymbols上的符号存储一起作为符号服务器,设置如下的符号路径:
set _NT_SYMBOL_PATH= symsrv*symsrv.dll*\\mybuilds\mysymbols
要让调试器从\\mybuilds\mysymbols 上的符号存储获得符号文件并保存到本地目录c:\localsymbols中,使用:
set _NT_SYMBOL_PATH=symsrv*symsrv.dll*c:\localsymbols*\\mybuilds\mysymbols
设置符号路径使得调试器从HTTP站点www.company.com/manysymbols 上获得符号并保存到本地网络目录\\localserver\myshare\mycache上,使用:
set _NT_SYMBOL_PATH=symsrv*symsrv.dll*\\localserver\myshare\mycache*http://www.company.com/manysymbols
最后一个示例是使用缩写形式:
set _NT_SYMBOL_PATH=srv*\\localserver\myshare\mycache*http://www.company.com/manysymbols
另外,符号路径中可以包含用分号分隔的多个目录或符号服务器。这使得可以从多个位置(或多个符号服务器)定位符号。如果某个二进制文件有一个不匹配的符号文件,则调试器不能再符号服务器中定位它,因为只检查精确的参数。但是,使用传统的符号路径,调试器可能找到名字对应但是不匹配的符号文件并成功加载。即使该文件从技术上来说是不正确的,但是还是可能提供一些有用信息。
压缩文件
SymSrv 兼容包含压缩文件的符号存储,只要这些文件是使用随Microsoft Windows SDK发布的 compress.exe 工具压缩的。压缩文件的文件扩展名末尾应该是一个下划线 (如module1.pd_ 或module2.db_)。详细信息,查看使用SymStore。
如果存储中的文件是压缩的,必须使用符号缓存。SymSrv将他们缓存之前会进行解压。
删除缓存
如果使用DownstreamStore 作为缓存,在任何时候都可以删除该目录来获得磁盘空间。
可能会有保存了很多不同程序和Windows版本的巨大的符号存储。如果升级了目标机上使用的Windows版本,可能这些缓存的符号都是匹配旧版本的。这些符号以后都不会再使用,所以这时正好可以删除缓存。
层叠符号缓存
可以指定任意多个用星号分开的符号缓存。它们称为层叠符号存储。
在开始的srv* 或symsrv*ServerDLL*之后,每个标记都指示了一个符号位置。最左边的会首先被检查。空的标记——用一行中的两个星号,或者在字符串末尾的一个星号标识——指定使用默认的符号缓存。
这里有一个符号路径的例子,使用两个符号缓存来保存从主符号存储访问过的信息。它们可以称为主存储、中间存储(mid-level store)和本地缓存:
srv*c:\localcache*\\interim\store*http://msdl.microsoft.com/download/symbols
这种情况下,SymSrv首先在c:\localcache 中查找符号文件。如果找到了,它返回这里的一个路径。如果没找到,则继续查找\\interim\store。如果在这里找到了符号文件,SymSrv将它复制到 c:\localcache 并返回路径。如果没找到, SymSrv将在http://msdl.microsoft.com/download/symbols 上的Microsoft公共符号存储中寻找;如果找到了,SymSrv会将它同时复制到\\interim\store 和c:\localcache。
使用下面的符号路径时有类似效果:
srv**\\interim\store*http://internetsite
这种情况下,本地缓存是默认的符号缓存,并且主存储是一个intetnet网站。中间存储指定为\\interim\store 。
当SymSrv 处理包含层叠存储的路径时,它会跳过任何不能读写的缓存。所以如果共享被关闭了,它会将文件通过丢失的存储复制到缓存中而不产生任何错误。这样的一个好处是,当主存储不可写的时候,可以指定多个主存储作为单个下游存储的源。
当从主存储获得压缩过的符号文件时,任何中间存储中保存的都是压缩形式的文件。只有路径中最底部的存储中会保存解压后的文件。
cache*localsymbolcache
另一种为符号创建本地缓存的方法是在符号路径中使用cache*localsymbolcache 字符串。这不是符号服务器部件的一部分,而是符号路径中的另一种部件。调试器将把从符号路径中在该字符串右边的任何部分访问到的符号保存到localsymbolcache 指定的目录中。这使得从任何位置访问过的符号都可以进行本地缓存,而不仅是从符号服务器访问的那些。
例如,下面的符号路径不会缓存从\\someshare 中获取的符号。从\\anothershare 中获取的符号会保存到c:\mysymbols 中,因为它出现在cache*c:\mysymbols 的右边。同样,从Microsoft公共符号存储中获得的符号也会保存到c:\mysymbols ,因为使用了符号服务器的常用语法 (srv 和两个或更多星号)。 此外,如果之后再使用.sympath+ 命令来向路径中添加其它位置,这些新的部分也会被缓存,因为他们都会被添加到符号路径的右边。
_NT_SYMBOL_PATH=\\someshare\that\cachestar\ignores;srv*c:\mysymbols*http://msdl.microsoft.com/download/symbols;cache*c:\mysymbols;\\anothershare\that\gets\cached
SymSrv 如何定位文件
SymSrv会为需要的符号文件创建一个完全限定的UNC路径(fully-qualified UNC path)。 这个路径以_NT_SYMBOL_PATH 环境变量中指定的指向符号存储的路径开头。SymbolServer 的函数之后用来确定该文件的名字;这个名字会加在路径上作为目录名。传递给SymbolServer的id、第2、3个参数会串起来组成另一个目录名加到路径上。如果这些值中有为0的,则会被忽略掉。
最后产生的目录会用来搜索符号文件或符号存储的指针文件。
如果搜索成功,SymbolServer 将路径传递给调用者并返回TRUE。如果没有找到,SymbolServer 返回FALSE。
使用AgeStore来控制缓存大小
AgeStore 工具可以用来删除缓存中早于指定时间的文件,或者将缓存大小控制在指定值以下。这在符号缓存太大的时候很有用。更详细的信息,查看AgeStore。
cc682
http://netroc682.spaces.live.com/
主要解决在登陆到Windows桌面之前崩溃的程序抓dump问题。有时候设置即时调试器也抓不到这种崩溃的dump文件,而Adplus.vbs设置在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options注册表项中似乎又不能运行。所以写了这个exe的小程序,用来调用adplus抓取dump。
使用方法是在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options项下面建立一个键,名字和要抓取dump的exe程序名字相同。然后在这个键下面建立一个名为Debugger的字符串值。值的内容和使用adplus抓dump一样,只是调试器文件为Adplusplus.exe。例如adplusplus.exe -quiet -crash -o I:\testdump –sc。注意一定要用-sc结尾。另外,adplus所在的目录必须添加到Path环境变量中。
例如现在有一个名为CrashService.exe的服务在启动时崩溃,需要抓取dump。Adplus安装在C:\Program Files\Debugging Tools for Windows下,Adplusplus.exe小工具在c:\根目录下,我们可以这样设置以获得dump。
- 将C:\Program Files\Debugging Tools for Windows添加到Path环境变量
- 在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options下创建CrashService.exe键,里面新建Debugger字符串值,内容为:c:\dplusplus -quiet -crash -o I:\testdump –sc
这样,重起机器之后,服务崩溃就可以在I:\testdump下找到相应的dump文件了。
Adplusplus.rar
cc682/NetRoc
http://netroc682.spaces.live.com/
符号文件:概述
当应用程序、库、驱动或操作系统被链接时,链接器在创建.exe和.dll文件时还会创建一些其它文件,即符号文件。
符号文件包含很多在运行时并不需要的数据,但是这些数据在调试过程中是非常有用的。
典型来说,符号文件可能包含:
- 全局变量
- 局部变量
- 函数名和它们的入口点地址
- 帧指针省略记录(Frame pointer omission (FPO) records)
- 源码行号
这些数据都被称为符号。例如,一个符号文件myprogram.pdb 可能包含了数千个符号,包括全局变量、函数名以及数千个局部变量。通常,软件同时生成两个版本的符号文件:包含公有和私有符号的完整符号文件,和一个只包含共有符号的有限制的文件。详细信息,查看公有和私有符号。
调试时,要确认调试器能访问和调试目标关联的符号文件。交互式的调试和调试崩溃转储文件都需要符号。需要得到要调试的代码的合适的符号并加载到调试器中。
Windows符号
Microsoft Windows NT和Windows 2000将他们的符号保存在扩展名为.pdb和.dbg的文件中。Windows XP和之后版本的Windows仅使用.pdb文件。Windows驱动程序可以使用其中之一。
编译器和链接器控制符号格式。Visual C++ 5.0链接器同时创建.pdb和.dbg符号文件——.dbg文件实质上指向.pdb文件。Visual C++ 6.0和之后的Visual Studio版本的链接器将所有符号都放入.pdb文件中。
所有基于Windows-NT的操作系统和驱动都构建有两个版本。发布版 (或零售版)的二进制文件相对较小,而 调试版 (checked build或debug build) 二进制文件要大一些,并带有更多调试符号。这些版本都有两种符号文件。当在Windows上调试目标时,必须有和目标机器上的Windows版本匹配的符号文件。
下表列出了在标准Windows符号树中包含的目录:
目录
|
包含的符号文件
|
ACM
|
Microsoft音频压缩管理器(Microsoft Audio Compression Manager )文件
|
COM
|
可执行文件(.com)
|
CPL
|
控制面板程序
|
DLL
|
动态链接库文件(.dll)
|
DRV
|
驱动文件(.drv)
|
EXE
|
可执行文件(.exe)
|
SCR
|
屏幕保护程序文件
|
SYS
|
驱动文件(.sys)
|
数据类型
所有代码中用typedef定义的结构都会包含,只要在程序中实际用过它们。但是,在头文件中定义但是没有实际使用到的符号不会包含在.pdb中,并且调试器不能访问它们。如果要让这样的类型在调试器中可用,可以将它们作为typedef声明的输入。例如,如果代码中有下面的内容,MY_DATA 结构会保存在.pdb符号文件中,并且可以被调试器显示出来:
typedef struct _MY_DATA {
. . .
} MY_DATA;
typedef MY_DATA *PMY_DATA;
另一方面,下面这样的代码就不足够,因为MY_DATA 和PMY_DATA 都是在初始的typedef中定义的,因此MY_DATA 本身并没有被任何typedef 定义使用到:
typedef struct _MY_DATA {
. . .
} MY_DATA, *PMY_DATA;
任何情况下,类型信息仅在完整符号文件中包含,而不在剥除了所有私有符号信息的符号文件中包含。更多信息,查看公有和私有符号。
公有和私有符号
当链接器构建一个全尺寸的.pdb或.dbg文件时,它包含两种截然不同的信息集合:私有符号数据和公有符号表。这些集合的不同点在于包含的条目和每条记录中的信息不一样。
私有符号数据包含以下内容:
- 函数
- 全局变量
- 局部变量
- 用户定义的结构、类和数据类型的信息
- 源代码文件名和文件生成的每条二进制指令对应的源码行号
公有符号表包含少一些的内容:
- 函数(除了用static声明的函数)
- 指定为extern的全局变量 (和其他在多个object 文件中可见的全局变量)
作为一般规则,公有符号表包含的恰好是那些需要跨源文件访问的内容。仅在一个对象文件(object file)中可见的项目——如static 函数、仅在一个文件中的全局变量和局部变量——都不包含在公有符号表中。
两种集合的数据在包含的每条项目的信息方面也有所不同。下面是私有符号数据中每条记录包含的典型数据:
- 条目的名字
- 条目在虚拟内存中的地址
- 每个函数的帧指针省略记录(FPO)
- 每个变量、结构和函数的数据类型
- 每个函数的参数类型和名字
- 每个局部变量的作用域
- 源码中每一行对应的符号
另一方面,公有符号表仅为每条记录包含以下内容:
- 条目的名字。
- 条目在它的模块的虚拟内存空间中的地址。对于函数来说,是入口地址。
- 每个函数的帧指针省略记录(FPO) 。
换句话说,公有符号数据在两方面是私有符号数据的一个子集:它包含的条目要少一些,并且每条记录包含的内容也要少一些。例如,公有符号不包含任何局部变量的信息。每个局部变量的地址、数据类型和作用域都仅包含在私有符号中。另一方面,函数在私有和公有符号中都包含,但是私有符号包含函数名、地址、FPO记录、输入参数名字和类型、以及返回值类型;而公有符号仅包含函数名、地址和FPO记录。
私有符号数据和公有符号表之间还有一个差异。公有符号表中的很多条目使用前缀或后缀来修饰。这些修饰名是由C、C++编译器和MASM汇编器添加的。典型的前缀由一系列下划线或字符串__imp_ (表示导入函数)组成。典型的后缀包含一个或多个at符号(@),后跟地址或其他指示字符串。链接器使用这些修饰符来消除歧义,因为不同模块中的函数名或全局变量名可能会重复。对于公有符号表是私有符号的子集的普通规则来说,这些修饰符是一个例外。
完整符号文件和省略的符号文件
完整符号文件同时包含私有符号和公有符号表。这种文件有时用私有符号文件来称呼,但是该名称有些令人误解,因为这种文件同时包含私有和公有符号。
省略的符号文件是仅包含公有符号表的小一些的文件——或者,某些情况下只是公有符号表的一个子集。这种文件有时称为公有符号文件。
创建完整和省略的符号文件
如果使用Visual Studio来构建二进制文件,可以创建完整的或省略的符号文件。当构建二进制文件的"调试版"时,Visual Studio一般会创建完整符号文件。当构建"发行版"时,Visual Studio一般不会创建符号文件,但是可以通过设置一些选项来生成完整的或省略的符号文件。
如果使用Build实用工具来构建二进制文件,会创建完整符号文件。
使用BinPlace 工具,可以从完整符号文件生成省略的符号文件。当使用最常用的BinPlace 选项(-a -x -s -n)时,省略的符号文件会放在-s开关之后指定的目录内,完整符号文件放在 -n 开关指定的目录内。当BinPlace 剪切符号文件时,省略符号文件和完整版本的文件会生成同样的签名和其他标识信息。这使得在调试时可以使用任一个版本。
使用PDBCopy 工具,可以通过移除完整符号文件中的私有符号数据来生成省略的符号文件。PDBCopy也可以移除公有符号表中的指定子集。详细信息,查看PDBCopy。
使用SymChk 工具,可以查看一个符号文件中是否包含私有符号。详细信息,查看SymChk。
在调试器中查看公有和私有符号
可以使用WinDbg、KD或CDB来查看符号。如果某个调试器可以访问一个完整符号文件,它可以同时拥有私有符号和公有符号表中的数据。私有符号要更加详细,而公有符号包含符号修饰符。
访问私有符号时,总是使用私有符号数据,因为这些符号在公有符号表中并没有包含。这些符号是不会添加修饰符的。
访问公有符号时,调试器的行为由特定的符号选项决定:
- SYMOPT_UNDNAME选项打开时,显示公有符号的名字时,不会包含修饰符。此外,在搜索符号时也跳过修饰名。关闭该选项时,显示公有符号时会显示修饰名,并且在搜索时也会包含。私有符号在任何情况下都不会包含修饰符。该选项在所有调试器中默认被打开。
- SYMOPT_PUBLICS_ONLY选项打开时,私有符号数据会被忽略,只使用公有符号表。所有调试器默认关闭该选项。
- SYMOPT_NO_PUBLICS选项打开时,公有符号表被忽略, 搜索符号信息时只使用私有符号数据。所有调试器中都默认关闭该选项。
- SYMOPT_AUTO_PUBLICS选项打开时(并且SYMOPT_PUBLICS_ONLY和SYMOPT_NO_PUBLICS都关闭) ,第一次符号搜索针对私有符号。如果需要的符号找到,则结束搜索。如果没有,则继续搜索共有符号表。由于公有符号一般包含的是私有符号的子集,所以通常公有符号表中的结果被跳过。
- 当SYMOPT_PUBLICS_ONLY、 SYMOPT_NO_PUBLICS和SYMOPT_AUTO_PUBLICS 选项都被关闭时,每次需要符号时都会同时搜索私有符号数据和公有符号表。但是,如果两处都找到了匹配的项,则使用私有符号中的那个。因此,这时的行为和SYMOPT_AUTO_PUBLICS 打开时一样,除了SYMOPT_AUTO_PUBLICS 可能会让搜索速度略微加快。
这里有一个三次使用x (Examine Symbols)命令的示例。第一次使用默认的符号选项,所以信息是从私有符号数据中来的。注意数组typingString的信息中包含地址、大小和数据类型。第二次,使用.symopt+ 4000命令,使得调试器忽略私有符号数据。当x命令再次运行时,使用公有符号;这次没有typingString 的大小和数据类型信息了。最后,使用.symopt- 2命令,使得调试器包含修饰符。使用x命令时,会显示包含修饰符的名字_typingString。
0:000> x /t /d *!*typingstring*
00434420 char [128] TimeTest!typingString = char [128] ""
0:000> .symopt+ 4000
0:000> x /t /d *!*typingstring*
00434420 <NoType> TimeTest!typingString = <no type information>
0:000> .symopt- 2
0:000> x /t /d *!*typingstring*
00434420 <NoType> TimeTest!_typingString = <no type information>
使用DBH工具查看公有和私有符号
另一种查看符号的方法是使用DBH工具。DBH使用和调试器一样的符号选项。 和调试器一样,DBH默认关闭 SYMOPT_PUBLICS_ONLY 和SYMOPT_NO_PUBLICS,并打开SYMOPT_UNDNAME 和 SYMOPT_AUTO_PUBLICS。这些默认设置可以通过命令行选项或DBH命令覆盖。
这里有三次使用DBH 命令addr 414fe0的一个例子。第一次,使用默认符号选项,所以信息来自私有符号数据。注意函数fgets 的信息包含地址、大小和数据类型。第二次使用了命令symopt +4000,使得DBH忽略私有符号数据。再次运行addr 414fe0时,fgets函数的信息中没有包含大小和类型。最后使用了symopt -2 ,使得DBH包含修饰符。最后一次执行addr 414fe0,显示的是包含修饰符的函数名_fgets。
pid:4308 mod:TimeTest[400000]: addr 414fe0
fgets
name : fgets
addr : 414fe0
size : 113
flags : 0
type : 7e
modbase : 400000
value : 0
reg : 0
scope : SymTagNull (0)
tag : SymTagFunction (5)
index : 7d
pid:4308 mod:TimeTest[400000]: symopt +4000
Symbol Options: 0x10c13
Symbol Options: 0x14c13
pid:4308 mod:TimeTest[400000]: addr 414fe0
fgets
name : fgets
addr : 414fe0
size : 0
flags : 0
type : 0
modbase : 400000
value : 0
reg : 0
scope : SymTagNull (0)
tag : SymTagPublicSymbol (a)
index : 7f
pid:4308 mod:TimeTest[400000]: symopt -2
Symbol Options: 0x14c13
Symbol Options: 0x14c11
pid:4308 mod:TimeTest[400000]: addr 414fe0
_fgets
name : _fgets
addr : 414fe0
size : 0
flags : 0
type : 0
modbase : 400000
value : 0
reg : 0
scope : SymTagNull (0)
tag : SymTagPublicSymbol (a)
index : 7f
安装Windows符号文件
在调试Windows内核或运行在Windows上的驱动或应用程序时,需要访问适合的符号文件。
如果在调试器运行时能访问internet,可能想适用Microsoft的公有符号存储。可以通过简单的使用一次.symfix (Set Symbol Store Path)命令来连接。完整的详细信息,查看使用符号服务器和符号存储。
如果想手动安装符号,至关重要的是注意这个基本规则: 主控机上的符号文件必须和 目标机上安装的Windows系统版本匹配。如果要在Windows 2000主控机上对Windows XP目标进行内核调试,需要在Windows 2000系统上安装Windows XP符号文件。如果要在同一台机器上对目标程序进行用户模式调试,需要安装该机器上使用的Windows系统匹配的符号文件。如果在分析内存转储文件,在调试计算机上安装的符号文件要和生成dump文件的操作系统版本匹配,而不是和进行调试会话的机器上的操作系统匹配。
注意 如果想使用主控机来调试数个不同的目标机,可能需要不止一个Windows版本的符号文件。这时要注意把每类符号安装到不同的目录中。
如果在一台连接到网络的Windows计算机上进行调试,把各种不同版本的符号安装到一台网络服务器上是很有用的。Microsoft调试器可以使用网络路径(\\server\share\dir)作为符号目录路径。这避免了在网络上的每台机器上安装符号。
安装在已崩溃的目标机上的符号文件,对主控机上的调试器是无用的。
安装Windows XP或之后系统的符号文件
- 主控机的磁盘驱动器上需要至少1000 MB的空闲空间。
- 在浏览器中打开
Windows Symbols 站点。
- 跟随链接来下载合适的符号包。
从网页上安装Windows 2000的符号文件
- 主控机的磁盘驱动器上需要至少1000 MB的空闲空间。
- 在浏览器中打开
Windows Symbols 站点。
- 跟随链接来下载合适的符号包。
从支持CD上安装Windows 2000符号文件
- 主控机的磁盘驱动器上需要至少500 MB空闲空间。
- 插入Windows 2000用户支持诊断CD(Windows 2000 Customer Support Diagnostics CD)。
- 在安装符号(Install Symbols)上点击。
- 选择安装零售版符号(free build)或安装调试版符号(checked build)。符号必须和被调试的操作系统匹配。
- 输入符号安装的路径,或者使用默认路径。默认路径是%windir%\symbols。
从Windows CD上安装Windows NT 4.0的符号文件
- 主控机的磁盘驱动器上需要至少450MB空闲空间。
- 从命令提示符上进入Windows NT 4.0 CD-ROM的\support\debug folder 目录。
-
运行 ExpndSym 命令。ExpndSym 需要两个命令行参数, Source_drive 和 Target_folder。 Source_drive 是CD-ROM驱动器。Target_folder 是要安装符号文件的路径。
使用该工具的典型例子如下:
expandsym d: c:\winnt
该例子中,符号会被安装到c:\winnt\symbols (不是in c:\winnt 本身)。
符号文件安装顺序
如果要在单个目录树中保存符号, 符号文件的安装顺序应该和操作系统文件的安装顺序一样:
按正确顺序安装符号文件
- 安装操作系统符号文件。
- 安装正确的Service Pack 包(如果有的话)。
- 安装在Service Pack 安装之后安装的任何热补丁(Hot Fixes)的符号文件(如果有的话)。
但是,好的安装应该把每个Service Pack 和热补丁的文件安装到不同的目录树下,并且将所有这些目录都加入符号搜索路径。调试器会自己寻找合适的符号。(由于符号文件有日期和时间戳,所以调试器知道哪个才是最新的。)查看符号路径获取详细信息。
安装用户模式符号
如果要调试用户模式程序,同样需要安装该程序的符号文件。
在拥有程序的符号但是没有Windows符号时也可以进行调试。但是,结果会有更多限制。仍然可以单步执行程序代码,但是任何调试器需要分析内核的操作(例如获得调用堆栈)的操作都可能失败。
cc682
http://netroc682.spaces.live.com/
丢一个比较难防的读写其他进程内存空间的方法出来,嘿嘿。貌似我还没想到什么好办法可以防住的。
内存空间不能跨进程访问的原因主要在于不同进程都有自己的页目录和页表。进程切换的很大一块也就是切换掉页目录。
Windows自己的ReadProcessMemory最终也是通过KeStackAttachProcess附加到目标进程空间执行拷贝的。但是中间的N个内核函数调用现在被很多保护系统Hook掉并保护起来了,所以要通过这层层关卡读到东西还是不那么简单的。Unhook?自己实现内核函数?都很麻烦。
其实,仅仅是需要拿到目标进程的页目录,然后直接从目标地址拷贝出来而已,完全可以用简单得多的办法来达到这个目的。
所有的XXAttachProcess函数最终都通过_KiSwapProcess切换进程环境,_KiSwapProcess中会将目标进程的页目录指针放入CR3。这个过程我们可以自己来实现,唯一需要的就是拿到目标进程的EPROCESS而已,而这有无数种方法可以达到。整个过程基本上没有能当作非正常访问的证据以阻止掉的。
首先来看一下几个内核结构:
每个进程都有一个内核里面的进程结构_EPROCESS,前面几个字段如下:
typedef struct _EPROCESS {
KPROCESS Pcb;
EX_PUSH_LOCK ProcessLock;
……
Pcb中就包含我们需要的页目录地址:
typedef struct _KPROCESS {
DISPATCHER_HEADER Header;
LIST_ENTRY ProfileListHead;
ULONG_PTR DirectoryTableBase[2];
……
系统一般只使用DirectoryTableBase[0],DirectoryTableBase[1]是用于hyperspace的。我们只需要拿到目标进程里面的DirectoryTableBase[0]值并直接设置到CR3中就可以读取了。
假设要读取PID为1000的进程从0x400000开始的100个字节,只需要按下面的步骤实现:
- 获取PID=1000的进程的_EPROCESS,读取_EPROCESS::Pcb::DirectoryTableBase[0]
- 将页目录的值mov到cr3中。
- 直接将0x400000的地址memcpy到内核空间地址中
- 恢复cr3的值。
下面是我测试使用的代码片断,图方便使用了PsLookupProcessByProcessId来获取EPROCESS,实际上这个函数多半被处理过的,所以很可能拿不到。解决的方法之一是可以通过PsSetCreateProcessNotifyRoutine挂一个回调,在里面通过PsLookupProcessByProcessId获取每个进程的EPROCESS地址并缓存起来后面使用。
这是DispatchIoCtrl里面的
switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
{
case
IOCTL_READMEM:
pstReadInfo = Irp->AssociatedIrp.SystemBuffer;
ntStatus = STATUS_SUCCESS;
if ( pstReadInfo->ulLen > 1000 ||
irpStack->Parameters.DeviceIoControl.InputBufferLength < pstReadInfo->ulLen ||
pstReadInfo->ulStart >= (PVOID)0x80000000 ||
(ULONG)pstReadInfo->ulStart + pstReadInfo->ulLen >= 0x80000000)
{
DbgPrint( "Read range invalid\r\n");
ntStatus = STATUS_UNSUCCESSFUL;
break;
}
__try
{
if (PsLookupProcessByProcessId((HANDLE)(pstReadInfo->ulPid),&pstEProcess) == STATUS_SUCCESS)
{
pbyBuff = (PUCHAR)ExAllocatePoolWithTag( NonPagedPool, pstReadInfo->ulLen, 'PMRD');
ASSERT( pbyBuff);
}
else
{
DbgPrint( "PsLookupProcessByProcessId fail\r\n");
ntStatus = STATUS_UNSUCCESSFUL;
break;
}
ntStatus = ReadProcessMemory( pstEProcess, pbyBuff, pstReadInfo->ulStart, pstReadInfo->ulLen);
if ( ntStatus == STATUS_SUCCESS)
{
RtlCopyMemory( pstReadInfo, pbyBuff, pstReadInfo->ulLen);
}
else
{
ntStatus = STATUS_UNSUCCESSFUL;
break;
}
ExFreePool( pbyBuff);
pbyBuff = NULL;
}
__except(1)
{
ntStatus=STATUS_UNSUCCESSFUL;
}
break;
……
这是读取内存的:
NTSTATUS
ReadProcessMemory( PEPROCESS
pstEProcess, PUCHAR
pucBuff, PVOID
pStart, ULONG
ulLen)
{
PKPROCESS
pstKProcess = NULL;
PEPROCESS
pstCurrent = NULL;
ULONG
ulPDT = 0;
ULONG
ulOldCr3 = 0;
pstKProcess = &pstEProcess->Pcb;
ulPDT = pstKProcess->DirectoryTableBase[0];
//load cr3
_asm
{
cli;
mov
eax, cr3;
mov
ulOldCr3, eax;
mov
eax, ulPDT;
mov
cr3,eax
}
_asm
sti;
RtlCopyMemory( pucBuff, pStart, ulLen); //直接复制内存
_asm
cli;
_asm
{
mov
eax, ulOldCr3;
mov
cr3,eax;
sti;
}
return
STATUS_SUCCESS;
}
这个代码有些细节并没有处理,只是尝试了一下新的访问进程内存的方式。除了拿EPROCESS之外,中间没有使用任何系统函数,所以常规办法基本上是没有办法挡住的。进程保护技术看来实现起来难度还是很大啊。还是那句话,一点突破,满盘皆输。
cc682/NetRoc
http://netroc682.spaces.live.com/
转发器
转发器是在一台计算机上运行的轻量级的代理服务器,用于在其他两台机器之间转发数据。转发器不会对数据进行任何处理。其他两台机器几乎不知道转发器的存在,从他们的角度来看就像直接和对方通信一样。
另外两台机器上运行的进程称为服务器和客户端。从转发器的角度来看,他们之间并没有根本的不同点,只是服务器首先启动,然后启动转发器,最后才启动客户端。
Windows调试工具包包含的转发器称为DbEngPrx (dbengprx.exe)。
本节包含:
激活转发器
使用转发器
转发器示例
激活转发器
要激活转发器连接,一般来说要先启动服务器,然后启动转发器,最后启动客户端。
也可以先启动转发器然后再启动服务器。但是除非使用了clicon ,否则客户端总是最后一个启动的。
第一步:启动服务器
服务器可以是一个调试服务器、一个进程服务器或者KD连接服务器。可以按照常规的办法启动,只是通信协议设置为和转发器连接而不是客户端。详细信息查看激活调试服务器、激活进程服务器或激活KD连接服务器。
如果创建服务器时使用了密码,该密码在客户端附加时才需要,而不是转发器创建时。
如果使用了hidden参数,服务器和平常一样也会被隐藏。转发器本身总是被隐藏的。
第二步:启动转发器
Windows调试工具包中包含的转发器称为DbEngPrx (dbengprx.exe)。
DbEngPrx可以使用这些传输协议:命名管道(NPIPe)、TCP和COM端口。
如果客户端和服务器使用安全套接字层(SSL),使用TCP协议来和转发器连接。如果客户端和服务器使用安全管道(SPIPE)协议,使用NPIPE和转发器连接。转发器会直接转发任何收到的数据——不会解析、加密或解密任何信息。所有的加解密都会由客户端和服务器来完成。
DbEnPrx 命令行语法如下:
dbengprx [-p] -c ClientTransport -s ServerTransport
上面命令的参数有以下可能值:
-p
使得DbEngPrx 在所有连接丢失之后仍然运行。
ClientTransport
指定和服务器之间的协议设置。该协议需要和服务器创建时指定的一样。协议语法如下:
npipe:server=Server,pipe=PipeName[,password=Password]
tcp:server=Server,port=Socket[,password=Password][,ipversion=6]
tcp:clicon=Server,port=Socket[,password=Password][,ipversion=6]
com:port=COMPort,baud=BaudRate,channel=COMChannel[,password=Password]
协议参数有以下这些:
Server
这是服务器创建的机器的网络名或IP地址。开头的两个反斜杠(\\)是可选的。
PipeName
如果使用NPIPE或SPIPE协议,PipeName是服务器创建时指定的管道名。
Socket
如果使用TCP或SSL协议,Socket是服务器创建时指定的套接字端口号。
clicon
指定服务器由反向连接来连接到转发器。ClientTransport 当且仅当服务器使用clicon时才能使用clicon。大多数时候,使用反向连接时转发器要比服务器先启动。
COMPort
使用COM协议时,COMPort 指定使用的COM端口。前缀"COM"是可选的——例如, "com2" 和"2"都可以使用。
BaudRate
使用COM协议时,BaudRate 要和服务器创建时指定的波特率匹配。
COMChannel
使用COM协议时,COMChannel 要和服务器创建时指定的频道号匹配。
Password
如果服务器创建时使用了密码,在创建客户端时必须指定 Password。它必须和原始密码匹配。密码是区分大小写的。如果提供了错误的密码,会提示错误信息"Error 0x80004005."
ipversion=6
(仅Debugging Tools for Windows 6.6.07 和更老的版本) 用TCP连接到Internet时强制调试器使用IPV6而不是V4。在Windows Vista和之后版本中,调试器会尝试默认使用IP v6
,所以没有必要使用该选项。
ServerTransport
指定客户端连接转发器是使用的协议。可用的协议语法如下:
npipe:pipe=PipeName[,hidden][,password=Password]
tcp:port=Socket[,hidden][,password=Password]
tcp:port=Socket,clicon=Client[,password=Password]
com:port=COMPort,baud=BaudRate,channel=COMChannel[,hidden][,password=Password]
The protocol parameters have the following meanings:
PipeName
如果使用NPIPE或SPIPE协议,PipeName 是管道名的字符串。每个管道名都应该标识一个唯一的转发器。如果试图重用管道名,会收到一条错误消息。PipeName 不能包含空格或引号。PipeName 可以包含printf 形式的数字格式化代码,如%x 或%d。转发器用DbEngPrx 的进程ID来替代它,第二个格式化代码用DbEngPrx 的线程ID替代。
Socket
使用TCP或SSL协议时,Socket 是套接字端口号。也可以用一个冒号来指定端口范围(例如port=2:7)。转发器会检查该范围内的端口是否可用。如果找到了一个空闲端口并且没有出现错误,转发器连接服务器会被创建起来。客户端需要指定连接到转发器的实际端口号。 (如果使用了clicon参数来进行反向连接,客户端也可以按这种方式指定端口范围,而转发器连接服务器必须指定实际使用的端口。)
clicon=Client
使用TCP或SSL协议并指定clicon参数时,会打开反向连接。这意味着转发器会试图连接客户端,而不是由客户端来发起连接。这在当防火墙阻止通常方向上的连接时会比较有效。客户端指定客户端所在的计算机的网络名或IP地址。两个反斜杠(\\)是可选的。
因为使用转发器来寻找特定客户端的方式,所以不能将多个客户端连接到一个转发器。如果连接被拒绝或丢失,必须重起转发器。
使用clicon 时,最好在转发器创建之前启动客户端,虽然常规顺序也可以使用(转发器在客户端之前)。
COMPort
使用COM协议时,COMPort 指定使用的COM端口。前缀"COM"是可选的—例如,"com2" 和"2"都可以使用。在ClientTransport 和ServerTransport 中不能使用相同的COM端口。
BaudRate
使用COM协议时,BaudRate 指定连接使用的波特率。硬件所支持的所有波特率都可以使用。如果在ClientTransport 和ServerTransport 中都使用COM协议,可以指定不同的波特率,但是客户端和服务器之间的连接速度会被较慢的一个限制。
COMChannel
如果使用COM协议,COMChannel 指定和调试客户端通信所使用的COM频道。它可以是0到254之间的任意值(包含0和254)。单个COM端口可以通过使用不同的频道号来支持多个连接。 (这和调试电缆使用COM的情况不同—那种情况下COM端口不能使用频道。)
hidden
避免服务器在有人使用-QR命令行选项显示所有活动服务器时被显示出来。
password=Password
要求客户端在连接到调试会话时提供指定的密码。Password 可以是任何字母和数字组成的字符串。
第三步:启动客户端
客户端可以是一个调试客户端或灵巧客户端——由服务器类型决定。详细信息,查看激活调试客户端、激活灵巧客户端或激活灵巧客户端(内核模式)。
如果服务器拒绝了连接(例如,密码错误),转发器和客户端都会被关闭。需要重起他们才能再次连接服务器。
使用转发器
转发器执行的规则很简单:
- 任何服务器和客户端发送到对方的数据都被不经过任何改变的直接传递。
- 服务器对于传输连接的任何操作都作用于转发器上(仅间接影响客户端)。
- 客户端对传输连接的任何操作都作用于转发器上(仅间接影响服务器)。
这意味着任何调试命令、调试器输出、控制按键和文件访问都像客户端和服务器直接连接一样起作用。转发器对任何命令都是不可见的。
中断连接的动作会作用于转发器。例如如果在客户端执行qq (Quit)命令,服务器会关闭,并且会向传输器发送一个关闭信号。这使得转发器退出 (除非它使用了-p选项)。又例如, .clients (List Debugging Clients)命令会列出所有客户端的计算机名,但是它显示的是服务器和转发器之间的连接协议。
如果服务器关闭,转发器会自动退出(除非它使用了-p选项)。当转发器关闭时,也会造成调试客户端退出,但是灵巧客户端不会。如果由于某些原因需要直接终止转发器,可以使用任务管理器或kill.exe工具。
转发器示例
假设有三台计算机\\BOXA、\\BOXB和 \\BOXC,并且希望他们分别作为服务器、转发器和客户端。
使用下面的方法在\\BOXA 上启动调试服务器,使用进程122 作为目标:
E:\Debugging Tools for Windows> cdb -server tcp:port=1025,password=wrought -p 122
然后在\\BOXB 上启动转发器:
C:\Misc> dbengprx -c tcp:server=BOXA,port=1025 -s npipe:pipe=MyPipe
最后,在\\BOXC 上启动调试客户端:
G:\Debugging Tools> windbg -remote npipe:server=BOXB,pipe=MyPipe,password=wrought
这里有另外一个例子。符号在远程位置127.0.0.30上。所以决定在目标所在的127.0.0.10计算机上使用进程服务器。转发器放在127.0.0.20。
同时决定使用反向连接,所以首先在127.0.0.30上启动客户端:
G:\Debugging Tools> windbg -premote tcp:clicon=127.0.0.20,port=1033 notepad.exe
然后启动127.0.0.20上的转发器:
C:\Misc> dbengprx -c tcp:clicon=127.0.0.10,port=1025 -s tcp:port=1033,clicon=127.0.0.10
最后启动127.0.0.10上的进程服务器:
E:\Debugging Tools for Windows> dbgsrv -t tcp:port=1025,clicon=127.0.0.20
关于使用转发器的更复杂示例,查看双防火墙。
高级远程调试
本节包含:
调试多台计算机上的目标:
居中的符号
双防火墙
调试多台计算机上的目标
调试器可以同时调试多个dump文件或活动的用户模式进程。查看调试多个目标获取详细信息。
即使多个活动目标在不同的系统上,也可以调试它们。可以简单的在每个系统上启动一个进程服务器,然后使用.attach (Attach to Process) 或.create (Create Process) 命令和-premote 参数指定适当的进程服务器。
当前系统或活动系统是当前正在调试的系统。如果这时再使用.attach 或.create 命令并且不指定-premote ,调试器会在当前系统附加、或创建一个进程。
关于如何控制这样的调试会话的详细信息,查看调试多个目标。
居中的符号
这种情况下有三台计算机。第一台运行目标程序,第二台包含符号,第三台由技术员使用。
由于灵巧客户端在每方面都类似常规的调试器,所以可以同时用来作为调试服务器。这使得可以用居中机器上的灵巧客户端将三台机器连接起来。
首先在\\BOXA 机器上启动进程服务器:
dbgsrv -t npipe:pipe=FarPipe
居中的机器名为\\BOXB ,使用-premote 和-server 参数启动调试器。假设目标程序的PID是400,符号路径是G:\MySymbols:
cdb -server npipe:pipe=NearPipe -premote npipe:server=BOXA,pipe=FarPipe -v -y g:\mysymbols -p 400
然后在第三台机器上如下启动调试客户端:
windbg -remote npipe:server=BOXB,pipe=NearPipe
第三台机器会用来控制调试,而第二台是实际用来处理调试和符号访问的机器。
双防火墙
假设需要对A建筑内的机器进行内核调试,而技术员在建筑C,他/她需要在那里访问符号。但是,两个建筑里面都有防火墙阻止外来的连接。
需要在另一个位置设置一个转发器——例如建筑B。然后可以从A连接到B,同时从C连接到B。
这种情况下需要4台计算机:
- 位于建筑A内的目标机。
- 位于建筑A里面的本地主机。该机器会运行KD连接服务器。它使用1394电缆连接到目标机,并且向外连接到转发器。假设它的IP地址是127.0.10.10。
- 位于建筑B内的计算机。他会运行转发器,假设它的IP是127.0.20.20。
- 技术员所在的建筑C。计算机上会运行WinDbg作为灵巧客户端。假设IP为127.0.30.30。
首先,确认目标机已经配置为可以调试并且被本地主机附加上去。本例中使用1394电缆。
然后,再127.0.20.20上启动转发器:
dbengprx -p -s tcp:port=9001 -c tcp:port=9000,clicon=127.0.10.10
第三步,在建筑A中的127.0.10.10机器上启动KD连接服务器:
kdsrv -t tcp:port=9000,clicon=127.0.20.20,password=longjump
最后,在建筑C的127.0.30.30机器上启动灵巧客户端。(这可以在启动建筑A的服务器之前或之后进行。)
windbg -k kdsrv:server=@{tcp:server=127.0.20.20,port=9001,password=longjump},trans=@{1394:channel=9} -y SymbolPath
5台计算机的情况
当符号在建筑C的计算机上,但是技术员在另外一台计算机上时,情况会更加复杂。
和刚才一样,假设127.0.30.30上有符号,并且它的本地名称为\\BOXC。灵巧客户端可以使用上面一样的命令但是增加一条-server 参数。由于没有人会使用这台机器,所以如果使用KD而不是WinDbg可以减少处理时间:
kd -server npipe:pipe=randomname -k kdsrv:server=@{tcp:server=127.0.20.20,port=9001,password=longjump},trans=@{1394:channel=9} -y SymbolPath
然后在建筑内其他地方的技术员可以用下面的命令启动调试客户端:
windbg -remote npipe:server=\\BOXC,pipe=randomname
注意,密码必须由连接链中的第一个非转发器(\\BOXC上的灵巧客户端)提供,而不是链尾的调试器提供。
cc682/NetRoc
http://netroc682.spaces.live.com/
KD连接服务器(内核模式)
通过KD连接服务器进行的内核模式调试需要在服务器上运行一个称为KD连接服务器的小程序。然后再客户端启动内核模式调试器。由于该客户端会处理所有实际操作,所以称为灵巧客户端。
Windows调试工具包中包含一个称为KdSrv (kdsrv.exe)的KD连接服务器。
两台计算机不需要运行相同版本的Windows;他们可以运行Windows NT、Windows 2000或之后版本的Windows。但是,客户端上使用的调试器和服务器上使用的KdSrv的二进制文件必须来自同一个Windows调试工具包版本。该方法不能用来调试dump文件。
要设置该远程会话,首先启动KD连接服务器,然后再激活灵巧客户端。可以有任意多个灵巧客户端通过一个单独的KD连接服务器进行操作,但是他们每个必须连接到不同的内核调试会话中。
本节包含:
激活KD连接服务器
搜索KD连接服务器
激活灵巧客户端(内核模式)
KD 连接服务器示例
控制KD连接服务器会话
激活KD连接服务器
Windows调试工具包中包含的KD连接服务器称为KdSrv (kdsrv.exe)。
KdSrv 支持几种传输协议:命名管道(NPIPE)、TCP、COM端口、安全管道(SPIPS)和安全套接字层(SSL)。 SPIPE和SSL仅在Windows 2000和之后版本中支持。
KdSrv的命令行语法由采用的协议决定,可以使用下面一些:
kdsrv -t npipe:pipe=PipeName[,hidden][,password=Password]
kdsrv -t tcp:port=Socket[,hidden][,password=Password][,ipversion=6]
kdsrv -t tcp:port=Socket,clicon=Client[,password=Password][,ipversion=6]
kdsrv -t com:port=COMPort,baud=BaudRate,channel=COMChannel[,hidden][,password=Password]
kdsrv -t spipe:proto=Protocol,{certuser=Cert|machuser=Cert},pipe=PipeName[,hidden][,password=Password]
kdsrv -t ssl:proto=Protocol,{certuser=Cert|machuser=Cert},port=Socket[,hidden][,password=Password]
kdsrv -t ssl:proto=Protocol,{certuser=Cert|machuser=Cert},port=Socket,clicon=Client[,password=Password]
上面命令的参数有下面一些可能的值:
PipeName
当使用NPIPE或SPIPE协议时,PipeName 是用作管道名的字符串。每个管道名都应该标识一个唯一的进程服务器。如果尝试重复使用管道名,会收到一条错误信息。PipeName 不能包含空格或引号。PipeName 可以包含数字的printf 形式格式化代码,例如%x 或%d。进程服务器会将它替换为KdSrv的进程ID。第二个这样的代码会被替换为KdSrv的线程ID。
Socket
当使用TCP或SSL时,Socket 是套接字的端口号。也可以用一个冒号来指定端口范围(例如port=2:7)。KdSrv会检查该范围内的端口是否可用。如果找到了一个空闲端口并且没有出现错误,KD连接服务器会被创建起来。灵巧客户端需要指定连接到服务器的实际端口号。 (如果使用了clicon参数来进行反向连接,灵巧客户端也可以按这种方式指定端口范围,而KD连接服务器必须指定实际使用的端口。)
clicon=Client
使用TCP或SSL协议并指定clicon 参数时,会打开反向连接。这意味着KD连接服务器会试图连接灵巧客户端,而不是由客户端来发起连接。这在当防火墙阻止通常方向上的连接时会比较有效。客户端指定灵巧客户端所在的计算机的网络名或IP地址。两个反斜杠(\\)是可选的。
因为使用进程服务器来寻找特定客户端的方式,所以不能将多个客户端连接到一个服务器。如果连接被拒绝或丢失,必须重起进程服务器。当使用-QR命令显示所有活动服务器时,反向连接的服务器不会被列举出来。
注意 使用clicon时,最好在KD连接服务器创建之前启动灵巧客户端,但是正常的顺序(服务器在客户端之前)也可以使用。
COMPort
使用COM协议时,COMPort 指定使用的COM端口。前缀"COM"是可选的—例如,"com2" 和"2"都可以使用。
BaudRate
使用COM协议时,BaudRate 指定连接使用的波特率。硬件所支持的所有波特率都可以使用。
COMChannel
如果使用COM协议,COMChannel 指定和调试客户端通信所使用的COM频道。它可以是0到254之间的任意值(包含0和254)。单个COM端口可以通过使用不同的频道号来支持多个连接。 (这和调试电缆使用COM的情况不同—那种情况下COM端口不能使用频道。)
Protocol
(Windows 2000 和之后的系统) 如果使用SSL或SPIPE,Protocol 指定安全频道(S-Channel)协议。它可以是tls1、pct1、ssl2或ssl3。
Cert
(Windows 2000和之后的系统) 如果使用SSL或SPIPE协议,Cert 用于指定证书。它可以是证书名或证书的特征码(由证书管理机构提供的16进制数字字符串)。如果使用certuser=Cert 形式的语法,调试器会在系统存储中查找证书(默认存储位置)。如果使用machuser=Cert 的语法,调试器在机器存储中查找证书。指定的证书必须支持服务器验证。
hidden
避免KD连接服务器在有人使用-QR命令行选项显示所有活动服务器时被显示出来。
password=Password
要求灵巧客户端在连接到KD连接服务器时提供指定的密码。Password 可以是任何字母和数字组成的字符串。
注意 在TCP、NPIPE或COM协议下使用密码仅提供低程度的保护,因为密码不会被加密。当密码和SSL或SPIPE协议一起使用时,会被加密。如果需要安全的远程会话,必须使用SSL或SPIPE协议!
ipversion=6
(仅Debugging Tools for Windows 6.6.07 和更老的版本) 用TCP连接到Internet时强制调试器使用IPV6而不是V4。在Windows Vista和之后版本中,调试器会尝试默认使用IP v6
,所以没有必要使用该选项。
搜索KD连接服务器
可以用KD、CDB和-QR命令行选项来获得某个网络服务器上运行的所有可用的KD连接服务器列表。
这个列表中可能包含已经不存在但是没有被完全关闭的服务器—连接到这样的服务器会产生一条错误信息。该列表也包含调试服务器和用户模式进程服务器。每种情况下的服务器类型都会被指明。
使用语法如下:
Debugger -QR \\Server
Debugger 可以是KD或CDB—两种情况下输出都一样。反斜杠(\\)开头的Server是可选的。
在WinDbg中,可以使用Connect to Remote Stub Server对话框来查看可用的KD连接服务器列表。
对于每个KD连接服务器都会按照用于启动调试客户端的完整的连接字符串形式列出(除了密码会用星号替代之外) 。进程连接服务器也在该列表中。查看File | Connect to Remote Stub获得详细信息。
激活灵巧客户端(内核模式)
一旦KD连接服务器被激活,就可以在另外一台计算机上启动一个灵巧客户端开始调试会话。
有两种不同的启动灵巧客户端的方法:和内核协议kdsrv 一起启动KD或WinDbg,或者使用WinDbg图形界面。
需要指定KD连接服务器所使用的传输协议。也可以指定KD连接服务器和目标机实际的内核连接协议,或者使用默认值。
灵巧客户端使用的协议必须和进程服务器匹配。启动灵巧客户端的常规语法由使用的协议决定。有下面一些选择:
Debugger -k kdsrv:server=@{npipe:server=Server,pipe=PipeName[,password=Password]},trans=@{ConnectType} [Options]
Debugger -k kdsrv:server=@{tcp:server=Server,port=Socket[,password=Password][,ipversion=6]},trans=@{ConnectType} [Options]
Debugger -k kdsrv:server=@{tcp:clicon=Server,port=Socket[,password=Password][,ipversion=6]},trans=@{ConnectType} [Options]
Debugger -k kdsrv:server=@{com:port=COMPort,baud=BaudRate,channel=COMChannel[,password=Password]},trans=@{ConnectType} [Options]
Debugger -k kdsrv:server=@{spipe:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,pipe=PipeName[,password=Password]},trans=@{ConnectType} [Options]
Debugger -k kdsrv:server=@{ssl:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,port=Socket[,password=Password]},trans=@{ConnectType} [Options]
Debugger -k kdsrv:server=@{ssl:proto=Protocol,{certuser=Cert|machuser=Cert},clicon=Server,port=Socket[,password=Password]},trans=@{ConnectType} [Options]
要使用图形界面连接到KD连接服务器,WinDbg必须在静止模式下—它必须以没有命令行参数的形式启动,或必须中止已有的调试会话。选择File | Connect to Remote Stub菜单命令。当Connect to Remote Stub Server 对话框出现后,在Connection string 文本框中输入下面的字符串之一:
npipe:server=Server,pipe=PipeName[,password=Password]
tcp:server=Server,port=Socket[,password=Password][,ipversion=6]
tcp:clicon=Server,port=Socket[,password=Password][,ipversion=6]
com:port=COMPort,baud=BaudRate,channel=COMChannel[,password=Password]
spipe:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,pipe=PipeName[,password=Password]
ssl:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,port=Socket[,password=Password]
ssl:proto=Protocol,{certuser=Cert|machuser=Cert},clicon=Server,port=Socket[,password=Password]
另外,也可以使用Browse 来定位激活的KD连接服务器。查看File | Connect to Remote Stub获取详细信息。
上面命令中的参数有下面一些可能的值:
Debugger
可以是KD或WinDbg。
Server
这是创建KD连接服务器的计算机的网络名字或IP地址。两个反斜杠(\\) 在命令行中是可选的,但是在WinDbg对话框中不能使用。
PipeName
如果使用NPIPE或SPIPE,PipeName 是KD连接服务器创建时设定的管道名。
Socket
如果使用TCP或SSL协议,Socket 和KD连接服务器创建时指定的套接字端口号一样。
clicon
指定KD连接服务器将试图通过反向连接来连接到灵巧客户端。客户端当且仅当服务器也使用clicon时才能使用clicon。大多数情况下,使用反向连接时,灵巧客户端应比KD连接服务器先启动。
COMPort
如果使用COM协议,COMPort 指定使用的COM端口。前缀"COM"是可选的—例如, "com2" 和"2"都可以使用。
BaudRate
如果使用COM协议,BaudRate 必须和KD连接服务器创建时选择的波特率匹配。
COMChannel
如果使用COM协议,COMChannel 必须和KD连接服务器创建时选择的频道号匹配。
Protocol
(Windows 2000 和之后的系统) 如果使用SSL或SPIPE协议,Protocol 必须和KD连接服务器创建时选择的安全协议匹配。
Cert
(Windows 2000 和之后的系统) 如果使用SSL或SPIPE协议,必须和KD连接服务器创建时指定一样的certuser=Cert 或machuser=Cert 参数。
Password
如果KD连接服务器创建时使用了密码,创建灵巧客户端时必须使用Password 。它必须和原始密码匹配。密码是大小心敏感的。如果密码错误,会出现"Error 0x80004005." 错误。
ipversion=6
(仅Debugging Tools for Windows 6.6.07 和之前的版本) 强制调试器在使用TCP连接到Internet时使用IP v6而不是v4。在Windows Vista和之后的版本中,调试器尝试默认使用IP v6,所以不再需要该选项。
ConnectType
用于告诉调试器如何连接到目标机。支持下面这些内核连接协议:
com:port=ComPort,baud=BaudRate
1394:channel=1394Channel[,symlink=1394Protocol]
usb2:targetname=String
com:pipe,port=\\VMHost\pipe\PipeName[,resets=0][,reconnect]
com:modem
关于这些协议的信息,查看选择内核调试设置。可以省略这些协议中任意一个参数——例如,可以设置 trans=@{com:} ——调试器会默认将这些值设置为KdSrv运行的计算机上的环境变量中指定的值。
Options
任何其它命令行参数都可以放在这里。查看命令行选项获得完整列表。
由于KD连接服务器类似于灵巧客户端的网关一样工作,其他的选项和在KdSrv所运行的计算机上启动内核调试器所使用的一样。不同之处在于,任何指定路径或文件名的选项都应该使用灵巧客户端所在计算机上的路径。
KD连接服务器示例
假设调试技术人员和被调试的计算机不在同一个地方。技术人员请在机器旁边的其他人将目标机和另一台计算机用调试电缆连接起来。
假设另一台计算机的IP地址是127.0.0.42。调试电缆将该计算机的COM1接口和目标机的调试端口连接起来。KD连接服务器使用下面的命令启动:
E:\Debugging Tools for Windows> kdsrv -t tcp:port=1027
然后在另一边,技术人员使用下面的命令将WinDbg作为灵巧客户端启动起来:
G:\Debugging Tools> windbg -k kdsrv:server=@{tcp:server=127.0.0.42,port=1027},trans=@{com:port=com1,baud=57600} -y SymbolPath
符号路径和灵巧客户端运行的计算机关联。
这里有另外一个例子。该示例中,选择使用NPIPE协议,调试器使用KD而不是WinDbg。第一个用户选择了管道名。可以是任意字母或和数字组成的字符串——本例中为"KernelPipe"。
E:\Debugging Tools for Windows> set _NT_DEBUG_PORT=com1
E:\Debugging Tools for Windows> kdsrv -t npipe:pipe=KernelPipe
技术人员不清楚所使用的名字,所以他/她在网络上查询管道名:
G:\Debugging Tools> cdb -QR 127.0.0.42
Servers on 127.0.0.42:
Debugger Server - npipe:Pipe=MainPipe
Remote Process Server - npipe:Pipe=AnotherPipe
Remote Kernel Debugger Server - npipe:Pipe=KernelPipe
显示出了三个管道。但是,只有一个是KD连接服务器——其他是调试服务器和用户模式进程服务器。所以可以用下面的命令来启动灵巧客户端:
G:\Debugging Tools> kd -k kdsrv:server=@{npipe:server=127.0.0.42,pipe=KernelPipe},trans=@{com:baud=57600} -y SymbolPath
注意,虽然设置了波特率,但是没有设置端口。这使得调试器默认使用由KdSrv运行的机器上的_NT_DEBUG_PORT 环境变量指定的端口。
控制KD连接服务器会话
当远程会话开始之后,灵巧客户端就可以像在KD连接服务器上调试目标机一样。除了路径是按照灵巧客户端所在计算机设置之外,所有命令也按同样的方式使用。
使用WinDbg作为灵巧客户端
当WinDbg启动作为KD连接服务器的灵巧客户端之后,可以使用Debug | Stop Debugging命令来结束调试会话。这时,WinDbg会进入静止模式并且不再连接到KD连接服务器。所有之后的调试都在WinDbg运行的计算机上进行。不能使用File | Kernel Debug 重新连接到KD连接服务器上——只能通过命令行来连接。
结束调试会话
KD或WinDBg可以使用常规办法退出或结束调试会话。查看结束调试会话获得详细信息。KD连接服务器会继续存在并可以被使用任意多次。 (也可以同时被任意多个调试会话使用。)
在每一方的计算机上都可以终止KD连接服务器。使用.endpsrv (End Process Server) 命令从灵巧客户端终止它。在KD连接服务器所在的计算机上,可以使用进程管理器终止kdsrv.exe进程。
如果希望KD连接服务器自己结束,可以使用任务管理器(Task Manager)结束它,或者在灵巧客户端使用.endpsrv (End Process Server) 命令。
cc682/NetRoc
http://netroc682.spaces.live.com/
其他远程调试方法
本节包含:
进程服务器(用户模式)
KD连接服务器(内核模式)
转发器
高级远程调试
注意 可以从内核调试器控制CDB(或NTSD)。这是另一种远程调试的形式。查看从内核调试器控制用户模式调试器获取更多细节。
进程服务器(用户模式)
通过进程服务器进行的远程调试需要在服务器上运行一个称为进程服务器的小应用程序。之后用户模式调试器在客户端上启动。由于该调试器会承担所有实际的操作,所以它被称为灵巧客户端(smart client)。
Windows调试工具包包含一个用户模式的进程服务器名为DbgSrv (dbgsrv.exe) 。
两台计算机不需要运行相同版本的Windows;他们可以运行Windows NT、Windows 2000或之后版本的Windows。但是,客户端使用的调试器和服务器端使用的DbgSrv必须是来自相同版本的Windows调试工具包中的二进制文件。该方法不能用于调试dump文件。
要设置该远程会话,要先设置进程服务器然后再激活灵巧客户端。任意数量的灵巧客户端可以通过单个进程服务器进行操作—这些调试会话会彼此分割不会相互影响。当某个调试会话结束时,进程服务器可以继续运行并且用于新的调试会话。
本节包含:
激活进程服务器
搜索进程服务器
激活灵巧客户端
进程服务器示例
控制进程服务器会话
激活进程服务器
Windows调试工具包中包含的进程服务器称为DbgSrv (dbgsrv.exe)。
DbgSrv支持几种传输协议:命名管道(NPIPE)、TCP、COM端口、安全管道(SPIPE)和安全套接字层(SSL)。SPIPE和SSL协议仅在Windows 2000 和之后的系统中支持。
DbgSrv 命令行语法依赖于所使用的协议。有下面一些不同的形式:
dbgsrv -t npipe:pipe=PipeName[,hidden][,password=Password] [-x | [-sifeo Executable] -c[s] AppCmdLine]
dbgsrv -t tcp:port=Socket[,hidden][,password=Password][,ipversion=6] [-x | [-sifeo Executable] -c[s] AppCmdLine]
dbgsrv -t tcp:port=Socket,clicon=Client[,password=Password][,ipversion=6] [-x | [-sifeo Executable] -c[s] AppCmdLine]
dbgsrv -t com:port=COMPort,baud=BaudRate,channel=COMChannel[,hidden][,password=Password] [-x | [-sifeo Executable] -c[s] AppCmdLine]
dbgsrv -t spipe:proto=Protocol,{certuser=Cert|machuser=Cert},pipe=PipeName[,hidden][,password=Password] [-x | [-sifeo Executable] -c[s] AppCmdLine]
dbgsrv -t ssl:proto=Protocol,{certuser=Cert|machuser=Cert},port=Socket[,hidden][,password=Password] [-x | [-sifeo Executable] -c[s] AppCmdLine]
dbgsrv -t ssl:proto=Protocol,{certuser=Cert|machuser=Cert},port=Socket,clicon=Client[,password=Password] [-x | [-sifeo Executable] -c[s] AppCmdLine]
上面命令中的参数有以下这些可能值:
PipeName
当使用NPIPE或SPIPE协议时,PipeName 是用作管道名的字符串。每个管道名都应该标识一个唯一的进程服务器。如果尝试重复使用管道名,会收到一条错误信息。PipeName 不能包含空格或引号。PipeName 可以包含数字的printf 形式格式化代码,例如%x 或%d。进程服务器会将它替换为DbgSrv的进程ID。第二个这样的代码会被替换为DbgSrv的线程ID。
Socket
当使用TCP或SSL时,Socket 是套接字的端口号。也可以用一个冒号来指定端口范围(例如port=2:7)。DbgSrv会检查该范围内的端口是否可用。如果找到了一个空闲端口并且没有出现错误,进程服务器会被创建起来。灵巧客户端需要指定连接到服务器的实际端口号。 (如果使用了clicon参数来进行反向连接,灵巧客户端也可以按这种方式指定端口范围,而进程服务器必须指定实际使用的端口。)
clicon=Client
使用TCP或SSL协议并指定clicon 参数时,会打开反向连接。这意味着进程服务器会试图连接调试客户端,而不是由灵巧客户端来发起连接。这在当防火墙阻止通常方向上的连接时会比较有效。客户端指定灵巧客户端所在的计算机的网络名或IP地址。两个反斜杠(\\)是可选的。
如果使用进程服务器来寻找特定客户端的方式,就不能将多个客户端连接到一个服务器。如果连接被拒绝或丢失,必须重起进程服务器。当使用-QR命令显示所有活动服务器时,反向连接的进程服务器不会被列举出来。
注意 使用clicon时,最好在进程服务器创建之前启动灵巧客户端,但是正常的顺序(服务器在客户端之前)也可以使用。
COMPort
使用COM协议时,COMPort 指定使用的COM端口。前缀"COM"是可选的—例如,"com2" 和"2"都可以使用。
BaudRate
使用COM协议时,BaudRate 指定连接使用的波特率。硬件所支持的所有波特率都可以使用。
COMChannel
如果使用COM协议,COMChannel 指定和调试客户端通信所使用的COM频道。它可以是0到254之间的任意值(包含0和254)。单个COM端口可以通过使用不同的频道号来支持多个连接。 (这和调试电缆使用COM的情况不同—那种情况下COM端口不能使用频道。)
Protocol
(Windows 2000 和之后的系统) 如果使用SSL或SPIPE,Protocol 指定安全频道(S-Channel)协议。它可以是tls1、pct1、ssl2或ssl3。
Cert
(Windows 2000和之后的系统) 如果使用SSL或SPIPE协议,Cert 用于指定证书。它可以是证书名或证书的特征码(由证书管理机构提供的16进制数字字符串)。如果使用certuser=Cert 形式的语法,调试器会在系统存储中查找证书(默认存储位置)。如果使用machuser=Cert 的语法,调试器在机器存储中查找证书。指定的证书必须支持服务器验证。
hidden
避免该服务器在使用-QR命令行选项显示所有活动服务器时被显示出来。
password=Password
要求灵巧客户端在连接到进程服务器时提供指定的密码。Password 可以是任何字母和数字组成的字符串。
注意 在TCP、NPIPE或COM协议下使用密码仅提供低程度的保护,因为密码不会被加密。当密码和SSL或SPIPE协议一起使用时,会被加密。如果需要安全的远程会话,必须使用SSL或SPIPE协议!
ipversion=6
(仅Debugging Tools for Windows 6.6.07 和更老的版本) 用TCP连接到Internet时强制调试器使用IPV6而不是V4。在Windows Vista和之后版本中,调试器会尝试默认使用IP v6
,所以没有必要使用该选项。
-x
忽略后面的命令行。在从一个可能在命令行中添加不需要的文本的程序启动DbgSrv时该选项很有用。
-sifeo Executable
挂起指定映像的映像文件执行选项(Image File Execution Option (IFEO))值。Executable需要包含可执行映像的文件名和扩展名。 -sifeo 选项使得DbgSrv可以设置成为使用--c 选项启动的映像的IFEO调试器,而不会因为IFEO设置造成递归请求。
-c
使DbgSrv创建一个新进程。可以用它来创建打算调试的进程。但是,该选项本身不会使得新进程被调试;必须找到它的PID并在启动灵巧客户端调试该进程时使用-p选项指定。
s
使得新创建的进程被立即挂起。如果使用该选项,建议使用CDB作为灵巧客户端。进程服务器启动之后,需要找出新进程的PID。然后就可以和-p PID一样,用-pr或-pb命令行选项来启动调试器了。
AppCmdLine
指定被创建进程的完整命令行。AppCmdLine 可以是Unicode或ASCII字符串,并且可以包含任何可打印字符。-c[s]参数后的任何文本都会被认为是AppCmdLine字符串的内容。
可以在计算机上启动任意数量的进程服务器。但是通常这是不需要的,因为一个进程服务器就可以被任意数量的灵巧客户端使用(每个都在不同的调试会话中)。
搜索进程服务器
可以用KC、CDB和-QR命令行选项来获得某个网络服务器上运行的所有可用的进程服务器列表。
这个列表中可能包含已经不存在但是没有被完全关闭的服务器—连接到这样的服务器会产生一条错误信息。该列表也包含调试服务器和KD连接服务器。每种情况下的服务器类型都会被指明。
使用语法如下:
Debugger -QR \\Server
Debugger 可以是KD或CDB—两种情况下输出都一样。反斜杠(\\)开头的Server是可选的。
在WinDbg中,可以使用Connect to Remote Stub Server对话框来查看可用服务器列表。
对于每个调试服务器都会按照用于启动调试客户端的完整的连接字符串形式列出(除了密码会用星号替代之外) 。KD连接服务器也在该列表中。查看File | Connect to Remote Stub获得详细信息。
激活灵巧客户端
一旦DbgSrv进程服务器被激活,就可以在另外一台计算机上启动一个灵巧客户端开始调试会话。
有两种不同的启动灵巧客户端的方法:使用-premote 命令行选项来启动CDB或WinDbg;或者使用WinDbg图形界面。
灵巧客户端使用的协议必须和进程服务器匹配。启动灵巧客户端的常规语法由使用的协议决定。有下面一些选择:
Debugger -premote npipe:server=Server,pipe=PipeName[,password=Password] [Options]
Debugger -premote tcp:server=Server,port=Socket[,password=Password][,ipversion=6] [Options]
Debugger -premote tcp:clicon=Server,port=Socket[,password=Password][,ipversion=6] [Options]
Debugger -premote com:port=COMPort,baud=BaudRate,channel=COMChannel[,password=Password] [Options]
Debugger -premote spipe:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,pipe=PipeName[,password=Password] [Options]
Debugger -premote ssl:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,port=Socket[,password=Password] [Options]
Debugger -premote ssl:proto=Protocol,{certuser=Cert|machuser=Cert},clicon=Server,port=Socket[,password=Password] [Options]
要使用图形界面连接到进程服务器,WinDbg必须在静止模式下—它必须以没有命令行参数的形式启动,或必须中止已有的调试会话。选择File | Connect to Remote Stub菜单命令。当Connect to Remote Stub Server 对话框出现后,在Connection string 文本框中输入下面的字符串之一:
npipe:server=Server,pipe=PipeName[,password=Password]
tcp:server=Server,port=Socket[,password=Password][,ipversion=6]
tcp:clicon=Server,port=Socket[,password=Password][,ipversion=6]
com:port=COMPort,baud=BaudRate,channel=COMChannel[,password=Password]
spipe:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,pipe=PipeName[,password=Password]
ssl:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,port=Socket[,password=Password]
ssl:proto=Protocol,{certuser=Cert|machuser=Cert},clicon=Server,port=Socket[,password=Password]
另外,也可以使用Browse 来定位激活的进程服务器。查看File | Connect to Remote Stub获取详细信息。
上面命令中的参数有下面一些可能的值:
Debugger
可以是CDB或WinDbg。
Server
这是创建进程服务器的计算机的网络名字或IP地址。两个反斜杠(\\) 在命令行中是可选的,但是在WinDbg对话框中不能使用。
PipeName
如果使用NPIPE或SPIPE,PipeName 是进程服务器创建时设定的管道名。
Socket
如果使用TCP或SSL协议,Socket 和进程服务器创建时指定的套接字端口号一样。
clicon
指定进程服务器将试图通过反向连接来连接到灵巧客户端。客户端当且仅当服务器也使用clicon时才能使用clicon。大多数情况下,使用反向连接时,灵巧客户端应比进程服务器先启动。
COMPort
如果使用COM协议,COMPort 指定使用的COM端口。前缀"COM"是可选的—例如, "com2" 和"2"都可以使用。
BaudRate
如果使用COM协议,BaudRate 必须和进程服务器创建时选择的波特率匹配。
COMChannel
如果使用COM协议,COMChannel 必须和进程服务器创建时选择的频道号匹配。
Protocol
(Windows 2000 和之后的系统) 如果使用SSL或SPIPE协议,Protocol 必须和进程服务器创建时选择的安全协议匹配。
Cert
(Windows 2000 和之后的系统) 如果使用SSL或SPIPE协议,必须和进程服务器创建时指定一样的certuser=Cert 或machuser=Cert 参数。
Password
如果进程服务器创建时使用了密码,创建灵巧客户端时必须使用Password 。它必须和原始密码匹配。密码是大小心敏感的。如果密码错误,会出现"Error 0x80004005." 错误。
ipversion=6
(仅Debugging Tools for Windows 6.6.07 和之前的版本) 强制调试器在使用TCP连接到Internet时使用IP v6而不是v4。在Windows Vista和之后的版本中,调试器尝试默认使用IP v6,所以不再需要该选项。
Options
任何其它命令行参数都可以放在这里。查看命令行选项获得完整列表。如果使用CDB,这里必须指定要调试的进程。如果使用WinDbg,可以在命令行或者通过图形界面指定要调试的进程。
由于进程服务器类似于灵巧客户端的网关一样工作,其他的选项和在同一台机器上调试用户模式程序时一样。 查看Starting the Debugger获得操作规程。
如果在.attach (Attach to Process) 或.create (Create Process)命令使用-premote选项,需要的参数和上面列出的相同。
进程服务器示例
假设一个人在一台名为\\BOX17的计算机上运行一个应用程序。该程序有一些问题,但是调试技术人员在其他地方。
第一个人使用DbgSrv在\\BOX17上设置进程服务器。目标应用程序的进程ID为122。使用TCP协议,套接字段口号为1025。用如下命令启动服务器:
E:\Debugging Tools for Windows> dbgsrv -t tcp:port=1025
在另一台计算机上,技术人员决定使用WinDbg作为灵巧客户端。它可以用如下命令启动:
G:\Debugging Tools> windbg -premote tcp:server=BOX17,port=1025 -p 122
这里有另一个例子。该例子中使用NPIPE,调试器使用CDB而不是WinDbg。第一个用户选择一个管道名。它可以是任何字母或数字—这个例子中为" AnotherPipe "。使用如下命令启动调试服务器:
E:\Debugging Tools for Windows> dbgsrv -t npipe:pipe=AnotherPipe
技术人员不确定使用了什么名字,所以他/她在网络上查询管道名:
G:\Debugging Tools> cdb -QR \\BOX17
Servers on \\BOX17:
Debugger Server - npipe:Pipe=MainPipe
Remote Process Server - npipe:Pipe=AnotherPipe
显示了两个管道。但是,只有一个是进程服务器—另一个是调试服务器,所以不是需要的那个。所以AnotherPipe肯定是正确的名字。然后可以使用下面的命令来启动灵巧客户端:
G:\Debugging Tools> cdb -premote npipe:server=BOX17,pipe=AnotherPipe -v sol.exe
关于使用进程服务器的更复杂的示例,查看居中的符号。
控制进程服务器会话
当远程会话开始之后,灵巧客户端就可以像在单台机器上调试目标程序一样使用。除了路径是按照灵巧客户端所在计算机设置之外,所有命令也按同样的方式使用。
使用WinDbg作为灵巧客户端
当WinDbg启动作为用户模式进程服务器的灵巧客户端之后,它会一直保持对该服务器的附加状态。如果调试会话结束了, File | Attach to a Process菜单命令或.tlist (List Process IDs)命令可以显示进程服务器所在的计算机上所有运行的进程。WinDbg 可以附加到任意进程上。
File | Open Executable命令不能使用。只能通过WinDbg命令行来创建一个新进程。
这种情况下,WinDbg不能调试本机上的进程,也不能开始内核调试会话。
结束会话
CDB或WinDbg可以通过常规方法退出或结束调试会话。查看结束调试会话获得详细信息。进程服务器会继续存在并可以被使用任意多次。 (也可以同时被任意多个调试会话使用。)
在每一方的计算机上都可以终止进程服务器。使用.endpsrv (End Process Server) 命令从灵巧客户端终止它。在进程服务器所在的计算机上,可以使用进程管理器终止dbgsrv.exe 进程。