[[675]] の続きで、事前バインド情報を表示するようにしてみた。 事前バインド情報はDLL一つにつきIMAGE_BOUND_IMPORT_DESCRIPTOR構造体一つ分として記録される。事前に計算されたアドレス値はIAT(FirstThunk)に書き込まれる。IMAGE_BOUND_IMPORT_DESCRIPTOR構造体ではバインドされたDLL名やタイムスタンプ、転送(forward)シンボルの有無が記録されている。 #more|| #outline|| ---- * ソース全体 show_bound_imports.py: #code|python|> import sys, logging, struct, ctypes # {{{ data structure definitions BYTE = ctypes.c_ubyte WORD = ctypes.c_ushort LONG = ctypes.c_long DWORD = ctypes.c_ulong class IMAGE_DOS_HEADER(ctypes.Structure): _fields_ = [ ('e_magic', WORD), # Magic number ('e_cblp', WORD), # Bytes on last page of file ('e_cp', WORD), # Pages in file ('e_crlc', WORD), # Relocations ('e_cparhdr', WORD), # Size of header in paragraphs ('e_minalloc', WORD), # Minimum extra paragraphs needed ('e_maxalloc', WORD), # Maximum extra paragraphs needed ('e_ss', WORD), # Initial (relative) SS value ('e_sp', WORD), # Initial SP value ('e_csum', WORD), # Checksum ('e_ip', WORD), # Initial IP value ('e_cs', WORD), # Initial (relative) CS value ('e_lfarlc', WORD), # File address of relocation table ('e_ovno', WORD), # Overlay number ('e_res', WORD * 4), # Reserved words ('e_oemid', WORD), # OEM identifier (for e_oeminfo) ('e_oeminfo', WORD), # OEM information; e_oemid specific ('e_res2', WORD * 10), # Reserved words ('e_lfanew', LONG), # File address of new exe header ] class IMAGE_FILE_HEADER(ctypes.Structure): _fields_ = [ ('Machine', WORD), ('NumberOfSections', WORD), ('TimeDateStamp', DWORD), ('PointerToSymbolTable', DWORD), ('NumberOfSymbols', DWORD), ('SizeOfOptionalHeader', WORD), ('Characteristics', WORD), ] class IMAGE_DATA_DIRECTORY(ctypes.Structure): _fields_ = [ ('VirtualAddress', DWORD), ('Size', DWORD), ] IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16 IMAGE_DIRECTORY_ENTRY_EXPORT = 0 # Export Directory IMAGE_DIRECTORY_ENTRY_IMPORT = 1 # Import Directory IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 # Resource Directory IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 # Exception Directory IMAGE_DIRECTORY_ENTRY_SECURITY = 4 # Security Directory IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 # Base Relocation Table IMAGE_DIRECTORY_ENTRY_DEBUG = 6 # Debug Directory IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 # Architecture Specific Data IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 # RVA of GP IMAGE_DIRECTORY_ENTRY_TLS = 9 # TLS Directory IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 # Load Configuration Directory IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 # Bound Import Directory in headers IMAGE_DIRECTORY_ENTRY_IAT = 12 # Import Address Table IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 # Delay Load Import Descriptors IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 # COM Runtime descriptor class IMAGE_OPTIONAL_HEADER(ctypes.Structure): _fields_ = [ ('Magic', WORD), ('MajorLinkerVersion', BYTE), ('MinorLinkerVersion', BYTE), ('SizeOfCode', DWORD), ('SizeOfInitializedData', DWORD), ('SizeOfUninitializedData', DWORD), ('AddressOfEntryPoint', DWORD), ('BaseOfCode', DWORD), ('BaseOfData', DWORD), ('ImageBase', DWORD), ('SectionAlignment', DWORD), ('FileAlignment', DWORD), ('MajorOperatingSystemVersion', WORD), ('MinorOperatingSystemVersion', WORD), ('MajorImageVersion', WORD), ('MinorImageVersion', WORD), ('MajorSubsystemVersion', WORD), ('MinorSubsystemVersion', WORD), ('Win32VersionValue', DWORD), ('SizeOfImage', DWORD), ('SizeOfHeaders', DWORD), ('CheckSum', DWORD), ('Subsystem', WORD), ('DllCharacteristics', WORD), ('SizeOfStackReserve', DWORD), ('SizeOfStackCommit', DWORD), ('SizeOfHeapReserve', DWORD), ('SizeOfHeapCommit', DWORD), ('LoaderFlags', DWORD), ('NumberOfRvaAndSizes', DWORD), ('DataDirectory', IMAGE_DATA_DIRECTORY * IMAGE_NUMBEROF_DIRECTORY_ENTRIES ), ] class IMAGE_NT_HEADERS(ctypes.Structure): _fields_ = [ ('Signature', DWORD), ('FileHeader', IMAGE_FILE_HEADER), ('OptionalHeader', IMAGE_OPTIONAL_HEADER), ] IMAGE_SIZEOF_SHORT_NAME = 8 class IMAGE_SECTION_HEADER_MISC(ctypes.Union): _fields_ = [ ('PhysicalAddress', DWORD), ('VirtualSize', DWORD), ] class IMAGE_SECTION_HEADER(ctypes.Structure): _fields_ = [ ('Name', BYTE * IMAGE_SIZEOF_SHORT_NAME), ('Misc', IMAGE_SECTION_HEADER_MISC), ('VirtualAddress', DWORD), ('SizeOfRawData', DWORD), ('PointerToRawData', DWORD), ('PointerToRelocations', DWORD), ('PointerToLinenumbers', DWORD), ('NumberOfRelocations', WORD), ('NumberOfLinenumbers', WORD), ('Characteristics', DWORD), ] class IMAGE_IMPORT_DESCRIPTOR_MISC(ctypes.Union): _fields_ = [ # 0 for terminating null import descriptor ('Characteristics', DWORD), # RVA to original unbound IAT (PIMAGE_THUNK_DATA) ('OriginalFirstThunk', DWORD), ] class IMAGE_IMPORT_DESCRIPTOR(ctypes.Structure): _fields_ = [ ('Misc', IMAGE_IMPORT_DESCRIPTOR_MISC), # 0 if not bound, # -1 if bound, and real date/time stamp # in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) # O.W. date/time stamp of DLL bound to (Old BIND) ('TimeDateStamp', DWORD), ('ForwarderChain', DWORD), # -1 if no forwarders ('Name', DWORD), # RVA to IAT (if bound this IAT has actual addresses) ('FirstThunk', DWORD), ] class IMAGE_IMPORT_BY_NAME(ctypes.Structure): _fields_ = [ ('Hint', WORD), ('Name', BYTE), ] class IMAGE_THUNK_DATA(ctypes.Union): _fields_ = [ ('ForwarderString', DWORD), # PBYTE ('Function', DWORD), # PDWORD ('Ordinal', DWORD), ('AddressOfData', DWORD), # PIMAGE_IMPORT_BY_NAME ] IMAGE_ORDINAL_FLAG64 = 0x8000000000000000 IMAGE_ORDINAL_FLAG32 = 0x80000000 def IMAGE_ORDINAL64(Ordinal): return (Ordinal & 0xffff) def IMAGE_ORDINAL32(Ordinal): return (Ordinal & 0xffff) def IMAGE_SNAP_BY_ORDINAL64(Ordinal): return ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0) def IMAGE_SNAP_BY_ORDINAL32(Ordinal): return ((Ordinal & IMAGE_ORDINAL_FLAG32) != 0) class IMAGE_EXPORT_DIRECTORY(ctypes.Structure): _fields_ = [ ('Characteristics', DWORD), ('TimeDateStamp', DWORD), ('MajorVersion', WORD), ('MinorVersion', WORD), ('Name', DWORD), ('Base', DWORD), ('NumberOfFunctions', DWORD), ('NumberOfNames', DWORD), ('AddressOfFunctions', DWORD), ('AddressOfNames', DWORD), ('AddressOfNameOrdinals', DWORD), ] class IMAGE_BOUND_IMPORT_DESCRIPTOR(ctypes.Structure): _fields_ = [ ('TimeDateStamp', DWORD), ('OffsetModuleName', WORD), ('NumberOfModuleForwarderRefs', WORD), # Array of zero or more IMAGE_BOUND_FORWARDER_REF follows ] class IMAGE_BOUND_FORWARDER_REF(ctypes.Structure): _fields_ = [ ('TimeDateStamp', DWORD), ('OffsetModuleName', WORD), ('Reserved', WORD), ] # }}} if 2 != len(sys.argv): print 'usage: python %s filename' % sys.argv[0] quit() file_name = sys.argv[1] logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s %(message)s', ) log = logging.getLogger('main') try: f = open(file_name, 'rb') except IOError, e: log.error("open(%s) faileed." % file_name) log.error(e) raise e # read IMAGE_DOS_HEADER f.seek(0, 0) sz = ctypes.sizeof(IMAGE_DOS_HEADER) data = f.read(sz) dos_header = IMAGE_DOS_HEADER() fit = min(len(data), sz) ctypes.memmove(ctypes.addressof(dos_header), data, fit) # read IMAGE_NT_HEADERS f.seek(dos_header.e_lfanew, 0) sz = ctypes.sizeof(IMAGE_NT_HEADERS) data = f.read(sz) pe_header = IMAGE_NT_HEADERS() fit = min(len(data), sz) ctypes.memmove(ctypes.addressof(pe_header), data, fit) file_header = pe_header.FileHeader opt_header = pe_header.OptionalHeader # enumerate IMAGE_SECTION_HEADER array section_headers = [] section_header_sz = ctypes.sizeof(IMAGE_SECTION_HEADER) for i in range(file_header.NumberOfSections): data = f.read(section_header_sz) section_header = IMAGE_SECTION_HEADER() fit = min(len(data), sz) ctypes.memmove(ctypes.addressof(section_header), data, fit) section_headers.append(section_header) # load to memory f.seek(0, 0) file_bytes = f.read() # read all file contents image_buf = ctypes.create_string_buffer(opt_header.SizeOfImage) ctypes.memset(image_buf, 0, opt_header.SizeOfImage) image_buf_ptr = ctypes.addressof(image_buf) # copy headers ctypes.memmove( image_buf_ptr, file_bytes, opt_header.SizeOfHeaders ) # copy section datas for h in section_headers: section_virtual_addr = h.VirtualAddress pointer_to_rawdata = h.PointerToRawData size_of_rawdata = h.SizeOfRawData if pointer_to_rawdata : ctypes.memmove( image_buf_ptr + section_virtual_addr, file_bytes[pointer_to_rawdata:pointer_to_rawdata + size_of_rawdata], size_of_rawdata) # Bound Import Descriptor bid_entry = opt_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] bid_rva = bid_entry.VirtualAddress bid_head = image_buf_ptr + bid_rva bid_sz = ctypes.sizeof(IMAGE_BOUND_IMPORT_DESCRIPTOR) bfr_sz = ctypes.sizeof(IMAGE_BOUND_FORWARDER_REF) offset = bid_head bids = [] while 1: bid = IMAGE_BOUND_IMPORT_DESCRIPTOR() ctypes.memmove( ctypes.addressof(bid), offset, bid_sz) if 0 == bid.TimeDateStamp: break bid.bfrs = [] offset = offset + bid_sz for i in xrange(bid.NumberOfModuleForwarderRefs): bfr = IMAGE_BOUND_FORWARDER_REF() ctypes.memmove( ctypes.addressof(bfr), offset, bfr_sz) offset = offset + bfr_sz bid.bfrs.append(bfr) bids.append(bid) for bid in bids: print "-----------------" mname_offset = bid.OffsetModuleName mname = ctypes.c_char_p(bid_head + mname_offset) print "ModuleName = %s" % mname.value print "TimeDateStamp = %d" % bid.TimeDateStamp print "NumberOfModuleForwarderRefs = %d" % bid.NumberOfModuleForwarderRefs for bfr in bid.bfrs: mname_offset = bfr.OffsetModuleName mname = ctypes.c_char_p(bid_head + mname_offset) print ">>> FWD >>>" print "\tModuleName = %s" % mname.value print "\tTimeDateStamp = %d" % bfr.TimeDateStamp print "\tReserved = 0x%04X" % bfr.Reserved ||< * 解説 セクションのコピーまでは [[674]] を初めとするこれまでのシリーズと同様。 ただし IMAGE_BOUND_IMPORT_DESCRIPTOR構造体、IMAGE_BOUND_FORWARDER_REF構造体の定義を追加している。 ** IMAGE_OPTIONAL_HEADER の DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] から IMAGE_BOUND_IMPORT_DESCRIPTOR配列の先頭アドレスを取得 DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]は、IMAGE_BOUND_IMPORT_DESCRIPTOR配列の先頭アドレス(RVA)を指している。 事前バインドされたDLL一つにつき、一つのIMAGE_BOUND_IMPORT_DESCRIPTOR構造体が用意される。 バインドされたシンボルの内、他のDLLに転送されるものが含まれる場合はIMAGE_BOUND_IMPORT_DESCRIPTOR構造体のNumberOfModuleForwarderRefsに転送されるDLLの数が記録され、IMAGE_BOUND_IMPORT_DESCRIPTOR構造体の直後に転送先DLLの情報が記録されたIMAGE_BOUND_FORWARDER_REF構造体が転送されるDLLの数だけ配列として並ぶ。 図にすると数のような構造になっている: #pre||> +------------------------------------------+ | IMAGE_OPTIONAL_HEADER | | ... | | +---------------------------------------+ | | DataDirectory | | | +------------------------------------+ | | | ... | | | +------------------------------------+ RVA指定 | | | IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT |-----+ | | +------------------------------------+ | | | | ... | | | +---------------------------------------+ | | ... | | +------------------------------------------+ | ... | +------------------------------------------+ | | IMAGE_BOUND_IMPORT_DESCRIPTOR[0] |<----+ | +---------------------------------------+ | | TimeDateStamp | | +---------------------------------------+ | | OffsetModuleName | => "foo.dll" | +---------------------------------------+ | | NumberOfModuleForwarderRefs | => 0 : 転送無し +------------------------------------------+ = IMAGE_BOUND_FORWARDER_REF配列無し | IMAGE_BOUND_IMPORT_DESCRIPTOR[1] | | +---------------------------------------+ | | TimeDateStamp | | +---------------------------------------+ | | OffsetModuleName | => "KERNEL32.dll" | +---------------------------------------+ | | NumberOfModuleForwarderRefs | => 5 : 転送有り +-+------------------------------------------+ = IMAGE_BOUND_FORWARDER_REFが5つ続く | IMAGE_BOUND_FORWARDER_REF[0] | | +---------------------------------------+ | | TimeDateStamp | | +---------------------------------------+ | | OffsetModuleName | => "NTDLL.dll" | +---------------------------------------+ | | Reserved | => 0 +------------------------------------------+ | IMAGE_BOUND_FORWARDER_REF[1] | | +---------------------------------------+ | | TimeDateStamp | | +---------------------------------------+ | | OffsetModuleName | => "xxyy.dll" | +---------------------------------------+ | | Reserved | => 0 +------------------------------------------+ ... +------------------------------------------+ | IMAGE_BOUND_FORWARDER_REF[4] | | +---------------------------------------+ | | TimeDateStamp | | +---------------------------------------+ | | OffsetModuleName | => "yyzz.dll" | +---------------------------------------+ | | Reserved | => 0 +-+----------------------------------------+-+ | IMAGE_BOUND_IMPORT_DESCRIPTOR[2] | | +---------------------------------------+ | | TimeDateStamp | | +---------------------------------------+ | | OffsetModuleName | => "bar.dll" | +---------------------------------------+ | | NumberOfModuleForwarderRefs | +------------------------------------------+ ... ||< 上図のような構造を取り出しているループが、次のコードとなる。 #code|python|> # Bound Import Descriptor bid_entry = opt_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] bid_rva = bid_entry.VirtualAddress bid_head = image_buf_ptr + bid_rva bid_sz = ctypes.sizeof(IMAGE_BOUND_IMPORT_DESCRIPTOR) bfr_sz = ctypes.sizeof(IMAGE_BOUND_FORWARDER_REF) offset = bid_head bids = [] # IMAGE_BOUND_IMPORT_DESCRIPTORのリスト while 1: bid = IMAGE_BOUND_IMPORT_DESCRIPTOR() ctypes.memmove( ctypes.addressof(bid), offset, bid_sz) if 0 == bid.TimeDateStamp: # 終端 break # IMAGE_BOUND_FORWARDER_REFのリストを用意 bid.bfrs = [] # IMAGE_BOUND_IMPORT_DESCRIPTORのサイズ分オフセットをずらす offset = offset + bid_sz for i in xrange(bid.NumberOfModuleForwarderRefs): # NumberOfModuleForwarderRefs分だけ # IMAGE_BOUND_FORWARDER_REF構造体を取得、リストに追加 bfr = IMAGE_BOUND_FORWARDER_REF() ctypes.memmove( ctypes.addressof(bfr), offset, bfr_sz) offset = offset + bfr_sz bid.bfrs.append(bfr) # IMAGE_BOUND_IMPORT_DESCRIPTORのリストに追加 bids.append(bid) ||< ** IMAGE_BOUND_IMPORT_DESCRIPTOR, IMAGE_BOUND_FORWARDER_REF構造体の OffsetModuleName に注意 IMAGE_BOUND_IMPORT_DESCRIPTOR, IMAGE_BOUND_FORWARDER_REF構造体の OffsetModuleName はRVA「ではない」。 最初のIMAGE_BOUND_IMPORT_DESCRIPTOR構造体のアドレス = DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]の指すRVAからのオフセットになっている。その点を考慮してダンプ出力をしているのが、次のソースコードとなる。 #code|python|> for bid in bids: print "-----------------" mname_offset = bid.OffsetModuleName mname = ctypes.c_char_p(bid_head + mname_offset) print "ModuleName = %s" % mname.value print "TimeDateStamp = %d" % bid.TimeDateStamp print "NumberOfModuleForwarderRefs = %d" % bid.NumberOfModuleForwarderRefs for bfr in bid.bfrs: mname_offset = bfr.OffsetModuleName mname = ctypes.c_char_p(bid_head + mname_offset) print ">>> FWD >>>" print "\tModuleName = %s" % mname.value print "\tTimeDateStamp = %d" % bfr.TimeDateStamp print "\tReserved = 0x%04X" % bfr.Reserved ||< ** 実行例 [[669]] のサンプルで、"EDITBIN /BIND"でバインドしたmain.exeを使ってみる。 #pre||> > show_bound_imports.py main.exe ----------------- ModuleName = dll01.dll TimeDateStamp = 1275494178 NumberOfModuleForwarderRefs = 0 ----------------- ModuleName = dll02.dll TimeDateStamp = 1275494182 NumberOfModuleForwarderRefs = 0 ----------------- ModuleName = KERNEL32.dll TimeDateStamp = 1237644444 NumberOfModuleForwarderRefs = 1 >>> FWD >>> ModuleName = NTDLL.DLL TimeDateStamp = 1234176751 Reserved = 0x0000 ||< "dumpbin /imports" の該当する箇所: Header contains the following bound import information: Bound to dll01.dll [4C067F22] Thu Jun 03 00:56:18 2010 Bound to dll02.dll [4C067F26] Thu Jun 03 00:56:22 2010 Bound to KERNEL32.dll [49C4F49C] Sat Mar 21 23:07:24 2009 Contained forwarders bound to NTDLL.DLL [49900AEF] Mon Feb 09 19:52:31 2009