class PEdump

pedump.rb by zed_0xff

http://zed.0xff.me
http://github.com/zed-0xff

Constants

CURDIRENTRY
CURSOR_HOTSPOT
CUR_ICO_HEADER

www.devsource.com/c/a/Architecture/Resources-From-PE-I/2/

EFI_IMAGE_DATA_DIRECTORY

www.intel.com/content/www/us/en/architecture-and-technology/unified-extensible-firmware-interface/efi-specifications-general-technology.html wiki.phoenix.com/wiki/index.php/EFI_TE_IMAGE_HEADER formats.kaitai.io/uefi_te/index.html ho.ax/tag/efi/ github.com/gdbinit/TELoader

GOOD_FUNCTION_NAME_RE
ICODIRENTRY
IMAGE_DATA_DIRECTORY
IMAGE_EXPORT_DIRECTORY

msdn.microsoft.com/en-us/library/ms809762.aspx

IMAGE_IMPORT_DESCRIPTOR

sandsprite.com/CodeStuff/Understanding_imports.html stackoverflow.com/questions/5631317/import-table-it-vs-import-address-table-iat

IMAGE_RESOURCE_DATA_ENTRY
IMAGE_RESOURCE_DIRECTORY
IMAGE_RESOURCE_DIRECTORY_ENTRY
IMAGE_SECTION_HEADER
IMAGE_SUBSYSTEMS

msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=VS.85).aspx

IMAGE_TLS_DIRECTORY32
IMAGE_TLS_DIRECTORY64
MAX_ERRORS
MAX_EXPORT_NUMBER_OF_NAMES
MAX_IMAGE_IMPORT_DESCRIPTORS
MINIDUMP_LOCATION_DESCRIPTOR
MINIDUMP_MEMORY_DESCRIPTOR
MINIDUMP_MEMORY_DESCRIPTOR64
MINIDUMP_MEMORY_INFO
MINIDUMP_STREAM_TYPE

msdn.microsoft.com/en-us/library/windows/desktop/ms680394(v=vs.85).aspx

MZ

www.delorie.com/djgpp/doc/exe/

RICH_IDS

data from raw.githubusercontent.com/dishather/richprint/master/comp_id.txt

ROOT_RES_NAMES
STRING
SUPPORTED_SIGNATURES
TE
VERSION

Attributes

fname[RW]
force[RW]
io[RW]
logger[RW]

Public Class Methods

dump(fname, params = {}) click to toggle source
# File lib/pedump.rb, line 328
def self.dump fname, params = {}
  new(fname, params).dump
end
logger() click to toggle source
# File lib/pedump/core.rb, line 35
def logger;    @@logger;   end
logger=(l;) click to toggle source
# File lib/pedump/core.rb, line 36
def logger= l; @@logger=l; end
new(io = nil, params = {}) click to toggle source
# File lib/pedump.rb, line 41
def initialize io = nil, params = {}
  if io.is_a?(Hash)
    @io, params = nil, io
  else
    @io = io
  end
  @force = params[:force]
  @logger = @@logger = Logger.create(params)
end
ordlookup(dll, ord, make_name: false) click to toggle source
# File lib/pedump/ordlookup.rb, line 5
def self.ordlookup(dll, ord, make_name: false)
  dll = dll.downcase
  @ordlookup ||= {}
  @ordlookup[dll] ||= 
    begin
      yml_fname = File.expand_path(File.dirname(__FILE__) + "/../../data/ordlookup/" + dll + ".yml")
      if File.exist?(yml_fname)
        YAML.load_file(yml_fname)
      else
        {}
      end
    end
  @ordlookup[dll][ord] || (make_name ? "ord#{ord}" : nil)
end
quiet() { || ... } click to toggle source
# File lib/pedump.rb, line 332
def self.quiet
  oldlevel = @@logger.level
  @@logger.level = ::Logger::FATAL
  yield
ensure
  @@logger.level = oldlevel
end

Public Instance Methods

_detect_format() click to toggle source
# File lib/pedump.rb, line 509
def _detect_format
  return :pe if @pe
  return :ne if @ne
  return :te if @te
  return :pe if pe()
  return :ne if ne()
  return :te if te()
  nil
