class Rex::OLE::Storage

Attributes

header[RW]

Public Class Methods

new(filename=nil, mode=STGM_READ) click to toggle source
# File lib/rex/ole/storage.rb, line 14
def initialize(filename=nil, mode=STGM_READ)
  @mode = mode
  @modified = nil

  @fd = nil
  @filename = nil
  @header = Header.new
  @difat = DIFAT.new self
  @fat = FAT.new self
  @minifat = MiniFAT.new self
  @directory = Directory.new self
  @ministream = Stream.new self

  if (filename)
    @filename = filename
    open(filename, mode)
    return
  end
end

Public Instance Methods

close() click to toggle source
# File lib/rex/ole/storage.rb, line 76
def close
  if (@modified) and (@mode != STGM_READ)
    write_to_disk
  end
  @fd.close
end
create_storage(name, mode=STGM_READ, parent_stg=nil) click to toggle source

storage manipulation functions

# File lib/rex/ole/storage.rb, line 148
def create_storage(name, mode=STGM_READ, parent_stg=nil)
  stg = SubStorage.new self
  stg.name = name
  parent_stg ||= @directory
  dlog("Adding storage #{name} to storage #{parent_stg.name}", 'rex', LEV_3)
  @directory.link_item(parent_stg, stg)
  stg
end
create_stream(name, mode=STGM_WRITE, parent_stg=nil) click to toggle source

stream manipulation functions

# File lib/rex/ole/storage.rb, line 111
  def create_stream(name, mode=STGM_WRITE, parent_stg=nil)
    if (stm = open_stream(name, mode, parent_stg))
      stm.close
      return nil
    end

    # eek, don't check the name for now
    # if we do, we cant create alot of streams (summary info for example)
=begin
    if (not Util.name_is_valid(name))
      return nil
    end
=end

    stm = Stream.new self
    stm.name = name
    parent_stg ||= @directory
    dlog("Adding stream #{name} to storage #{parent_stg.name}", 'rex', LEV_3)
    @directory.link_item(parent_stg, stm)
    @modified = true
    stm
  end
each() { |el| ... } click to toggle source
# File lib/rex/ole/storage.rb, line 35
def each
  @directory.each { |el|
    yield el
  }
end
inspect() click to toggle source
# File lib/rex/ole/storage.rb, line 83
def inspect
  ret = ""
  ret << "header = %s\n" % @header.to_s

  ret << "*** %u DIFAT sectors\n" % @difat.length
  ret << @difat.to_s << "\n"

  ret << "*** %u FAT sectors\n" % @fat.length
  ret << @fat.to_s << "\n"

  ret << "*** %u MiniFAT sectors:\n" % @minifat.length
  if (@minifat.length > 0)
    ret << @minifat.to_s << "\n"
  end

  ret << "*** ministream (%u bytes):\n" % @ministream.length
  if (@ministream.length > 0)
    ret << @ministream.to_s << "\n"
  end

  ret << "*** %u directory entries\n" % @directory.num_entries
  ret << @directory.to_s << "\n"
end
name() click to toggle source
# File lib/rex/ole/storage.rb, line 42
def name
  @filename
end
next_mini_sector(sect) click to toggle source
# File lib/rex/ole/storage.rb, line 383
def next_mini_sector(sect)
  return SECT_END if (sect >= @minifat.length)
  @minifat[sect]
end
next_sector(sect) click to toggle source
# File lib/rex/ole/storage.rb, line 368
def next_sector(sect)
  return SECT_END if (sect >= @fat.length)
  @fat[sect]
end
open(filename, mode) click to toggle source
# File lib/rex/ole/storage.rb, line 47
def open(filename, mode)
  if (mode == STGM_READWRITE)
    fmode = 'r+b'
  elsif (mode == STGM_WRITE)
    fmode = 'w+b'
  else
    fmode = 'rb'
  end

  @fd = File.new(filename, fmode)

  # don't read for new files
  if (mode == STGM_WRITE)
    # ensure there is a root
    write_to_disk
    return
  end

  # parse the header
  @header.read @fd
  @difat.read
  @fat.read @difat
  @minifat.read
  @directory.read
  # NOTE: we can't use read_stream_data here (must read using regular FAT, regardless of size)
  # read data using the root node's start/length
  @ministream << read_data(@directory)
