Class: Rex::OLE::Storage

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/ole/storage.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filename = nil, mode = STGM_READ) ⇒ Storage

Returns a new instance of Storage.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/rex/ole/storage.rb', line 15

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

Instance Attribute Details

#headerObject

Returns the value of attribute header.



13
14
15
# File 'lib/rex/ole/storage.rb', line 13

def header
  @header
end

Instance Method Details

#closeObject



77
78
79
80
81
82
# File 'lib/rex/ole/storage.rb', line 77

def close
  if (@modified) and (@mode != STGM_READ)
    write_to_disk
  end
  @fd.close
end

#create_storage(name, mode = STGM_READ, parent_stg = nil) ⇒ Object

storage manipulation functions



149
150
151
152
153
154
155
156
# File 'lib/rex/ole/storage.rb', line 149

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) ⇒ Object

stream manipulation functions



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/rex/ole/storage.rb', line 112

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

#eachObject



36
37
38
39
40
# File 'lib/rex/ole/storage.rb', line 36

def each
  @directory.each { |el|
    yield el
  }
end

#inspectObject



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/rex/ole/storage.rb', line 84

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

#nameObject



43
44
45
# File 'lib/rex/ole/storage.rb', line 43

def name
  @filename
end

#next_mini_sector(sect) ⇒ Object



384
385
386
387
# File 'lib/rex/ole/storage.rb', line 384

def next_mini_sector(sect)
  return SECT_END if (sect >= @minifat.length)
  @minifat[sect]
end

#next_sector(sect) ⇒ Object



369
370
371
372
# File 'lib/rex/ole/storage.rb', line 369

def next_sector(sect)
  return SECT_END if (sect >= @fat.length)
  @fat[sect]
end

#open(filename, mode) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/rex/ole/storage.rb', line 48

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) ⇒ Object



158
159
160
# File 'lib/rex/ole/storage.rb', line 158

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) ⇒ Object



135
136
137
138
139
140
141
142
143
# File 'lib/rex/ole/storage.rb', line 135

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) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/rex/ole/storage.rb', line 293

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) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/rex/ole/storage.rb', line 322

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) ⇒ Object



375
376
377
378
379
380
381
382
# File 'lib/rex/ole/storage.rb', line 375

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) ⇒ Object



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/rex/ole/storage.rb', line 352

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) ⇒ Object



285
286
287
288
289
290
291
# File 'lib/rex/ole/storage.rb', line 285

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) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/rex/ole/storage.rb', line 218

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) ⇒ Object



238
239
240
241
# File 'lib/rex/ole/storage.rb', line 238

def write_mini_sector_raw(sect, sbuf)
  dlog("Writing mini sector 0x%02x" % sect, 'rex', LEV_3)
  @ministream << sbuf
end

#write_mini_stream(stm) ⇒ Object



272
273
274
275
276
277
278
279
280
281
282
# File 'lib/rex/ole/storage.rb', line 272

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) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/rex/ole/storage.rb', line 189

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) ⇒ Object



211
212
213
214
215
# File 'lib/rex/ole/storage.rb', line 211

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) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/rex/ole/storage.rb', line 259

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_diskObject

low-level functions



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/rex/ole/storage.rb', line 166

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_dataObject



245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/rex/ole/storage.rb', line 245

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