|
| 1 | +# -*- coding:binary -*- |
| 2 | +require 'spec_helper' |
| 3 | + |
| 4 | +require 'rex/ole' |
| 5 | + |
| 6 | +describe Rex::OLE::Header do |
| 7 | + |
| 8 | + subject(:header) do |
| 9 | + described_class.new |
| 10 | + end |
| 11 | + |
| 12 | + describe ".new" do |
| 13 | + it "returns a Rex::OLE::Header instance" do |
| 14 | + expect(described_class.new).to be_a(Rex::OLE::Header) |
| 15 | + end |
| 16 | + |
| 17 | + it { expect(header.instance_variable_get(:@_abSig)).to eq(Rex::OLE::SIG) } |
| 18 | + it { expect(header.instance_variable_get(:@_clid)).to be_a(Rex::OLE::CLSID) } |
| 19 | + it { expect(header.instance_variable_get(:@_uByteOrder)).to eq(Rex::OLE::LITTLE_ENDIAN) } |
| 20 | + it { expect(header.instance_variable_get(:@_uMinorVersion)).to eq(0x3e) } |
| 21 | + it { expect(header._uMajorVersion).to eq(0x03) } |
| 22 | + it { expect(header.instance_variable_get(:@_uSectorShift)).to eq(9) } |
| 23 | + it { expect(header._uMiniSectorShift).to eq(6) } |
| 24 | + it { expect(header.instance_variable_get(:@_csectDir)).to be_nil } |
| 25 | + it { expect(header._csectFat).to be_nil } |
| 26 | + it { expect(header._sectDirStart).to be_nil } |
| 27 | + it { expect(header.instance_variable_get(:@_signature)).to eq(0) } |
| 28 | + it { expect(header._ulMiniSectorCutoff).to eq(0x1000) } |
| 29 | + it { expect(header._sectMiniFatStart).to eq(Rex::OLE::SECT_END) } |
| 30 | + it { expect(header._csectMiniFat).to eq(0) } |
| 31 | + it { expect(header._sectDifStart).to eq(Rex::OLE::SECT_END) } |
| 32 | + it { expect(header._csectDif).to eq(0) } |
| 33 | + it { expect(header._sectFat).to be_an(Array) } |
| 34 | + it { expect(header.instance_variable_get(:@_sectFat)).to be_empty } |
| 35 | + it { expect(header.sector_size).to eq(1 << 9) } |
| 36 | + it { expect(header.mini_sector_size).to eq(1 << 6) } |
| 37 | + it { expect(header.idx_per_sect).to eq((1 << 9) / 4) } |
| 38 | + end |
| 39 | + |
| 40 | + describe "#set_defaults" do |
| 41 | + it "sets OLECF signature" do |
| 42 | + header.set_defaults |
| 43 | + expect(header.instance_variable_get(:@_abSig)).to eq(Rex::OLE::SIG) |
| 44 | + end |
| 45 | + |
| 46 | + it "setup a class identifier (guid)" do |
| 47 | + header.set_defaults |
| 48 | + expect(header.instance_variable_get(:@_clid)).to be_a(Rex::OLE::CLSID) |
| 49 | + end |
| 50 | + |
| 51 | + it "sets byte order identifier as little endian" do |
| 52 | + header.set_defaults |
| 53 | + expect(header.instance_variable_get(:@_uByteOrder)).to eq(Rex::OLE::LITTLE_ENDIAN) |
| 54 | + end |
| 55 | + |
| 56 | + it "sets the minor version to 0x3e" do |
| 57 | + header.set_defaults |
| 58 | + expect(header.instance_variable_get(:@_uMinorVersion)).to eq(0x3e) |
| 59 | + end |
| 60 | + |
| 61 | + it "sets the major version to 0x3" do |
| 62 | + header.set_defaults |
| 63 | + expect(header._uMajorVersion).to eq(0x03) |
| 64 | + end |
| 65 | + |
| 66 | + it "sets the size of sectors to 9" do |
| 67 | + header.set_defaults |
| 68 | + expect(header.instance_variable_get(:@_uSectorShift)).to eq(9) |
| 69 | + end |
| 70 | + |
| 71 | + it "sets the size of mini-sectors to 6" do |
| 72 | + header.set_defaults |
| 73 | + expect(header._uMiniSectorShift).to eq(6) |
| 74 | + end |
| 75 | + |
| 76 | + it "sets the number of sectors in the directory chain to nil" do |
| 77 | + header.set_defaults |
| 78 | + expect(header.instance_variable_get(:@_csectDir)).to be_nil |
| 79 | + end |
| 80 | + |
| 81 | + it "sets the number of sectors in the FAT chain to nil" do |
| 82 | + header.set_defaults |
| 83 | + expect(header._csectFat).to be_nil |
| 84 | + end |
| 85 | + |
| 86 | + it "sets first sector in the directory chain to nil" do |
| 87 | + header.set_defaults |
| 88 | + expect(header._sectDirStart).to be_nil |
| 89 | + end |
| 90 | + |
| 91 | + it "sets the signature used for transactioning to zero" do |
| 92 | + header.set_defaults |
| 93 | + expect(header.instance_variable_get(:@_signature)).to eq(0) |
| 94 | + end |
| 95 | + |
| 96 | + it "sets the maximum size of mini-streams to 4096" do |
| 97 | + header.set_defaults |
| 98 | + expect(header._ulMiniSectorCutoff).to eq(0x1000) |
| 99 | + end |
| 100 | + |
| 101 | + it "sets the first sector in the mini-FAT chain to end of chain" do |
| 102 | + header.set_defaults |
| 103 | + expect(header._sectMiniFatStart).to eq(Rex::OLE::SECT_END) |
| 104 | + end |
| 105 | + |
| 106 | + it "sets the number of sectors in the mini-FAT chain to 0" do |
| 107 | + header.set_defaults |
| 108 | + expect(header._csectMiniFat).to eq(0) |
| 109 | + end |
| 110 | + |
| 111 | + it "sets the first sector in the DIF chain to end of chain" do |
| 112 | + header.set_defaults |
| 113 | + expect(header._sectDifStart).to eq(Rex::OLE::SECT_END) |
| 114 | + end |
| 115 | + |
| 116 | + it "sets the number of sectors in the DIF chain to 0" do |
| 117 | + header.set_defaults |
| 118 | + expect(header._csectDif).to eq(0) |
| 119 | + end |
| 120 | + |
| 121 | + it "creates an array for the sectors of the first 109 FAT sectors" do |
| 122 | + header.set_defaults |
| 123 | + expect(header._sectFat).to be_an(Array) |
| 124 | + end |
| 125 | + |
| 126 | + it "creates an empty array for the FAT sectors" do |
| 127 | + header.set_defaults |
| 128 | + expect(header.instance_variable_get(:@_sectFat)).to be_empty |
| 129 | + end |
| 130 | + end |
| 131 | + |
| 132 | + describe "#to_s" do |
| 133 | + subject(:header_string) { header.to_s } |
| 134 | + |
| 135 | + it "returns an String" do |
| 136 | + expect(header_string).to be_an(String) |
| 137 | + end |
| 138 | + |
| 139 | + it "starts with {" do |
| 140 | + expect(header_string).to start_with('{') |
| 141 | + end |
| 142 | + |
| 143 | + it "ends with {" do |
| 144 | + expect(header_string).to end_with('}') |
| 145 | + end |
| 146 | + |
| 147 | + it "includes the OLECF signature" do |
| 148 | + expect(header_string).to match(/_abSig => "\\xd0\\xcf\\x11\\xe0\\xa1\\xb1\\x1a\\xe1"/) |
| 149 | + end |
| 150 | + |
| 151 | + it "includes the class identifier value" do |
| 152 | + expect(header_string).to match(/_clid => 00000000-0000-0000-0000-000000000000/) |
| 153 | + end |
| 154 | + |
| 155 | + it "includes the minor version value" do |
| 156 | + expect(header_string).to match(/_uMinorVersion => 0x003e/) |
| 157 | + end |
| 158 | + |
| 159 | + it "includes the major version value" do |
| 160 | + expect(header_string).to match(/_uMajorVersion => 0x0003/) |
| 161 | + end |
| 162 | + |
| 163 | + it "includes the byte order identifier value" do |
| 164 | + expect(header_string).to match(/_uByteOrder => 0xfffe/) |
| 165 | + end |
| 166 | + |
| 167 | + it "includes the size of sectors value" do |
| 168 | + expect(header_string).to match(/_uSectorShift => 0x0009/) |
| 169 | + end |
| 170 | + |
| 171 | + it "includes the size of mini-sectors value" do |
| 172 | + expect(header_string).to match(/_uMiniSectorShift => 0x0006/) |
| 173 | + end |
| 174 | + |
| 175 | + it "includes the number of sectors in the directory chain" do |
| 176 | + expect(header_string).to match(/_csectDir => UNALLOCATED/) |
| 177 | + end |
| 178 | + |
| 179 | + it "includes the number of sectors in the FAT chain" do |
| 180 | + expect(header_string).to match(/_csectFat => UNALLOCATED/) |
| 181 | + end |
| 182 | + |
| 183 | + it "includes the first sector in the directory chain" do |
| 184 | + expect(header_string).to match(/_sectDirStart => UNALLOCATED/) |
| 185 | + end |
| 186 | + |
| 187 | + it "includes the signature used for transactioning" do |
| 188 | + expect(header_string).to match(/_signature => 0x00000000/) |
| 189 | + end |
| 190 | + |
| 191 | + it "includes the maximum size of mini-streams" do |
| 192 | + expect(header_string).to match(/_uMiniSectorCutoff => 0x00001000/) |
| 193 | + end |
| 194 | + |
| 195 | + it "includes the first sector in the mini-FAT chain value" do |
| 196 | + expect(header_string).to match(/_sectMiniFatStart => 0xfffffffe/) |
| 197 | + end |
| 198 | + |
| 199 | + it "includes the number of sectors in the mini-FAT chain" do |
| 200 | + expect(header_string).to match(/_csectMiniFat => 0x00000000/) |
| 201 | + end |
| 202 | + |
| 203 | + it "includes the first sector in the DIF chain value" do |
| 204 | + expect(header_string).to match(/_sectDifStart => 0xfffffffe/) |
| 205 | + end |
| 206 | + |
| 207 | + it "includes the number of sectors in the DIF chain" do |
| 208 | + expect(header_string).to match(/_csectDif => 0x00000000/) |
| 209 | + end |
| 210 | + end |
| 211 | + |
| 212 | + describe "#read" do |
| 213 | + context "when reading empty header" do |
| 214 | + let(:empty_fd) do |
| 215 | + s = '' |
| 216 | + StringIO.new(s, 'rb') |
| 217 | + end |
| 218 | + |
| 219 | + it "raises NoMethodError" do |
| 220 | + expect { header.read(empty_fd) }.to raise_error(NoMethodError) |
| 221 | + end |
| 222 | + end |
| 223 | + |
| 224 | + |
| 225 | + context "when reading header with invalid signature" do |
| 226 | + let(:incorrect_fd) do |
| 227 | + s = 'A' * Rex::OLE::HDR_SZ |
| 228 | + StringIO.new(s, 'rb') |
| 229 | + end |
| 230 | + |
| 231 | + it "raises RuntimeError" do |
| 232 | + expect { header.read(incorrect_fd) }.to raise_error(RuntimeError) |
| 233 | + end |
| 234 | + end |
| 235 | + |
| 236 | + context "when reading header with valid signature" do |
| 237 | + let(:correct_fd) do |
| 238 | + hdr = "" |
| 239 | + hdr << Rex::OLE::SIG |
| 240 | + hdr << 'A' * 16 # @_clid |
| 241 | + hdr << 'BB' # @_uMinorVersion |
| 242 | + hdr << 'CC' # @_uMajorVersion |
| 243 | + hdr << 'DD' # @_uByteOrder |
| 244 | + hdr << 'EE' # @_uSectorShift |
| 245 | + hdr << 'FF' # @_uMiniSectorShift |
| 246 | + hdr << '123456' # padding |
| 247 | + hdr << 'GGGG' # @_csectDir |
| 248 | + hdr << 'HHHH' # @_csectFat |
| 249 | + hdr << 'IIII' # @_sectDirStart |
| 250 | + hdr << 'JJJJ' # @_signature |
| 251 | + hdr << 'KKKK' # @_ulMiniSectorCutoff |
| 252 | + hdr << 'LLLL' # @_sectMiniFatStart |
| 253 | + hdr << 'MMMM' # @_csectMiniFat |
| 254 | + hdr << 'NNNN' # @_sectDifStart |
| 255 | + hdr << 'OOOO' # @_csectDif |
| 256 | + hdr << 'P' * 109 * 4 # @_sectFat |
| 257 | + |
| 258 | + StringIO.new(hdr, 'rb') |
| 259 | + end |
| 260 | + |
| 261 | + it "sets clsid from input" do |
| 262 | + header.read(correct_fd) |
| 263 | + expect(header.instance_variable_get(:@_clid).to_s).to eq("41414141-4141-4141-4141-414141414141") |
| 264 | + end |
| 265 | + |
| 266 | + it "sets minor version from input" do |
| 267 | + header.read(correct_fd) |
| 268 | + expect(header.instance_variable_get(:@_uMinorVersion)).to eq(0x4242) |
| 269 | + end |
| 270 | + |
| 271 | + it "sets major version from input" do |
| 272 | + header.read(correct_fd) |
| 273 | + expect(header._uMajorVersion).to eq(0x4343) |
| 274 | + end |
| 275 | + |
| 276 | + it "sets byte order from input" do |
| 277 | + header.read(correct_fd) |
| 278 | + expect(header.instance_variable_get(:@_uByteOrder)).to eq(0x4444) |
| 279 | + end |
| 280 | + |
| 281 | + it "sets the size of sectors from input" do |
| 282 | + header.read(correct_fd) |
| 283 | + expect(header.instance_variable_get(:@_uSectorShift)).to eq(0x4545) |
| 284 | + end |
| 285 | + |
| 286 | + it "sets the size of mini-sectors from input" do |
| 287 | + header.read(correct_fd) |
| 288 | + expect(header._uMiniSectorShift).to eq(0x4646) |
| 289 | + end |
| 290 | + |
| 291 | + it "sets the number of sectors in the directory chain from input" do |
| 292 | + header.read(correct_fd) |
| 293 | + expect(header.instance_variable_get(:@_csectDir)).to eq(0x47474747) |
| 294 | + end |
| 295 | + |
| 296 | + it "sets the number of sectors in the FAT chain from input" do |
| 297 | + header.read(correct_fd) |
| 298 | + expect(header._csectFat).to eq(0x48484848) |
| 299 | + end |
| 300 | + |
| 301 | + it "sets the first sector in the directory chain from input" do |
| 302 | + header.read(correct_fd) |
| 303 | + expect(header._sectDirStart).to eq(0x49494949) |
| 304 | + end |
| 305 | + |
| 306 | + it "sets the signature used for transactioning from input" do |
| 307 | + header.read(correct_fd) |
| 308 | + expect(header.instance_variable_get(:@_signature)).to eq(0x4a4a4a4a) |
| 309 | + end |
| 310 | + |
| 311 | + it "sets the maximum size of mini-streams from input" do |
| 312 | + header.read(correct_fd) |
| 313 | + expect(header._ulMiniSectorCutoff).to eq(0x4b4b4b4b) |
| 314 | + end |
| 315 | + |
| 316 | + it "sets the first sector in the mini-FAT chain from input" do |
| 317 | + header.read(correct_fd) |
| 318 | + expect(header._sectMiniFatStart).to eq(0x4c4c4c4c) |
| 319 | + end |
| 320 | + |
| 321 | + it "sets the number of sectors in the mini-FAT chain from input" do |
| 322 | + header.read(correct_fd) |
| 323 | + expect(header._csectMiniFat).to eq(0x4d4d4d4d) |
| 324 | + end |
| 325 | + |
| 326 | + it "sets the first sector in the DIF chain from input" do |
| 327 | + header.read(correct_fd) |
| 328 | + expect(header._sectDifStart).to eq(0x4e4e4e4e) |
| 329 | + end |
| 330 | + |
| 331 | + it "sets the number of sectors in the DIF chain from input" do |
| 332 | + header.read(correct_fd) |
| 333 | + expect(header._csectDif).to eq(0x4f4f4f4f) |
| 334 | + end |
| 335 | + |
| 336 | + it "creates an array for the FAT sectors from input" do |
| 337 | + header.read(correct_fd) |
| 338 | + expect(header._sectFat.length).to eq(109) |
| 339 | + end |
| 340 | + end |
| 341 | + end |
| 342 | + |
| 343 | + describe "#write" do |
| 344 | + context "when default header" do |
| 345 | + it "writes 76 bytes" do |
| 346 | + fd = StringIO.new('', 'wb') |
| 347 | + header.write(fd) |
| 348 | + expect(fd.string.length).to eq(76) |
| 349 | + end |
| 350 | + end |
| 351 | + end |
| 352 | +end |
0 commit comments