# -*- coding:binary -*- require 'spec_helper' require 'rex/ole' describe Rex::OLE::Header do subject(:header) do described_class.new end describe ".new" do it "returns a Rex::OLE::Header instance" do expect(described_class.new).to be_a(Rex::OLE::Header) end it { expect(header.instance_variable_get(:@_abSig)).to eq(Rex::OLE::SIG) } it { expect(header.instance_variable_get(:@_clid)).to be_a(Rex::OLE::CLSID) } it { expect(header.instance_variable_get(:@_uByteOrder)).to eq(Rex::OLE::LITTLE_ENDIAN) } it { expect(header.instance_variable_get(:@_uMinorVersion)).to eq(0x3e) } it { expect(header._uMajorVersion).to eq(0x03) } it { expect(header.instance_variable_get(:@_uSectorShift)).to eq(9) } it { expect(header._uMiniSectorShift).to eq(6) } it { expect(header.instance_variable_get(:@_csectDir)).to be_nil } it { expect(header._csectFat).to be_nil } it { expect(header._sectDirStart).to be_nil } it { expect(header.instance_variable_get(:@_signature)).to eq(0) } it { expect(header._ulMiniSectorCutoff).to eq(0x1000) } it { expect(header._sectMiniFatStart).to eq(Rex::OLE::SECT_END) } it { expect(header._csectMiniFat).to eq(0) } it { expect(header._sectDifStart).to eq(Rex::OLE::SECT_END) } it { expect(header._csectDif).to eq(0) } it { expect(header._sectFat).to be_an(Array) } it { expect(header.instance_variable_get(:@_sectFat)).to be_empty } it { expect(header.sector_size).to eq(1 << 9) } it { expect(header.mini_sector_size).to eq(1 << 6) } it { expect(header.idx_per_sect).to eq((1 << 9) / 4) } end describe "#set_defaults" do it "sets OLECF signature" do header.set_defaults expect(header.instance_variable_get(:@_abSig)).to eq(Rex::OLE::SIG) end it "setup a class identifier (guid)" do header.set_defaults expect(header.instance_variable_get(:@_clid)).to be_a(Rex::OLE::CLSID) end it "sets byte order identifier as little endian" do header.set_defaults expect(header.instance_variable_get(:@_uByteOrder)).to eq(Rex::OLE::LITTLE_ENDIAN) end it "sets the minor version to 0x3e" do header.set_defaults expect(header.instance_variable_get(:@_uMinorVersion)).to eq(0x3e) end it "sets the major version to 0x3" do header.set_defaults expect(header._uMajorVersion).to eq(0x03) end it "sets the size of sectors to 9" do header.set_defaults expect(header.instance_variable_get(:@_uSectorShift)).to eq(9) end it "sets the size of mini-sectors to 6" do header.set_defaults expect(header._uMiniSectorShift).to eq(6) end it "sets the number of sectors in the directory chain to nil" do header.set_defaults expect(header.instance_variable_get(:@_csectDir)).to be_nil end it "sets the number of sectors in the FAT chain to nil" do header.set_defaults expect(header._csectFat).to be_nil end it "sets first sector in the directory chain to nil" do header.set_defaults expect(header._sectDirStart).to be_nil end it "sets the signature used for transactioning to zero" do header.set_defaults expect(header.instance_variable_get(:@_signature)).to eq(0) end it "sets the maximum size of mini-streams to 4096" do header.set_defaults expect(header._ulMiniSectorCutoff).to eq(0x1000) end it "sets the first sector in the mini-FAT chain to end of chain" do header.set_defaults expect(header._sectMiniFatStart).to eq(Rex::OLE::SECT_END) end it "sets the number of sectors in the mini-FAT chain to 0" do header.set_defaults expect(header._csectMiniFat).to eq(0) end it "sets the first sector in the DIF chain to end of chain" do header.set_defaults expect(header._sectDifStart).to eq(Rex::OLE::SECT_END) end it "sets the number of sectors in the DIF chain to 0" do header.set_defaults expect(header._csectDif).to eq(0) end it "creates an array for the sectors of the first 109 FAT sectors" do header.set_defaults expect(header._sectFat).to be_an(Array) end it "creates an empty array for the FAT sectors" do header.set_defaults expect(header.instance_variable_get(:@_sectFat)).to be_empty end end describe "#to_s" do subject(:header_string) { header.to_s } it "returns an String" do expect(header_string).to be_an(String) end it "starts with {" do expect(header_string).to start_with('{') end it "ends with {" do expect(header_string).to end_with('}') end it "includes the OLECF signature" do expect(header_string).to match(/_abSig => "\\xd0\\xcf\\x11\\xe0\\xa1\\xb1\\x1a\\xe1"/) end it "includes the class identifier value" do expect(header_string).to match(/_clid => 00000000-0000-0000-0000-000000000000/) end it "includes the minor version value" do expect(header_string).to match(/_uMinorVersion => 0x003e/) end it "includes the major version value" do expect(header_string).to match(/_uMajorVersion => 0x0003/) end it "includes the byte order identifier value" do expect(header_string).to match(/_uByteOrder => 0xfffe/) end it "includes the size of sectors value" do expect(header_string).to match(/_uSectorShift => 0x0009/) end it "includes the size of mini-sectors value" do expect(header_string).to match(/_uMiniSectorShift => 0x0006/) end it "includes the number of sectors in the directory chain" do expect(header_string).to match(/_csectDir => UNALLOCATED/) end it "includes the number of sectors in the FAT chain" do expect(header_string).to match(/_csectFat => UNALLOCATED/) end it "includes the first sector in the directory chain" do expect(header_string).to match(/_sectDirStart => UNALLOCATED/) end it "includes the signature used for transactioning" do expect(header_string).to match(/_signature => 0x00000000/) end it "includes the maximum size of mini-streams" do expect(header_string).to match(/_uMiniSectorCutoff => 0x00001000/) end it "includes the first sector in the mini-FAT chain value" do expect(header_string).to match(/_sectMiniFatStart => 0xfffffffe/) end it "includes the number of sectors in the mini-FAT chain" do expect(header_string).to match(/_csectMiniFat => 0x00000000/) end it "includes the first sector in the DIF chain value" do expect(header_string).to match(/_sectDifStart => 0xfffffffe/) end it "includes the number of sectors in the DIF chain" do expect(header_string).to match(/_csectDif => 0x00000000/) end end describe "#read" do context "when reading empty header" do let(:empty_fd) do s = '' StringIO.new(s, 'rb') end it "raises NoMethodError" do expect { header.read(empty_fd) }.to raise_error(NoMethodError) end end context "when reading header with invalid signature" do let(:incorrect_fd) do s = 'A' * Rex::OLE::HDR_SZ StringIO.new(s, 'rb') end it "raises RuntimeError" do expect { header.read(incorrect_fd) }.to raise_error(RuntimeError) end end context "when reading header with valid signature" do let(:correct_fd) do hdr = "" hdr << Rex::OLE::SIG hdr << 'A' * 16 # @_clid hdr << 'BB' # @_uMinorVersion hdr << 'CC' # @_uMajorVersion hdr << 'DD' # @_uByteOrder hdr << 'EE' # @_uSectorShift hdr << 'FF' # @_uMiniSectorShift hdr << '123456' # padding hdr << 'GGGG' # @_csectDir hdr << 'HHHH' # @_csectFat hdr << 'IIII' # @_sectDirStart hdr << 'JJJJ' # @_signature hdr << 'KKKK' # @_ulMiniSectorCutoff hdr << 'LLLL' # @_sectMiniFatStart hdr << 'MMMM' # @_csectMiniFat hdr << 'NNNN' # @_sectDifStart hdr << 'OOOO' # @_csectDif hdr << 'P' * 109 * 4 # @_sectFat StringIO.new(hdr, 'rb') end it "sets clsid from input" do header.read(correct_fd) expect(header.instance_variable_get(:@_clid).to_s).to eq("41414141-4141-4141-4141-414141414141") end it "sets minor version from input" do header.read(correct_fd) expect(header.instance_variable_get(:@_uMinorVersion)).to eq(0x4242) end it "sets major version from input" do header.read(correct_fd) expect(header._uMajorVersion).to eq(0x4343) end it "sets byte order from input" do header.read(correct_fd) expect(header.instance_variable_get(:@_uByteOrder)).to eq(0x4444) end it "sets the size of sectors from input" do header.read(correct_fd) expect(header.instance_variable_get(:@_uSectorShift)).to eq(0x4545) end it "sets the size of mini-sectors from input" do header.read(correct_fd) expect(header._uMiniSectorShift).to eq(0x4646) end it "sets the number of sectors in the directory chain from input" do header.read(correct_fd) expect(header.instance_variable_get(:@_csectDir)).to eq(0x47474747) end it "sets the number of sectors in the FAT chain from input" do header.read(correct_fd) expect(header._csectFat).to eq(0x48484848) end it "sets the first sector in the directory chain from input" do header.read(correct_fd) expect(header._sectDirStart).to eq(0x49494949) end it "sets the signature used for transactioning from input" do header.read(correct_fd) expect(header.instance_variable_get(:@_signature)).to eq(0x4a4a4a4a) end it "sets the maximum size of mini-streams from input" do header.read(correct_fd) expect(header._ulMiniSectorCutoff).to eq(0x4b4b4b4b) end it "sets the first sector in the mini-FAT chain from input" do header.read(correct_fd) expect(header._sectMiniFatStart).to eq(0x4c4c4c4c) end it "sets the number of sectors in the mini-FAT chain from input" do header.read(correct_fd) expect(header._csectMiniFat).to eq(0x4d4d4d4d) end it "sets the first sector in the DIF chain from input" do header.read(correct_fd) expect(header._sectDifStart).to eq(0x4e4e4e4e) end it "sets the number of sectors in the DIF chain from input" do header.read(correct_fd) expect(header._csectDif).to eq(0x4f4f4f4f) end it "creates an array for the FAT sectors from input" do header.read(correct_fd) expect(header._sectFat.length).to eq(109) end end end describe "#write" do context "when default header" do it "writes 76 bytes" do fd = StringIO.new('', 'wb') header.write(fd) expect(fd.string.length).to eq(76) end end end end