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.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/rex/ole/storage.rb', line 19

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.



17
18
19
# File 'lib/rex/ole/storage.rb', line 17

def header
  @header
end

Instance Method Details

#closeObject



81
82
83
84
85
86
# File 'lib/rex/ole/storage.rb', line 81

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



153
154
155
156
157
158
159
160
# File 'lib/rex/ole/storage.rb', line 153

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



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/rex/ole/storage.rb', line 116

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



40
41
42
43
44
# File 'lib/rex/ole/storage.rb', line 40

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

#inspectObject



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/rex/ole/storage.rb', line 88

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



47
48
49
# File 'lib/rex/ole/storage.rb', line 47

def name
	@filename
end

#next_mini_sector(sect) ⇒ Object



388
389
390
391
# File 'lib/rex/ole/storage.rb', line 388

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

#next_sector(sect) ⇒ Object



373
374
375
376
# File 'lib/rex/ole/storage.rb', line 373

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

#open(filename, mode) ⇒ Object



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

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



162
163
164
# File 'lib/rex/ole/storage.rb', line 162

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



139
140
141
142
143
144
145
146
147
# File 'lib/rex/ole/storage.rb', line 139

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



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

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



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

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



379
380
381
382
383
384
385
386
# File 'lib/rex/ole/storage.rb', line 379

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



356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/rex/ole/storage.rb', line 356

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



289
290
291
292
293
294
295
# File 'lib/rex/ole/storage.rb', line 289

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



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/rex/ole/storage.rb', line 222

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



242
243
244
245
# File 'lib/rex/ole/storage.rb', line 242

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



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/rex/ole/storage.rb', line 276

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



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/rex/ole/storage.rb', line 193

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



215
216
217
218
219
# File 'lib/rex/ole/storage.rb', line 215

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



263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/rex/ole/storage.rb', line 263

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



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/rex/ole/storage.rb', line 170

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



249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/rex/ole/storage.rb', line 249

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