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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +52 -0
- data/Gemfile +6 -0
- data/LICENSE +27 -0
- data/README.md +32 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rex/ole/clsid.rb +44 -0
- data/lib/rex/ole/difat.rb +138 -0
- data/lib/rex/ole/directory.rb +228 -0
- data/lib/rex/ole/direntry.rb +237 -0
- data/lib/rex/ole/docs/dependencies.txt +8 -0
- data/lib/rex/ole/docs/references.txt +1 -0
- data/lib/rex/ole/fat.rb +96 -0
- data/lib/rex/ole/header.rb +201 -0
- data/lib/rex/ole/minifat.rb +74 -0
- data/lib/rex/ole/propset.rb +141 -0
- data/lib/rex/ole/samples/create_ole.rb +27 -0
- data/lib/rex/ole/samples/dir.rb +35 -0
- data/lib/rex/ole/samples/dump_stream.rb +34 -0
- data/lib/rex/ole/samples/ole_info.rb +23 -0
- data/lib/rex/ole/storage.rb +392 -0
- data/lib/rex/ole/stream.rb +50 -0
- data/lib/rex/ole/substorage.rb +46 -0
- data/lib/rex/ole/util.rb +154 -0
- data/lib/rex/ole/version.rb +5 -0
- data/lib/rex/ole.rb +203 -0
- data/rex-ole.gemspec +26 -0
- data.tar.gz.sig +2 -0
- metadata +208 -0
- metadata.gz.sig +0 -0
@@ -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
|