end
open_storage(name, mode=STGM_READ, parent_stg=nil) click to toggle source
# File lib/rex/ole/storage.rb, line 157
def open_storage(name, mode=STGM_READ, parent_stg=nil)
  @directory.find_stream_by_name_and_type(name, STGTY_STORAGE)
end
open_stream(name, mode=STGM_READ, parent_stg=nil) click to toggle source
# File lib/rex/ole/storage.rb, line 134
def open_stream(name, mode=STGM_READ, parent_stg=nil)
  parent_stg ||= @directory
  stm = parent_stg.find_stream_by_name_and_type(name, STGTY_STREAM)
  if (stm)
    # TODO: optimize out the need to read all of the data up-front
    stm << read_stream_data(stm)
  end
  stm
end
read_data(direntry) click to toggle source
# File lib/rex/ole/storage.rb, line 292
def read_data(direntry)
  ret = ""
  visited = []
  left = direntry.length
  sect = direntry.start_sector
  while (sect != SECT_END)
    if (visited.include?(sect))
      raise RuntimeError, 'Sector chain loop detected (0x%08x)' % sect
    end
    visited << sect

    # how much to read?
    block = @header.sector_size
    block = left if (block > left)

    # read it.
    dlog("read_data - reading 0x%x bytes" % block, 'rex', LEV_3)
    buf = read_sector(sect, block)
    ret << buf
    left -= buf.length

    # done?
    break if (left == 0)

    sect = next_sector(sect)
  end
  ret
end
read_data_mini(direntry) click to toggle source
# File lib/rex/ole/storage.rb, line 321
def read_data_mini(direntry)
  ret = ""
  visited = []
  left = direntry.length
  sect = direntry.start_sector
  while (sect != SECT_END)
    if (visited.include?(sect))
      raise RuntimeError, 'Sector chain loop detected (0x%08x mini)' % sect
    end
    visited << sect

    # how much to read?
    block = @header.mini_sector_size
    block = left if (block > left)

    # read it.
    dlog("read_data_mini - reading 0x%x bytes" % block, 'rex', LEV_3)
    buf = read_mini_sector(sect, block)
    ret << buf
    left -= buf.length

    # done?
    break if (left == 0)

    sect = next_mini_sector(sect)
  end
  ret
end
read_mini_sector(sect, len) click to toggle source
# File lib/rex/ole/storage.rb, line 374
def read_mini_sector(sect, len)
  dlog("Reading mini sector 0x%x" % sect, 'rex', LEV_3)
  off = (@header.mini_sector_size * sect)
  dlog("Reading from offset 0x%x of ministream" % off, 'rex', LEV_3)
  @ministream.seek(off)
  data = @ministream.read(len)
  data
end
read_sector(sect, len) click to toggle source
# File lib/rex/ole/storage.rb, line 351
def read_sector(sect, len)
  off = ((sect + 1) * @header.sector_size)
  @fd.seek(off, ::IO::SEEK_SET)
  buf = @fd.read(len)
  if (not buf)
    if (@fd.eof?)
      raise RuntimeError, 'EOF while reading sector data (0x%08x)' % sect
    else
      raise RuntimeError, 'Unknown error while reading sector data (0x%08x)' % sect
    end
  end
  if (buf.length != len)
    raise RuntimeError, 'Insufficient data for sector (0x%08x): got %u of %u' % [sect, buf.length, len]
  end
  buf
end
read_stream_data(direntry) click to toggle source
# File lib/rex/ole/storage.rb, line 284
def read_stream_data(direntry)
  if (direntry.length < @header._ulMiniSectorCutoff)
    return read_data_mini(direntry)
  end

  read_data(direntry)
