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

日記/2010/06/09/PEファイルのエクスポート情報を表示するPython

日記/2010/06/09/PEファイルのエクスポート情報を表示するPython

日記 / 2010 / 06 / 09 / PEファイルのエクスポート情報を表示するPython
id: 674 所有者: msakamoto-sf    作成日: 2010-06-09 18:58:46
カテゴリ: Python Windows 

日記/2010/05/31/PEファイルのインポート情報を表示するPython(作りかけ) から少し寄り道して、エクスポート情報をダンプしてみる。
エクスポート情報は、インポート情報よりも単純な作りになっている。

  1. IMAGE_OPTIONAL_HEADER の DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] から IMAGE_EXPORT_DIRECTORY 構造体を取得
  2. IMAGE_EXPORT_DIRECTORY のAddressOfFunctions, AddressOfNames, AddressOfNameOrdinals をダンプ
    1. AddressOfFunctionsは、エクスポートしたシンボルのアドレス(DWORD)の配列の先頭RVA。NumberOfFunctionsが配列の要素数。
    2. AddressOfNames, AddressOfNameOrdinalsは、エクスポートしたシンボル名と序数の配列の先頭RVA。NumberOfNamesが配列の要素数。
      1. AddressOfNamesの各要素は、シンボル名へのRVA(DWORD)になっている。
      2. AddressOfNameOrdinalsの各要素は、序数(WORD)になっている。

なおエクスポートシンボルが別のDLLに転送(forwarding)される場合は、AddressOfFunctionsの該当シンボルのRVAが実行コードのアドレスではなく、IMAGE_EXPORT_DIRECTORY構造体と同じセクション内のRVAを指し、"モジュール名.シンボル名"で転送先のDLL名とシンボル名を指定するようになっている。


ソース全体

