0x01 绑定导入表概念程序员
绑定导入表(The Bound Import Directory)。它包含了可让加载器判断绑定的地址是否合法的信息。描述它的数据结构是IMAGE_BOUND_IMPORT_DESCRIPTOR,目录表就是这种结构的数组,每一项都对应一个被绑定过的DLL。windows
每当PE装载器装入PE文件时,检查导入表并将相关DLL映射到进程空间地址。而后经过遍历IAT里的IMAGE_THUNK_DATA数组并用导入函数的真实地址替换它,这一步须要不少时间。若是程序员事先能正确预测函数地址,PE装载器就不用每次装入PE文件时都去修正IMAGE_THUNK_DATA值了,绑定导入就是这种思想的产物。api
当一个可执行文件被绑定(例如经过绑定程序Visual Studio的Bind.exe)时,IAT中的IMAGE_THUNK_DATA结构被导入函数的实际地址改写了。磁盘中的可执行文件,他们的IAT里有的存放的是相关DLL导出函数的实际内存地址。这样可使应用程序更快的进程初始化,而且使用较少的存储器。数组
在执行整个进程期间,bind程序作了两个重要假设:数据结构
©当进程初始化时,须要的DLL实际加载到了它们的首选基地址中。函数
©自从绑定操做执行以来,DLL导出表中引用的符号位置一直没有改变。工具
固然,若是上面的两个假设中有一个是假的,IAT中全部地址均是无效的,加载器会检查这种状况并作出相应反应,加载器从INT表里得到所须要的信息来解决导入API的地址问题。对于一个可执行文件的装入,INT是不须要的。可是,若是没有,可执行文件是不能被绑定的。微软的连接器彷佛老是生成一个INT,可是在很长一段时间里,Borland 的连接器(TLINK)没有这样作, 由Borland生成的文件是不能被绑定的。spa
因为不知用户运行的是Windows 2000仍是Windows XP,没法将系统DLL绑定起来,所以程序安装时是绑定程序的最佳时机。Windows 安装器的BindImage将作这些工做。另外,IMAGEHLP.DLL提供了BindImageEx函数。无论用什么方式,绑定都是一个好主意。若是加载器肯定绑定信息是当前的,可执行文件的装入会更快,若是绑定信息已经变得陈旧了,也不会影响程序的运行。code
对于加载器来讲,使绑定变得有效的一个关键步骤是肯定在IAT表中的绑定信息是不是当前的。当一个可执行文件被绑定时,被参考的DLL信息放入了文件中,加载器检查这些信息来作一个快速的綁定有效性验证。blog
数据目录表( 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与大小清零便可。
总结:
此数据项两个做用:
一、根据TimeDateStamp和OffsetModuleName字段的值咱们就能够判断IAT表中的信息是否已通过期。
二、解决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中,虽然存在绑定导入表,可是内容却未空,意味着当前模块没有绑定其余模块。