end
_dump_handle(h) click to toggle source
# File lib/pedump.rb, line 459
def _dump_handle h
  if pe(h) # also calls mz(h)
    rich_hdr h
    resources h
    imports h   # also calls tls(h)
    exports h
    packer h
  elsif te(h)
  end
end
_read_resource_directory_tree(f) click to toggle source
# File lib/pedump/resources.rb, line 12
def _read_resource_directory_tree f
  return nil unless pe(f) && pe(f).ioh && f
  res_dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::RESOURCE]
  return [] if !res_dir || (res_dir.va == 0 && res_dir.size == 0)
  res_va = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::RESOURCE].va
  res_section = @pe.section_table.find{ |t| t.VirtualAddress == res_va }
  unless res_section
    logger.warn "[?] can't find resource section for va=0x#{res_va.to_s(16)}"
    return []
  end
  f.seek res_section.PointerToRawData
  IMAGE_RESOURCE_DIRECTORY.base = res_section.PointerToRawData
  #@resource_data_base = res_section.PointerToRawData - res_section.VirtualAddress
  IMAGE_RESOURCE_DIRECTORY.read(f)
end
_scan_pe_resources(f=@io, dir=nil) click to toggle source
# File lib/pedump/resources.rb, line 379
def _scan_pe_resources f=@io, dir=nil
  dir ||= resource_directory(f)
  return nil unless dir
  @pe_res_errors ||= 0
  r = []
  dir.entries.each_with_index do |entry,idx|
    case entry.data
      when IMAGE_RESOURCE_DIRECTORY
        if dir == @resource_directory # root resource directory
          entry_type =
            if entry.Name & 0x8000_0000 == 0
              # root resource directory & entry name is a number
              ROOT_RES_NAMES[entry.Name] || entry.name
            else
              entry.name
            end
          r += _scan_pe_resources(f,entry.data).each do |res|
            res.type = entry_type
            res.parse f
          end
        else
          r += _scan_pe_resources(f,entry.data).each do |res|
            res.name = res.name == "##{res.lang}" ? entry.name : "#{entry.name} / #{res.name}"
            res.id ||= entry.Name if entry.Name.is_a?(Numeric) && entry.Name < 0x8000_0000
          end
        end
      when IMAGE_RESOURCE_DATA_ENTRY
        file_offset = va2file(entry.data.OffsetToData, :quiet => (@pe_res_errors > MAX_ERRORS))
        unless file_offset
          @pe_res_errors += 1
          if @pe_res_errors > MAX_ERRORS
            PEdump.logger.warn "[?] too many errors getting resource data, stopped on #{idx} of #{dir.entries.size}"
            break
          end
        end
        r << Resource.new(
          nil,          # type
          entry.name,
          nil,          # id
          entry.Name,   # lang
          #entry.data.OffsetToData + @resource_data_base,
          file_offset,
          entry.data.Size,
          entry.data.CodePage,
          entry.data.Reserved
        )
      else
        if entry.data
          logger.error "[!] invalid resource entry: #{entry.data.inspect}"
        else
          # show NULL entries only in verbose mode
          logger.info  "[!] invalid resource entry: #{entry.data.inspect}"
        end
    end
  end
  r.flatten.compact
end
data_directory(f=@io) click to toggle source
# File lib/pedump.rb, line 470
def data_directory f=@io
  if pe(f)
    pe.ioh && pe.ioh.DataDirectory
  elsif te(f)
    te.DataDirectory
  end