show_exports.py:

  1.  import sys, logging, struct, ctypes
  2.  
  3.  # {{{ data structure definitions
  4.  
  5.  BYTE = ctypes.c_ubyte
  6.  WORD = ctypes.c_ushort
  7.  LONG = ctypes.c_long
  8.  DWORD = ctypes.c_ulong
  9.  
  10.  class IMAGE_DOS_HEADER(ctypes.Structure):
  11.   _fields_ = [
  12.   ('e_magic', WORD), # Magic number
  13.   ('e_cblp', WORD), # Bytes on last page of file
  14.   ('e_cp', WORD), # Pages in file
  15.   ('e_crlc', WORD), # Relocations
  16.   ('e_cparhdr', WORD), # Size of header in paragraphs
  17.   ('e_minalloc', WORD), # Minimum extra paragraphs needed
  18.   ('e_maxalloc', WORD), # Maximum extra paragraphs needed
  19.   ('e_ss', WORD), # Initial (relative) SS value
  20.   ('e_sp', WORD), # Initial SP value
  21.   ('e_csum', WORD), # Checksum
  22.   ('e_ip', WORD), # Initial IP value
  23.   ('e_cs', WORD), # Initial (relative) CS value
  24.   ('e_lfarlc', WORD), # File address of relocation table
  25.   ('e_ovno', WORD), # Overlay number
  26.   ('e_res', WORD * 4), # Reserved words
  27.   ('e_oemid', WORD), # OEM identifier (for e_oeminfo)
  28.   ('e_oeminfo', WORD), # OEM information; e_oemid specific
  29.   ('e_res2', WORD * 10), # Reserved words
  30.   ('e_lfanew', LONG), # File address of new exe header
  31.   ]
  32.  
  33.  class IMAGE_FILE_HEADER(ctypes.Structure):
  34.   _fields_ = [
  35.   ('Machine', WORD),
  36.   ('NumberOfSections', WORD),
  37.   ('TimeDateStamp', DWORD),
  38.   ('PointerToSymbolTable', DWORD),
  39.   ('NumberOfSymbols', DWORD),
  40.   ('SizeOfOptionalHeader', WORD),
  41.   ('Characteristics', WORD),
  42.   ]
  43.  
  44.  class IMAGE_DATA_DIRECTORY(ctypes.Structure):
  45.   _fields_ = [
  46.   ('VirtualAddress', DWORD),
  47.   ('Size', DWORD),
  48.   ]
  49.  
  50.  IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
  51.  
  52.  IMAGE_DIRECTORY_ENTRY_EXPORT = 0 # Export Directory
  53.  IMAGE_DIRECTORY_ENTRY_IMPORT = 1 # Import Directory
  54.  IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 # Resource Directory
  55.  IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 # Exception Directory
  56.  IMAGE_DIRECTORY_ENTRY_SECURITY = 4 # Security Directory
  57.  IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 # Base Relocation Table
  58.  IMAGE_DIRECTORY_ENTRY_DEBUG = 6 # Debug Directory
  59.  IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 # Architecture Specific Data
  60.  IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 # RVA of GP
  61.  IMAGE_DIRECTORY_ENTRY_TLS = 9 # TLS Directory
  62.  IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 # Load Configuration Directory
  63.  IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 # Bound Import Directory in headers
  64.  IMAGE_DIRECTORY_ENTRY_IAT = 12 # Import Address Table
  65.  IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 # Delay Load Import Descriptors
  66.  IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 # COM Runtime descriptor
  67.  
  68.  
  69.  class IMAGE_OPTIONAL_HEADER(ctypes.Structure):
  70.   _fields_ = [
  71.   ('Magic', WORD),
  72.   ('MajorLinkerVersion', BYTE),
  73.   ('MinorLinkerVersion', BYTE),
  74.   ('SizeOfCode', DWORD),
  75.   ('SizeOfInitializedData', DWORD),
  76.   ('SizeOfUninitializedData', DWORD),
  77.   ('AddressOfEntryPoint', DWORD),
  78.   ('BaseOfCode', DWORD),
  79.   ('BaseOfData', DWORD),
  80.   ('ImageBase', DWORD),
  81.   ('SectionAlignment', DWORD),
  82.   ('FileAlignment', DWORD),
  83.   ('MajorOperatingSystemVersion', WORD),
  84.   ('MinorOperatingSystemVersion', WORD),
  85.   ('MajorImageVersion', WORD),
  86.   ('MinorImageVersion', WORD),
  87.   ('MajorSubsystemVersion', WORD),
  88.   ('MinorSubsystemVersion', WORD),
  89.   ('Win32VersionValue', DWORD),
  90.   ('SizeOfImage', DWORD),
  91.   ('SizeOfHeaders', DWORD),
  92.   ('CheckSum', DWORD),
  93.   ('Subsystem', WORD),
  94.   ('DllCharacteristics', WORD),
  95.   ('SizeOfStackReserve', DWORD),
  96.   ('SizeOfStackCommit', DWORD),
  97.   ('SizeOfHeapReserve', DWORD),
  98.   ('SizeOfHeapCommit', DWORD),
  99.   ('LoaderFlags', DWORD),
  100.   ('NumberOfRvaAndSizes', DWORD),
  101.   ('DataDirectory', IMAGE_DATA_DIRECTORY * IMAGE_NUMBEROF_DIRECTORY_ENTRIES ),
  102.   ]
  103.  
  104.  class IMAGE_NT_HEADERS(ctypes.Structure):
  105.   _fields_ = [
  106.   ('Signature', DWORD),
  107.   ('FileHeader', IMAGE_FILE_HEADER),
  108.   ('OptionalHeader', IMAGE_OPTIONAL_HEADER),
  109.   ]
  110.  
  111.  IMAGE_SIZEOF_SHORT_NAME = 8
  112.  
  113.  class IMAGE_SECTION_HEADER_MISC(ctypes.Union):
  114.   _fields_ = [
  115.   ('PhysicalAddress', DWORD),
  116.   ('VirtualSize', DWORD),
  117.   ]
  118.  
  119.  class IMAGE_SECTION_HEADER(ctypes.Structure):
  120.   _fields_ = [
  121.   ('Name', BYTE * IMAGE_SIZEOF_SHORT_NAME),
  122.   ('Misc', IMAGE_SECTION_HEADER_MISC),
  123.   ('VirtualAddress', DWORD),
  124.   ('SizeOfRawData', DWORD),
  125.   ('PointerToRawData', DWORD),
  126.   ('PointerToRelocations', DWORD),
  127.   ('PointerToLinenumbers', DWORD),
  128.   ('NumberOfRelocations', WORD),
  129.   ('NumberOfLinenumbers', WORD),
  130.   ('Characteristics', DWORD),
  131.   ]
  132.  
  133.  class IMAGE_IMPORT_DESCRIPTOR_MISC(ctypes.Union):
  134.   _fields_ = [
  135.   # 0 for terminating null import descriptor
  136.   ('Characteristics', DWORD),
  137.   # RVA to original unbound IAT (PIMAGE_THUNK_DATA)
  138.   ('OriginalFirstThunk', DWORD),
  139.   ]
  140.  
  141.  class IMAGE_IMPORT_DESCRIPTOR(ctypes.Structure):
  142.   _fields_ = [
  143.   ('Misc', IMAGE_IMPORT_DESCRIPTOR_MISC),
  144.   # 0 if not bound,
  145.   # -1 if bound, and real date/time stamp
  146.   # in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
  147.   # O.W. date/time stamp of DLL bound to (Old BIND)
  148.   ('TimeDateStamp', DWORD),
  149.   ('ForwarderChain', DWORD), # -1 if no forwarders
  150.   ('Name', DWORD),
  151.   # RVA to IAT (if bound this IAT has actual addresses)
  152.   ('FirstThunk', DWORD),
  153.   ]
  154.  
  155.  
  156.  class IMAGE_IMPORT_BY_NAME(ctypes.Structure):
  157.   _fields_ = [
  158.   ('Hint', WORD),
  159.   ('Name', BYTE),
  160.   ]
  161.  
  162.  class IMAGE_THUNK_DATA(ctypes.Union):
  163.   _fields_ = [
  164.   ('ForwarderString', DWORD), # PBYTE
  165.   ('Function', DWORD), # PDWORD
  166.   ('Ordinal', DWORD),
  167.   ('AddressOfData', DWORD), # PIMAGE_IMPORT_BY_NAME
  168.   ]
  169.  
  170.  IMAGE_ORDINAL_FLAG64 = 0x8000000000000000
  171.  IMAGE_ORDINAL_FLAG32 = 0x80000000
  172.  
  173.  def IMAGE_ORDINAL64(Ordinal):
  174.   return (Ordinal & 0xffff)
  175.  
  176.  def IMAGE_ORDINAL32(Ordinal):
  177.   return (Ordinal & 0xffff)
  178.  
  179.  def IMAGE_SNAP_BY_ORDINAL64(Ordinal):
  180.   return ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0)
  181.  
  182.  def IMAGE_SNAP_BY_ORDINAL32(Ordinal):
  183.   return ((Ordinal & IMAGE_ORDINAL_FLAG32) != 0)
  184.  
  185.  class IMAGE_EXPORT_DIRECTORY(ctypes.Structure):
  186.   _fields_ = [
  187.   ('Characteristics', DWORD),
  188.   ('TimeDateStamp', DWORD),
  189.   ('MajorVersion', WORD),
  190.   ('MinorVersion', WORD),
  191.   ('Name', DWORD),
  192.   ('Base', DWORD),
  193.   ('NumberOfFunctions', DWORD),
  194.   ('NumberOfNames', DWORD),
  195.   ('AddressOfFunctions', DWORD),
  196.   ('AddressOfNames', DWORD),
  197.   ('AddressOfNameOrdinals', DWORD),
  198.   ]
  199.  
  200.  # }}}
  201.  
  202.  if 2 != len(sys.argv):
  203.   print 'usage: python %s filename' % sys.argv[0]
  204.   quit()
  205.  
  206.  file_name = sys.argv[1]
  207.  
  208.  logging.basicConfig(
  209.   level=logging.DEBUG,
  210.   format='%(asctime)s %(name)s %(levelname)s %(message)s',
  211.   )
  212.  log = logging.getLogger('main')
  213.  
  214.  try:
  215.   f = open(file_name, 'rb')
  216.  except IOError, e:
  217.   log.error("open(%s) faileed." % file_name)
  218.   log.error(e)
  219.   raise e
  220.  
  221.  # read IMAGE_DOS_HEADER
  222.  
  223.  f.seek(0, 0)
  224.  sz = ctypes.sizeof(IMAGE_DOS_HEADER)
  225.  data = f.read(sz)
  226.  dos_header = IMAGE_DOS_HEADER()
  227.  fit = min(len(data), sz)
  228.  ctypes.memmove(ctypes.addressof(dos_header), data, fit)
  229.  
  230.  # read IMAGE_NT_HEADERS
  231.  
  232.  f.seek(dos_header.e_lfanew, 0)
  233.  sz = ctypes.sizeof(IMAGE_NT_HEADERS)
  234.  data = f.read(sz)
  235.  pe_header = IMAGE_NT_HEADERS()
  236.  fit = min(len(data), sz)
  237.  ctypes.memmove(ctypes.addressof(pe_header), data, fit)
  238.  
  239.  file_header = pe_header.FileHeader
  240.  opt_header = pe_header.OptionalHeader
  241.  
  242.  # enumerate IMAGE_SECTION_HEADER array
  243.  section_headers = []
  244.  section_header_sz = ctypes.sizeof(IMAGE_SECTION_HEADER)
  245.  for i in range(file_header.NumberOfSections):
  246.   data = f.read(section_header_sz)
  247.   section_header = IMAGE_SECTION_HEADER()
  248.   fit = min(len(data), sz)
  249.   ctypes.memmove(ctypes.addressof(section_header), data, fit)
  250.   section_headers.append(section_header)
  251.  
  252.  # load to memory
  253.  f.seek(0, 0)
  254.  file_bytes = f.read() # read all file contents
  255.  image_buf = ctypes.create_string_buffer(opt_header.SizeOfImage)
  256.  ctypes.memset(image_buf, 0, opt_header.SizeOfImage)
  257.  image_buf_ptr = ctypes.addressof(image_buf)
  258.  
  259.  # copy headers
  260.  ctypes.memmove(
  261.   image_buf_ptr,
  262.   file_bytes,
  263.   opt_header.SizeOfHeaders
  264.   )
  265.  
  266.  # copy section datas
  267.  for h in section_headers:
  268.   section_virtual_addr = h.VirtualAddress
  269.   pointer_to_rawdata = h.PointerToRawData
  270.   size_of_rawdata = h.SizeOfRawData
  271.   if pointer_to_rawdata :
  272.   ctypes.memmove(
  273.   image_buf_ptr + section_virtual_addr,
  274.   file_bytes[pointer_to_rawdata:pointer_to_rawdata + size_of_rawdata],
  275.   size_of_rawdata)
  276.  
  277.  export_directory_entry = opt_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
  278.  export_directory_rva = export_directory_entry.VirtualAddress
  279.  export_directory_head = image_buf_ptr + export_directory_rva
  280.  
  281.  export_directory = IMAGE_EXPORT_DIRECTORY()
  282.  ctypes.memmove(
  283.   ctypes.addressof(export_directory),
  284.   export_directory_head,
  285.   ctypes.sizeof(IMAGE_EXPORT_DIRECTORY))
  286.  
  287.  print "IMAGE_EXPORT_DIRECTORY:"
  288.  print "Characteristics = %04X" % export_directory.Characteristics
  289.  print "TimeDateStamp = %04X" % export_directory.TimeDateStamp
  290.  print "MajorVersion = %02X" % export_directory.MajorVersion
  291.  print "MinorVersion = %02X" % export_directory.MinorVersion
  292.  name = ctypes.c_char_p(image_buf_ptr + export_directory.Name)
  293.  print "Name = %s [RVA=%04X]" % (name.value, export_directory.Name)
  294.  print "Base = %04X" % export_directory.Base
  295.  print "NumberOfFunctions = %04X" % export_directory.NumberOfFunctions
  296.  print "NumberOfNames = %04X" % export_directory.NumberOfNames
  297.  print "AddressOfFunctions RVA = %04X" % export_directory.AddressOfFunctions
  298.  print "AddressOfNames RVA = %04X" % export_directory.AddressOfNames
  299.  print "AddressOfNameOrdinals RVA = %04X" % export_directory.AddressOfNameOrdinals
  300.  
  301.  def is_forwarded(sheads, ed_rva, func_rva):
  302.   for sh in sheads:
  303.   rva_min = sh.VirtualAddress
  304.   rva_max = sh.VirtualAddress + sh.Misc.VirtualSize - 1
  305.   if rva_min <= ed_rva and ed_rva <= rva_max:
  306.   if rva_min <= func_rva and func_rva <= rva_max:
  307.   return True
  308.   return False
  309.  
  310.  print "-----------------"
  311.  print "Functions:"
  312.  function_rva_sz = ctypes.sizeof(DWORD)
  313.  offset = image_buf_ptr + export_directory.AddressOfFunctions
  314.  for i in xrange(export_directory.NumberOfFunctions):
  315.   function_rva = DWORD()
  316.   ctypes.memmove(
  317.   ctypes.addressof(function_rva),
  318.   offset,
  319.   function_rva_sz)
  320.   offset = offset + function_rva_sz
  321.   print "\tFunction[%d] RVA = %08X" % (i, function_rva.value),
  322.   if is_forwarded(
  323.   section_headers,
  324.   export_directory_rva,
  325.   function_rva.value):
  326.   fname = ctypes.c_char_p(image_buf_ptr + function_rva.value)
  327.   print " Forwarded To : '%s'" % fname.value
  328.   print
  329.  
  330.  print "-----------------"
  331.  print "Names:"
  332.  name_rva_sz = ctypes.sizeof(DWORD)
  333.  offset = image_buf_ptr + export_directory.AddressOfNames
  334.  for i in xrange(export_directory.NumberOfNames):
  335.   name_rva = DWORD()
  336.   ctypes.memmove(
  337.   ctypes.addressof(name_rva),
  338.   offset,
  339.   name_rva_sz)
  340.   offset = offset + name_rva_sz
  341.   name = ctypes.c_char_p(image_buf_ptr + name_rva.value)
  342.   print "\tName[%d] = %s [RVA=%04X]" % (i, name.value, name_rva.value)
  343.  
  344.  
  345.  print "-----------------"
  346.  print "Ordinals:"
  347.  ordinal_sz = ctypes.sizeof(WORD)
  348.  offset = image_buf_ptr + export_directory.AddressOfNameOrdinals
  349.  for i in xrange(export_directory.NumberOfNames):
  350.   ordinal = WORD()
  351.   ctypes.memmove(
  352.   ctypes.addressof(ordinal),
  353.   offset,
  354.   ordinal_sz)
  355.   offset = offset + ordinal_sz
  356.   print "\tOrdinal[%d] RAW=%d, +BASE=%d" % (
  357.   i, ordinal.value, ordinal.value + export_directory.Base)

