Hello.dll里面有什么
我们使用文本查看工具进行查看, 显然这些乱码我们完全无法阅读。
所有DLL文件的结构都如下图所示, 由这几部分组成。
DOS Stub和PE Signature
其中的DOS Stub和PE Signature我们可以从上图看到,This program cannot be run in DOS mode
和PE
。我们也可以使用十六进制查看工具进行查看。其中DOS Stub部分的位置是固定的,而PE Signature部分的地址在3C处指明了。
COFF file header
DOS Stub和PE 标识由于是ASCII形式存储在文件中的,所以我们可以通过文本查看工具或者16进制查看工具看到,但是剩下的部分使用文本查看工具看起来就很费劲了,好在微软为我们提供了查看二进制可执行文件的工具——dumpbin.exe。
使用dumpbin /HEADERS Hello.dll
命令就可以将hello.dll的头部信息格式化成我们人类可读的形式并输出到屏幕上了。
我们可以看到FILE HEADER VALUES
部分就是我们的COFF file header了,
这部分告诉我们这个文件是x64架构的机器上的DLL(8665 machine(x64)
);
由两部分组成(2 number of sections
),
然后是一个时间戳,默认标明了这个DLL创建的时间(59BDE631 time date stamp Sat Sep 16 20:04:17 2017
这个实际上可以是任意值,它不一定必须是时间戳,你可以在构建的时候指定它的值);
可选头的大小是0xF0(F0 size of optional header
);
这个文件有2022个字符(2022 characteristics
);
这个文件包含可执行代码(Excutable
,并不能表明这是一个EXE可执行程序,只是说明这是一个能被加载并执行的DLL);
这是一个64位的二进制文件,所以可以处理更大(大于2GB)的地址,32-bit的二进制文件则同(Application can handle large (>2GB) addresses
);
这是一个DLL而不是EXE(DLL
)
可选头文件(Option Header)
可选头文件有两种类型,PE32和PE32+,分别标识32位二进制文件和64位二进制文件。
我们可以看到magic 前边有一个数字(魔数),用来确保链接器正确的解析数据类型;
我们可以在此处看到入口点为空,这就是我们希望看到的,因为我们在链接时使用了/NOENTRY
选项指定它没有入口点;
这个image的首选基地址是0X70000000到0X70002FFF,还有一些对齐信息
image的大小是0x3000 比特(12Kb),头部大小是0x400比特(1Kb),DLL特征字0x160比特
除此之外还有一些其他的扩展信息
还有一些有额外的关于DLL里面有什么的元数据的目录,可以看到这些目录里都有RVA(Relative Virtual Address)和size,
Relative Virtual Address(RVA,虚拟地址):
相对于DLL被加载到内存中的初始地址的偏移量
运行时真实的内存地址 = DLL的基地址 + RVA(Address in Memory = DLL Base Address + RVA)
如果想要计算对应的RVA只需要用下面公式即可
RVA = Address in Memory - DLL Base Address
例如:
- 函数GetGreeting() 在Hello.dll 中的RVA=0x2000,
- Hello.dll 加载到初始地址为0x7000’0000的内存中
可以得出函数GetGreeting()在地址为0x7000‘2000处(0x7000’0000 + 0x2000 = 0x7000‘2000)
Section Header
紧跟着Option Header的是一个接一个的Section Header,Section Header告诉我们如何在DLL中找到实际的数据。
如图所示是Hello.dll中的第一个Section——.text Section(包含代码的代码段),并且它需要映射到内存中以供执行和读取,
我们可以看到虚拟地址为0x1000,这就是上面说的RVA,表明了这部分在DLL被加载时在内存中的位置。virtual size表示这部分占用内存的大小,可以看到我们这个程序代码只占8比特(bytes),除此之外还包含了一些其他信息。
第二个Section——.rdata Section(只读数据段, 可以通过标志位(flags 0x40000040)看出,这个section具有只读权限,不是可执行程序,也不可写),它也需要被映射进内存页中。
数据段还包含了一些其他数据,从上边的option header中我们可以看到Export Directory(0x2040)和Debug Directory(0x2020)的RVA是被包含在.rdata section中的(0x2000到0x20D7)
小结
从Hello.dll的头部信息中我们知道,当Hello.dll加载到进程中时,它将在内存中占用0x3000 bytes(3页)的空间。
- 一页包含头部信息(headers)
- 一页包含 代码段(.text section)
- 一页包好只读数据段(.rdata section)
(这些不是唯一可能的 section,我们可以在其他DLL中找到其他 几种 section)
Hello.dll在一对目录(directories)中有一些额外的元数据
- 一个调试目录(debug Directory)
- 一个导出目录 (export directory)
这些数据包含在 .rdata section 中
查看 Sections的具体内容
我们可以使用dumpbin.exe 方便的查看各个Section的具体内容
Section #1: .text (代码段)
我们使用dumpbin /rawdata /section:.text Hello.dll
命令可以看到代码段以16进制的形式显示到屏幕上,
使用dumpbin /disasm:BYTES /section:.text Hello.dll
命令可以同事查看代码段的反汇编和16进制形式
Section #2: .rdata (只读数据段)
使用dumpbin /rawdata /sectiion:.rdata Hello.dll
命令可以显示16进制形式的只读数据段。
可以看到我们程序中的字符串常量在只读数据段中。我们之前讨论过的两个Directory也在只读数据段中,如图所示,我们用不同的颜色高亮标记了它们。但这也是不可读的,我们完全看不懂这些Directory中到底是什么。
使用命令dumpbin /exports Hello.dll
可以看到DLL的导出表(Export Directory)