end
dos_stub(f=@io) click to toggle source
# File lib/pedump.rb, line 353
def dos_stub f=@io
  @dos_stub ||=
    begin
      return nil unless mz = mz(f)
      dos_stub_offset = mz.header_paragraphs.to_i * 0x10
      dos_stub_size   = mz.lfanew.to_i - dos_stub_offset
      if dos_stub_offset < 0
        logger.warn "[?] invalid DOS stub offset #{dos_stub_offset}"
        nil
      elsif f && dos_stub_offset > f.size
        logger.warn "[?] DOS stub offset beyond EOF: #{dos_stub_offset}"
        nil
      elsif dos_stub_size < 0
        logger.warn "[?] invalid DOS stub size #{dos_stub_size}"
        nil
      elsif dos_stub_size == 0
        # no DOS stub, it's ok
        nil
      elsif !f
        # no open file, it's ok
        nil
      else
        return nil if dos_stub_size == MZ::SIZE && dos_stub_offset == 0
        if dos_stub_size > 0x1000
          logger.warn "[?] DOS stub size too big (#{dos_stub_size}), limiting to 0x1000"
          dos_stub_size = 0x1000
        end
        f.seek dos_stub_offset
        DOSStub.new(f.read(dos_stub_size)).tap do |dos_stub|
          dos_stub.offset = dos_stub_offset
          if dos_stub['Rich']
            if @rich_hdr = RichHdr.from_dos_stub(dos_stub)
              dos_stub[dos_stub.index(@rich_hdr)..-1] = ''
            end
          end
        end
      end
    end
end
dump(f=@io) click to toggle source

OPTIONAL: assigns @mz, @rich_hdr, @pe, etc

# File lib/pedump.rb, line 448
def dump f=@io
  if f.is_a?(String)
    File.open(f,'rb'){ |f| _dump_handle(f) }
  elsif f.is_a?(::IO)
    _dump_handle f
  elsif @io
    _dump_handle @io
  end
  self
end
exports(f=@io) click to toggle source
# File lib/pedump.rb, line 755
def exports f=@io
  if pe(f)
    pe_exports(f)
  elsif ne(f)
    ne(f).exports
  end
end
imphash(f=@io) click to toggle source
# File lib/pedump.rb, line 578
def imphash f=@io
  return @imphash if @imphash
  return nil unless pe(f) && pe(f).ioh && f

  imports = imports(f)
  return nil if imports.empty?

  a = []
  imports.each do |iid|
    next unless iid.module_name

    names = [iid.original_first_thunk, iid.first_thunk].compact.flatten.map do |x|
      x.name || PEdump.ordlookup(iid.module_name, x.ordinal, make_name: true)
    end.compact.map(&:downcase).uniq
    libname = iid.module_name.downcase.sub(/\.(ocx|sys|dll)$/,'') # as in python's pefile
    names.each do |name|
      a << "#{libname}.#{name}"
    end
  end

  return nil if a.empty?
  @imphash = Digest::MD5.hexdigest(a.join(","))
end
imports(f=@io) click to toggle source
# File lib/pedump.rb, line 568
def imports f=@io
  if pe(f)
    pe_imports(f)
  elsif ne(f)
    ne(f).imports
  else
    []
  end
end
logger=(l) click to toggle source
# File lib/pedump.rb, line 324
def logger= l
  @logger = @@logger = l
end
mz(f=@io) click to toggle source
# File lib/pedump.rb, line 340
def mz f=@io
  @mz ||= f && MZ.read(f).tap do |mz|
    if mz.signature != 'MZ' && mz.signature != 'ZM'
      if @force
        #logger.warn  "[?] no MZ signature. want: 'MZ' or 'ZM', got: #{mz.signature.inspect}"
      else
        #logger.error "[!] no MZ signature. want: 'MZ' or 'ZM', got: #{mz.signature.inspect}. (not forced)"
        return nil
      end
    end
  end
end
ne(f=@io) click to toggle source
# File lib/pedump/ne.rb, line 402
def ne f=@io
  return @ne if defined?(@ne)
  @ne ||=
    begin
      ne_offset = mz(f) && mz(f).lfanew
      if ne_offset.nil?
        logger.debug "[!] NULL NE offset (e_lfanew)."
        nil
      elsif ne_offset > f.size
        logger.fatal "[!] NE offset beyond EOF."
        nil
      else
        f.seek ne_offset
        if f.read(2) == 'NE'
          f.seek ne_offset
          NE.read f
        else
          nil
        end
      end
    end
end
ne?() click to toggle source
# File lib/pedump.rb, line 523
def ne?
  _detect_format() == :ne
end
packer(f=@io) click to toggle source

packer / compiler detection

