作りかけ。 - PE(Portable Executable)ファイルフォーマット その2 ~ インポート情報とインポート関数の列挙 ~ -- http://hp.vector.co.jp/authors/VA050396/tech_07.html に出てくる、PEヘッダー情報を基にして、実際にPEイメージがロードされた時と同様なレイアウトで、ヘッダーやセクションデータをメモリ上にコピーする箇所だけとりあえず作ってみようとした。 #more|| foo.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 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), ] # }}} 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 section_headers = [] # dump IMAGE_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 # "SizeOfImage"分のcharバッファを確保 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) # とりあえずhexダンプ # New Line flag nl = 0 # Offset (= RVA) offset = 0 for i in xrange(len(image_buf)): if nl == 0: print "%08X - %08X " % (offset, offset + 16), print "%02X" % ord(image_buf[i]), nl = nl + 1 offset = offset + 1 if nl > 15: nl = 0 print ||< 実際にデバッガのmemory dumpと付き合わせたり、バイナリエディタの表示と見比べたところ、大凡問題ないように見える。 もうちょっと作り込んで、インポート情報周りをダンプ表示出来るようにしたい。