解説

ソースコードの275行目まではIMAGE_EXPORT_DIRECTORYの定義が加わっている(185行目)以外は 日記/2010/05/31/PEファイルのインポート情報を表示するPython(作りかけ) と同様。
それ以降について簡単に紹介する。

IMAGE_OPTIONAL_HEADER の DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] から IMAGE_EXPORT_DIRECTORY 構造体を取得

この辺は見たまま。

export_directory_entry = opt_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
# IMAGE_EXPORT_DIRECTORY構造体のRVAを取得して・・・
export_directory_rva = export_directory_entry.VirtualAddress
# ベースアドレスに加算。
export_directory_head = image_buf_ptr + export_directory_rva
 
# ctypesモジュールのmemmoveを使ってIMAGE_EXPORT_DIRECTORYとしてメモリイメージをコピー
export_directory = IMAGE_EXPORT_DIRECTORY()
ctypes.memmove(
    ctypes.addressof(export_directory), 
    export_directory_head,
    ctypes.sizeof(IMAGE_EXPORT_DIRECTORY))
 
# あとは各メンバを16進ダンプ表示。
print "IMAGE_EXPORT_DIRECTORY:"
print "Characteristics = %04X" % export_directory.Characteristics
print "TimeDateStamp = %04X" % export_directory.TimeDateStamp
print "MajorVersion = %02X" % export_directory.MajorVersion
print "MinorVersion = %02X" % export_directory.MinorVersion
name = ctypes.c_char_p(image_buf_ptr + export_directory.Name)
print "Name = %s [RVA=%04X]" % (name.value, export_directory.Name)
print "Base = %04X" % export_directory.Base
print "NumberOfFunctions = %04X" % export_directory.NumberOfFunctions
print "NumberOfNames = %04X" % export_directory.NumberOfNames
print "AddressOfFunctions RVA = %04X" % export_directory.AddressOfFunctions
print "AddressOfNames RVA = %04X" % export_directory.AddressOfNames
print "AddressOfNameOrdinals RVA = %04X" % export_directory.AddressOfNameOrdinals

