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,237 @@
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
+ #
12
+ # This class serves as the base class for SubStorage, Stream, and Directory head
13
+ #
14
+ class DirEntry
15
+
16
+ attr_accessor :sid
17
+ attr_accessor :_sidChild, :_sidLeftSib, :_sidRightSib
18
+
19
+ def initialize(stg)
20
+ @stg = stg
21
+
22
+ # default to a root entry :)
23
+ @sid = 0
24
+ @_ab = "Root Entry"
25
+ @_cb = nil # NOTE: this is not used until pack
26
+ @_mse = STGTY_ROOT
27
+ @_bflags = 0
28
+ @_sidLeftSib = SECT_FREE
29
+ @_sidRightSib = SECT_FREE
30
+ @_sidChild = SECT_FREE
31
+ @_clsId = CLSID.new
32
+ @_dwUserFlags = 0
33
+ @_ctime = "\x00" * 8
34
+ @_mtime = "\x00" * 8
35
+ @_sectStart = SECT_END
36
+ @_ulSize = 0
37
+
38
+ # keep track of logical children (in a tree)
39
+ @children = []
40
+ end
41
+
42
+
43
+ def length
44
+ @_ulSize
45
+ end
46
+
47
+ def <<(expr)
48
+ @children << expr
49
+ end
50
+
51
+ def each
52
+ @children.each { |de|
53
+ yield de
54
+ }
55
+ end
56
+
57
+
58
+ def type
59
+ @_mse
60
+ end
61
+ def type=(arg)
62
+ @_mse = arg
63
+ end
64
+
65
+ def name
66
+ @_ab
67
+ end
68
+ def name=(arg)
69
+ # XXX: validate?
70
+ @_ab = arg
71
+ end
72
+
73
+ def start_sector
74
+ @_sectStart
75
+ end
76
+ def start_sector=(expr)
77
+ @_sectStart = expr
78
+ end
79
+
80
+
81
+ # NOTE: this will not look at children
82
+ def find_stream_by_name_and_type(name, type)
83
+ @children.each { |de|
84
+ next if (de.type != type)
85
+
86
+ if (de.name == name)
87
+ return de
88
+ end
89
+ }
90
+ nil
91
+ end
92
+
93
+
94
+ def find_by_sid(sid, de=self)
95
+ if (de.sid == sid)
96
+ return de
97
+ end
98
+ @children.each { |cde|
99
+ ret = find_by_sid(sid, cde)
100
+ if (ret)
101
+ return ret
102
+ end
103
+ }
104
+ nil
105
+ end
106
+
107
+
108
+ #
109
+ # low-level functions
110
+ #
111
+ def from_s(sid, buf)
112
+ @sid = sid
113
+ @_ab = Util.getUnicodeString(buf[0x00,64])
114
+ @_cb = Util.get16(buf, 0x40)
115
+
116
+ # too big?
117
+ if (@_cb > 0x40)
118
+ raise RuntimeError, 'Invalid directory entry name length %#x' % @_cb
119
+ end
120
+
121
+ # mismatch?
122
+ if (@_ab.length > 0)
123
+ declen = ((@_cb) / 2) - 1
124
+ if (declen != @_ab.length)
125
+ raise RuntimeError, 'Directory entry name and length mismatch (%d != %d)' % [declen, @_ab.length]
126
+ end
127
+ end
128
+
129
+ @_mse = Util.get8(buf, 0x42)
130
+ @_bflags = Util.get8(buf, 0x43)
131
+ @_sidLeftSib = Util.get32(buf, 0x44)
132
+ @_sidRightSib = Util.get32(buf, 0x48)
133
+ @_sidChild = Util.get32(buf, 0x4c)
134
+
135
+ # only used for storages..
136
+ @_clsId = CLSID.new(buf[0x50,16])
137
+ @_dwUserFlags = Util.get32(buf, 0x60)
138
+ @_ctime = buf[0x64,8]
139
+ @_mtime = buf[0x6c,8]
140
+
141
+ # only used for streams...
142
+ @_sectStart = Util.get32(buf, 0x74)
143
+ if (@stg.header._uMajorVersion == 4)
144
+ @_ulSize = Util.get64(buf, 0x78)
145
+ else
146
+ @_ulSize = Util.get32(buf, 0x78)
147
+ end
148
+
149
+ # ignore _dptPropType and pad
150
+ end
151
+
152
+
153
+ def pack
154
+ @_sectStart ||= SECT_END
155
+ @_cb = (@_ab.length + 1) * 2
156
+
157
+ data = ""
158
+ data << Util.putUnicodeString(@_ab) # gets padded/truncated to 0x40 bytes
159
+ data << Util.pack16(@_cb)
160
+ data << Util.pack8(@_mse)
161
+ data << Util.pack8(@_bflags)
162
+ data << Util.pack32(@_sidLeftSib)
163
+ data << Util.pack32(@_sidRightSib)
164
+ data << Util.pack32(@_sidChild)
165
+ data << @_clsId.pack
166
+ data << Util.pack32(@_dwUserFlags)
167
+ data << @_ctime
168
+ data << @_mtime
169
+ data << Util.pack32(@_sectStart)
170
+ data << Util.pack64(@_ulSize)
171
+ data
172
+ end
173
+
174
+
175
+ def to_s(extra_spaces=0)
176
+ @_sectStart ||= SECT_END
177
+ @_cb = (@_ab.length + 1) * 2
178
+
179
+ spstr = " " * extra_spaces
180
+
181
+ ret = "%s{\n" % spstr
182
+ ret << "%s sid => 0x%x" % [spstr, @sid]
183
+ ret << ",\n"
184
+ ret << "%s _ab => \"%s\"" % [spstr, Util.Printable(@_ab)]
185
+ ret << ",\n"
186
+ ret << "%s _cb => 0x%04x" % [spstr, @_cb]
187
+ ret << ",\n"
188
+ ret << "%s _mse => 0x%02x" % [spstr, @_mse]
189
+ ret << ",\n"
190
+ ret << "%s _bflags => 0x%02x" % [spstr, @_bflags]
191
+ ret << ",\n"
192
+ ret << "%s _sidLeftSib => 0x%08x" % [spstr, @_sidLeftSib]
193
+ ret << ",\n"
194
+ ret << "%s _sidRightSib => 0x%08x" % [spstr, @_sidRightSib]
195
+ ret << ",\n"
196
+ ret << "%s _sidChild => 0x%08x" % [spstr, @_sidChild]
197
+ ret << ",\n"
198
+ ret << "%s _clsId => %s" % [spstr, @_clsId.to_s]
199
+ ret << ",\n"
200
+ ret << "%s _dwUserFlags => 0x%08x" % [spstr, @_dwUserFlags]
201
+ ret << ",\n"
202
+ ret << "%s _ctime => %s" % [spstr, Rex::Text.to_hex_dump(@_ctime).strip]
203
+ ret << "\n"
204
+ ret << "%s _mtime => %s" % [spstr, Rex::Text.to_hex_dump(@_mtime).strip]
205
+ ret << "\n"
206
+ ret << "%s _sectStart => 0x%08x" % [spstr, @_sectStart]
207
+ ret << ",\n"
208
+ ret << "%s _ulSize => 0x%016x" % [spstr, @_ulSize]
209
+ if (@_mse == STGTY_STREAM)
210
+ ret << ",\n"
211
+ ret << "%s data =>\n" % spstr
212
+ if (@data)
213
+ #ret << Util.Printable(@data)
214
+ ret << Rex::Text.to_hex_dump(@data).strip
215
+ else
216
+ if (@_ulSize > 0)
217
+ ret << "--NOT OPENED YET--"
218
+ end
219
+ end
220
+ elsif (@_mse == STGTY_STORAGE) or (@_mse == STGTY_ROOT)
221
+ if (@children.length > 0)
222
+ ret << ",\n"
223
+ ret << "%s *children* =>\n" % spstr
224
+ @children.each { |de|
225
+ ret << de.to_s(extra_spaces+2)
226
+ ret << "\n"
227
+ }
228
+ end
229
+ end
230
+ ret << "\n"
231
+ ret << "%s}" % spstr
232
+ end
233
+
234
+ end
235
+
236
+ end
237
+ end
@@ -0,0 +1,8 @@
1
+ Object Dependencies
2
+ ------------------ ---------------------
3
+ User Data None
4
+ Header Fat, DIFat, Directory, MiniStream
5
+ DIFat Fat
6
+ Fat Directory, *Fat, User Data, MiniStream
7
+ Directory User Data, MiniStream
8
+ MiniFat MiniStreams
@@ -0,0 +1 @@
1
+ [MS-CFB].pdf
@@ -0,0 +1,96 @@
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 FAT < DIFAT
12
+
13
+ #
14
+ # low-level functions
15
+ #
16
+ def read(difat)
17
+ @entries = []
18
+ cnt = left = @stg.header._csectFat
19
+ difat.each { |fs|
20
+ break if (left == 0)
21
+
22
+ if (fs != SECT_FREE)
23
+ buf = @stg.read_sector(fs, @stg.header.sector_size)
24
+ arr = Util.get32array(buf)
25
+
26
+ # hax!
27
+ if (@entries[fs] == SECT_DIF)
28
+ # chop the next ptr
29
+ @entries += arr.slice!(0, arr.length - 1)
30
+ else
31
+ @entries += arr
32
+ end
33
+ left -= 1
34
+ end
35
+ }
36
+
37
+ if (left != 0)
38
+ raise RuntimeError, 'Only found %u of %u sectors' % [(cnt - left), cnt]
39
+ end
40
+ end
41
+
42
+ def allocate_sector(type=nil)
43
+ idx = @entries.index(SECT_FREE)
44
+ if (not idx)
45
+ # add a sector worth
46
+ idx = @entries.length
47
+ @stg.header.idx_per_sect.times {
48
+ @entries << SECT_FREE
49
+ }
50
+ end
51
+
52
+ # mark the sector as in use
53
+ if (type)
54
+ @entries[idx] = type
55
+ else
56
+ # default normal sectors to end of chain
57
+ @entries[idx] = SECT_END
58
+ end
59
+ idx
60
+ end
61
+
62
+ def write(difat)
63
+ # we build the difat as we write these..
64
+ difat.reset
65
+
66
+ # allocate the sectors
67
+ fat_sects = []
68
+ left = @entries.length
69
+ while (left > 0)
70
+ if (left > @stg.header.idx_per_sect)
71
+ left -= @stg.header.idx_per_sect
72
+ else
73
+ left = 0
74
+ end
75
+ fat_sects << allocate_sector(SECT_FAT)
76
+ end
77
+
78
+ # write the fat into the difat/allocated sectors
79
+ copy = @entries.dup
80
+ fat_sects.each { |fs|
81
+ part = copy.slice!(0, @stg.header.idx_per_sect)
82
+ sbuf = Util.pack32array(part)
83
+
84
+ if (sbuf.length != @stg.header.sector_size)
85
+ raise RuntimeError, 'Unsupported number of fat sectors (not multiple of idx per sect)'
86
+ end
87
+
88
+ @stg.write_sector_raw(fs, sbuf)
89
+ difat << fs
90
+ }
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,201 @@
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
+ #
9
+ # Should we support major == 4 && sectorshift == 0xc ?
10
+ #
11
+
12
+ module Rex
13
+ module OLE
14
+
15
+ require 'rex/ole/util'
16
+
17
+ class Header
18
+
19
+ attr_accessor :_csectFat, :_sectFat
20
+ attr_accessor :_csectMiniFat, :_sectMiniFatStart
21
+ attr_accessor :_ulMiniSectorCutoff, :_uMiniSectorShift
22
+ attr_accessor :_csectDif, :_sectDifStart
23
+ attr_accessor :_sectDirStart
24
+ attr_accessor :_uMajorVersion
25
+
26
+ attr_accessor :sector_size, :idx_per_sect
27
+ attr_accessor :mini_sector_size
28
+
29
+ def initialize
30
+ set_defaults
31
+
32
+ # calculate some numbers (save a little math)
33
+ @sector_size = 1 << @_uSectorShift
34
+ @mini_sector_size = 1 << @_uMiniSectorShift
35
+ @idx_per_sect = @sector_size / 4
36
+ end
37
+
38
+ def set_defaults
39
+ @_abSig = SIG
40
+ @_clid = CLSID.new
41
+ @_uByteOrder = LITTLE_ENDIAN
42
+
43
+ @_uMinorVersion = 0x3e
44
+ @_uMajorVersion = 0x03
45
+
46
+ @_uSectorShift = 9 # 512 byte sectors
47
+ @_uMiniSectorShift = 6 # 64 byte mini-sectors
48
+
49
+ @_csectDir = nil # TBD (v4 only, 1 required)
50
+
51
+ @_csectFat = nil # TBD (one required)
52
+ @_sectDirStart = nil # TBD (one required)
53
+
54
+ @_signature = 0 # no transactions support
55
+
56
+ @_ulMiniSectorCutoff = 0x1000 # 4k
57
+ @_sectMiniFatStart = SECT_END # TBD
58
+ @_csectMiniFat = 0 # TBD
59
+
60
+ @_sectDifStart = SECT_END # TBD (default to none)
61
+ @_csectDif = 0 # TBD (default to none)
62
+
63
+ @_sectFat = [] # TBD
64
+ end
65
+
66
+ def to_s
67
+ ret = "{\n"
68
+ ret << " _abSig => \"%s\"" % Util.Printable(@_abSig)
69
+ ret << ",\n"
70
+ ret << " _clid => %s" % @_clid.to_s
71
+ ret << ",\n"
72
+ ret << " _uMinorVersion => 0x%04x" % @_uMinorVersion
73
+ ret << ",\n"
74
+ ret << " _uMajorVersion => 0x%04x" % @_uMajorVersion
75
+ ret << ",\n"
76
+ ret << " _uByteOrder => 0x%04x" % @_uByteOrder
77
+ ret << ",\n"
78
+ ret << " _uSectorShift => 0x%04x" % @_uSectorShift
79
+ ret << ",\n"
80
+ ret << " _uMiniSectorShift => 0x%04x" % @_uMiniSectorShift
81
+ ret << ",\n"
82
+
83
+ if (@_csectDir)
84
+ ret << " _csectDir => 0x%08x" % @_csectDir
85
+ else
86
+ ret << " _csectDir => UNALLOCATED" % @_csectDir
87
+ end
88
+ ret << ",\n"
89
+
90
+ if (@_csectFat)
91
+ ret << " _csectFat => 0x%08x" % @_csectFat
92
+ else
93
+ ret << " _csectFat => UNALLOCATED"
94
+ end
95
+ ret << ",\n"
96
+
97
+ if (@_sectDirStart)
98
+ ret << " _sectDirStart => 0x%08x" % @_sectDirStart
99
+ else
100
+ ret << " _sectDirStart => UNALLOCATED"
101
+ end
102
+ ret << ",\n"
103
+
104
+ ret << " _signature => 0x%08x" % @_signature
105
+ ret << ",\n"
106
+ ret << " _uMiniSectorCutoff => 0x%08x" % @_ulMiniSectorCutoff
107
+ ret << ",\n"
108
+ ret << " _sectMiniFatStart => 0x%08x" % @_sectMiniFatStart
109
+ ret << ",\n"
110
+ ret << " _csectMiniFat => 0x%08x" % @_csectMiniFat
111
+ ret << ",\n"
112
+ ret << " _sectDifStart => 0x%08x" % @_sectDifStart
113
+ ret << ",\n"
114
+ ret << " _csectDif => 0x%08x" % @_csectDif
115
+ #ret << ",\n"
116
+ #ret << " _sectFat => "
117
+ #ret << Rex::Text.to_hex_dump32array(@_sectFat)
118
+ ret << "\n}"
119
+ ret
120
+ end
121
+
122
+ #
123
+ # low-level functions
124
+ #
125
+ def read(fd)
126
+ buf = fd.read(HDR_SZ)
127
+
128
+ @_abSig = buf[0x00,8]
129
+ if (@_abSig != SIG) and (@_abSig != SIG_BETA)
130
+ raise RuntimeError, 'Invalid signature for OLE file'
131
+ end
132
+ @_clid = CLSID.new(buf[0x08,16])
133
+
134
+ @_uByteOrder = Util.get16(buf, 0x1c)
135
+ Util.set_endian(@_uByteOrder)
136
+
137
+ @_uMinorVersion = Util.get16(buf, 0x18)
138
+ @_uMajorVersion = Util.get16(buf, 0x1a)
139
+
140
+ @_uSectorShift = Util.get16(buf, 0x1e)
141
+ @_uMiniSectorShift = Util.get16(buf, 0x20)
142
+
143
+ # ignore reserved bytes
144
+
145
+ @_csectDir = Util.get32(buf, 0x28) # NOTE: only for v4 files
146
+
147
+ @_csectFat = Util.get32(buf, 0x2c)
148
+ @_sectDirStart = Util.get32(buf, 0x30)
149
+
150
+ @_signature = Util.get32(buf, 0x34)
151
+
152
+ @_ulMiniSectorCutoff = Util.get32(buf, 0x38)
153
+ @_sectMiniFatStart = Util.get32(buf, 0x3c)
154
+ @_csectMiniFat = Util.get32(buf, 0x40)
155
+
156
+ @_sectDifStart = Util.get32(buf, 0x44)
157
+ @_csectDif = Util.get32(buf, 0x48)
158
+
159
+ @_sectFat = Util.get32array(buf[0x4c, (109 * 4)])
160
+ end
161
+
162
+ def write(fd)
163
+ hdr = ""
164
+ hdr << @_abSig
165
+ hdr << @_clid.pack
166
+ hdr << Util.pack16(@_uMinorVersion)
167
+ hdr << Util.pack16(@_uMajorVersion)
168
+ hdr << Util.pack16(@_uByteOrder)
169
+ hdr << Util.pack16(@_uSectorShift)
170
+ hdr << Util.pack16(@_uMiniSectorShift)
171
+ if (@_uMajorVersion == 0x04)
172
+ hdr << "\x00" * 6 # reserved bytes
173
+ hdr << Util.pack32(@_csectDir)
174
+ else
175
+ hdr << "\x00" * 10 # reserved bytes
176
+ end
177
+
178
+ fs_count = @_csectFat
179
+ fs_count ||= 0
180
+ hdr << Util.pack32(fs_count)
181
+
182
+ dir_start = @_sectDirStart
183
+ dir_start ||= SECT_END
184
+ hdr << Util.pack32(dir_start)
185
+
186
+ hdr << Util.pack32(@_signature)
187
+ hdr << Util.pack32(@_ulMiniSectorCutoff)
188
+ hdr << Util.pack32(@_sectMiniFatStart)
189
+ hdr << Util.pack32(@_csectMiniFat)
190
+ hdr << Util.pack32(@_sectDifStart)
191
+ hdr << Util.pack32(@_csectDif)
192
+ hdr << Util.pack32array(@_sectFat)
193
+
194
+ fd.seek(0, ::IO::SEEK_SET)
195
+ fd.write(hdr)
196
+ end
197
+
198
+ end
199
+
200
+ end
201
+ end
@@ -0,0 +1,74 @@
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 MiniFAT < DIFAT
12
+
13
+ #
14
+ # low-level functions
15
+ #
16
+ def read
17
+ @entries = []
18
+
19
+ visited = []
20
+ sect = @stg.header._sectMiniFatStart
21
+ @stg.header._csectMiniFat.times { |idx|
22
+ break if sect == SECT_END
23
+
24
+ if (visited.include?(sect))
25
+ raise RuntimeError, 'Sector chain loop detected (0x%08x)' % sect
26
+ end
27
+ visited << sect
28
+
29
+ buf = @stg.read_sector(sect, @stg.header.sector_size)
30
+ @stg.header.idx_per_sect.times { |idx|
31
+ @entries << Util.get32(buf, (idx*4))
32
+ }
33
+ sect = @stg.next_sector(sect)
34
+ }
35
+ end
36
+
37
+ def allocate_sector
38
+ idx = @entries.index(SECT_FREE)
39
+
40
+ if (not idx)
41
+ # add a sector worth
42
+ idx = @entries.length
43
+ @stg.header.idx_per_sect.times {
44
+ @entries << SECT_FREE
45
+ }
46
+ end
47
+
48
+ # default mini-sectors to end of chain
49
+ @entries[idx] = SECT_END
50
+ idx
51
+ end
52
+
53
+ def write
54
+ return if (@entries.length < 1)
55
+
56
+ mf_start = nil
57
+ mfs_count = 0
58
+ prev_sect = nil
59
+ copy = @entries.dup
60
+ while (copy.length > 0)
61
+ part = copy.slice!(0, @stg.header.idx_per_sect)
62
+ sbuf = Util.pack32array(part)
63
+ idx = @stg.write_sector(sbuf, nil, prev_sect)
64
+ mfs_count += 1
65
+ mf_start ||= idx
66
+ end
67
+ @stg.header._sectMiniFatStart = mf_start
68
+ @stg.header._csectMiniFat = mfs_count
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end