## # $Id$ # Version: $Revision$ ## ## # Rex::OLE - an OLE implementation # written in 2010 by Joshua J. Drake ## module Rex module OLE # # This class serves as the base class for SubStorage, Stream, and Directory head # class DirEntry attr_accessor :sid attr_accessor :_sidChild, :_sidLeftSib, :_sidRightSib def initialize(stg) @stg = stg # default to a root entry :) @sid = 0 @_ab = "Root Entry" @_cb = nil # NOTE: this is not used until pack @_mse = STGTY_ROOT @_bflags = 0 @_sidLeftSib = SECT_FREE @_sidRightSib = SECT_FREE @_sidChild = SECT_FREE @_clsId = CLSID.new @_dwUserFlags = 0 @_ctime = "\x00" * 8 @_mtime = "\x00" * 8 @_sectStart = SECT_END @_ulSize = 0 # keep track of logical children (in a tree) @children = [] end def length @_ulSize end def <<(expr) @children << expr end def each @children.each { |de| yield de } end def type @_mse end def type=(arg) @_mse = arg end def name @_ab end def name=(arg) # XXX: validate? @_ab = arg end def start_sector @_sectStart end def start_sector=(expr) @_sectStart = expr end # NOTE: this will not look at children def find_stream_by_name_and_type(name, type) @children.each { |de| next if (de.type != type) if (de.name == name) return de end } nil end def find_by_sid(sid, de=self) if (de.sid == sid) return de end @children.each { |cde| ret = find_by_sid(cde, sid) if (ret) return ret end } nil end # # low-level functions # def from_s(sid, buf) @sid = sid @_ab = Util.getUnicodeString(buf[0x00,64]) @_cb = Util.get16(buf, 0x40) # too big? if (@_cb > 0x40) raise RuntimeError, 'Invalid directory entry name length %#x' % @_cb end # mismatch? if (@_ab.length > 0) declen = ((@_cb) / 2) - 1 if (declen != @_ab.length) raise RuntimeError, 'Directory entry name and length mismatch (%d != %d)' % [declen, @_ab.length] end end @_mse = Util.get8(buf, 0x42) @_bflags = Util.get8(buf, 0x43) @_sidLeftSib = Util.get32(buf, 0x44) @_sidRightSib = Util.get32(buf, 0x48) @_sidChild = Util.get32(buf, 0x4c) # only used for storages.. @_clsId = CLSID.new(buf[0x50,16]) @_dwUserFlags = Util.get32(buf, 0x60) @_ctime = buf[0x64,8] @_mtime = buf[0x6c,8] # only used for streams... @_sectStart = Util.get32(buf, 0x74) if (@stg.header._uMajorVersion == 4) @_ulSize = Util.get64(buf, 0x78) else @_ulSize = Util.get32(buf, 0x78) end # ignore _dptPropType and pad end def pack @_sectStart ||= SECT_END @_cb = (@_ab.length + 1) * 2 data = "" data << Util.putUnicodeString(@_ab) # gets padded/truncated to 0x40 bytes data << Util.pack16(@_cb) data << Util.pack8(@_mse) data << Util.pack8(@_bflags) data << Util.pack32(@_sidLeftSib) data << Util.pack32(@_sidRightSib) data << Util.pack32(@_sidChild) data << @_clsId.pack data << Util.pack32(@_dwUserFlags) data << @_ctime data << @_mtime data << Util.pack32(@_sectStart) data << Util.pack64(@_ulSize) data end def to_s(extra_spaces=0) @_sectStart ||= SECT_END @_cb = (@_ab.length + 1) * 2 spstr = " " * extra_spaces ret = "%s{\n" % spstr ret << "%s sid => 0x%x" % [spstr, @sid] ret << ",\n" ret << "%s _ab => \"%s\"" % [spstr, Util.Printable(@_ab)] ret << ",\n" ret << "%s _cb => 0x%04x" % [spstr, @_cb] ret << ",\n" ret << "%s _mse => 0x%02x" % [spstr, @_mse] ret << ",\n" ret << "%s _bflags => 0x%02x" % [spstr, @_bflags] ret << ",\n" ret << "%s _sidLeftSib => 0x%08x" % [spstr, @_sidLeftSib] ret << ",\n" ret << "%s _sidRightSib => 0x%08x" % [spstr, @_sidRightSib] ret << ",\n" ret << "%s _sidChild => 0x%08x" % [spstr, @_sidChild] ret << ",\n" ret << "%s _clsId => %s" % [spstr, @_clsId.to_s] ret << ",\n" ret << "%s _dwUserFlags => 0x%08x" % [spstr, @_dwUserFlags] ret << ",\n" ret << "%s _ctime => %s" % [spstr, Rex::Text.to_hex_dump(@_ctime).strip] ret << "\n" ret << "%s _mtime => %s" % [spstr, Rex::Text.to_hex_dump(@_mtime).strip] ret << "\n" ret << "%s _sectStart => 0x%08x" % [spstr, @_sectStart] ret << ",\n" ret << "%s _ulSize => 0x%016x" % [spstr, @_ulSize] if (@_mse == STGTY_STREAM) ret << ",\n" ret << "%s data =>\n" % spstr if (@data) #ret << Util.Printable(@data) ret << Rex::Text.to_hex_dump(@data).strip else if (@_ulSize > 0) ret << "--NOT OPENED YET--" end end elsif (@_mse == STGTY_STORAGE) or (@_mse == STGTY_ROOT) if (@children.length > 0) ret << ",\n" ret << "%s *children* =>\n" % spstr @children.each { |de| ret << de.to_s(extra_spaces+2) ret << "\n" } end end ret << "\n" ret << "%s}" % spstr end end end end