Export Address Table(EAT) をダンプする

まず、EAT内の各RVAについて実際のシンボルアドレスなのか、転送のためIMAGE_EXPORT_DIRECTORYと同じセクション内なのかを判別する関数を用意する:

# sheadsはsection_headers, ed_rvaはIMAGE_EXPORT_DIRECTORY構造体のRVA, func_rvaはシンボルのRVA
def is_forwarded(sheads, ed_rva, func_rva):
  for sh in sheads:
    # セクションのRVAの範囲(min, max)を計算する。
    rva_min = sh.VirtualAddress
    rva_max = sh.VirtualAddress + sh.Misc.VirtualSize - 1
    if rva_min <= ed_rva and ed_rva <= rva_max:
      if rva_min <= func_rva and func_rva <= rva_max:
        # ed_rvaとfunc_rvaが両方範囲内なら、同じセクションにあるので転送有りと判断
        return True
  return False

実際にEATを調べる。この辺は 日記/2010/05/31/PEファイルのインポート情報を表示するPython(作りかけ) に至まで頻出した定型パターンで、要素(今回はDWORD)の型分だけoffsetを増加していき、ctypesモジュールのmemmove()でメモリイメージをコピーしていく。

print "-----------------"
print "Functions:"
function_rva_sz = ctypes.sizeof(DWORD)
offset = image_buf_ptr + export_directory.AddressOfFunctions
for i in xrange(export_directory.NumberOfFunctions):
  function_rva = DWORD()
  ctypes.memmove(
      ctypes.addressof(function_rva), 
      offset, 
      function_rva_sz)
  offset = offset + function_rva_sz
  print "\tFunction[%d] RVA = %08X" % (i, function_rva.value), 
  if is_forwarded(
      section_headers, 
      export_directory_rva, 
      function_rva.value):
    # 転送有りなら、RVAを文字列ポインタとして読み込み、表示
    fname = ctypes.c_char_p(image_buf_ptr + function_rva.value)
    print "  Forwarded To : '%s'" % fname.value
  print

