rex-ole 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,141 @@
1
+ # -*- coding: binary -*-
2
+
3
+ ##
4
+ # Rex::OLE - an OLE implementation
5
+ # written in 2010 by Joshua J. Drake <jduck [at] metasploit.com>
6
+ ##
7
+
8
+ module Rex
9
+ module OLE
10
+
11
+ class Property
12
+
13
+ def initialize(id, type, data)
14
+ @id = id
15
+ @type = type
16
+ @data = data
17
+ end
18
+
19
+ def pack_pio(off = 0)
20
+ [ @id, off ].pack('V*')
21
+ end
22
+
23
+ def pack_data
24
+ buf = [ @type ].pack('V')
25
+ case @type
26
+ when VT_BLOB
27
+ buf << [ @data.length ].pack('V')
28
+ when VT_CF
29
+ buf << [ 4 + @data.length, -1 ].pack('V*')
30
+ end
31
+ buf << @data
32
+ buf
33
+ end
34
+
35
+ def to_s
36
+ "Rex::OLE::Property - to_s unimplemented"
37
+ end
38
+
39
+ end
40
+
41
+ class PropertySet
42
+
43
+ def initialize(fmtid = nil)
44
+ @fmtid = CLSID.new(fmtid)
45
+ @properties = []
46
+ end
47
+
48
+ def <<(val)
49
+ @properties << val
50
+ end
51
+
52
+ def pack_fno(off = 0)
53
+ @fmtid.pack + [ off ].pack('V')
54
+ end
55
+
56
+ def pack_data
57
+ # Pack all the property data
58
+ data = []
59
+ dlen = 0
60
+ @properties.each { |p|
61
+ dat = p.pack_data
62
+ dlen += dat.length
63
+ data << dat
64
+ }
65
+
66
+ buf = ''
67
+ # First the header
68
+ off = 8 + (@properties.length * 8)
69
+ buf << [ off + dlen, @properties.length ].pack('V*')
70
+ # Now, the Property Id and Offset for each
71
+ @properties.each_with_index { |p,x|
72
+ buf << p.pack_pio(off)
73
+ off += data[x].length
74
+ }
75
+ # Finally, all the data
76
+ buf << data.join
77
+ buf
78
+ end
79
+
80
+ def to_s
81
+ "Rex::OLE::PropertySet - to_s unimplemented"
82
+ end
83
+
84
+ end
85
+
86
+ class PropertySetStream
87
+
88
+ def initialize
89
+ @byte_order = 0xfffe
90
+ @ole_version = 0
91
+ @os_version = 1
92
+ @os_platform = 2
93
+ @clsid = CLSID.new
94
+
95
+ @propsets = []
96
+ end
97
+
98
+ def <<(ps)
99
+ @propsets << ps
100
+ end
101
+
102
+ def pack
103
+ buf = ''
104
+
105
+ # First, add the header
106
+ buf << [
107
+ @byte_order,
108
+ @ole_version,
109
+ @os_version,
110
+ @os_platform
111
+ ].pack('vvvv')
112
+ buf << @clsid.pack
113
+ buf << [@propsets.length].pack('V')
114
+
115
+ # Pack all the PropertySet children
116
+ data = []
117
+ @propsets.each { |p|
118
+ data << p.pack_data
119
+ }
120
+
121
+ # Next, add all the FMTID and Offset headers
122
+ off = buf.length + (20 * @propsets.length)
123
+ @propsets.each_with_index { |ps,x|
124
+ buf << ps.pack_fno(off)
125
+ off += data[x].length
126
+ }
127
+
128
+ # Finally, add all the data
129
+ buf << data.join
130
+ buf
131
+ end
132
+
133
+ def to_s
134
+ "Rex::OLE::PropertySetStream - to_s unimplemented"
135
+ end
136
+
137
+ end
138
+
139
+
140
+ end
141
+ end
@@ -0,0 +1,27 @@
1
+ # -*- coding: binary -*-
2
+
3
+ msfbase = __FILE__
4
+ while File.symlink?(msfbase)
5
+ msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
6
+ end
7
+ path = File.expand_path(File.dirname(msfbase))
8
+ path += "/../../../"
9
+ $:.unshift(path)
10
+
11
+
12
+ require 'rex/ole'
13
+
14
+ if (ARGV.length < 1)
15
+ $stderr.puts "usage: make_ole <file>"
16
+ exit(1)
17
+ end
18
+
19
+ document = ARGV.shift
20
+
21
+ if (stg = Rex::OLE::Storage.new(document, Rex::OLE::STGM_WRITE))
22
+ if (stm = stg.create_stream("testing"))
23
+ stm << "A" * 1024
24
+ stm.close
25
+ end
26
+ stg.close
27
+ end
@@ -0,0 +1,35 @@
1
+ # -*- coding: binary -*-
2
+
3
+ msfbase = __FILE__
4
+ while File.symlink?(msfbase)
5
+ msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
6
+ end
7
+ path = File.expand_path(File.dirname(msfbase))
8
+ path += "/../../../"
9
+ $:.unshift(path)
10
+
11
+
12
+ require 'rex/ole'
13
+
14
+ if (ARGV.length < 1)
15
+ $stderr.puts "usage: dir <file>"
16
+ exit(1)
17
+ end
18
+
19
+ document = ARGV.shift
20
+
21
+
22
+ # recursive printer :)
23
+ def show_entries(ent, spaces=0)
24
+ spstr = " " * spaces
25
+
26
+ puts "%s + #{ent.name}" % spstr
27
+ ent.each { |el|
28
+ show_entries(el, spaces+2)
29
+ }
30
+ end
31
+
32
+ if (stg = Rex::OLE::Storage.new(document))
33
+ show_entries(stg)
34
+ stg.close
35
+ end
@@ -0,0 +1,34 @@
1
+ # -*- coding: binary -*-
2
+
3
+ msfbase = __FILE__
4
+ while File.symlink?(msfbase)
5
+ msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
6
+ end
7
+ path = File.expand_path(File.dirname(msfbase))
8
+ path += "/../../../"
9
+ $:.unshift(path)
10
+
11
+ require 'rex/ole'
12
+
13
+ if (ARGV.length < 2)
14
+ $stderr.puts "usage: dump_stream <file> <stream>"
15
+ exit(1)
16
+ end
17
+
18
+ document = ARGV.shift
19
+ stream = ARGV.shift
20
+
21
+ if (stg = Rex::OLE::Storage.new(document))
22
+ if (stm = stg.open_stream(stream))
23
+ data = stm.read(stm.length)
24
+ data ||= ""
25
+ $stderr.puts "Successfully opened the \"%s\" stream (%u bytes)" % [stream, data.length]
26
+ $stdout.print data
27
+ stm.close
28
+ else
29
+ $stderr.puts "Unable to open stream: #{stream}"
30
+ end
31
+ stg.close
32
+ else
33
+ $stderr.puts "Unable to open storage: #{document}"
34
+ end
@@ -0,0 +1,23 @@
1
+ # -*- coding: binary -*-
2
+
3
+ msfbase = __FILE__
4
+ while File.symlink?(msfbase)
5
+ msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
6
+ end
7
+ path = File.expand_path(File.dirname(msfbase))
8
+ path += "/../../../"
9
+ $:.unshift(path)
10
+
11
+ require 'rex/ole'
12
+
13
+ if (ARGV.length < 1)
14
+ $stderr.puts "usage: ole_info <file>"
15
+ exit(1)
16
+ end
17
+
18
+ document = ARGV.shift
19
+
20
+ if (stg = Rex::OLE::Storage.new(document))
21
+ puts stg.inspect
22
+ stg.close
23
+ end
@@ -0,0 +1,392 @@
1
+ # -*- coding: binary -*-
2
+
3
+ ##
4
+ # Rex::OLE - an OLE implementation
5
+ # written in 2010 by Joshua J. Drake <jduck [at] metasploit.com>
6
+ ##
7
+
8
+ module Rex
9
+ module OLE
10
+
11
+ class Storage
12
+
13
+ attr_accessor :header
14
+
15
+ def initialize(filename=nil, mode=STGM_READ)
16
+ @mode = mode
17
+ @modified = nil
18
+
19
+ @fd = nil
20
+ @filename = nil
21
+ @header = Header.new
22
+ @difat = DIFAT.new self
23
+ @fat = FAT.new self
24
+ @minifat = MiniFAT.new self
25
+ @directory = Directory.new self
26
+ @ministream = Stream.new self
27
+
28
+ if (filename)
29
+ @filename = filename
30
+ open(filename, mode)
31
+ return
32
+ end
33
+ end
34
+
35
+
36
+ def each
37
+ @directory.each { |el|
38
+ yield el
39
+ }
40
+ end
41
+
42
+
43
+ def name
44
+ @filename
45
+ end
46
+
47
+
48
+ def open(filename, mode)
49
+ if (mode == STGM_READWRITE)
50
+ fmode = 'r+b'
51
+ elsif (mode == STGM_WRITE)
52
+ fmode = 'w+b'
53
+ else
54
+ fmode = 'rb'
55
+ end
56
+
57
+ @fd = File.new(filename, fmode)
58
+
59
+ # don't read for new files
60
+ if (mode == STGM_WRITE)
61
+ # ensure there is a root
62
+ write_to_disk
63
+ return
64
+ end
65
+
66
+ # parse the header
67
+ @header.read @fd
68
+ @difat.read
69
+ @fat.read @difat
70
+ @minifat.read
71
+ @directory.read
72
+ # NOTE: we can't use read_stream_data here (must read using regular FAT, regardless of size)
73
+ # read data using the root node's start/length
74
+ @ministream << read_data(@directory)
75
+ end
76
+
77
+ def close
78
+ if (@modified) and (@mode != STGM_READ)
79
+ write_to_disk
80
+ end
81
+ @fd.close
82
+ end
83
+
84
+ def inspect
85
+ ret = ""
86
+ ret << "header = %s\n" % @header.to_s
87
+
88
+ ret << "*** %u DIFAT sectors\n" % @difat.length
89
+ ret << @difat.to_s << "\n"
90
+
91
+ ret << "*** %u FAT sectors\n" % @fat.length
92
+ ret << @fat.to_s << "\n"
93
+
94
+ ret << "*** %u MiniFAT sectors:\n" % @minifat.length
95
+ if (@minifat.length > 0)
96
+ ret << @minifat.to_s << "\n"
97
+ end
98
+
99
+ ret << "*** ministream (%u bytes):\n" % @ministream.length
100
+ if (@ministream.length > 0)
101
+ ret << @ministream.to_s << "\n"
102
+ end
103
+
104
+ ret << "*** %u directory entries\n" % @directory.num_entries
105
+ ret << @directory.to_s << "\n"
106
+ end
107
+
108
+
109
+ #
110
+ # stream manipulation functions
111
+ #
112
+ def create_stream(name, mode=STGM_WRITE, parent_stg=nil)
113
+ if (stm = open_stream(name, mode, parent_stg))
114
+ stm.close
115
+ return nil
116
+ end
117
+
118
+ # eek, don't check the name for now
119
+ # if we do, we cant create alot of streams (summary info for example)
120
+ =begin
121
+ if (not Util.name_is_valid(name))
122
+ return nil
123
+ end
124
+ =end
125
+
126
+ stm = Stream.new self
127
+ stm.name = name
128
+ parent_stg ||= @directory
129
+ dlog("Adding stream #{name} to storage #{parent_stg.name}", 'rex', LEV_3)
130
+ @directory.link_item(parent_stg, stm)
131
+ @modified = true
132
+ stm
133
+ end
134
+
135
+ def open_stream(name, mode=STGM_READ, parent_stg=nil)
136
+ parent_stg ||= @directory
137
+ stm = parent_stg.find_stream_by_name_and_type(name, STGTY_STREAM)
138
+ if (stm)
139
+ # TODO: optimize out the need to read all of the data up-front
140
+ stm << read_stream_data(stm)
141
+ end
142
+ stm
143
+ end
144
+
145
+
146
+ #
147
+ # storage manipulation functions
148
+ #
149
+ def create_storage(name, mode=STGM_READ, parent_stg=nil)
150
+ stg = SubStorage.new self
151
+ stg.name = name
152
+ parent_stg ||= @directory
153
+ dlog("Adding storage #{name} to storage #{parent_stg.name}", 'rex', LEV_3)
154
+ @directory.link_item(parent_stg, stg)
155
+ stg
156
+ end
157
+
158
+ def open_storage(name, mode=STGM_READ, parent_stg=nil)
159
+ @directory.find_stream_by_name_and_type(name, STGTY_STORAGE)
160
+ end
161
+
162
+
163
+ #
164
+ # low-level functions
165
+ #
166
+ def write_to_disk
167
+ # reset FAT/DIFAT
168
+ @difat = DIFAT.new self
169
+ @fat = FAT.new self
170
+
171
+ @header.write @fd
172
+ write_user_data
173
+
174
+ # NOTE: we call write_stream here since we MUST write this to
175
+ # the regular stream (regardless of size)
176
+ ms_start = write_stream(@ministream)
177
+ @directory.set_ministream_params(ms_start, @ministream.length)
178
+
179
+ @minifat.write
180
+ @directory.write
181
+ @fat.write(@difat)
182
+ @difat.write
183
+
184
+ # write it again, now that its complete
185
+ @header.write @fd
186
+ @fd.flush
187
+ end
188
+
189
+ def write_sector(sbuf, type=nil, prev_sect=nil)
190
+ len = sbuf.length
191
+ if (len != @header.sector_size)
192
+ # pad it if less
193
+ if (len < @header.sector_size)
194
+ sbuf = sbuf.dup
195
+ sbuf << "\x00" * (@header.sector_size - len)
196
+ else
197
+ raise RuntimeError, 'not sector sized!'
198
+ end
199
+ end
200
+
201
+ # write the data
202
+ idx = @fat.allocate_sector(type)
203
+ # point previous sector to here
204
+ if (prev_sect)
205
+ @fat[prev_sect] = idx
206
+ end
207
+ write_sector_raw(idx, sbuf)
208
+ return idx
209
+ end
210
+
211
+ def write_sector_raw(sect, sbuf)
212
+ dlog("Writing sector 0x%02x" % sect, 'rex', LEV_3)
213
+ @fd.seek((sect + 1) * @header.sector_size, ::IO::SEEK_SET)
214
+ @fd.write(sbuf)
215
+ end
216
+
217
+
218
+ def write_mini_sector(sbuf, prev_sect=nil)
219
+ len = sbuf.length
220
+ if (len != @header.mini_sector_size)
221
+ if (len < @header.mini_sector_size)
222
+ sbuf = sbuf.dup
223
+ sbuf << "\x00" * (@header.mini_sector_size - len)
224
+ else
225
+ raise RuntimeError, 'not mini sector sized!'
226
+ end
227
+ end
228
+
229
+ idx = @minifat.allocate_sector
230
+ # point the previous mini sector to here
231
+ if (prev_sect)
232
+ @minifat[prev_sect] = idx
233
+ end
234
+ write_mini_sector_raw(idx, sbuf)
235
+ idx
236
+ end
237
+
238
+ def write_mini_sector_raw(sect, sbuf)
239
+ dlog("Writing mini sector 0x%02x" % sect, 'rex', LEV_3)
240
+ @ministream << sbuf
241
+ end
242
+
243
+
244
+
245
+ def write_user_data
246
+ @directory.each_entry { |stm|
247
+ # only regular streams this pass
248
+ next if (stm.type != STGTY_STREAM)
249
+
250
+ if (stm.length >= @header._ulMiniSectorCutoff)
251
+ stm.start_sector = write_stream(stm)
252
+ else
253
+ # NOTE: stm_start is a minifat value
254
+ stm.start_sector = write_mini_stream(stm)
255
+ end
256
+ }
257
+ end
258
+
259
+ def write_stream(stm)
260
+ dlog("Writing \"%s\" to regular stream" % stm.name, 'rex', LEV_3)
261
+ stm_start = nil
262
+ prev_sect = nil
263
+ stm.seek(0)
264
+ while (sbuf = stm.read(@header.sector_size))
265
+ sect = write_sector(sbuf, nil, prev_sect)
266
+ stm_start ||= sect
267
+ prev_sect = sect
268
+ end
269
+ stm_start
270
+ end
271
+
272
+ def write_mini_stream(stm)
273
+ dlog("Writing \"%s\" to mini stream" % stm.name, 'rex', LEV_3)
274
+ prev_sect = nil
275
+ stm.seek(0)
276
+ while (sbuf = stm.read(@header.mini_sector_size))
277
+ sect = write_mini_sector(sbuf, prev_sect)
278
+ stm_start ||= sect
279
+ prev_sect = sect
280
+ end
281
+ stm_start
282
+ end
283
+
284
+
285
+ def read_stream_data(direntry)
286
+ if (direntry.length < @header._ulMiniSectorCutoff)
287
+ return read_data_mini(direntry)
288
+ end
289
+
290
+ read_data(direntry)
291
+ end
292
+
293
+ def read_data(direntry)
294
+ ret = ""
295
+ visited = []
296
+ left = direntry.length
297
+ sect = direntry.start_sector
298
+ while (sect != SECT_END)
299
+ if (visited.include?(sect))
300
+ raise RuntimeError, 'Sector chain loop detected (0x%08x)' % sect
301
+ end
302
+ visited << sect
303
+
304
+ # how much to read?
305
+ block = @header.sector_size
306
+ block = left if (block > left)
307
+
308
+ # read it.
309
+ dlog("read_data - reading 0x%x bytes" % block, 'rex', LEV_3)
310
+ buf = read_sector(sect, block)
311
+ ret << buf
312
+ left -= buf.length
313
+
314
+ # done?
315
+ break if (left == 0)
316
+
317
+ sect = next_sector(sect)
318
+ end
319
+ ret
320
+ end
321
+
322
+ def read_data_mini(direntry)
323
+ ret = ""
324
+ visited = []
325
+ left = direntry.length
326
+ sect = direntry.start_sector
327
+ while (sect != SECT_END)
328
+ if (visited.include?(sect))
329
+ raise RuntimeError, 'Sector chain loop detected (0x%08x mini)' % sect
330
+ end
331
+ visited << sect
332
+
333
+ # how much to read?
334
+ block = @header.mini_sector_size
335
+ block = left if (block > left)
336
+
337
+ # read it.
338
+ dlog("read_data_mini - reading 0x%x bytes" % block, 'rex', LEV_3)
339
+ buf = read_mini_sector(sect, block)
340
+ ret << buf
341
+ left -= buf.length
342
+
343
+ # done?
344
+ break if (left == 0)
345
+
346
+ sect = next_mini_sector(sect)
347
+ end
348
+ ret
349
+ end
350
+
351
+
352
+ def read_sector(sect, len)
353
+ off = ((sect + 1) * @header.sector_size)
354
+ @fd.seek(off, ::IO::SEEK_SET)
355
+ buf = @fd.read(len)
356
+ if (not buf)
357
+ if (@fd.eof?)
358
+ raise RuntimeError, 'EOF while reading sector data (0x%08x)' % sect
359
+ else
360
+ raise RuntimeError, 'Unknown error while reading sector data (0x%08x)' % sect
361
+ end
362
+ end
363
+ if (buf.length != len)
364
+ raise RuntimeError, 'Insufficient data for sector (0x%08x): got %u of %u' % [sect, buf.length, len]
365
+ end
366
+ buf
367
+ end
368
+
369
+ def next_sector(sect)
370
+ return SECT_END if (sect >= @fat.length)
371
+ @fat[sect]
372
+ end
373
+
374
+
375
+ def read_mini_sector(sect, len)
376
+ dlog("Reading mini sector 0x%x" % sect, 'rex', LEV_3)
377
+ off = (@header.mini_sector_size * sect)
378
+ dlog("Reading from offset 0x%x of ministream" % off, 'rex', LEV_3)
379
+ @ministream.seek(off)
380
+ data = @ministream.read(len)
381
+ data
382
+ end
383
+
384
+ def next_mini_sector(sect)
385
+ return SECT_END if (sect >= @minifat.length)
386
+ @minifat[sect]
387
+ end
388
+
389
+ end
390
+
391
+ end
392
+ end
@@ -0,0 +1,50 @@
1
+ # -*- coding: binary -*-
2
+
3
+ ##
4
+ # Rex::OLE - an OLE implementation
5
+ # written in 2010 by Joshua J. Drake <jduck [at] metasploit.com>
6
+ ##
7
+
8
+ module Rex
9
+ module OLE
10
+
11
+ class Stream < DirEntry
12
+
13
+ def initialize(stg)
14
+ super
15
+
16
+ # for reading/writing from this
17
+ @offset = 0
18
+ @_mse = STGTY_STREAM
19
+ end
20
+
21
+ def close
22
+ @mode = nil
23
+ @offset = nil
24
+ end
25
+
26
+ def seek(offset)
27
+ @offset = offset
28
+ end
29
+
30
+ def read(len)
31
+ return nil if (not @data)
32
+
33
+ ret = @data[@offset, len]
34
+ @offset += len
35
+ ret
36
+ end
37
+
38
+ def <<(expr)
39
+ if (not @data)
40
+ @data = expr.dup
41
+ else
42
+ @data << expr
43
+ end
44
+ @_ulSize = @data.length
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end