end
write_mini_sector(sbuf, prev_sect=nil) click to toggle source
# File lib/rex/ole/storage.rb, line 217
def write_mini_sector(sbuf, prev_sect=nil)
  len = sbuf.length
  if (len != @header.mini_sector_size)
    if (len < @header.mini_sector_size)
      sbuf = sbuf.dup
      sbuf << "\x00" * (@header.mini_sector_size - len)
    else
      raise RuntimeError, 'not mini sector sized!'
    end
  end

  idx = @minifat.allocate_sector
  # point the previous mini sector to here
  if (prev_sect)
    @minifat[prev_sect] = idx
  end
  write_mini_sector_raw(idx, sbuf)
  idx
end
write_mini_sector_raw(sect, sbuf) click to toggle source
# File lib/rex/ole/storage.rb, line 237
def write_mini_sector_raw(sect, sbuf)
  dlog("Writing mini sector 0x%02x" % sect, 'rex', LEV_3)
  @ministream << sbuf
end
write_mini_stream(stm) click to toggle source
# File lib/rex/ole/storage.rb, line 271
def write_mini_stream(stm)
  dlog("Writing \"%s\" to mini stream" % stm.name, 'rex', LEV_3)
  prev_sect = nil
  stm.seek(0)
  while (sbuf = stm.read(@header.mini_sector_size))
    sect = write_mini_sector(sbuf, prev_sect)
    stm_start ||= sect
    prev_sect = sect
  end
  stm_start
end
write_sector(sbuf, type=nil, prev_sect=nil) click to toggle source
# File lib/rex/ole/storage.rb, line 188
def write_sector(sbuf, type=nil, prev_sect=nil)
  len = sbuf.length
  if (len != @header.sector_size)
    # pad it if less
    if (len < @header.sector_size)
      sbuf = sbuf.dup
      sbuf << "\x00" * (@header.sector_size - len)
    else
      raise RuntimeError, 'not sector sized!'
    end
  end

  # write the data
  idx = @fat.allocate_sector(type)
  # point previous sector to here
  if (prev_sect)
    @fat[prev_sect] = idx
  end
  write_sector_raw(idx, sbuf)
  return idx
end
write_sector_raw(sect, sbuf) click to toggle source
# File lib/rex/ole/storage.rb, line 210
def write_sector_raw(sect, sbuf)
  dlog("Writing sector 0x%02x" % sect, 'rex', LEV_3)
  @fd.seek((sect + 1) * @header.sector_size, ::IO::SEEK_SET)
  @fd.write(sbuf)
end
write_stream(stm) click to toggle source
# File lib/rex/ole/storage.rb, line 258
def write_stream(stm)
  dlog("Writing \"%s\" to regular stream" % stm.name, 'rex', LEV_3)
  stm_start = nil
  prev_sect = nil
  stm.seek(0)
  while (sbuf = stm.read(@header.sector_size))
    sect = write_sector(sbuf, nil, prev_sect)
    stm_start ||= sect
    prev_sect = sect
  end
  stm_start
end
write_to_disk() click to toggle source

low-level functions

# File lib/rex/ole/storage.rb, line 165
def write_to_disk
  # reset  FAT/DIFAT
  @difat = DIFAT.new self
  @fat = FAT.new self

  @header.write @fd
  write_user_data

  # NOTE: we call write_stream here since we MUST write this to
  # the regular stream (regardless of size)
  ms_start = write_stream(@ministream)
  @directory.set_ministream_params(ms_start, @ministream.length)

  @minifat.write
  @directory.write
  @fat.write(@difat)
  @difat.write

  # write it again, now that its complete
  @header.write @fd
  @fd.flush
end
write_user_data() click to toggle source
# File lib/rex/ole/storage.rb, line 244
def write_user_data
  @directory.each_entry { |stm|
    # only regular streams this pass
    next if (stm.type != STGTY_STREAM)

    if (stm.length >= @header._ulMiniSectorCutoff)
      stm.start_sector = write_stream(stm)
    else
      # NOTE: stm_start is a minifat value
      stm.start_sector = write_mini_stream(stm)
    end
  }
end