# File lib/pedump.rb, line 910
def packer f=@io
  @packer ||= pe(f) && @pe.ioh &&
    begin
      if PEdump::Packer.all.size == 0
        logger.error "[?] no packer definitions found"
        nil
      else
        Packer.of f, :pedump => self
      end
    end
end
Also aliased as: packers
packers(f=@io)
Alias for: packer
pe(f=@io) click to toggle source
# File lib/pedump/pe.rb, line 115
def pe f=@io
  @pe ||=
    begin
      pe_offset = mz(f) && mz(f).lfanew
      if pe_offset.nil?
        logger.debug "[!] NULL PE offset (e_lfanew). cannot continue."
        nil
      elsif pe_offset > f.size
        logger.fatal "[!] PE offset beyond EOF. cannot continue."
        nil
      else
        f.seek pe_offset
        PE.read f, :force => @force
      end
    end
end
pe?() click to toggle source
# File lib/pedump.rb, line 519
def pe?
  _detect_format() == :pe
end
pe_exports(f=@io) click to toggle source
# File lib/pedump.rb, line 763
def pe_exports f=@io
  return @exports if @exports
  return nil unless pe(f) && pe(f).ioh && f
  dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::EXPORT]
  return nil if !dir || (dir.va == 0 && dir.size == 0)
  va = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::EXPORT].va
  file_offset = va2file(va)
  return nil unless file_offset
  if !f.checked_seek(file_offset) || f.eof?
    logger.warn "[?] exports info beyond EOF"
    return nil
  end
  @exports = IMAGE_EXPORT_DIRECTORY.read(f).tap do |x|
    x.entry_points = []
    x.name_ordinals = []
    x.names = []
    if x.Name.to_i != 0 && (ofs = va2file(x.Name))
      f.seek ofs
      if f.eof?
        logger.warn "[?] export ofs 0x#{ofs.to_s(16)} beyond EOF"
        nil
      else
        x.name = f.gets("\x00").chomp("\x00")
      end
    end
    if x.NumberOfFunctions.to_i > 0
      if x.AddressOfFunctions.to_i !=0 && (ofs = va2file(x.AddressOfFunctions))
        f.seek ofs
        x.entry_points = []
        x.NumberOfFunctions.times do
          if f.eof?
            logger.warn "[?] got EOF while reading exports entry_points"
            break
          end
          x.entry_points << f.read(4).unpack('V').first
        end
      end
      if x.AddressOfNameOrdinals.to_i !=0 && (ofs = va2file(x.AddressOfNameOrdinals))
        f.seek ofs
        x.name_ordinals = []
        x.NumberOfNames.times do
          if f.eof?
            logger.warn "[?] got EOF while reading exports name_ordinals"
            break
          end
          x.name_ordinals << f.read(2).unpack('v').first + x.Base
        end
      end
    end
    if x.NumberOfNames.to_i > 0 && x.AddressOfNames.to_i !=0 && (ofs = va2file(x.AddressOfNames))
      f.seek ofs
      x.names = []
      x.NumberOfNames.times do
        if f.eof?
          logger.warn "[?] got EOF while reading exports names"
          break
        end
        x.names << f.read(4).unpack('V').first
      end
      nErrors = 0
      x.names.size.times do |i|
        begin
          f.seek va2file(x.names[i])
          x.names[i] = f.gets("\x00").to_s.chomp("\x00")
        rescue
          nErrors += 1
          if nErrors > MAX_ERRORS
            logger.warn "[?] too many errors getting export names, stopped on #{i} of #{x.names.size}"
            x.names = x.names[0,i]
            break
          end
          nil
        end
      end
    end

    ord2name = {}
    if x.names && x.names.any?
      n = x.NumberOfNames
      if n > MAX_EXPORT_NUMBER_OF_NAMES
        logger.warn "[?] NumberOfNames too big (#{x.NumberOfNames}), limiting to #{MAX_EXPORT_NUMBER_OF_NAMES}"
        n = MAX_EXPORT_NUMBER_OF_NAMES
      end
      n.times do |i|
        ord2name[x.name_ordinals[i]] ||= []
        ord2name[x.name_ordinals[i]] << x.names[i]
      end
    end

    x.functions = []
    x.entry_points.each_with_index do |ep,i|
      names = ord2name[i+x.Base]
      names = names.join(', ') if names
      next if ep.to_i == 0 && names.nil?
      x.functions << ExportedFunction.new(names, i+x.Base, ep)
    end
  end