Export Name Table (ENT) をダンプする

IMAGE_EXPORT_DIRECTORYのAddressOfNamesのRVAから、EATと同様にDWORDとして配列要素を辿っていく。

print "-----------------"
print "Names:"
name_rva_sz = ctypes.sizeof(DWORD)
offset = image_buf_ptr + export_directory.AddressOfNames
for i in xrange(export_directory.NumberOfNames):
  name_rva = DWORD()
  ctypes.memmove(
      ctypes.addressof(name_rva), 
      offset, 
      name_rva_sz)
  offset = offset + name_rva_sz
  name = ctypes.c_char_p(image_buf_ptr + name_rva.value)
  print "\tName[%d] = %s [RVA=%04X]" % (i, name.value, name_rva.value)

序数テーブルの方も、AddressOfNameOrdinalsから同様に辿っていく。

print "-----------------"
print "Ordinals:"
ordinal_sz = ctypes.sizeof(WORD)
offset = image_buf_ptr + export_directory.AddressOfNameOrdinals
for i in xrange(export_directory.NumberOfNames):
  ordinal = WORD()
  ctypes.memmove(
      ctypes.addressof(ordinal), 
      offset, 
      ordinal_sz)
  offset = offset + ordinal_sz
  print "\tOrdinal[%d] RAW=%d, +BASE=%d" % (
      i, ordinal.value, ordinal.value + export_directory.Base)

