Land #4095, specs for Rex::OLE

bug/bundler_fix
Tod Beardsley 2014-12-30 14:10:16 -06:00
commit 135faeee29
No known key found for this signature in database
GPG Key ID: BD63D0A3EA19CAAC
7 changed files with 1155 additions and 2 deletions

View File

@ -96,7 +96,7 @@ class DirEntry
return de
end
@children.each { |cde|
ret = find_by_sid(cde, sid)
ret = find_by_sid(sid, cde)
if (ret)
return ret
end

View File

@ -4,6 +4,9 @@ require 'spec_helper'
require 'rex/ole'
describe Rex::OLE::CLSID do
before(:each) do
Rex::OLE::Util.set_endian(Rex::OLE::LITTLE_ENDIAN)
end
let(:sample_clsid) { "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff" }
@ -35,7 +38,6 @@ describe Rex::OLE::CLSID do
describe "#to_s" do
it "returns printable clsid" do
Rex::OLE::Util.set_endian(Rex::OLE::LITTLE_ENDIAN)
expect(clsid.to_s).to eq('33221100-5544-7766-8899-aabbccddeeff')
end

View File

@ -0,0 +1,294 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/ole'
describe Rex::OLE::DIFAT do
before(:each) do
Rex::OLE::Util.set_endian(Rex::OLE::LITTLE_ENDIAN)
end
let(:storage) do
Rex::OLE::Storage.new
end
subject(:difat) do
described_class.new(storage)
end
describe ".new" do
it "returns a Rex::OLE::DIFAT instance" do
expect(described_class.new(storage)).to be_a(Rex::OLE::DIFAT)
end
it "initializes @stg" do
expect(difat.instance_variable_get(:@stg)).to eq(storage)
end
it "initializes @entries" do
expect(difat.instance_variable_get(:@entries)).to be_an(Array)
end
it "initializes @entries as empty array" do
expect(difat.instance_variable_get(:@entries)).to be_empty
end
end
describe "#[]=" do
context "when the entry doesn't exist" do
it "sets an element in the @entries array" do
difat[0] = 1
expect(difat.instance_variable_get(:@entries)[0]).to eq(1)
end
end
context "when the entry exists" do
it "replaces the element in the @entries array" do
difat[0] = 1
difat[0] = 2
expect(difat.instance_variable_get(:@entries)[0]).to eq(2)
end
end
end
describe "#[]" do
context "when the entry doesn't exist" do
it "returns nil" do
expect(difat[3]).to eq(nil)
end
end
context "when the entry exists" do
it "returns the entry value" do
difat[3] = 31
expect(difat[3]).to eq(31)
end
end
end
describe "#+" do
context "when @entries is empty" do
it "sets the @entries values" do
difat + [1, 2]
expect(difat.instance_variable_get(:@entries)).to eq([1, 2])
end
end
context "when @entries isn't empty" do
it "concatenates the array to @entries" do
difat[2] = 0
difat + [1, 2]
expect(difat.instance_variable_get(:@entries)).to eq([nil, nil, 0, 1, 2])
end
end
end
describe "#<<" do
it "concatenates the element to the @entries array" do
difat[0] = 1
difat << 3
expect(difat.instance_variable_get(:@entries)).to eq([1, 3])
end
end
describe "#length" do
subject(:difat_length) do
difat.length
end
context "when @entries is empty" do
it "returns 0" do
is_expected.to eq(0)
end
end
context "when @entries isn't empty" do
it "returns the @entries length" do
difat[0] = 1
difat[1] = 2
is_expected.to eq(2)
end
end
end
describe "#slice!" do
context "when @entries is empty" do
it "returns empty array" do
expect(difat.slice!(0, 1)).to eq([])
end
end
context "when start is out of range" do
it "returns nil" do
difat[0] = 1
expect(difat.slice!(10, 1)).to eq(nil)
end
end
context "when stop is 0" do
it "returns empty array" do
difat[0] = 1
expect(difat.slice!(0, 0)).to eq([])
end
it "doesn't delete nothing" do
difat[0] = 1
difat.slice!(0, 0)
expect(difat[0]).to eq(1)
end
end
context "when @entries is long enough" do
it "returns the deleted elements" do
difat + [1, 2]
expect(difat.slice!(0, 1)).to eq([1])
end
it "deletes the elements in the range" do
difat + [1, 2]
difat.slice!(0, 1)
expect(difat.instance_variable_get(:@entries)).to eq([2])
end
end
end
describe "#reset" do
it "resets the @entries array" do
difat[0] = 1
difat.reset
expect(difat.length).to eq(0)
end
end
describe "#each" do
it "calls the block for every @entries element" do
difat + [1, 2, 3]
res = 0
difat.each { |elem| res += elem}
expect(res).to eq(1 + 2 + 3)
end
end
describe "#to_s" do
subject(:difat_string) do
difat.to_s
end
it "returns an String" do
is_expected.to be_an(String)
end
it "starts with {" do
is_expected.to start_with('{')
end
it "ends with }" do
is_expected.to end_with('}')
end
it "contains @entries values" do
difat + [Rex::OLE::SECT_FAT, 1, 2, 3, Rex::OLE::SECT_DIF, Rex::OLE::SECT_FREE, Rex::OLE::SECT_END]
is_expected.to match(/FAT, 0x1, 0x2, 0x3, DIF, FREE, END/)
end
end
describe "#read" do
context "when difat is empty" do
it "returns nil" do
expect(difat.read).to be_nil
end
end
end
describe "#write" do
context "when entries is empty" do
it "returns 0" do
expect(difat.write).to eq(0)
end
it "fills the first 109 FAT sectors in the storage header" do
difat.write
storage = difat.instance_variable_get(:@stg)
expect(storage.header._sectFat.length).to eq(109)
end
it "fills the first 109 FAT sectors in the storage header with SECT_FREE" do
difat.write
storage = difat.instance_variable_get(:@stg)
storage.header._sectFat.each { |s|
expect(s).to eq(Rex::OLE::SECT_FREE)
}
end
end
context "when entries length is less than 109" do
let(:entries) { [1] * 20 }
it "returns the number of entries" do
difat + entries
expect(difat.write).to eq(20)
end
it "fills the first 109 FAT sectors in the storage header" do
difat + entries
difat.write
storage = difat.instance_variable_get(:@stg)
expect(storage.header._sectFat.length).to eq(109)
end
it "fills the first FAT sectors with the entries" do
difat + entries
difat.write
storage = difat.instance_variable_get(:@stg)
(0..entries.length - 1).each { |i|
expect(storage.header._sectFat[i]).to eq(1)
}
end
it "fills the remaining FAT sectors with FREE sectors" do
difat + entries
difat.write
storage = difat.instance_variable_get(:@stg)
(entries.length..109 - 1).each { |i|
expect(storage.header._sectFat[i]).to eq(Rex::OLE::SECT_FREE)
}
end
end
context "when entries length is 109" do
let(:entries) { [1] * 109 }
it "returns the number of entries" do
difat + entries
expect(difat.write).to eq(109)
end
it "fills the first 109 FAT sectors in the storage header" do
difat + entries
difat.write
storage = difat.instance_variable_get(:@stg)
expect(storage.header._sectFat.length).to eq(109)
end
it "fills the first 109 FAT sectors with the entries" do
difat + entries
difat.write
storage = difat.instance_variable_get(:@stg)
(0..storage.header._sectFat.length - 1).each { |i|
expect(storage.header._sectFat[i]).to eq(1)
}
end
end
context "when entries length is greater than 109" do
let(:entries) { [1] * 110 }
it "raises a RuntimeError" do
difat + entries
expect { difat.write }.to raise_error(RuntimeError)
end
end
end
end

View File

@ -0,0 +1,401 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/ole'
describe Rex::OLE::DirEntry do
before(:each) do
Rex::OLE::Util.set_endian(Rex::OLE::LITTLE_ENDIAN)
end
let(:storage) do
Rex::OLE::Storage.new
end
subject(:dir_entry) do
described_class.new(storage)
end
describe ".new" do
it "returns a Rex::OLE::DirEntry instance" do
expect(described_class.new(storage)).to be_a(Rex::OLE::DirEntry)
end
it { expect(dir_entry.instance_variable_get(:@stg)).to eq(storage) }
it { expect(dir_entry.sid).to eq(0) }
it { expect(dir_entry.instance_variable_get(:@_ab)).to eq('Root Entry') }
it { expect(dir_entry.instance_variable_get(:@_cb)).to be_nil }
it { expect(dir_entry.instance_variable_get(:@_mse)).to eq(Rex::OLE::STGTY_ROOT) }
it { expect(dir_entry.instance_variable_get(:@_bflags)).to eq(0) }
it { expect(dir_entry._sidLeftSib).to eq(Rex::OLE::SECT_FREE) }
it { expect(dir_entry._sidRightSib).to eq(Rex::OLE::SECT_FREE) }
it { expect(dir_entry._sidChild).to eq(Rex::OLE::SECT_FREE) }
it { expect(dir_entry.instance_variable_get(:@_clsId)).to be_a(Rex::OLE::CLSID) }
it { expect(dir_entry.instance_variable_get(:@_dwUserFlags)).to eq(0) }
it { expect(dir_entry.instance_variable_get(:@_ctime)).to eq("\x00" * 8) }
it { expect(dir_entry.instance_variable_get(:@_mtime)).to eq("\x00" * 8) }
it { expect(dir_entry.instance_variable_get(:@_sectStart)).to eq(Rex::OLE::SECT_END) }
it { expect(dir_entry.instance_variable_get(:@_ulSize)).to eq(0) }
it { expect(dir_entry.instance_variable_get(:@children)).to be_an(Array) }
it { expect(dir_entry.instance_variable_get(:@children)).to be_empty }
end
describe "#length" do
it "returns _ulSize" do
dir_entry.instance_variable_set(:@_ulSize, 28)
expect(dir_entry.length).to eq(28)
end
end
describe "#<<" do
it "increments the children array" do
dir_entry << 1
children = dir_entry.instance_variable_get(:@children)
expect(children.length).to eq(1)
end
it "appends to the children array" do
dir_entry << 1
children = dir_entry.instance_variable_get(:@children)
expect(children).to eq([1])
end
end
describe "#each" do
it "calls the block for every children element" do
dir_entry << 1
dir_entry << 2
dir_entry << 3
res = 0
dir_entry.each { |elem| res += elem}
expect(res).to eq(1 + 2 + 3)
end
end
describe "#type" do
it "returns the _mse field" do
expect(dir_entry.type).to eq(Rex::OLE::STGTY_ROOT)
end
end
describe "#type=" do
it "modifies the _mse field" do
dir_entry.type = 3838
expect(dir_entry.instance_variable_get(:@_mse)).to eq(3838)
end
end
describe "#name" do
it "returns the _ab field" do
expect(dir_entry.name).to eq('Root Entry')
end
end
describe "#name=" do
it "modifies the _ab field" do
dir_entry.name = 'test'
expect(dir_entry.instance_variable_get(:@_ab)).to eq('test')
end
end
describe "#start_sector" do
it "returns the _sectStart field" do
expect(dir_entry.start_sector).to eq(Rex::OLE::SECT_END)
end
end
describe "#start_sector=" do
it "modifies the _sectStart field" do
dir_entry.start_sector = Rex::OLE::SECT_FREE
expect(dir_entry.instance_variable_get(:@_sectStart)).to eq(Rex::OLE::SECT_FREE)
end
end
describe "#find_stream_by_name_and_type" do
context "when any children matches the search criteria" do
it "returns nil" do
expect(dir_entry.find_stream_by_name_and_type('name', Rex::OLE::STGTY_ROOT)).to be_nil
end
end
context "when one children matches the search criteria" do
let(:stream) { Rex::OLE::Stream.new(storage) }
let(:name) { 'name' }
let(:type) { Rex::OLE::STGTY_ROOT }
it "returns the matching stream" do
stream.name = name
stream.type = type
dir_entry << stream
expect(dir_entry.find_stream_by_name_and_type(name, type)).to eq(stream)
end
end
context "when several children matches the search criteria" do
let(:stream) { Rex::OLE::Stream.new(storage) }
let(:stream_two) { Rex::OLE::Stream.new(storage) }
let(:name) { 'name' }
let(:type) { Rex::OLE::STGTY_ROOT }
let(:sid) { 2 }
it "returns the first matching stream" do
stream.name = name
stream.type = type
dir_entry << stream
stream_two.name = name
stream_two.type = type
stream_two.sid = sid
dir_entry << stream_two
expect(dir_entry.find_stream_by_name_and_type(name, type)).to eq(stream)
end
end
end
describe "#find_by_sid" do
let(:stream) { Rex::OLE::Stream.new(storage) }
let(:another_stream) { Rex::OLE::Stream.new(storage) }
context "when self match the criteria" do
it "returns self" do
expect(dir_entry.find_by_sid(0, dir_entry)).to eq(dir_entry)
end
end
context "when self and a children stream match the criteria" do
it "returns self" do
stream.sid = 0
dir_entry << stream
expect(dir_entry.find_by_sid(0, dir_entry)).to eq(dir_entry)
end
end
context "when only one children stream match the criteria" do
it "returns the child stream" do
stream.sid = 20
dir_entry << stream
expect(dir_entry.find_by_sid(20, dir_entry)).to eq(stream)
end
end
context "when several children stream match the criteria" do
it "returns the first child" do
stream.sid = 20
stream.name = 'stream'
dir_entry << stream
another_stream.sid = 20
another_stream.name = 'another'
dir_entry << another_stream
expect(dir_entry.find_by_sid(20, dir_entry)).to eq(stream)
end
end
end
describe "#from_s" do
let(:valid_direntry) do
"\x52\x00\x6f\x00\x6f\x00\x74\x00\x20\x00\x45\x00\x6e\x00\x74\x00\x72\x00\x79\x00\x00\x00" + # name (_ab)
("\x00" * 42) + # padding
"\x16\x00" + # _cb
"\x05" + # _mse
"\x00" + #_bflags
"\xff\xff\xff\xff" + # _sidLeftSib
"\xff\xff\xff\xff" + # _sidRightSib
"\xff\xff\xff\xff" + # _sidChild
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + # clsid
"\x00\x00\x00\x00" + # _dwUserFlags
"\x00\x00\x00\x00\x00\x00\x00\x00" + # _ctime
"\x00\x00\x00\x00\x00\x00\x00\x00" + # _metime
"\xfe\xff\xff\xff" + # _sectStart
"\x00\x00\x00\x00\x00\x00\x00\x00" # _ulSize
end
let(:invalid_name_length)do
"\x52\x00\x6f\x00\x6f\x00\x74\x00\x20\x00\x45\x00\x6e\x00\x74\x00\x72\x00\x79\x00\x00\x00" + # name (_ab)
("\x00" * 42) + # padding
"\x41\x00" + # _cb (invalid, major than 0x40)
"\x05" + # _mse
"\x00" + #_bflags
"\xff\xff\xff\xff" + # _sidLeftSib
"\xff\xff\xff\xff" + # _sidRightSib
"\xff\xff\xff\xff" + # _sidChild
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + # clsid
"\x00\x00\x00\x00" + # _dwUserFlags
"\x00\x00\x00\x00\x00\x00\x00\x00" + # _ctime
"\x00\x00\x00\x00\x00\x00\x00\x00" + # _metime
"\xfe\xff\xff\xff" + # _sectStart
"\x00\x00\x00\x00\x00\x00\x00\x00" # _ulSize
end
let(:mismatch_length) do
"\x52\x00\x6f\x00\x6f\x00\x74\x00\x20\x00\x45\x00\x6e\x00\x74\x00\x72\x00\x79\x00\x00\x00" + # name (_ab)
("\x00" * 42) + # padding
"\x13\x00" + # _cb (invalid length, shorter than real name length)
"\x05" + # _mse
"\x00" + #_bflags
"\xff\xff\xff\xff" + # _sidLeftSib
"\xff\xff\xff\xff" + # _sidRightSib
"\xff\xff\xff\xff" + # _sidChild
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + # clsid
"\x00\x00\x00\x00" + # _dwUserFlags
"\x00\x00\x00\x00\x00\x00\x00\x00" + # _ctime
"\x00\x00\x00\x00\x00\x00\x00\x00" + # _metime
"\xfe\xff\xff\xff" + # _sectStart
"\x00\x00\x00\x00\x00\x00\x00\x00" # _ulSize
end
let(:sid) { 0 }
context "when name length major than 64" do
it "raises RuntimeError" do
expect { dir_entry.from_s(sid, invalid_name_length) }.to raise_error(RuntimeError)
end
end
context "when name length doesn't match real length" do
it "raises RuntimeError" do
expect { dir_entry.from_s(sid, mismatch_length) }.to raise_error(RuntimeError)
end
end
context "when valid buf" do
it "uses argument sid" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.sid).to eq(sid)
end
it "parses _ab from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_ab)).to eq('Root Entry')
end
it "parses _cb from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_cb)).to eq(22)
end
it "parses _mse from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_mse)).to eq(Rex::OLE::STGTY_ROOT)
end
it "parses _bflags from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_bflags)).to eq(0)
end
it "parses _sidLeftSib from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry._sidLeftSib).to eq(Rex::OLE::SECT_FREE)
end
it "parses _sidRightSib from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry._sidRightSib).to eq(Rex::OLE::SECT_FREE)
end
it "parses _sidChild from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry._sidChild).to eq(Rex::OLE::SECT_FREE)
end
it "parses _clsId from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_clsId)).to be_a(Rex::OLE::CLSID)
end
it "parses _dwUserFlags from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_dwUserFlags)).to eq(0)
end
it "parses _ctime from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_ctime)).to eq("\x00" * 8)
end
it "parses _mtime from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_mtime)).to eq("\x00" * 8)
end
it "parses _sectStart from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_sectStart)).to eq(Rex::OLE::SECT_END)
end
it "parses _ulSize from buf" do
dir_entry.from_s(sid, valid_direntry)
expect(dir_entry.instance_variable_get(:@_ulSize)).to eq(0)
end
end
end
describe "#pack" do
it "returns an string" do
expect(dir_entry.pack).to be_an(String)
end
it "includes the unicode dir entry name" do
expect(dir_entry.pack).to match(/R\x00o\x00o\x00t\x00 \x00E\x00n\x00t\x00r\x00y\x00/)
end
context "when _sectStart is undefined" do
it "sets _sectStart to SECT_END" do
dir_entry.instance_variable_set(:@_sectStart, nil)
dir_entry.pack
expect(dir_entry.instance_variable_get(:@_sectStart)).to eq(Rex::OLE::SECT_END)
end
end
context "when _sectStart is defined" do
it "doesn't modify _sectStart value" do
dir_entry.instance_variable_set(:@_sectStart, Rex::OLE::SECT_FREE)
dir_entry.pack
expect(dir_entry.instance_variable_get(:@_sectStart)).to eq(Rex::OLE::SECT_FREE)
end
end
it "sets _cb as the unicode length of the name" do
dir_entry.pack
expect(dir_entry.instance_variable_get(:@_cb)).to eq("Root Entry\x00".length * 2)
end
end
describe "#to_s" do
it "returns an string" do
expect(dir_entry.to_s).to be_an(String)
end
it "starts with {" do
expect(dir_entry.to_s).to start_with('{')
end
it "ends with }" do
expect(dir_entry.to_s).to end_with('}')
end
it "contains the entry name" do
expect(dir_entry.to_s).to match(/Root Entry/)
end
context "when _sectStart is undefined" do
it "sets _sectStart to SECT_END" do
dir_entry.instance_variable_set(:@_sectStart, nil)
dir_entry.to_s
expect(dir_entry.instance_variable_get(:@_sectStart)).to eq(Rex::OLE::SECT_END)
end
end
context "when _sectStart is defined" do
it "doesn't modify _sectStart value" do
dir_entry.instance_variable_set(:@_sectStart, Rex::OLE::SECT_FREE)
dir_entry.to_s
expect(dir_entry.instance_variable_get(:@_sectStart)).to eq(Rex::OLE::SECT_FREE)
end
end
it "sets _cb as the unicode length of the name" do
dir_entry.to_s
expect(dir_entry.instance_variable_get(:@_cb)).to eq("Root Entry\x00".length * 2)
end
end
end