end
pe_imports(f=@io) click to toggle source
# File lib/pedump.rb, line 602
def pe_imports f=@io
  return @imports if @imports
  return nil unless pe(f) && pe(f).ioh && f

  dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::IMPORT]
  return [] if !dir || (dir.va == 0 && dir.size == 0)

  file_offset = va2file(dir.va)
  return nil unless file_offset

  # scan TLS first, to catch many fake imports trick from
  # http://code.google.com/p/corkami/source/browse/trunk/asm/PE/manyimportsW7.asm
  tls_aoi = nil
  if (tls = tls(f)) && tls.any?
    tls_aoi = tls.first.AddressOfIndex.to_i - @pe.ioh.ImageBase.to_i
    tls_aoi = tls_aoi > 0 ? va2file(tls_aoi) : nil
  end

  r = []; t = nil
  if f.checked_seek(file_offset)
    while true
      if tls_aoi && tls_aoi == file_offset+16
        # catched the neat trick! :)
        # f.tell + 12  =  offset of 'FirstThunk' field from start of IMAGE_IMPORT_DESCRIPTOR structure
        logger.warn "[!] catched the 'imports terminator in TLS trick'"
        # http://code.google.com/p/corkami/source/browse/trunk/asm/PE/manyimportsW7.asm
        break
      end
      if r.size >= MAX_IMAGE_IMPORT_DESCRIPTORS
        logger.warn "[!] too many IMAGE_IMPORT_DESCRIPTORs, not reading more than #{r.size}"
        break
      end
      t = IMAGE_IMPORT_DESCRIPTOR.read(f)
      break if t.Name.to_i == 0 # also catches EOF
      r << t
      file_offset += IMAGE_IMPORT_DESCRIPTOR::SIZE
    end
  else
    logger.warn "[?] imports info beyond EOF"
  end

  n_bad_names = 0
  logger.warn "[?] non-empty last IMAGE_IMPORT_DESCRIPTOR: #{t.inspect}" if t && !t.empty?
  @imports = r
  r = nil
  @imports.each_with_index do |x, iidx|
    if n_bad_names > MAX_ERRORS
      logger.warn "[!] too many bad imported function names. skipping further imports parsing"
      @imports = @imports[0,iidx]
      break
    end
    if x.Name.to_i != 0 && (ofs = va2file(x.Name))
      begin
      f.seek ofs
      rescue
        logger.warn "[?] cannot seek to #{ofs} (VA=0x#{x.Name.to_i.to_s(16)} for reading imports, skipped"
        next
      end
      x.module_name = f.gets("\x00").to_s.chomp("\x00")
    end
    [:original_first_thunk, :first_thunk].each do |tbl|
      camel = tbl.capitalize.to_s.gsub(/_./){ |char| char[1..-1].upcase}
      if x[camel].to_i != 0 && (ofs = va2file(x[camel])) && f.checked_seek(ofs)
        x[tbl] ||= []
        if pe.x64?
          x[tbl] << t while (t = f.read(8).to_s.unpack('Q').first).to_i != 0
        else
          x[tbl] << t while (t = f.read(4).to_s.unpack('V').first).to_i != 0
        end
      end
      cache = {}
      bits = pe.x64? ? 64 : 32
      mask = 2**(bits-1)
      idx = -1
      x[tbl] && x[tbl].map! do |t|
        idx += 1
        va = x[camel].to_i + idx*4
        cache[t] ||=
          if t & mask > 0                                 # 0x8000_0000(_0000_0000)
            ImportedFunction.new(nil,nil,t & (mask-1),va) # 0x7fff_ffff(_ffff_ffff)
          elsif ofs=va2file(t, :quiet => true)
            if !f.checked_seek(ofs) || f.eof?
              logger.warn "[?] import ofs 0x#{ofs.to_s(16)} VA=0x#{t.to_s(16)} beyond EOF"
              nil
            else
              hint = f.read(2).unpack('v').first
              name = f.gets("\x00").to_s.chomp("\x00")
              if !name.empty? && name !~ GOOD_FUNCTION_NAME_RE
                n_bad_names += 1
                if n_bad_names > MAX_ERRORS
                  nil
                else
                  ImportedFunction.new(hint, name, nil, va)
                end
              else
                ImportedFunction.new(hint, name, nil, va)
              end
            end
          elsif tbl == :original_first_thunk
            # OriginalFirstThunk entries can not be invalid, show a warning msg
            logger.warn "[?] invalid VA 0x#{t.to_s(16)} in #{camel}[#{idx}] for #{x.module_name}"
            nil
          elsif tbl == :first_thunk
            # FirstThunk entries can be invalid, so `info` msg only
            logger.info "[?] invalid VA 0x#{t.to_s(16)} in #{camel}[#{idx}] for #{x.module_name}"
            nil
          else
            raise "You are not supposed to be here! O_o"
          end
      end
      x[tbl] && x[tbl].compact!
    end # [:original_first_thunk, :first_thunk].each
    if x.original_first_thunk && !x.first_thunk
      logger.warn "[?] import table: empty FirstThunk for #{x.module_name}"
    elsif !x.original_first_thunk && x.first_thunk
      logger.info "[?] import table: empty OriginalFirstThunk for #{x.module_name}"
    elsif logger.debug?
      # compare all but VAs
      if x.original_first_thunk != x.first_thunk
        logger.debug "[?] import table: OriginalFirstThunk != FirstThunk for #{x.module_name}"
      end
    end
  end # r.each
  @imports