dumpbin の出力と比較

C言語系/memos/VC++/05, モジュール定義ファイル(".DEF")とDLLのエクスポート でのサンプル、dll_lib.dllについてdumpbinの出力と比べてみる。

"dumpbin /exports"

File Type: DLL

  Section contains the following exports for dll_lib.dll

    00000000 characteristics
    4C05F0F2 time date stamp Wed Jun 02 14:49:38 2010
        0.00 version
           5 ordinal base
           9 number of functions
           7 number of names

    ordinal hint RVA      name

          9    0 00001020 bar
         10    1 00001000 foo
         11    2 00001040 func1
          5    3 00001050 func2
          7    4 00001070 func4
         12    5 00001090 funcX
         13    6 000010A0 funcY
          6      00001060 [NONAME]
          8      00001080 [NONAME]

"show_exports.py"

IMAGE_EXPORT_DIRECTORY:
Characteristics = 0000
TimeDateStamp = 4C05F0F2
MajorVersion = 00
MinorVersion = 00
Name = dll_lib.dll [RVA=9B46]
Base = 0005
NumberOfFunctions = 0009
NumberOfNames = 0007
AddressOfFunctions RVA = 9AF8
AddressOfNames RVA = 9B1C
AddressOfNameOrdinals RVA = 9B38
-----------------
Functions:
        Function[0] RVA = 00001050
        Function[1] RVA = 00001060
        Function[2] RVA = 00001070
        Function[3] RVA = 00001080
        Function[4] RVA = 00001020
        Function[5] RVA = 00001000
        Function[6] RVA = 00001040
        Function[7] RVA = 00001090
        Function[8] RVA = 000010A0
