纯属业余分析一些应用程序的需要,特意去了解了下Windows下的PE文件格式,相比MSDN的其它内容,PE文件规范文件的部分描述算是相当的晦涩了。好记性不如烂笔头,搭好框架后就此记录,以便日后抄袭。代码解决了导入和导出的部分,其余部分日后再作添加和完善,可以继续利用Ruby动态语言的优势、魔幻般的语法糖和强悍的元编程能力构造更便利的代码。
#encoding:gbk
require "delegate"
raise RuntimeError,"This program cannot be run in platform #{RUBY_PLATFORM}" if /mswin|mingw/ !~ RUBY_PLATFORM
#Add convenient method to buildin classes
class Fixnum
#Byte
def to_sb
s = StringIO.new
s.printf('%02x',self)
s.string
end
#Word
def to_sw
s = StringIO.new
s.printf('%04x',self)
s.string
end
#Double Word
def to_sd
s = StringIO.new
s.printf('%08x',self)
s.string
end
#Test specified bit field
def test_b(n)
self & 2 ** n != 0
end
#Masked data
def part_b(a)
result = 0
if a.class == Array or a.class == Range
a.each do |item|
result |= self & 2 ** item
end
end
result
end
#Read little endian data
def Fixnum.read(n,bytes)
bytes.slice(0,n).reverse.collect{|item| item.to_sb}.join('').hex
end
#Read little endian data from fixed location
def Fixnum.readn(ind_s,nlen,bytes)
tmpbytes = bytes.slice(ind_s,nlen)
Fixnum.read(nlen,tmpbytes)
end
end
class Bignum
#Byte
def to_sb
s = StringIO.new
s.printf('%02x',self)
s.string
end
#Word
def to_sw
s = StringIO.new
s.printf('%04x',self)
s.string
end
#Double Word
def to_sd
s = StringIO.new
s.printf('%08x',self)
s.string
end
#Double Long Long
def to_ll
s = StringIO.new
s.printf('%016x',self)
s.string
end
#Test specified bit field
def test_b(n)
self & 2 ** n != 0
end
#Masked data
def part_b(a)
result = 0
if a.class == Array or a.class == Range
a.each do |item|
result |= self & 2 ** item
end
end
result
end
end
class Array
def to_hex
s = StringIO.new
self.each do |item|
s.print item.to_sb
end
s.string
end
def to_num
to_hex.hex
end
def to_lenum
s = StringIO.new
self.reverse.each do |item|
s.print item.to_sb
end
s.string.hex
end
def to_repl
self.select{|c| c != 0}.pack('C*')
end
end
module PE
#Image signature
IMAGE_DOS_SIGNATURE = 0x4D5A #MZ
IMAGE_NT_SIGNATURE = 0x50450000 #PE00
#Machine types
IMAGE_FILE_MACHINE_UNKNOWN = 0
IMAGE_FILE_MACHINE_I386 = 0x014c # Intel 386.
IMAGE_FILE_MACHINE_R3000 = 0x0162 # MIPS little-endian, 0x160 big-endian
IMAGE_FILE_MACHINE_R4000 = 0x0166 # MIPS little-endian
IMAGE_FILE_MACHINE_R10000 = 0x0168 # MIPS little-endian
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x0169 # MIPS little-endian WCE v2
IMAGE_FILE_MACHINE_ALPHA = 0x0184 # Alpha_AXP
IMAGE_FILE_MACHINE_SH3 = 0x01a2 # SH3 little-endian
IMAGE_FILE_MACHINE_SH3DSP = 0x01a3
IMAGE_FILE_MACHINE_SH3E = 0x01a4 # SH3E little-endian
IMAGE_FILE_MACHINE_SH4 = 0x01a6 # SH4 little-endian
IMAGE_FILE_MACHINE_SH5 = 0x01a8 # SH5
IMAGE_FILE_MACHINE_ARM = 0x01c0 # ARM Little-Endian
IMAGE_FILE_MACHINE_THUMB = 0x01c2 # ARM Thumb/Thumb-2 Little-Endian
IMAGE_FILE_MACHINE_ARMNT = 0x01c4 # ARM Thumb-2 Little-Endian
IMAGE_FILE_MACHINE_AM33 = 0x01d3
IMAGE_FILE_MACHINE_POWERPC = 0x01F0 # IBM PowerPC Little-Endian
IMAGE_FILE_MACHINE_POWERPCFP = 0x01f1
IMAGE_FILE_MACHINE_IA64 = 0x0200 # Intel 64
IMAGE_FILE_MACHINE_MIPS16 = 0x0266 # MIPS
IMAGE_FILE_MACHINE_ALPHA64 = 0x0284 # ALPHA64
IMAGE_FILE_MACHINE_MIPSFPU = 0x0366 # MIPS
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x0466 # MIPS
IMAGE_FILE_MACHINE_AXP64 = IMAGE_FILE_MACHINE_ALPHA64
IMAGE_FILE_MACHINE_TRICORE = 0x0520 # Infineon
IMAGE_FILE_MACHINE_CEF = 0x0CEF
IMAGE_FILE_MACHINE_EBC = 0x0EBC # EFI Byte Code
IMAGE_FILE_MACHINE_AMD64 = 0x8664 # AMD64 (K8)
IMAGE_FILE_MACHINE_M32R = 0x9041 # M32R little-endian
IMAGE_FILE_MACHINE_CEE = 0xC0EE
#Characteristics
IMAGE_FILE_RELOCS_STRIPPED = 0x0001 # Relocation info stripped from file.
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 # File is executable (i.e. no unresolved external references).
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 # Line nunbers stripped from file.
IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 # Local symbols stripped from file.
IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010 # Aggressively trim working set
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 # App can handle >2gb addresses
IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 # Bytes of machine word are reversed.
IMAGE_FILE_32BIT_MACHINE = 0x0100 # 32 bit word machine.
IMAGE_FILE_DEBUG_STRIPPED = 0x0200 # Debugging info stripped from file in .DBG file
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 # If Image is on removable media, copy and run from the swap file.
IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 # If Image is on Net, copy and run from the swap file.
IMAGE_FILE_SYSTEM = 0x1000 # System File.
IMAGE_FILE_DLL = 0x2000 # File is a DLL.
IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 # File should only be run on a UP machine
IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 # Bytes of machine word are reversed.
# Subsystem Values
IMAGE_SUBSYSTEM_UNKNOWN = 0 # Unknown subsystem.
IMAGE_SUBSYSTEM_NATIVE = 1 # Image doesn't require a subsystem.
IMAGE_SUBSYSTEM_WINDOWS_GUI = 2 # Image runs in the Windows GUI subsystem.
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3 # Image runs in the Windows character subsystem.
IMAGE_SUBSYSTEM_OS2_CUI = 5 # image runs in the OS/2 character subsystem.
IMAGE_SUBSYSTEM_POSIX_CUI = 7 # image runs in the Posix character subsystem.
IMAGE_SUBSYSTEM_NATIVE_WINDOWS = 8 # image is a native Win9x driver.
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9 # Image runs in the Windows CE subsystem.
IMAGE_SUBSYSTEM_EFI_APPLICATION = 10 #
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11 #
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12 #
IMAGE_SUBSYSTEM_EFI_ROM = 13
IMAGE_SUBSYSTEM_XBOX = 14
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16
# DllCharacteristics Entries
#IMAGE_LIBRARY_PROCESS_INIT = 0x0001 # Reserved.
#IMAGE_LIBRARY_PROCESS_TERM = 0x0002 # Reserved.
#IMAGE_LIBRARY_THREAD_INIT = 0x0004 # Reserved.
#IMAGE_LIBRARY_THREAD_TERM = 0x0008 # Reserved.
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020 # Image can handle a high entropy 64-bit virtual address space.
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 # DLL can move.
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080 # Code Integrity Image
IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100 # Image is NX compatible
IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200 # Image understands isolation and doesn't want it
IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400 # Image does not use SEH. No SE handler may reside in this image
IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800 # Do not bind this image.
IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000 # Image should execute in an AppContainer
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000 # Driver uses WDM model
IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000 # Image supports Control Flow Guard.
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
# Directory Entries
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_COPYRIGHT = 7 # (X86 usage)
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
# Section characteristics.
#
# IMAGE_SCN_TYPE_REG = 0x00000000 # Reserved.
# IMAGE_SCN_TYPE_DSECT = 0x00000001 # Reserved.
# IMAGE_SCN_TYPE_NOLOAD = 0x00000002 # Reserved.
# IMAGE_SCN_TYPE_GROUP = 0x00000004 # Reserved.
IMAGE_SCN_TYPE_NO_PAD = 0x00000008 # Reserved.
# IMAGE_SCN_TYPE_COPY = 0x00000010 # Reserved.
IMAGE_SCN_CNT_CODE = 0x00000020 # Section contains code.
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 # Section contains initialized data.
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 # Section contains uninitialized data.
IMAGE_SCN_LNK_OTHER = 0x00000100 # Reserved.
IMAGE_SCN_LNK_INFO = 0x00000200 # Section contains comments or some other type of information.
# IMAGE_SCN_TYPE_OVER = 0x00000400 # Reserved.
IMAGE_SCN_LNK_REMOVE = 0x00000800 # Section contents will not become part of image.
IMAGE_SCN_LNK_COMDAT = 0x00001000 # Section contents comdat.
# = 0x00002000 # Reserved.
# IMAGE_SCN_MEM_PROTECTED - Obsolete = 0x00004000
IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000 # Reset speculative exceptions handling bits in the TLB entries for this section.
IMAGE_SCN_GPREL = 0x00008000 # Section content can be accessed relative to GP
IMAGE_SCN_MEM_FARDATA = 0x00008000
# IMAGE_SCN_MEM_SYSHEAP - Obsolete = 0x00010000
IMAGE_SCN_MEM_PURGEABLE = 0x00020000
IMAGE_SCN_MEM_16BIT = 0x00020000
IMAGE_SCN_MEM_LOCKED = 0x00040000
IMAGE_SCN_MEM_PRELOAD = 0x00080000
IMAGE_SCN_ALIGN_1BYTES = 0x00100000 #
IMAGE_SCN_ALIGN_2BYTES = 0x00200000 #
IMAGE_SCN_ALIGN_4BYTES = 0x00300000 #
IMAGE_SCN_ALIGN_8BYTES = 0x00400000 #
IMAGE_SCN_ALIGN_16BYTES = 0x00500000 # Default alignment if no others are specified.
IMAGE_SCN_ALIGN_32BYTES = 0x00600000 #
IMAGE_SCN_ALIGN_64BYTES = 0x00700000 #
IMAGE_SCN_ALIGN_128BYTES = 0x00800000 #
IMAGE_SCN_ALIGN_256BYTES = 0x00900000 #
IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 #
IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 #
IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 #
IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 #
IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 #
# Unused = 0x00F00000
IMAGE_SCN_ALIGN_MASK = 0x00F00000
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 # Section contains extended relocations.
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 # Section can be discarded.
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 # Section is not cachable.
IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 # Section is not pageable.
IMAGE_SCN_MEM_SHARED = 0x10000000 # Section is shareable.
IMAGE_SCN_MEM_EXECUTE = 0x20000000 # Section is executable.
IMAGE_SCN_MEM_READ = 0x40000000 # Section is readable.
IMAGE_SCN_MEM_WRITE = 0x80000000 # Section is writeable.
#
# TLS Characteristic Flags
#
IMAGE_SCN_SCALE_INDEX = 0x00000001 # Tls index is scaled
#Optional magic number
IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
IMAGE_ROM_OPTIONAL_HDR_MAGIC = 0x107
#Max size of directory entries
IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
#Section header short name max length
IMAGE_SIZEOF_SHORT_NAME = 8
$TYPE_CLASS_INFORMATION = Hash.new
$UN_REGISTER_FLAG = true
class KeyAddress < DelegateClass(Hash)
def initialize(init = nil)
content = Hash.new
fixed = false
if init.class == Hash
content.merge!(init)
fixed = true
end
yield content,fixed if block_given?
super(content)
self.keys.each do |k|
add_method(k)
end
end
def add_item(k,v)
self[k] = v
add_method(k)
end
def add_method(k)
KeyAddress.class_eval <<-EOF
def #{k.downcase}
self['#{k}']
end
EOF
end
end
class TypeInfo
attr_accessor :type,:cls,:nsize,:scheme
def initialize(type,cls,scheme = nil,nsize = 0)
@type = type
@cls = cls
@scheme = scheme
if scheme.nil?
@nsize = nsize
else
nsize = 0
@scheme.each do |n,v|
if v =~ /\[[0-9]+\]/
pattern = Regexp.new(/(.*)\[([0-9]+)\]/)
m = pattern.match(v)
if not $TYPE_CLASS_INFORMATION.has_key?(m[1])
raise RuntimeError,"Type [#{m[1]}] not found in list of registered."
end
nsize += $TYPE_CLASS_INFORMATION[m[1]].nsize * instance_eval(m[2])
else
if not $TYPE_CLASS_INFORMATION.has_key?(v)
raise RuntimeError,"Type [#{v}] not found in list of registered."
end
nsize += $TYPE_CLASS_INFORMATION[v].nsize
end
end
@nsize = nsize
end
$TYPE_CLASS_INFORMATION[@type] = self
end
def TypeInfo.fixup(obj,bytes)
nseek = 0
if obj.class == String
nsize = $TYPE_CLASS_INFORMATION[obj].nsize
obj = Fixnum.read(nsize,bytes)
nseek += nsize
else
obj.scheme.each do |k,v|
if v =~ /\[[0-9]+\]/
pattern = Regexp.new(/(.*)\[([0-9]+)\]/)
m = pattern.match(v)
n = obj.instance_eval(m[2])
obj[k] = Array.new
ti = $TYPE_CLASS_INFORMATION[m[1]]
n.times do
o = nil
if ti.cls.respond_to?(:new)
o = ti.cls.new
else
o = ti.type
end
o,new_nseek = TypeInfo.fixup(o,bytes.slice(nseek,ti.nsize))
nseek += new_nseek
obj[k] << o
end
else
ti = $TYPE_CLASS_INFORMATION[v]
o = nil
if ti.cls.respond_to?(:new)
o = ti.cls.new
else
o = ti.type
end
o,new_nseek = TypeInfo.fixup(o,bytes.slice(nseek,ti.nsize))
nseek += new_nseek
obj[k] = o
end
end
end
return obj,nseek
end
end
class UglyParser
attr_accessor :ntbs
def initialize(code)
t = StringIO.new(code)
state = false
pattern = Regexp.new(/[ \t]*([A-Za-z0-9_]+)[ \t]+([A-Za-z0-9_\[\]]+);.*/)
@ntbs = Hash.new
t.readlines.each do |line|
next if line.strip.empty? or line.lstrip.start_with?('//')
if line.strip == '<<TAG_BEGIN>>'
state = true
else
break if line.strip == '<<TAG_END>>'
end
if state
m = pattern.match(line)
@ntbs[m[2]] = m[1] if m and m.size == 3
end
end
fine_tbs = Hash.new
@ntbs.each do |k,v|
if k =~ /\[[0-9]+\]/
pattern = Regexp.new(/(.*)(\[[0-9]+\])/)
m = pattern.match(k)
fine_tbs[m[1]] = v + m[2]
else
fine_tbs[k] = v
end
end
@ntbs = fine_tbs
if block_given?
yield self
end
end
end
class Image < DelegateClass(Array)
attr_accessor :name,:bytes
def initialize(input)
case input
#File path
when String
@name = :name
@bytes = IO.read(input,mode:'rb').bytes
#Loaded bytes
when Array
@bytes = input.clone
#Image file object
when File
@name = input.to_path
@bytes = input.read.bytes
#Just prompt
else
raise RuntimeError,"Could not fetch image bytes from #{input.class}."
end
super(@bytes)
#Invoke from block
if block_given?
yield self
end
end
end
#Delegate to buildin Hash
class BaseStruct < DelegateClass(Hash)
attr_accessor :bytes,:scheme,:nsize
def initialize(defs,input,tn,cls)
if $UN_REGISTER_FLAG
$TYPE_CLASS_INFORMATION['BYTE'] = TypeInfo.new('BYTE',Fixnum,nil,1)
$TYPE_CLASS_INFORMATION['WORD'] = TypeInfo.new('WORD',Fixnum,nil,2)
$TYPE_CLASS_INFORMATION['DWORD'] = TypeInfo.new('DWORD',Fixnum,nil,4)
$TYPE_CLASS_INFORMATION['LONG'] = TypeInfo.new('LONG',Fixnum,nil,4)
$TYPE_CLASS_INFORMATION['ULONGLONG'] = TypeInfo.new('ULONGLONG',Fixnum,nil,8)
$UN_REGISTER_FLAG = false
end
parser = UglyParser.new(defs)
@scheme = parser.ntbs
inner = Hash.new
@scheme.keys.each do |k|
inner[k] = nil
end
super(inner)
ti = TypeInfo.new(tn,cls,@scheme)
@nsize = ti.nsize
TypeInfo.fixup(self,input) if not input.nil?
@scheme.keys.each do |k|
cls.class_eval <<-EOF
def #{k.downcase}
self['#{k}']
end
EOF
end
end
end
class DosStub < BaseStruct
def initialize(input = nil)
defs = <<-EOF
//winnt.h
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
<<TAG_BEGIN>>
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
<<TAG_END>>
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
EOF
super(defs,input,'IMAGE_DOS_HEADER',self.class)
end
end
class CoffHeader < BaseStruct
def initialize(input = nil)
defs = <<-EOF
//winnt.h
typedef struct _IMAGE_FILE_HEADER {
<<TAG_BEGIN>>
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
<<TAG_END>>
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
EOF
super(defs,input,'IMAGE_FILE_HEADER',self.class)
end
end
class ImageDataDirectory < BaseStruct
def initialize(input = nil)
defs = <<-EOF
//winnt.h
typedef struct _IMAGE_DATA_DIRECTORY {
<<TAG_BEGIN>>
DWORD VirtualAddress;
DWORD Size;
<<TAG_END>>
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
EOF
super(defs,input,'IMAGE_DATA_DIRECTORY',self.class)
end
end
class OptionalHeader32 < BaseStruct
def initialize(input = nil)
defs = <<-EOF
//winnt.h
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
<<TAG_BEGIN>>
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[#{IMAGE_NUMBEROF_DIRECTORY_ENTRIES.to_s}];
<<TAG_END>>
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
EOF
o = ImageDataDirectory.new
super(defs,input,'IMAGE_OPTIONAL_HEADER32',self.class)
end
end
class OptionalHeader64 < BaseStruct
def initialize(input = nil)
defs = <<-EOF
//winnt.h
typedef struct _IMAGE_OPTIONAL_HEADER64 {
<<TAG_BEGIN>>
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[#{IMAGE_NUMBEROF_DIRECTORY_ENTRIES.to_s}];
<<TAG_END>>
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
EOF
o = ImageDataDirectory.new
super(defs,input,'IMAGE_OPTIONAL_HEADER64',self.class)
end
end
class RomOptionalHeader < BaseStruct
def initialize(input = nil)
defs = <<-EOF
//winnt.h
typedef struct _IMAGE_ROM_OPTIONAL_HEADER {
<<TAG_BEGIN>>
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD BaseOfBss;
DWORD GprMask;
DWORD CprMask[4];
DWORD GpValue;
<<TAG_END>>
} IMAGE_ROM_OPTIONAL_HEADER, *PIMAGE_ROM_OPTIONAL_HEADER;
EOF
super(defs,input,'IMAGE_ROM_OPTIONAL_HEADER',self.class)
end
end
class ImageNTHeaders32 < BaseStruct
def initialize(input = nil)
defs = <<-EOF
//winnt.h
typedef struct _IMAGE_NT_HEADERS {
<<TAG_BEGIN>>
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
<<TAG_END>>
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
EOF
o = CoffHeader.new
o = OptionalHeader32.new
super(defs,input,'IMAGE_NT_HEADERS32',self.class)
end
end
class ImageNTHeaders64 < BaseStruct
def initialize(input = nil)
defs = <<-EOF
//winnt.h
typedef struct _IMAGE_NT_HEADERS64 {
<<TAG_BEGIN>>
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
<<TAG_END>>
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
EOF
o = CoffHeader.new
o = OptionalHeader64.new
super(defs,input,'IMAGE_NT_HEADERS64',self.class)
end
end
class SectionHeader < BaseStruct
def initialize(input = nil)
defs = <<-EOF
//winnt.h
typedef struct _IMAGE_SECTION_HEADER {
<<TAG_BEGIN>>
BYTE Name[#{IMAGE_SIZEOF_SHORT_NAME.to_s}];
// union {
// DWORD PhysicalAddress;
DWORD VirtualSize;
// } Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
<<TAG_END>>
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
EOF
super(defs,input,'IMAGE_SECTION_HEADER',self.class)
end
end
class ImportDirectoryTable
attr_accessor :import_lookup_table_rva,:time_stamp,:forward_chain,:name_rav,:import_address_table_rav
attr_accessor :image,:name,:hintnames
def initialize(nstart,imagedesc)
@image = imagedesc.image
@import_lookup_table_rva = Fixnum.readn(nstart + 0,4,@image)
@time_stamp = Fixnum.readn(nstart + 4,4,@image)
@forward_chain = Fixnum.readn(nstart + 8,4,@image)
@name_rav = Fixnum.readn(nstart + 12,4,@image)
@import_address_table_rav = Fixnum.readn(nstart + 16,4,@image)
if not null?
noffset = imagedesc.rav2offset(@name_rav)
@name = imagedesc.read_mem_cstr(noffset)
end
@hintnames = Array.new
end
def null?
@import_lookup_table_rva == 0 and @time_stamp == 0 and @forward_chain == 0 and @name_rav == 0 and @import_address_table_rav == 0
end
end
class ImportLookupTableArray
attr_accessor :lookuptable
def initialize(nstart,imagedesc)
image = imagedesc.image
arch = imagedesc.image_type
@lookuptable = Array.new
nsize = 0
while true
data = nil
if arch == 'PE32'
data = Fixnum.readn(nstart + nsize,4,image)
nsize += 4
elsif arch =='PE32+'
data = Fixnum.readn(nstart + nsize,8,image)
nsize += 8
end
break if data == 0
@lookuptable << data
end
end
end
class HintNameTable
attr_accessor :image,:use_hint,:hint,:name
def initialize(imagedesc,ilt_item)
@image = imagedesc.image
arch = imagedesc.image_type
if arch == 'PE32'
@use_hint = ilt_item.test_b(31)
elsif arch =='PE32+'
@use_hint = ilt_item.test_b(63)
end
if @use_hint
@hint = ilt_item.part_b(0..15)
@name = nil
else
hntab_rva = ilt_item.part_b(0..30)
@hint = nil
noffset = imagedesc.rav2offset(hntab_rva)
@name = imagedesc.read_mem_cstr(noffset + 2)
end
end
end
class ExportDirectoryTable
attr_accessor :image,:name,:ordinal_list,:name_list
attr_accessor :exportflags,:timestamp,:majorversion,:minorversion,:namerva,:ordinalbase,:addresstableentries,:numberofnamepointers,:exportaddresstablerva,:namepointerrva,:ordinaltablerva
attr_accessor :imagedesc
def initialize(nstart,imagedesc)
@imagedesc = imagedesc
@image = imagedesc.image
@exportflags = Fixnum.readn(nstart + 0,4,@image)
@timestamp = Fixnum.readn(nstart + 4,4,@image)
@majorversion = Fixnum.readn(nstart + 8,2,@image)
@minorversion = Fixnum.readn(nstart + 10,2,@image)
@namerva = Fixnum.readn(nstart + 12,4,@image)
@ordinalbase = Fixnum.readn(nstart + 16,4,@image)
@addresstableentries = Fixnum.readn(nstart + 20,4,@image)
@numberofnamepointers = Fixnum.readn(nstart + 24,4,@image)
@exportaddresstablerva = Fixnum.readn(nstart + 28,4,@image)
@namepointerrva = Fixnum.readn(nstart + 32,4,@image)
@ordinaltablerva = Fixnum.readn(nstart + 36,4,@image)
set_name
set_ordinal_list
set_name_list
end
def set_name
noffset = @imagedesc.rav2offset(@namerva)
@name = @imagedesc.read_mem_cstr(noffset)
end
def set_ordinal_list
@ordinal_list = Array.new
noffset = @imagedesc.rav2offset(@ordinaltablerva)
nsize = 0
@numberofnamepointers.times do
o = Fixnum.readn(noffset + nsize,2,@image)
@ordinal_list << o
nsize += 2
end
end
def set_name_list
@name_list = Array.new
noffset = @imagedesc.rav2offset(@namepointerrva)
nsize = 0
@numberofnamepointers.times do
o = Fixnum.readn(noffset + nsize,4,@image)
offset_o = @imagedesc.rav2offset(o)
namestr = @imagedesc.read_mem_cstr(offset_o)
@name_list << namestr
nsize += 4
end
end
end
class ImageDesc
attr_accessor :image,:dosheader,:ntheader,:sections,:image_type
def initialize(image,dosheader,ntheader,sections,image_type)
@image,@dosheader,@ntheader,@sections,@image_type = image,dosheader,ntheader,sections,image_type
end
def res_idata
raise RuntimeError,'No import data directory.' if @ntheader.fileheader.numberofsections < 2
imports = Array.new
imp_va = @ntheader.optionalheader.datadirectory[1].virtualaddress
page_start = rav2offset(imp_va)
nsize = 20
while idt = ImportDirectoryTable.new(page_start,self)
break if idt.null?
noffset = rav2offset(idt.import_lookup_table_rva)
lookuptable = ImportLookupTableArray.new(noffset,self).lookuptable
lookuptable.each do |lt|
hnt = HintNameTable.new(self,lt)
idt.hintnames << hnt
end
imports << idt
page_start += nsize
end
imports
end
def res_edata
raise RuntimeError,'No import data directory.' if @ntheader.fileheader.numberofsections < 1
exp_va = @ntheader.optionalheader.datadirectory[0].virtualaddress
page_start = rav2offset(exp_va)
edt = ExportDirectoryTable.new(page_start,self)
end
def rav2offset(rav)
hit_section = nil
@sections.each do |section|
if rav >= section.virtualaddress and rav <= section.virtualaddress + section.sizeofrawdata
hit_section = section
#puts "RAV[#{rav.to_sd}] hit #{section.name.to_repl} between [#{section.virtualaddress.to_sd} ~ #{(section.virtualaddress + section.sizeofrawdata).to_sd}],file offset at[#{(section.pointertorawdata + (rav - hit_section.virtualaddress)).to_sd}] between [#{section.pointertorawdata.to_sd} ~ #{(section.virtualaddress + section.sizeofrawdata).to_sd}] "
break
end
end
raise RuntimeError,"RAV[#{rav.to_sd}] out of sections virtual address space." if hit_section.nil?
hit_section.pointertorawdata + (rav - hit_section.virtualaddress)
end
def read_mem_cstr(nstart)
result = StringIO.new
while @image[nstart] != 0
result.putc @image[nstart]
nstart += 1
end
result.string
end
end
class PEParser
attr_accessor :image,:keyaddr
def initialize(src)
@image = Image.new(src)
@keyaddr = KeyAddress.new
end
def parse
image_type = nil
if @image.slice(0,2).to_num != IMAGE_DOS_SIGNATURE
raise RuntimeError,'Not valid image file - test from IMAGE_DOS_SIGNATURE.'
end
@keyaddr.add_item('dos_start',0)
dosstub = DosStub.new
dosstub,nsize = TypeInfo.fixup(dosstub,@image.slice(0,dosstub.nsize))
@keyaddr.add_item('pe_start',dosstub.e_lfanew)
if @image.slice(@keyaddr.pe_start,4).to_num != IMAGE_NT_SIGNATURE
raise RuntimeError,'Not valid image file - test from IMAGE_NT_SIGNATURE.'
end
@keyaddr.add_item('coff_start',@keyaddr.pe_start + 4)
@keyaddr.add_item('optional_start',@keyaddr.coff_start + CoffHeader.new.nsize)
pe_type = @image.slice(@keyaddr.optional_start,2).to_lenum
ntheader,nsize = nil,0
if pe_type == IMAGE_NT_OPTIONAL_HDR32_MAGIC
image_type = 'PE32'
ntheader = ImageNTHeaders32.new
ntheader,nsize = TypeInfo.fixup(ntheader,@image.slice(@keyaddr.pe_start,ntheader.nsize))
else
if pe_type == IMAGE_NT_OPTIONAL_HDR64_MAGIC
image_type = 'PE32+'
ntheader = ImageNTHeaders64.new
ntheader,nsize = TypeInfo.fixup(ntheader,@image.slice(@keyaddr.pe_start,ntheader.nsize))
else
puts 'MAGIC:' + pe_type.to_s(16)
raise RuntimeError,'Never mind file type - test from IMAGE_NT_OPTIONAL_HDR**_MAGIC.'
end
end
@keyaddr.add_item('nsections',ntheader.fileheader.numberofsections)
@keyaddr.add_item('nsizeofoptionalheader',ntheader.fileheader.sizeofoptionalheader)
@keyaddr.add_item('sections_start',@keyaddr.optional_start + @keyaddr.nsizeofoptionalheader)
sections = Array.new
@keyaddr.nsections.times do |i|
s = SectionHeader.new
s,nsize = TypeInfo.fixup(s,@image.slice(@keyaddr.sections_start + s.nsize * i,s.nsize))
sections << s
end
ImageDesc.new(@image,dosstub,ntheader,sections,image_type)
end
end
end
这来一个简单的测试用例,输出动态库的import和export的简单信息。
pf = PE::PEParser.new('D:\\Bandicam\\bdcap32.dll')
pedec = pf.parse
imps = pedec.res_idata
$stderr.puts "-----imports-----"
imps.each do |imp|
$stderr.puts "[#{imp.name}]"
imp.hintnames.each do |hn|
if hn.use_hint
$stderr.puts "\t" + hn.hint.to_s
else
$stderr.puts "\t" + hn.name
end
end
end
edt = pedec.res_edata
$stderr.puts "-----exports-----"
$stderr.puts "[#{edt.name}]"
edt.name_list.each do |n|
$stderr.puts "\t" + n
end