rex-ole 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 96d83d1e258c69844bac338287ba147f27e79092
|
4
|
+
data.tar.gz: 7b939852df46e093e63355d852b5860178bdefc9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 450d8312a1b4a2846999802543eab21bdae5894649195aa53d8f72a7e99fa8077df6a1b5414f2ff1adcfbcce3dfeb4c1e57c0419b4ccbf0c5ea81a87e8d97983
|
7
|
+
data.tar.gz: 9578bb85804856ee572f3d2f9f6c18b74363ac86694526de63f0bf34b2825be6759bf882a0d6150dde94463854e575e0a3841fb827b45744bcf9492fb337e7fe
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting the project maintainers at msfdev@metasploit.com. If
|
39
|
+
the incident involves a committer, you may report directly to
|
40
|
+
egypt@metasploit.com or todb@metasploit.com.
|
41
|
+
|
42
|
+
All complaints will be reviewed and investigated and will result in a
|
43
|
+
response that is deemed necessary and appropriate to the circumstances.
|
44
|
+
Maintainers are obligated to maintain confidentiality with regard to the
|
45
|
+
reporter of an incident.
|
46
|
+
|
47
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
48
|
+
version 1.3.0, available at
|
49
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
50
|
+
|
51
|
+
[homepage]: http://contributor-covenant.org
|
52
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (C) 2012-2013, Rapid7, Inc.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of Rapid7 LLC nor the names of its contributors
|
15
|
+
may be used to endorse or promote products derived from this software
|
16
|
+
without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
22
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
25
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Rex::OLE
|
2
|
+
|
3
|
+
Ruby Exploitation(rex) Library for reading/writing Object-Linking-and-Embedding (OLE) files and streams. Ported over from Joshua Drake's original code inside Metasploit Framework.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'rex-ole'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install rex-ole
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
## Development
|
24
|
+
|
25
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
26
|
+
|
27
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/rapid7/rex-ole. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
32
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rex/ole"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,44 @@
|
|
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
|
+
module Rex
|
10
|
+
module OLE
|
11
|
+
|
12
|
+
class CLSID
|
13
|
+
|
14
|
+
def initialize(buf=nil)
|
15
|
+
@buf = buf
|
16
|
+
@buf ||= "\x00" * 16
|
17
|
+
end
|
18
|
+
|
19
|
+
def pack
|
20
|
+
@buf
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
ret = ""
|
25
|
+
ret << "%08x" % Util.get32(@buf, 0)
|
26
|
+
ret << "-"
|
27
|
+
ret << "%04x" % Util.get16(@buf, 4)
|
28
|
+
ret << "-"
|
29
|
+
ret << "%04x" % Util.get16(@buf, 6)
|
30
|
+
ret << "-"
|
31
|
+
idx = 0
|
32
|
+
last8 = @buf[8,8]
|
33
|
+
last8.unpack('C*').each { |byte|
|
34
|
+
ret << [byte].pack('C').unpack('H*')[0]
|
35
|
+
ret << "-" if (idx == 1)
|
36
|
+
idx += 1
|
37
|
+
}
|
38
|
+
ret
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,138 @@
|
|
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 DIFAT
|
12
|
+
|
13
|
+
def initialize stg
|
14
|
+
@stg = stg
|
15
|
+
@entries = []
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# convenience access to entries
|
20
|
+
#
|
21
|
+
def []=(idx,expr)
|
22
|
+
@entries[idx] = expr
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](idx)
|
26
|
+
@entries[idx]
|
27
|
+
end
|
28
|
+
|
29
|
+
def +(expr)
|
30
|
+
@entries += expr
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def <<(expr)
|
35
|
+
@entries << expr
|
36
|
+
end
|
37
|
+
|
38
|
+
def length
|
39
|
+
@entries.length
|
40
|
+
end
|
41
|
+
|
42
|
+
def slice!(start,stop)
|
43
|
+
@entries.slice!(start,stop)
|
44
|
+
end
|
45
|
+
|
46
|
+
def reset
|
47
|
+
@entries = []
|
48
|
+
end
|
49
|
+
|
50
|
+
def each
|
51
|
+
@entries.each { |el|
|
52
|
+
yield el
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# woop
|
58
|
+
#
|
59
|
+
def to_s
|
60
|
+
ret = "{ "
|
61
|
+
@entries.each { |el|
|
62
|
+
ret << ", " if (ret.length > 2)
|
63
|
+
case el
|
64
|
+
when SECT_END
|
65
|
+
ret << "END"
|
66
|
+
when SECT_DIF
|
67
|
+
ret << "DIF"
|
68
|
+
when SECT_FAT
|
69
|
+
ret << "FAT"
|
70
|
+
when SECT_FREE
|
71
|
+
ret << "FREE"
|
72
|
+
else
|
73
|
+
ret << "0x%x" % el
|
74
|
+
end
|
75
|
+
}
|
76
|
+
ret << " }"
|
77
|
+
ret
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# low-level functions
|
82
|
+
#
|
83
|
+
def read
|
84
|
+
@entries = []
|
85
|
+
|
86
|
+
# start with the header part
|
87
|
+
@entries += @stg.header._sectFat
|
88
|
+
|
89
|
+
# double indirect fat
|
90
|
+
sect = @stg.header._sectDifStart
|
91
|
+
while (sect != SECT_END)
|
92
|
+
if (@entries.include?(sect))
|
93
|
+
raise RuntimeError, 'Sector chain loop detected (0x%08x)' % sect
|
94
|
+
end
|
95
|
+
|
96
|
+
@entries << sect
|
97
|
+
buf = @stg.read_sector(sect, @stg.header.sector_size)
|
98
|
+
|
99
|
+
# the last sect ptr in the block becomes the next entry
|
100
|
+
sect = Util.get32(buf, ((@stg.header.idx_per_sect)-1) * 4)
|
101
|
+
end
|
102
|
+
|
103
|
+
# don't need these free ones, but it doesn't hurt to keep them.
|
104
|
+
#@difat.delete(SECT_FREE)
|
105
|
+
end
|
106
|
+
|
107
|
+
def write
|
108
|
+
len = @entries.length
|
109
|
+
first109 = @entries.dup
|
110
|
+
|
111
|
+
rest = nil
|
112
|
+
if (len > 109)
|
113
|
+
rest = first109.slice!(109,len)
|
114
|
+
end
|
115
|
+
|
116
|
+
@stg.header._sectFat = []
|
117
|
+
@stg.header._sectFat += first109
|
118
|
+
if (len < 109)
|
119
|
+
need = 109 - len
|
120
|
+
need.times {
|
121
|
+
@stg.header._sectFat << SECT_FREE
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
if (rest and rest.length > 0)
|
126
|
+
raise RuntimeError, 'TODO: support writing DIF properly!'
|
127
|
+
# may require adding more fat sectors :-/
|
128
|
+
#@stg.header._csectDif = rest.length
|
129
|
+
#@stg.header._sectDifStart = idx
|
130
|
+
end
|
131
|
+
|
132
|
+
@stg.header._csectFat = len
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,228 @@
|
|
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
|
+
require 'rex/ole/direntry'
|
12
|
+
|
13
|
+
#
|
14
|
+
# This class serves as the root directory entry in addition to
|
15
|
+
# an abstraction around the concept of a directory as a whole.
|
16
|
+
#
|
17
|
+
class Directory < DirEntry
|
18
|
+
|
19
|
+
# XXX: num_entries is not maintained once a stream/storage is added!
|
20
|
+
attr_accessor :num_entries
|
21
|
+
|
22
|
+
def initialize(stg)
|
23
|
+
super
|
24
|
+
|
25
|
+
@num_entries = 1
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# woop, recursive each
|
30
|
+
def yield_entries(de, &block)
|
31
|
+
block.call(de)
|
32
|
+
de.each { |el|
|
33
|
+
yield_entries(el, &block)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
def each_entry(&block)
|
37
|
+
yield_entries(self, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def set_ministream_params(start, size)
|
42
|
+
@_sectStart = start
|
43
|
+
@_ulSize = size
|
44
|
+
end
|
45
|
+
|
46
|
+
def link_item(parent, child)
|
47
|
+
# set sid, advance count
|
48
|
+
child.sid = @num_entries
|
49
|
+
@num_entries += 1
|
50
|
+
|
51
|
+
# link item to siblings and/or parent
|
52
|
+
if (parent._sidChild == DIR_NOSTREAM)
|
53
|
+
parent._sidChild = child.sid
|
54
|
+
dlog("Linking #{child.name} as THE child of #{parent.name} as sid #{child.sid}", 'rex', LEV_3)
|
55
|
+
else
|
56
|
+
sib = nil
|
57
|
+
parent.each { |el|
|
58
|
+
if (el._sidLeftSib == DIR_NOSTREAM)
|
59
|
+
sib = el
|
60
|
+
el._sidLeftSib = child.sid
|
61
|
+
dlog("Linking #{child.name} as the LEFT sibling of #{sib.name} as sid #{child.sid}", 'rex', LEV_3)
|
62
|
+
break
|
63
|
+
end
|
64
|
+
if (el._sidRightSib == DIR_NOSTREAM)
|
65
|
+
sib = el
|
66
|
+
el._sidRightSib = child.sid
|
67
|
+
dlog("Linking #{child.name} as the RIGHT sibling of #{sib.name} as sid #{child.sid}", 'rex', LEV_3)
|
68
|
+
break
|
69
|
+
end
|
70
|
+
}
|
71
|
+
if (not sib)
|
72
|
+
raise RuntimeError, 'Unable to find a sibling to link to in the directory'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
parent << child
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
#
|
80
|
+
# low-level functions
|
81
|
+
#
|
82
|
+
def from_s(sid, buf)
|
83
|
+
super
|
84
|
+
|
85
|
+
if (@_sidRightSib != DIR_NOSTREAM)
|
86
|
+
raise RuntimeError, 'Root Entry is invalid! (has right sibling)'
|
87
|
+
end
|
88
|
+
if (@_sidLeftSib != DIR_NOSTREAM)
|
89
|
+
raise RuntimeError, 'Root Entry is invalid! (has left sibling)'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def read
|
94
|
+
@children = []
|
95
|
+
visited = []
|
96
|
+
entries = []
|
97
|
+
root_node = nil
|
98
|
+
sect = @stg.header._sectDirStart
|
99
|
+
while (sect != SECT_END)
|
100
|
+
|
101
|
+
if (visited.include?(sect))
|
102
|
+
raise RuntimeError, 'Sector chain loop detected (0x%08x)' % sect
|
103
|
+
end
|
104
|
+
visited << sect
|
105
|
+
|
106
|
+
sbuf = @stg.read_sector(sect, @stg.header.sector_size)
|
107
|
+
while (sbuf.length >= DIRENTRY_SZ)
|
108
|
+
debuf = sbuf.slice!(0, DIRENTRY_SZ)
|
109
|
+
|
110
|
+
type = Util.get8(debuf, 0x42)
|
111
|
+
case type
|
112
|
+
when STGTY_ROOT
|
113
|
+
if (entries.length != 0)
|
114
|
+
raise RuntimeError, 'Root Entry found, but not first encountered!'
|
115
|
+
end
|
116
|
+
if (root_node)
|
117
|
+
raise RuntimeError, 'Multiple root directory sectors detected (0x%08x)' % sect
|
118
|
+
end
|
119
|
+
de = self
|
120
|
+
root_node = de
|
121
|
+
|
122
|
+
when STGTY_STORAGE
|
123
|
+
de = SubStorage.new @stg
|
124
|
+
|
125
|
+
when STGTY_STREAM
|
126
|
+
de = Stream.new @stg
|
127
|
+
|
128
|
+
when STGTY_INVALID
|
129
|
+
# skip invalid entries
|
130
|
+
next
|
131
|
+
|
132
|
+
else
|
133
|
+
raise RuntimeError, 'Unsupported directory entry type (0x%02x)' % type
|
134
|
+
end
|
135
|
+
|
136
|
+
# read content
|
137
|
+
de.from_s(entries.length, debuf)
|
138
|
+
entries << de
|
139
|
+
end
|
140
|
+
sect = @stg.next_sector(sect)
|
141
|
+
end
|
142
|
+
|
143
|
+
@num_entries = entries.length
|
144
|
+
|
145
|
+
# sort out the tree structure, starting with the root
|
146
|
+
if (@_sidChild != DIR_NOSTREAM)
|
147
|
+
populate_children(entries, root_node, @_sidChild)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
# recursively add entries to their proper parents :)
|
153
|
+
def populate_children(entries, parent, sid)
|
154
|
+
node = entries[sid]
|
155
|
+
dlog("populate_children(entries, \"#{parent.name}\", #{sid}) - node: #{node.name}", 'rex', LEV_3)
|
156
|
+
parent << node
|
157
|
+
if (node.type == STGTY_STORAGE) and (node._sidChild != DIR_NOSTREAM)
|
158
|
+
populate_children(entries, node, node._sidChild)
|
159
|
+
end
|
160
|
+
if (node._sidLeftSib != DIR_NOSTREAM)
|
161
|
+
populate_children(entries, parent, node._sidLeftSib)
|
162
|
+
end
|
163
|
+
if (node._sidRightSib != DIR_NOSTREAM)
|
164
|
+
populate_children(entries, parent, node._sidRightSib)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# NOTE: this may not be necessary if we were to use each_entry
|
169
|
+
def flatten_tree(entries, parent)
|
170
|
+
entries << parent
|
171
|
+
parent.each { |el|
|
172
|
+
flatten_tree(entries, el)
|
173
|
+
}
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
def write
|
178
|
+
# flatten the directory again
|
179
|
+
entries = []
|
180
|
+
flatten_tree(entries, self)
|
181
|
+
dlog("flattened tree has #{entries.length} entries...", 'rex', LEV_3)
|
182
|
+
|
183
|
+
# count directory sectors
|
184
|
+
ds_count = entries.length / 4
|
185
|
+
if ((entries.length % 4) > 0)
|
186
|
+
# one more sector to hold the rest
|
187
|
+
ds_count += 1
|
188
|
+
end
|
189
|
+
|
190
|
+
# put the root entry first
|
191
|
+
sbuf = self.pack
|
192
|
+
|
193
|
+
# add the rest
|
194
|
+
prev_sect = nil
|
195
|
+
dir_start = nil
|
196
|
+
entries.each { |de|
|
197
|
+
# we already got the root entry, no more!
|
198
|
+
next if (de.type == STGTY_ROOT)
|
199
|
+
|
200
|
+
dir = de.pack
|
201
|
+
dlog("writing dir entry #{de.name}", 'rex', LEV_3)
|
202
|
+
sbuf << dir
|
203
|
+
|
204
|
+
if (sbuf.length == @stg.header.sector_size)
|
205
|
+
# we have a full sector, add it!
|
206
|
+
sect = @stg.write_sector(sbuf, nil, prev_sect)
|
207
|
+
prev_sect = sect
|
208
|
+
dir_start ||= sect
|
209
|
+
# reset..
|
210
|
+
sbuf = ""
|
211
|
+
end
|
212
|
+
}
|
213
|
+
|
214
|
+
# still a partial sector left?
|
215
|
+
if (sbuf.length > 0)
|
216
|
+
# add it! (NOTE: it will get padded with nul bytes if its not sector sized)
|
217
|
+
sect = @stg.write_sector(sbuf, nil, prev_sect)
|
218
|
+
prev_sect = sect
|
219
|
+
dir_start ||= sect
|
220
|
+
end
|
221
|
+
|
222
|
+
@stg.header._sectDirStart = dir_start
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
end
|