232 lines
5.0 KiB
Ruby
232 lines
5.0 KiB
Ruby
##
|
|
# $Id$
|
|
# Version: $Revision$
|
|
##
|
|
|
|
##
|
|
# Rex::OLE - an OLE implementation
|
|
# written in 2010 by Joshua J. Drake <jduck [at] metasploit.com>
|
|
##
|
|
|
|
module Rex
|
|
module OLE
|
|
|
|
require 'rex/ole/direntry'
|
|
|
|
#
|
|
# This class serves as the root directory entry in addition to
|
|
# an abstraction around the concept of a directory as a whole.
|
|
#
|
|
class Directory < DirEntry
|
|
|
|
# XXX: num_entries is not maintained once a stream/storage is added!
|
|
attr_accessor :num_entries
|
|
|
|
def initialize(stg)
|
|
super
|
|
|
|
@num_entries = 1
|
|
end
|
|
|
|
|
|
# woop, recursive each
|
|
def yield_entries(de, &block)
|
|
block.call(de)
|
|
de.each { |el|
|
|
yield_entries(el, &block)
|
|
}
|
|
end
|
|
def each_entry(&block)
|
|
yield_entries(self, &block)
|
|
end
|
|
|
|
|
|
def set_ministream_params(start, size)
|
|
@_sectStart = start
|
|
@_ulSize = size
|
|
end
|
|
|
|
def link_item(parent, child)
|
|
# set sid, advance count
|
|
child.sid = @num_entries
|
|
@num_entries += 1
|
|
|
|
# link item to siblings and/or parent
|
|
if (parent._sidChild == DIR_NOSTREAM)
|
|
parent._sidChild = child.sid
|
|
dlog("Linking #{child.name} as THE child of #{parent.name} as sid #{child.sid}", 'rex', LEV_3)
|
|
else
|
|
sib = nil
|
|
parent.each { |el|
|
|
if (el._sidLeftSib == DIR_NOSTREAM)
|
|
sib = el
|
|
el._sidLeftSib = child.sid
|
|
dlog("Linking #{child.name} as the LEFT sibling of #{sib.name} as sid #{child.sid}", 'rex', LEV_3)
|
|
break
|
|
end
|
|
if (el._sidRightSib == DIR_NOSTREAM)
|
|
sib = el
|
|
el._sidRightSib = child.sid
|
|
dlog("Linking #{child.name} as the RIGHT sibling of #{sib.name} as sid #{child.sid}", 'rex', LEV_3)
|
|
break
|
|
end
|
|
}
|
|
if (not sib)
|
|
raise RuntimeError, 'Unable to find a sibling to link to in the directory'
|
|
end
|
|
end
|
|
parent << child
|
|
end
|
|
|
|
|
|
#
|
|
# low-level functions
|
|
#
|
|
def from_s(sid, buf)
|
|
super
|
|
|
|
if (@_sidRightSib != DIR_NOSTREAM)
|
|
raise RuntimeError, 'Root Entry is invalid! (has right sibling)'
|
|
end
|
|
if (@_sidLeftSib != DIR_NOSTREAM)
|
|
raise RuntimeError, 'Root Entry is invalid! (has left sibling)'
|
|
end
|
|
end
|
|
|
|
def read
|
|
@children = []
|
|
visited = []
|
|
entries = []
|
|
root_node = nil
|
|
sect = @stg.header._sectDirStart
|
|
while (sect != SECT_END)
|
|
|
|
if (visited.include?(sect))
|
|
raise RuntimeError, 'Sector chain loop detected (0x%08x)' % sect
|
|
end
|
|
visited << sect
|
|
|
|
sbuf = @stg.read_sector(sect, @stg.header.sector_size)
|
|
while (sbuf.length >= DIRENTRY_SZ)
|
|
debuf = sbuf.slice!(0, DIRENTRY_SZ)
|
|
|
|
type = Util.get8(debuf, 0x42)
|
|
case type
|
|
when STGTY_ROOT
|
|
if (entries.length != 0)
|
|
raise RuntimeError, 'Root Entry found, but not first encountered!'
|
|
end
|
|
if (root_node)
|
|
raise RuntimeError, 'Multiple root directory sectors detected (0x%08x)' % sect
|
|
end
|
|
de = self
|
|
root_node = de
|
|
|
|
when STGTY_STORAGE
|
|
de = SubStorage.new @stg
|
|
|
|
when STGTY_STREAM
|
|
de = Stream.new @stg
|
|
|
|
when STGTY_INVALID
|
|
# skip invalid entries
|
|
next
|
|
|
|
else
|
|
raise RuntimeError, 'Unsupported directory entry type (0x%02x)' % type
|
|
end
|
|
|
|
# read content
|
|
de.from_s(entries.length, debuf)
|
|
entries << de
|
|
end
|
|
sect = @stg.next_sector(sect)
|
|
end
|
|
|
|
@num_entries = entries.length
|
|
|
|
# sort out the tree structure, starting with the root
|
|
if (@_sidChild != DIR_NOSTREAM)
|
|
populate_children(entries, root_node, @_sidChild)
|
|
end
|
|
end
|
|
|
|
|
|
# recursively add entries to their proper parents :)
|
|
def populate_children(entries, parent, sid)
|
|
node = entries[sid]
|
|
dlog("populate_children(entries, \"#{parent.name}\", #{sid}) - node: #{node.name}", 'rex', LEV_3)
|
|
parent << node
|
|
if (node.type == STGTY_STORAGE) and (node._sidChild != DIR_NOSTREAM)
|
|
populate_children(entries, node, node._sidChild)
|
|
end
|
|
if (node._sidLeftSib != DIR_NOSTREAM)
|
|
populate_children(entries, parent, node._sidLeftSib)
|
|
end
|
|
if (node._sidRightSib != DIR_NOSTREAM)
|
|
populate_children(entries, parent, node._sidRightSib)
|
|
end
|
|
end
|
|
|
|
# NOTE: this may not be necessary if we were to use each_entry
|
|
def flatten_tree(entries, parent)
|
|
entries << parent
|
|
parent.each { |el|
|
|
flatten_tree(entries, el)
|
|
}
|
|
end
|
|
|
|
|
|
def write
|
|
# flatten the directory again
|
|
entries = []
|
|
flatten_tree(entries, self)
|
|
dlog("flattened tree has #{entries.length} entries...", 'rex', LEV_3)
|
|
|
|
# count directory sectors
|
|
ds_count = entries.length / 4
|
|
if ((entries.length % 4) > 0)
|
|
# one more sector to hold the rest
|
|
ds_count += 1
|
|
end
|
|
|
|
# put the root entry first
|
|
sbuf = self.pack
|
|
|
|
# add the rest
|
|
prev_sect = nil
|
|
dir_start = nil
|
|
entries.each { |de|
|
|
# we already got the root entry, no more!
|
|
next if (de.type == STGTY_ROOT)
|
|
|
|
dir = de.pack
|
|
dlog("writing dir entry #{de.name}", 'rex', LEV_3)
|
|
sbuf << dir
|
|
|
|
if (sbuf.length == @stg.header.sector_size)
|
|
# we have a full sector, add it!
|
|
sect = @stg.write_sector(sbuf, nil, prev_sect)
|
|
prev_sect = sect
|
|
dir_start ||= sect
|
|
# reset..
|
|
sbuf = ""
|
|
end
|
|
}
|
|
|
|
# still a partial sector left?
|
|
if (sbuf.length > 0)
|
|
# add it! (NOTE: it will get padded with nul bytes if its not sector sized)
|
|
sect = @stg.write_sector(sbuf, nil, prev_sect)
|
|
prev_sect = sect
|
|
dir_start ||= sect
|
|
end
|
|
|
|
@stg.header._sectDirStart = dir_start
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
end
|