符号文件
编译器和链接器在创建二进制镜像文件(诸如exe、dll、sys)时,伴生的后缀名为(".dbg",".sym",".pdb")的包含镜像文件编译、链接过程中生成的符号信息的文件称为符号文件。
具体来说,符号信息包括如下内容:
1 // 全局变量(类型、名称、地址) 2 // 局部变量(类型、名称、地址) 3 // 函数(名称、原型、地址) 4 // 结构体类型定义
符号路径
符号路径是调试器寻找符号文件的方向,它可以是本地文件夹路径、可访问的UNC路径、或者是符号服务器路径。符号服务器有一套命名规则,使得调试软件能够正确找到需要的符号文件。为了降低网络访问的成本,WinDbg会将从服务器上下载到的符号文件,保存在本地缓存中,以后调试器需要符号文件的时候,先从缓存中寻找,找不到的时候再到服务器上下载。
设置符号路径
WinDbg允许用户指定一个或多个目录来存放符号文件,并使用环境变量"_NT_SYMBOL_PATH"来指向这些目录的位置。这样WinDbg在启动时就会自动到这些目录中搜索相应的符号文件。对操作系统内部模块的符号文件,你可以在微软官方网站上下载符号文件包(现在不提供离线下载了)。但是操作系统经常升级会导致系统中很多模块的实际版本与已经发布的符号文件无法匹配,因此,实际使用过程中最常用的方式是根据使用的模块版本访问微软提供的符号服务器下载(http://msdl.microsoft.com/download/symbols)。
手动设置:
从微软服务器下载符号文件,并将文件保存到本地目录:"C:\Symbols"。
命令设置:
// 显示当前的路径设置 0:000> .sympath Symbol search path is: SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols Expanded Symbol search path is: srv*c:\symbols*http://msdl.microsoft.com/download/symbols // 新增路径设置 0:000> .sympath SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols Symbol search path is: SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols Expanded Symbol search path is: srv*c:\symbols*http://msdl.microsoft.com/download/symbols // 覆盖原来的路径设置 0:000> .sympath C:\NewSymbols Symbol search path is: C:\NewSymbols Expanded Symbol search path is: c:\newsymbols // 追加一个新路径设置 0:000> .sympath+ C:\NewSymbols2 Symbol search path is: C:\NewSymbols;C:\NewSymbols2 Expanded Symbol search path is: c:\newsymbols;c:\newsymbols2 // 要注意的是,使用.sympath改变或新增符号路径后,符号文件并不会自动更新,应再执行.reload命令手动更新。 0:000> .reload Reloading current modules .....
符号选项
修改符号选项可以更好的使用其它符号功能。
// 显示当前设置:.symopt 0:000> .symopt Symbol options are 0x30236: 0x00000002 - SYMOPT_UNDNAME 0x00000004 - SYMOPT_DEFERRED_LOADS 0x00000010 - SYMOPT_LOAD_LINES 0x00000020 - SYMOPT_OMAP_FIND_NEAREST 0x00000200 - SYMOPT_FAIL_CRITICAL_ERRORS 0x00010000 - SYMOPT_AUTO_PUBLICS 0x00020000 - SYMOPT_NO_IMAGE_SEARCH // 增加选项:.symopt+ Flags 0:000> .symopt+ 1 Symbol options are 0x30237: 0x00000001 - SYMOPT_CASE_INSENSITIVE //新增加选项 0x00000002 - SYMOPT_UNDNAME 0x00000004 - SYMOPT_DEFERRED_LOADS 0x00000010 - SYMOPT_LOAD_LINES 0x00000020 - SYMOPT_OMAP_FIND_NEAREST 0x00000200 - SYMOPT_FAIL_CRITICAL_ERRORS 0x00010000 - SYMOPT_AUTO_PUBLICS 0x00020000 - SYMOPT_NO_IMAGE_SEARCH // 删除选项:.symopt- Flags 0:000> .symopt- 1 Symbol options are 0x30236: 0x00000002 - SYMOPT_UNDNAME 0x00000004 - SYMOPT_DEFERRED_LOADS 0x00000010 - SYMOPT_LOAD_LINES 0x00000020 - SYMOPT_OMAP_FIND_NEAREST 0x00000200 - SYMOPT_FAIL_CRITICAL_ERRORS 0x00010000 - SYMOPT_AUTO_PUBLICS 0x00020000 - SYMOPT_NO_IMAGE_SEARCH
各Flage含义如下:
//值 可读名称 描述 0x1 SYMOPT_CASE_INSENSITIVE 符号名称不区分大小写 0x2 SYMOPT_UNDNAME 符号名称未修饰 0x4 SYMOPT_DEFERRED_LOADS 延迟加载 0x8 SYMOPT_NO_CPP 关闭C++转换,C++中的::符号将以__显示 0x10 SYMOPT_LOAD_LINES 从源文件中加载行号 0x20 SYMOPT_OMAP_FIND_NEAREST 如果由于编译器优化导致找不到对应的符号,就以最近的一个符号代
替之 0x40 SYMOPT_LOAD_ANYTHING 使得符号匹配的时候,匹配原则较松散,不那么严格。 0x80 SYMOPT_IGNORE_CVREC 忽略镜像文件头中的CV记录 0x100 SYMOPT_NO_UNQUALIFIED_LOADS 只在已加载模块中搜索符号,如果搜索符号失败,不会自动加载新模
块。 0x200 SYMOPT_FAIL_CRITICAL_ERRORS 不显示文件访问错误对话框。 0x400 SYMOPT_EXACT_SYMBOLS 进行最严格的符号文件检查,只要有微小的差异,符号文件都不会被
加载。 0x800 SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 允许从内存的一个绝对地址处读取符号信息。 0x1000 SYMOPT_IGNORE_NT_SYMPATH 忽视在环境变量中设置的符号路径,也忽视被调试进程的执行路径。
也就是说,当搜索符号文件的时候,不会从这些路径中搜索。 0x2000 SYMOPT_INCLUDE_32BIT_MODULES 让运行在安腾系统上的调试器,也枚举32位模块。 0x4000 SYMOPT_PUBLICS_ONLY 仅搜索符号文件的公共(PUBLIC)符号表,忽略私有符号表。 0x8000 SYMOPT_NO_PUBLICS 不搜索符号文件的公共(PUBLIC)符号表 0x10000 SYMOPT_AUTO_PUBLICS 先搜索pdb文件的私有符号表,如果在其中找到对应的符号,就不再
搜索公共(PUBLIC)符号表,这可以加快搜索速度。 0x20000 SYMOPT_NO_IMAGE_SEARCH 不搜索镜像拷贝 0x40000 SYMOPT_SECURE 安全模式,让调试器尽量不影响到主机。 0x80000 SYMOPT_NO_PROMPTS 不显示符号代理服务器的认证对话框,将导致某些时候无法访问符号
服务器 0x80000000 SYMOPT_DEBUG 显示符号搜索的详细过程和信息
符号加载
WinDbg默认采用延迟模式加载符号,也就是直到符号被使用的时候,才将符号文件加载到调试器中并进行解析。
你可以使用符号选项中介绍的命令来开启和关闭延迟加载设置。
// 关闭延迟加载 0:000> .symopt- 4 //0x00000004 = SYMOPT_DEFERRED_LOADS //开启延迟加载 0:000> .symopt+ 4 //0x00000004 = SYMOPT_DEFERRED_LOADS
立即加载符号文件
// 立即加载指定模块的符号文件 0:000> lm start end module name 00000000`76f00000 00000000`76ffa000 USER32 (deferred) 00000000`77000000 00000000`7711f000 kernel32 (deferred) //未加载kernel32符号文件 00000000`77120000 00000000`772c9000 ntdll (pdb symbols) c:\symbols\ntdll.pdb\6192BFDB9F04442995FFCB0BE95172E12\ntdll.pdb 00000001`3fb40000 00000001`3fd2e52d hisfans (deferred) 000007fe`fd260000 000007fe`fd2cb000 KERNELBASE (deferred) 000007fe`fd650000 000007fe`fd719000 USP10 (deferred) 000007fe`fda60000 000007fe`fdaad000 WS2_32 (deferred) 000007fe`fdab0000 000007fe`fdacf000 sechost (deferred) 000007fe`fdd30000 000007fe`fdd3e000 LPK (deferred) 000007fe`fde20000 000007fe`fdefb000 ADVAPI32 (deferred) 000007fe`fe020000 000007fe`fe091000 SHLWAPI (deferred) 000007fe`fe0a0000 000007fe`fe0a8000 NSI (deferred) 000007fe`fe230000 000007fe`fe297000 GDI32 (deferred) 000007fe`fe2a0000 000007fe`fe33f000 msvcrt (deferred) 000007fe`fe340000 000007fe`fe46d000 RPCRT4 (deferred) 000007fe`fe470000 000007fe`ff1f8000 SHELL32 (deferred) // 加载kernel32模块符号文件 0:000> ld kernel32 Symbols loaded for kernel32
重新加载符号文件
使用.reload命令重新加载符号文件时,默认情况下,调试器不会立刻根据符号路径重新搜索并加载新的符号文件,而是推迟到调试器下一次(再次)使用到此模块或符号时,才会进行更新。
你可以使用/f参数(force),迫使调试器立刻搜索并重新加载新的符号文件,这样就不会等到下一次使用时在更新。
// .reload其它参数说明 /v:将搜索过程中的详细信息都显示出来。 /i:不检查pdb文件的版本信息; /l:只显示模块信息,内核模式下,和“lm n t”命令类似,但显示内容比后者更多,因为包含了用户模块信息; /n:仅重载内核符号,不重载用户符号; /o:强制覆盖符号库中的符号文件,即使版本相同; /d:用户层模式下使用Windbg时的默认选项,重载调试器模块列表中的所有模块; /s:内核模式下使用Windbg时的默认选项,重载系统模块列表中的所有模块,另外,如果调试器在用户模式下运行,要加载内核模块,也必须使用/s选项,否则调试器将只会在调试器模块列表中搜索而导致找不到内核模块; /u:卸载指定模块。如发现当前符号版本不对,使用/u开关先卸载之再重新加载。
验证符号
使用.reload重新加载符号文件的时候,符号文件可能会出现不匹配的情况。可以使用下面的命令验证一个模块的符号文件。
0:000> !chksym ntdll _PEB ntdll.dll Timestamp: 4CE7C8F9 SizeOfImage: 1A9000 pdb: ntdll.pdb pdb sig: 6192BFDB-9F04-4429-95FF-CB0BE95172E1 age: 2 Loaded pdb is c:\symbols\ntdll.pdb\6192BFDB9F04442995FFCB0BE95172E12\ntdll.pdb ntdll.pdb pdb sig: 6192BFDB-9F04-4429-95FF-CB0BE95172E1 age: 2 MATCH: ntdll.pdb and ntdll.dll
搜索符号
全局搜索
// 格式: x [参数] [模块!符号] // 搜索ntdll中的所有OpenProcess函数 0:000> x ntdll!*OpenProcess* 00000000`77171570 ntdll!ZwOpenProcess = <no type information> 00000000`77171570 ntdll!NtOpenProcess = <no type information> 00000000`77171610 ntdll!ZwOpenProcessTokenEx = <no type information> 00000000`771722d0 ntdll!ZwOpenProcessToken = <no type information> 00000000`77171610 ntdll!NtOpenProcessTokenEx = <no type information> 00000000`771722d0 ntdll!NtOpenProcessToken = <no type information> // 也可以加/D参数,使用DML显示 0:000> x /D ntdll!*OpenProcess* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 00000000`77171570 ntdll!ZwOpenProcess = <no type information> 00000000`77171570 ntdll!NtOpenProcess = <no type information> 00000000`77171610 ntdll!ZwOpenProcessTokenEx = <no type information> 00000000`771722d0 ntdll!ZwOpenProcessToken = <no type information> 00000000`77171610 ntdll!NtOpenProcessTokenEx = <no type information> 00000000`771722d0 ntdll!NtOpenProcessToken = <no type information> // 如果想知道某个函数是在哪个模块中定义的,可以使用如下命令 0:000> x *!NtOpenProcess 00000000`77171570 ntdll!NtOpenProcess = <no type information>
附近搜索
如果知道了符号的大概地址,但不能确定确切的符号名称,可以使用"ln"命令(List Nearest)。它根据给定的地址列出附近一定范围内的所有符号。
// 在指定地址0x7c8179f0附近寻找符号 0:000> ln 7c8179f0 (7c8179c3) kernel32!NlsServerInitialize+0x29 | (7c8179fe) kernel32!AllocTables
。。。