关于绑定导入表
0x01 绑定导入表概念
绑定导入表(The Bound Import Directory)。它包含了可以让加载器判断绑定的地址是否合法的信息。描述它的数据结构是IMAGE_BOUND_IMPORT_DESCRIPTOR,目录表就是这种结构的数组,每一项都对应一个被绑定过的DLL。
每当PE装载器装入PE文件时,检查导入表并将相关DLL映射到进程空间地址。然后通过遍历IAT里的IMAGE_THUNK_DATA数组并用导入函数的真实地址替换它,这一步需要很多时间。如果程序员事先能正确预测函数地址,PE装载器就不用每次装入PE文件时都去修正IMAGE_THUNK_DATA值了,绑定导入就是这种思想的产物。
当一个可执行文件被绑定(例如通过绑定程序Visual Studio的Bind.exe)时,IAT中的IMAGE_THUNK_DATA结构被导入函数的实际地址改写了。磁盘中的可执行文件,他们的IAT里有的存放的是相关DLL导出函数的实际内存地址。这样可以使应用程序更快的进程初始化,并且使用较少的存储器。
在执行整个进程期间,bind程序做了两个重要假设:
©当进程初始化时,需要的DLL实际加载到了它们的首选基地址中。
©自从绑定操作执行以来,DLL导出表中引用的符号位置一直没有改变。
当然,如果上面的两个假设中有一个是假的,IAT中所有地址均是无效的,加载器会检查这种情况并做出相应反应,加载器从INT表里获得所需要的信息来解决导入API的地址问题。对于一个可执行文件的装入,INT是不需要的。但是,如果没有,可执行文件是不能被绑定的。微软的链接器似乎总是生成一个INT,但是在很长一段时间里,Borland 的链接器(TLINK)没有这样做, 由Borland生成的文件是不能被绑定的。
由于不知用户运行的是Windows 2000还是Windows XP,无法将系统DLL绑定起来,因此程序安装时是绑定程序的最佳时机。Windows 安装器的BindImage将做这些工作。另外,IMAGEHLP.DLL提供了BindImageEx函数。不管用什么方式,绑定都是一个好主意。如果加载器确定绑定信息是当前的,可执行文件的装入会更快,如果绑定信息已经变得陈旧了,也不会影响程序的运行。
对于加载器来说,使绑定变得有效的一个关键步骤是确定在IAT表中的绑定信息是否是当前的。当一个可执行文件被绑定时,被参考的DLL信息放入了文件中,加载器检查这些信息来做一个快速的綁定有效性验证。
数据目录表( DataDirectory )的第11个成员指向绑定输入。绑定输入以一个IMAGE_BOUND_IMPORT_DESCRIPTOR结构的数组开始,一个绑定可执行文件包含一系列这样的结构,每个IBID结构都指出了一个已经被绑定输入DLL的时间/日期戳。IBID 的结构如下:
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR { DWORD TimeDateStamp; WORD OffsetModuleName; WORD NumberOfModuleForwarderRefs; } IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
TimeDateStamp。包含一个被导入的DLL的时间/日期戳;允许加载器快速判定绑定是不是新的。这个成员必须和要引用的DLL的文件头信息相吻合,否则就会加载器去手动计算新IAT,这种情况一般发生在DLL版本不同时或者DLL映像被重定位时。
OffsetModuleName。包含了以第一个IMAGE_BOUND_IMPORT_DESCRIPTOR为基址,DLL名称字符串(ASCII且以null结束)的偏移(非RVA)。
NumberOfModuleForwarderRefs。是紧接着本结构后的另一个IMAGE_BOUND_FORWARDER_REF结构数组的元素个数。
typedef struct _IMAGE_BOUND_FORWARDER_REF { DWORD TimeDateStamp; WORD OffsetModuleName; WORD Reserved; } IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;
这个结构数组干什么用的?注意到Forwarder这个词,我们在导出表中的函数的转发,就是一个函数自己不实现而是把调用请求转发给另一个DLL中的函数。这里的IMAGE_BOUND_FORWARDER_REF结构就是用来记录接受转发的另一个DLL的校验信息,如果这个DLL还有导出转发,那么在该DLL中也有IMAGE_BOUND_FORWARDER_REF结构描述第三个DLL的校验信息。
当绑定一个API被转发到另一个DLL时,被转发到的DLL的有效性也要被检查。这样,IMAGE_BOUND_FORWARDER_REF和IMAGE_BOUND_IMPORT_DESCRIPTOR结构是交叉存取的。例如链接到HeapAlloc,它被转向到NTDLL中的RtlAllocateHeap,然后对可执行文件运行BIND。在EXE里,已经有一个针对KERNEL32.DLL 的IBID, 它后面跟着一个针对NTDLL.DLL 的IMAGE_BOUND_FORWARDER_REF。紧跟在后面可能是另外的你输入并绑定的针对其他DLL的IBID。Windows目录里的应用程序就是典型的绑定输入结构程序,其IAT已指向相关DLL的函数。
为了方便实现,Microsoft 的一些编译器( 如Visual Studio)都提供了bind.exe 这样的工具,由它检查PE文件的导入表,并用导入函数的真实地址替换IAT里的IMAGE_THUNK_DATA值。当文件装入时,PE装载器必定检查地址的有效性。如果DLL版本不同于PE文件存放的相关信息,或DLE需要重定位那么装载器认为原先计算的地址是无效的,它必定遍历OriginalFirstThunk 指向的数组以获取输入函数新地址,产生一个新的IAT,绑定输入表去除是不会影响程序正常运行的,去除方法是将图10.19中的绑定数据清零,然后再将目录表中的Bound import的RVA与大小清零即可。
总结:
此数据项两个作用:
1、根据TimeDateStamp和OffsetModuleName字段的值我们就可以判断IAT表中的信息是否已经过期。
2、解决DLL转发问题
0x02 绑定导入表分析
以win7x86 下记事本程序为例:
*** wait with pending attach Symbol search path is: srv* Executable search path is: ModLoad: 00120000 00150000 C:\Windows\system32\notepad.exe ModLoad: 77a10000 77b4c000 C:\Windows\SYSTEM32\ntdll.dll ModLoad: 765f0000 766c4000 C:\Windows\system32\kernel32.dll ModLoad: 75a70000 75abb000 C:\Windows\system32\KERNELBASE.dll ModLoad: 761b0000 76250000 C:\Windows\system32\ADVAPI32.dll ModLoad: 76540000 765ec000 C:\Windows\system32\msvcrt.dll ModLoad: 77ba0000 77bb9000 C:\Windows\SYSTEM32\sechost.dll ModLoad: 76d10000 76db1000 C:\Windows\system32\RPCRT4.dll ModLoad: 75fc0000 7600e000 C:\Windows\system32\GDI32.dll ModLoad: 76c00000 76cc9000 C:\Windows\system32\USER32.dll ModLoad: 77b80000 77b8a000 C:\Windows\system32\LPK.dll ModLoad: 766d0000 7676d000 C:\Windows\system32\USP10.dll ModLoad: 76770000 767eb000 C:\Windows\system32\COMDLG32.dll ModLoad: 76250000 762a7000 C:\Windows\system32\SHLWAPI.dll ModLoad: 74800000 7499e000 C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.17514_none_41e6975e2bd6f2b2\COMCTL32.dll ModLoad: 76dc0000 77a0a000 C:\Windows\system32\SHELL32.dll ModLoad: 6fd90000 6fde1000 C:\Windows\system32\WINSPOOL.DRV ModLoad: 76aa0000 76bfc000 C:\Windows\system32\ole32.dll ModLoad: 76a10000 76a9f000 C:\Windows\system32\OLEAUT32.dll ModLoad: 74d70000 74d79000 C:\Windows\system32\VERSION.dll ModLoad: 77c20000 77c3f000 C:\Windows\system32\IMM32.DLL ModLoad: 767f0000 768bc000 C:\Windows\system32\MSCTF.dll ModLoad: 75920000 7592c000 C:\Windows\system32\CRYPTBASE.dll ModLoad: 74560000 745a0000 C:\Windows\system32\uxtheme.dll ModLoad: 74270000 74283000 C:\Windows\system32\dwmapi.dll (a70.f44): Break instruction exception - code 80000003 (first chance) eax=7ffdd000 ebx=00000000 ecx=00000000 edx=77aaf1d3 esi=00000000 edi=00000000 eip=77a44108 esp=0143fc94 ebp=0143fcc0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!DbgBreakPoint: 77a44108 cc int 3 0:001> dt 00120000 _IMAGE_DOS_HEADER ntdll!_IMAGE_DOS_HEADER +0x000 e_magic : 0x5a4d +0x002 e_cblp : 0x90 +0x004 e_cp : 3 +0x006 e_crlc : 0 +0x008 e_cparhdr : 4 +0x00a e_minalloc : 0 +0x00c e_maxalloc : 0xffff +0x00e e_ss : 0 +0x010 e_sp : 0xb8 +0x012 e_csum : 0 +0x014 e_ip : 0 +0x016 e_cs : 0 +0x018 e_lfarlc : 0x40 +0x01a e_ovno : 0 +0x01c e_res : [4] 0 +0x024 e_oemid : 0 +0x026 e_oeminfo : 0 +0x028 e_res2 : [10] 0 +0x03c e_lfanew : 0n224 0:001> dt 00120000+e0 _IMAGE_NT_HEADERS ntdll!_IMAGE_NT_HEADERS +0x000 Signature : 0x4550 +0x004 FileHeader : _IMAGE_FILE_HEADER +0x018 OptionalHeader : _IMAGE_OPTIONAL_HEADER 0:001> dt 1200f8 _IMAGE_OPTIONAL_HEADER ntdll!_IMAGE_OPTIONAL_HEADER +0x000 Magic : 0x10b +0x002 MajorLinkerVersion : 0x9 '' +0x003 MinorLinkerVersion : 0 '' +0x004 SizeOfCode : 0xa800 +0x008 SizeOfInitializedData : 0x22400 +0x00c SizeOfUninitializedData : 0 +0x010 AddressOfEntryPoint : 0x3689 +0x014 BaseOfCode : 0x1000 +0x018 BaseOfData : 0xc000 +0x01c ImageBase : 0x120000 +0x020 SectionAlignment : 0x1000 +0x024 FileAlignment : 0x200 +0x028 MajorOperatingSystemVersion : 6 +0x02a MinorOperatingSystemVersion : 1 +0x02c MajorImageVersion : 6 +0x02e MinorImageVersion : 1 +0x030 MajorSubsystemVersion : 6 +0x032 MinorSubsystemVersion : 1 +0x034 Win32VersionValue : 0 +0x038 SizeOfImage : 0x30000 +0x03c SizeOfHeaders : 0x400 +0x040 CheckSum : 0x39741 +0x044 Subsystem : 2 +0x046 DllCharacteristics : 0x8140 +0x048 SizeOfStackReserve : 0x40000 +0x04c SizeOfStackCommit : 0x11000 +0x050 SizeOfHeapReserve : 0x100000 +0x054 SizeOfHeapCommit : 0x1000 +0x058 LoaderFlags : 0 +0x05c NumberOfRvaAndSizes : 0x10 +0x060 DataDirectory : [16] _IMAGE_DATA_DIRECTORY 0:001> dt 0x120158+0x58 _IMAGE_DATA_DIRECTORY ntdll!_IMAGE_DATA_DIRECTORY +0x000 VirtualAddress : 0x278 +0x004 Size : 0x128 0:001> db 00120000 +0x278 l 130 00120278 7e d9 5b 4a 80 00 00 00-ad da 5b 4a 8d 00 01 00 ~.[J......[J.... 00120288 db da 5b 4a 9a 00 00 00-dd d9 5b 4a a4 00 00 00 ..[J......[J.... 00120298 2f db 5b 4a ae 00 00 00-6f da 5b 4a b9 00 00 00 /.[J....o.[J.... 001202a8 25 da 5b 4a c4 00 00 00-01 db 5b 4a d1 00 00 00 %.[J......[J.... 001202b8 4b db 5b 4a dd 00 00 00-c7 da 5b 4a ea 00 00 00 K.[J......[J.... 001202c8 05 db 5b 4a f4 00 00 00-76 d9 5b 4a 00 01 00 00 ..[J....v.[J.... 001202d8 ca da 5b 4a 0d 01 00 00-db da 5b 4a 9a 00 00 00 ..[J......[J.... 001202e8 2b db 5b 4a 1a 01 00 00-00 00 00 00 00 00 00 00 +.[J............ 001202f8 41 44 56 41 50 49 33 32-2e 64 6c 6c 00 4b 45 52 ADVAPI32.dll.KER 00120308 4e 45 4c 33 32 2e 64 6c-6c 00 4e 54 44 4c 4c 2e NEL32.dll.NTDLL. 00120318 44 4c 4c 00 47 44 49 33-32 2e 64 6c 6c 00 55 53 DLL.GDI32.dll.US 00120328 45 52 33 32 2e 64 6c 6c-00 6d 73 76 63 72 74 2e ER32.dll.msvcrt. 00120338 64 6c 6c 00 43 4f 4d 44-4c 47 33 32 2e 64 6c 6c dll.COMDLG32.dll 00120348 00 53 48 45 4c 4c 33 32-2e 64 6c 6c 00 57 49 4e .SHELL32.dll.WIN 00120358 53 50 4f 4f 4c 2e 44 52-56 00 6f 6c 65 33 32 2e SPOOL.DRV.ole32. 00120368 64 6c 6c 00 53 48 4c 57-41 50 49 2e 64 6c 6c 00 dll.SHLWAPI.dll. 00120378 43 4f 4d 43 54 4c 33 32-2e 64 6c 6c 00 4f 4c 45 COMCTL32.dll.OLE 00120388 41 55 54 33 32 2e 64 6c-6c 00 56 45 52 53 49 4f AUT32.dll.VERSIO 00120398 4e 2e 64 6c 6c 00 00 00-00 00 00 00 00 00 00 00 N.dll...........
分析一下,第一行4a5bd97eh为TimeDateStamp值,80h为当前绑定dll名的相对偏移,00h表示当前dll转发dll个数为0,所以也就不存在IMAGE_BOUND_FORWARDER_REF结构。
0:001> db 00120278+80 001202f8 41 44 56 41 50 49 33 32-2e 64 6c 6c 00 4b 45 52 ADVAPI32.dll.KER
第一行,右半部分,4a5bdaad为TimeDateStamp值,8dh为当前绑定dll名的相对偏移,01h表示当前dll转发dll个数为1,所以后面跟着的8个字节,为IMAGE_BOUND_FORWARDER_REF结构。
0:001> db 00120278+8d 00120305 4b 45 52 4e 45 4c 33 32-2e 64 6c 6c 00 4e 54 44 KERNEL32.dll.NTD
可以看到在IMAGE_BOUND_FORWARDER_REF结构中,4a5bdadbh为TimeDateStamp值,转发dll名字偏移为9d,查看内存发现时ntdll,也就是说调用kernel32.dll中函数时,会转发到ntdll中。
0:001> db 00120278+9a 00120312 4e 54 44 4c 4c 2e 44 4c-4c 00 47 44 49 33 32 2e NTDLL.DLL.GDI32.
观察当前保定导出表内存,发现大部分dll都没有转发,只有kernel32.dll转发到ntdll.dll。
我们接着再以加载的Kernel32.dll为例:
0:001> dt 765f0000 _IMAGE_DOS_HEADER ntdll!_IMAGE_DOS_HEADER +0x000 e_magic : 0x5a4d +0x002 e_cblp : 0x90 +0x004 e_cp : 3 +0x006 e_crlc : 0 +0x008 e_cparhdr : 4 +0x00a e_minalloc : 0 +0x00c e_maxalloc : 0xffff +0x00e e_ss : 0 +0x010 e_sp : 0xb8 +0x012 e_csum : 0 +0x014 e_ip : 0 +0x016 e_cs : 0 +0x018 e_lfarlc : 0x40 +0x01a e_ovno : 0 +0x01c e_res : [4] 0 +0x024 e_oemid : 0 +0x026 e_oeminfo : 0 +0x028 e_res2 : [10] 0 +0x03c e_lfanew : 0n240 0:001> dt 765f00f0 _IMAGE_NT_HEADERS ntdll!_IMAGE_NT_HEADERS +0x000 Signature : 0x4550 +0x004 FileHeader : _IMAGE_FILE_HEADER +0x018 OptionalHeader : _IMAGE_OPTIONAL_HEADER 0:001> dt 765F0108 _IMAGE_OPTIONAL_HEADER ntdll!_IMAGE_OPTIONAL_HEADER +0x000 Magic : 0x10b +0x002 MajorLinkerVersion : 0x9 '' +0x003 MinorLinkerVersion : 0 '' +0x004 SizeOfCode : 0xc5000 +0x008 SizeOfInitializedData : 0xe000 +0x00c SizeOfUninitializedData : 0 +0x010 AddressOfEntryPoint : 0x4cd6f +0x014 BaseOfCode : 0x1000 +0x018 BaseOfData : 0xc0000 +0x01c ImageBase : 0x765f0000 +0x020 SectionAlignment : 0x1000 +0x024 FileAlignment : 0x1000 +0x028 MajorOperatingSystemVersion : 6 +0x02a MinorOperatingSystemVersion : 1 +0x02c MajorImageVersion : 6 +0x02e MinorImageVersion : 1 +0x030 MajorSubsystemVersion : 6 +0x032 MinorSubsystemVersion : 1 +0x034 Win32VersionValue : 0 +0x038 SizeOfImage : 0xd4000 +0x03c SizeOfHeaders : 0x1000 +0x040 CheckSum : 0xdca19 +0x044 Subsystem : 3 +0x046 DllCharacteristics : 0x140 +0x048 SizeOfStackReserve : 0x40000 +0x04c SizeOfStackCommit : 0x1000 +0x050 SizeOfHeapReserve : 0x100000 +0x054 SizeOfHeapCommit : 0x1000 +0x058 LoaderFlags : 0 +0x05c NumberOfRvaAndSizes : 0x10 +0x060 DataDirectory : [16] _IMAGE_DATA_DIRECTORY 0:001> dt 765F0168+58 _IMAGE_DATA_DIRECTORY ntdll!_IMAGE_DATA_DIRECTORY +0x000 VirtualAddress : 0x288 +0x004 Size : 0x408 0:001> db 77a10000 +0x288 77a10288 00 00 00 00 40 00 00 42-00 00 00 00 00 00 00 00 ....@..B........ 77a10298 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a102a8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a102b8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a102c8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a102d8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a102e8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a102f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0:001> db 77a10288+40 77a102c8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a102d8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a102e8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a102f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a10308 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a10318 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a10328 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 77a10338 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
可以发现,加载的kernel32.dll中,虽然存在绑定导入表,但是内容却未空,意味着当前模块没有绑定其他模块。