-----------------
Names:
        Name[0] = bar [RVA=9B52]
        Name[1] = foo [RVA=9B56]
        Name[2] = func1 [RVA=9B5A]
        Name[3] = func2 [RVA=9B60]
        Name[4] = func4 [RVA=9B66]
        Name[5] = funcX [RVA=9B6C]
        Name[6] = funcY [RVA=9B72]
-----------------
Ordinals:
        Ordinal[0] RAW=4, +BASE=9
        Ordinal[1] RAW=5, +BASE=10
        Ordinal[2] RAW=6, +BASE=11
        Ordinal[3] RAW=0, +BASE=5
        Ordinal[4] RAW=2, +BASE=7
        Ordinal[5] RAW=7, +BASE=12
        Ordinal[6] RAW=8, +BASE=13

比較結果

show_exports.pyの結果から、ENT, 序数テーブルについてはシンボル名の昇順、EATについては対応する序数の昇順に並んでいるように思われる。
序数テーブルの要素数は、ENTと同じ数しか用意されていない。つまり序数のみでエクスポートしたシンボルについては、ENTや序数テーブルには記録されない。
dumpbinの出力結果を見ると、序数のみのエクスポートシンボルについては空いている序数を自動的に振られているようだ。

例えば"func1"というシンボルのアドレスを取得したい場合は・・・

  1. ENTを探索して、"func1"が3番目に見つかる。
  2. 序数テーブルの3番目は、RAWデータで"6"(Ordinal Baseを加算すると11)、つまりEAT[6]。
  3. EAT[6]をみると、RVAで 0x00001040 となり、これは"dumpbin /exports"の出力結果と一致する。

序数"13"からシンボルのアドレスを取得したい場合は・・・

  1. 13からOrdinal Baseの5を引くと "8"、つまりEAT[8]。
  2. EAT[8]をみると、RVAで 0x000010A0 となり、これは"dumpbin /exports"の出力結果と一致する。

序数"5"の場合はOrdinal Baseを引くと0, つまりEAT[0]になり、0x00001050 となりこれも"dumpbin /exports"と一致する。

show_exports.pyの場合は「シンボル名<>序数<>アドレス」の対応付けまでは行っていない。"dumpbin /exports"であれば対応付けが済んだ一覧を表示してくれるので、実際はdumpbinの方が便利だろう。


プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-06-09 18:59:16
md5:28d2d895cc015215eda5b3c1c6b3c265
sha1:c56b3ea14bf7ba17a9fc697676c6bc791414ca0e
コメント
コメントを投稿するにはログインして下さい。