home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

日記/2010/06/10/PEファイルの事前バインド情報を表示するPython (v1)

日記/2010/06/10/PEファイルの事前バインド情報を表示するPython (v1)

日記 / 2010 / 06 / 10 / PEファイルの事前バインド情報を表示するPython (v1)
id: 676 所有者: msakamoto-sf    作成日: 2010-06-10 12:04:38
カテゴリ: Python Windows 

日記/2010/06/09/PEファイルのインポート情報を表示するPython(一区切り) の続きで、事前バインド情報を表示するようにしてみた。
事前バインド情報はDLL一つにつきIMAGE_BOUND_IMPORT_DESCRIPTOR構造体一つ分として記録される。事前に計算されたアドレス値はIAT(FirstThunk)に書き込まれる。IMAGE_BOUND_IMPORT_DESCRIPTOR構造体ではバインドされたDLL名やタイムスタンプ、転送(forward)シンボルの有無が記録されている。


ソース全体

show_bound_imports.py:

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

解説

セクションのコピーまでは 日記/2010/06/09/PEファイルのエクスポート情報を表示するPython を初めとするこれまでのシリーズと同様。
ただし 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の数だけ配列として並ぶ。
図にすると数のような構造になっている:

+------------------------------------------+
| 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           |
+------------------------------------------+
...

上図のような構造を取り出しているループが、次のコードとなる。

# 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からのオフセットになっている。その点を考慮してダンプ出力をしているのが、次のソースコードとなる。

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

実行例

C言語系/memos/VC++/06, DLLの事前バインド(BindImageEx()) のサンプルで、"EDITBIN /BIND"でバインドしたmain.exeを使ってみる。

> 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

プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-06-10 12:04:58
md5:138a85ccc7fbd41a9f952ab9f73154e9
sha1:f5b5a3f4862025bce3bcdfc059fd3ca80ba1c618
コメント
コメントを投稿するにはログインして下さい。