View File

@ -0,0 +1,355 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/ole'
describe Rex::OLE::Header do
before(:each) do
Rex::OLE::Util.set_endian(Rex::OLE::LITTLE_ENDIAN)
end
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 << "\xfe\xff" # @_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(Rex::OLE::LITTLE_ENDIAN)
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

View File

@ -0,0 +1,98 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/ole'
describe Rex::OLE::MiniFAT do
before(:each) do
Rex::OLE::Util.set_endian(Rex::OLE::LITTLE_ENDIAN)
end
let(:storage) do
Rex::OLE::Storage.new
end
subject(:minifat) do
described_class.new(storage)
end
describe "#allocate_sector" do
context "when entries is empty" do
it "returns index 0" do
expect(minifat.allocate_sector).to eq(0)
end
it "allocates idx_per_sect entries" do
minifat.allocate_sector
storage = minifat.instance_variable_get(:@stg)
expect(minifat.length).to eq(storage.header.idx_per_sect)
end
it "marks the first entry as SECT_END" do
minifat.allocate_sector
expect(minifat[0]).to eq(Rex::OLE::SECT_END)
end
it "marks the remaining entries as SECT_FREE" do
minifat.allocate_sector
storage = minifat.instance_variable_get(:@stg)
(1..storage.header.idx_per_sect - 1).each do |i|
expect(minifat[i]).to eq(Rex::OLE::SECT_FREE)
end
end
end
context "when entries include a free sector" do
it "returns the free sector index entry" do
minifat + [1, 2, Rex::OLE::SECT_FREE]
expect(minifat.allocate_sector).to eq(2)
end
end
context "when entries don't include a free sector" do
it "returns index of a new entry" do
minifat + [1, 2, 3]
expect(minifat.allocate_sector).to eq(3)
end
it "allocates idx_per_sect entries" do
minifat + [1, 2, 3]
minifat.allocate_sector
storage = minifat.instance_variable_get(:@stg)
expect(minifat.length).to eq(storage.header.idx_per_sect + 3)
end
it "marks the first entry as SECT_END" do
minifat + [1, 2, 3]
minifat.allocate_sector
expect(minifat[3]).to eq(Rex::OLE::SECT_END)
end
it "marks the remaining entries as SECT_FREE" do
minifat + [1, 2, 3]
minifat.allocate_sector
storage = minifat.instance_variable_get(:@stg)
(4..3 + storage.header.idx_per_sect - 1).each do |i|
expect(minifat[i]).to eq(Rex::OLE::SECT_FREE)
end
end
end
end
describe "#read" do
context "when the MiniFAT in the storage is empty" do
it "returns zero" do
expect(minifat.read).to eq(0)
end
end
end
describe "#write" do
context "when entries is empty" do
it "returns nil" do
expect(minifat.write).to be_nil
end
end
end
end

View File

@ -4,6 +4,9 @@ require 'spec_helper'
require 'rex/ole'
describe Rex::OLE::Util do
before(:each) do
Rex::OLE::Util.set_endian(Rex::OLE::LITTLE_ENDIAN)
end
describe ".Hexify32array" do
subject(:hex_array) { described_class.Hexify32array(arr) }