end
resource_directory(f=@io) click to toggle source
# File lib/pedump/resources.rb, line 3
def resource_directory f=@io
  @resource_directory ||=
    if pe(f)
      _read_resource_directory_tree(f)
    elsif ne(f)
      ne(f).resource_directory(f)
    end
end
resources(f=@io) click to toggle source

resources

# File lib/pedump.rb, line 893
def resources f=@io
  @resources ||=
    if pe(f)
      _scan_pe_resources(f)
    elsif ne(f)
      ne(f).resources(f)
    end
end
rich(f=@io)
Alias for: rich_hdr
rich_hdr(f=@io) click to toggle source
# File lib/pedump.rb, line 393
def rich_hdr f=@io
  dos_stub(f) && @rich_hdr
end
Also aliased as: rich_header, rich
rich_header(f=@io)
Alias for: rich_hdr
section_table(f=@io)
Alias for: sections
sections(f=@io) click to toggle source
# File lib/pedump.rb, line 478
def sections f=@io
  if pe(f)
    pe.section_table
  elsif ne(f)
    ne.segments
  elsif te(f)
    te.sections
  end
end
Also aliased as: section_table
security(f=@io) click to toggle source
# File lib/pedump/security.rb, line 2
def security f=@io
  return nil unless pe(f) && pe(f).ioh && f
  dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::SECURITY]
  return nil if !dir || dir.va == 0

  # IMAGE_DIRECTORY_ENTRY_SECURITY
  # Points to a list of WIN_CERTIFICATE structures, defined in WinTrust.H.
  # Not mapped into memory as part of the image.
  # Therefore, the VirtualAddress field is a file offset, rather than an RVA.
  #
  # http://msdn.microsoft.com/en-us/magazine/bb985997.aspx

  f.seek dir.va
  r = []
  ofs = f.tell
  while !f.eof? && (f.tell-ofs < dir.size)
    r << WIN_CERTIFICATE.read(f)
  end
  r
end
Also aliased as: signature
signature(f=@io)
Alias for: security
strings(f=@io) click to toggle source
# File lib/pedump/resources.rb, line 227
def strings f=@io
  r = []
  Array(resources(f)).find_all{ |x| x.type == 'STRING'}.each do |res|
    res.data.each_with_index do |string,idx|
      r << STRING.new( ((res.id.to_i-1)<<4) + idx, res.lang, string ) unless string.empty?
    end
  end
  r
