说惯了面向硬件的操作系统,也应该说说操作系统的主要使用对象–用户程序。要深入理解程序的运作过程,则需要了解编译原理、链接、装载和运行库等知识。一般都是IDE封装了这些过程,一个简单的Shift+F5指令封装了“预编译+编译+汇编+链接+装载”全过程,而要想分割独立地控制或挖掘这些子过程,则不得不借助一些工具,如预编译编译程序cl、汇编器as、链接器link、查看工具dumpbin(针对Windows PE文件结构)、binutils工具套件(配合gcc针对ELF文件结构)等。
对于dumpbin,其主要功能是查看.lib.dll.exe等文件或库中包含了那些函数或其他相关的符号信息。
C:> dumpbin
usage: DUMPBIN [options] [files] dumpbin使用方式:dumpbin 选项参数 文件名。
dumpbin选项指令详解
选项 | 备注 |
---|---|
/ALL | 显示出代码反汇编外的所有信息,配合/DISASM可显示反汇编,配合/RAWDATA:NONE可以省略文件中的二进制详细信息(推荐) |
/ARCHIVEMEMBERS | 显示有关库成员对象的最少信息(Summary) |
/CLRHEADER | CLRHEADER 显示有关在任何托管程序中使用的 .NET 头的信息。输出显示 .NET 头及其中各节的位置和大小(以字节计) |
/DEPENDENTS | 显示该模块依赖的DLL名称 |
/DIRECTIVES | 显示.obj文件中编译器为后续链接产生DLL准备的.directive段导出符号信息,用于后续产生导出表 |
/DISASM | 显示代码段的反汇编结果 |
/EXPORTS | 显示该模块导出的符号信息,一般通过_declspec(dllexport)修饰函数或者编写.def模块脚本统一地控制DLL导出符号**【1】** |
/IMPORTS | 显示该模块依赖的DLL列表以及各DLL导入的函数符号集合 (序号ordinal+函数符号名)**【2】** |
/HEADERS | 显示模块的文件头(FILE HEADER和OPTIONAL HEADER)和各段的属性(name\size\address\flags等) |
/FPO | 显示模块框架指针优化FPO记录 |
/LINENUMBERS | 显示COFF行号,如果对象文件是用程序数据库 (/Zi)、C7 兼容 (/Z7) 或仅限行号 (/Zd) 编译的,则它包含行号。如果可执行文件或 DLL 是与生成调试信息 (/DEBUG) 链接的,则它包含 COFF 行号。 |
/LINKERMEMBER[:{1|2}] | 显示库中定义的公共符号。指定参数 1 将按对象顺序显示符号及其偏移量。指定参数 2 将显示对象的偏移量和索引号,然后按字母顺序列出这些符号及每个符号的对象索引。若要两个输出都获得,指定不带数字参数的 /LINKERMEMBER。 |
/LOADCONFIG | 显示模块的 IMAGE_LOAD_CONFIG_DIRECTORY结构 |
/OUT:filename | 显示本次dumpbin命令的结果输出文件名**【3】**,默认情况下,信息将显示到stdout |
/PDBPATH[:VERBOSE]filename | filename为要为其查找匹配.pdb文件的.dll或.exe文件名。VERBOSE(可选)为报告曾尝试在其中定位.pdb文件的所有目录。/PDBPATH将沿调试器搜索.pdb文件的同一路径搜索计算机,并将报告那些.pdb文件(若有)和filename中指定的文件相对应 |
/RAWDATA[:{1|2|4|8|NONE}[,number]] | 此选项显示文件中每节的原始内容。参数说明:1,默认值,内容以十六进制单字节显示,如果内容具有打印的表示形式,则还显示为ASCII字符;2,内容显示为十六进制的2字节值;4,内容显示为十六进制的恶4字节值;8,内容显示为十六进制的8字节值;NONE,取消显示原始数据,此参数对控制/ALL输出很有用;number,显示的行被设置为每行具有number个值的宽度。 |
/RELOCATIONS | 显示本模块中所有的RVA重定位信息 |
/SECTION:section | 显示指定section的所有相关信息,单独列举选项/HEADERS的字段信息 |
/SYMBOLS | 显示COFF PE文件结构的符号表信息(包括段符号)**【4】** |
/UNWINDINFO | 显示该模块(exe和dll)中转储结构化异常处理(SHE)表的展开描述符 |
脚注
[1]:一般推荐使用.def模块定义脚本,这是因为考虑到编译器函数修饰的存在,DLL用C语言编写,默认__cdecl调用规范(此时不进行函数修饰),但Windows下系统API基本都是__stdcall(WINAPI的宏定义)调用规范,故而可以在.def脚本中再次定义函数导出符号的命名方式,从而可以无视编译器带来的函数符号修饰后的古怪命名方式。除了定义导出符号的范围、命名方式、序号外,还可以在.def脚本中定义DLL的堆大小、输出文件名、各个段的属性、堆栈大小、版本号等。
[2]:COFF PE文件结构体系下符号导入导出关系
[3]:采用dumpbin /ALL a.dll > outfile.txt更简单直接
[4]:C:>dumpbin /SYMBOLS TestMath.obj
File Type: COFF OBJECT
COFF SYMBOL TABLE
000 00000000 DEBUG notype Filename | .file
main.cpp
002 000B1FDB ABS notype Static | @comp.id
003 00000000 SECT1 notype Static | .drectve
Section length 26, #relocs 0, #linenums 0, checksum 722C964F
005 00000000 SECT2 notype Static | .text
Section length 23, #relocs 1, #linenums 0, checksum 459FF65F, selection 1 (pick no duplicates)
007 00000000 SECT2 notype () External | _main
008 00000000 UNDEF notype () External | [email protected]@YAXXZ (void __cdecl MyDump(void))
String Table Size = 0x10 bytes
开头的 3 位数字是符号索引/号码。
如果第三列包含 SECTx,则符号在对象文件的那一节中定义。但如果出现 UNDEF,则它不在那个对象中定义并且必须在其他地方被解析。
第五列 (Static, External) 说明符号是否只在那个对象的内部可见,或者是否是公共的(外部可见)。
编号行中的最后一列是符号名(修饰名和未修饰名)。