end
supported_file?(f=@io) click to toggle source
# File lib/pedump.rb, line 489
def supported_file? f=@io
  pos = f.tell
  sig = f.read(2)
  f.seek(pos)
  if SUPPORTED_SIGNATURES.include?(sig)
    true
  else
    unless @not_supported_sig_warned
      msg = "no supported signature. want: #{SUPPORTED_SIGNATURES.join("/")}, got: #{sig.inspect}"
      if @force
        logger.warn  "[?] #{msg}"
      else
        logger.error "[!] #{msg}. (not forced)"
      end
      @not_supported_sig_warned = true
    end
    false
  end
end
te(f=@io) click to toggle source
# File lib/pedump/te.rb, line 48
def te f=@io
  return @te if defined?(@te)
  @te ||=
    begin
      te_offset = 0
      f.seek te_offset
      if f.read(2) == 'VZ'
        f.seek te_offset
        EFI_TE_IMAGE_HEADER.read f, :force => @force
      else
        nil
      end
    end
end
te?() click to toggle source
# File lib/pedump.rb, line 527
def te?
  _detect_format() == :te
end
te_shift() click to toggle source
# File lib/pedump/te.rb, line 40
def te_shift
  if @te
    @te.StrippedSize - EFI_TE_IMAGE_HEADER::REAL_SIZE
  else
    0
  end
end
tls(f=@io) click to toggle source

TLS

# File lib/pedump.rb, line 866
def tls f=@io
  @tls ||= pe(f) && pe(f).ioh && f &&
    begin
      dir = @pe.ioh.DataDirectory[IMAGE_DATA_DIRECTORY::TLS]
      return nil if !dir || dir.va == 0
      return nil unless file_offset = va2file(dir.va)
      f.seek file_offset
      if f.eof?
        logger.info "[?] TLS info beyond EOF"
        return nil
      end

      klass = @pe.x64? ? IMAGE_TLS_DIRECTORY64 : IMAGE_TLS_DIRECTORY32
      nEntries = [1,dir.size / klass.const_get('SIZE')].max
      r = []
      nEntries.times do
        break if f.eof? || !(entry = klass.read(f))
        r << entry
      end
      r
    end
end
va2file(va, h={}) click to toggle source
# File lib/pedump.rb, line 399
def va2file va, h={}
  return nil if va.nil?

  va0 = va # save for log output of original addr
  if pe?
    # most common case, do nothing
  elsif te?
    va = va - te_shift()
  end

  sections.each do |s|
    if (s.VirtualAddress...(s.VirtualAddress+s.VirtualSize)).include?(va)
      offset = va - s.VirtualAddress
      return (s.PointerToRawData + offset) if offset < s.SizeOfRawData
    end
  end

  # not found with regular search. assume any of VirtualSize was 0, and try with RawSize
  sections.each do |s|
    if (s.VirtualAddress...(s.VirtualAddress+s.SizeOfRawData)).include?(va)
      offset = va - s.VirtualAddress
      return (s.PointerToRawData + offset) if offset < s.SizeOfRawData
    end
  end

  # still not found, bad/zero VirtualSizes & RawSizes ?

  # a special case - PE without sections
  return va if sections.empty?

  # check if only one section
  if sections.size == 1 || sections.all?{ |s| s.VirtualAddress.to_i == 0 }
    s = sections.first
    offset = va - s.VirtualAddress
    return (s.PointerToRawData + offset) if offset < s.SizeOfRawData
    #return va - s.VirtualAddress + s.PointerToRawData
  end

  # TODO: not all VirtualAdresses == 0 case

  if h[:quiet]
    logger.debug "[?] can't find file_offset of VA 0x#{va0.to_i.to_s(16)} (quiet=true)"
  else
    logger.error "[?] can't find file_offset of VA 0x#{va0.to_i.to_s(16)}"
  end
  nil
end
version_info(f=@io) click to toggle source
# File lib/pedump.rb, line 902
def version_info f=@io
  resources(f) && resources(f).find_all{ |res| res.type == 'VERSION' }.map(&:data).flatten
end