Skip to content

Commit 0dfd8e2

Browse files
committed
Land rapid7#3846, Rex::ImageSource specs
2 parents ee92648 + e4f71d7 commit 0dfd8e2

File tree

4 files changed

+377
-2
lines changed

4 files changed

+377
-2
lines changed

lib/rex/image_source/image_source.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ def read_asciiz(offset)
2929
# FIXME, make me better
3030
string = ''
3131
loop do
32-
char = read(offset, 1)
33-
break if char == "\x00"
32+
begin
33+
char = read(offset, 1)
34+
rescue RangeError
35+
break
36+
end
37+
break if char.nil? || char == "\x00"
3438
offset += 1
3539
string << char
3640
end
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# -*- coding:binary -*-
2+
require 'spec_helper'
3+
4+
require 'rex/image_source/disk'
5+
6+
describe Rex::ImageSource::Disk do
7+
8+
let(:path) do
9+
File.join(Msf::Config.data_directory, "templates", "template_x86_windows_old.exe")
10+
end
11+
12+
let(:file) do
13+
File.new(path)
14+
end
15+
16+
subject do
17+
described_class.new(file)
18+
end
19+
20+
it_should_behave_like 'Rex::ImageSource::ImageSource'
21+
22+
describe "#initialize" do
23+
subject(:disk_class) do
24+
described_class.allocate
25+
end
26+
27+
context "when _len not sent as argument" do
28+
let(:_file) { file }
29+
30+
it "initializes size to file length" do
31+
disk_class.send(:initialize, file)
32+
expect(disk_class.size).to eq(4608)
33+
end
34+
end
35+
36+
context "when _offset not sent as argument" do
37+
let(:_file) { file }
38+
it "initializes file_offset to 0" do
39+
disk_class.send(:initialize, file)
40+
expect(disk_class.file_offset).to eq(0)
41+
end
42+
end
43+
end
44+
45+
describe "#read" do
46+
context "when offset less than 0" do
47+
let(:offset) { -1 }
48+
let(:len) { 20 }
49+
50+
it "raises a RangeError" do
51+
expect { subject.read(offset, len) }.to raise_error(RangeError)
52+
end
53+
end
54+
55+
context "offset plus len greater than size" do
56+
let(:offset) { 0 }
57+
let(:len) { 16000 }
58+
59+
it "raises a RangeError" do
60+
expect { subject.read(offset, len) }.to raise_error(RangeError)
61+
end
62+
end
63+
64+
context "when offset and len inside range" do
65+
let(:offset) { 0 }
66+
let(:len) { 2 }
67+
68+
it "returns file contents" do
69+
expect(subject.read(offset, len)). to eq('MZ')
70+
end
71+
end
72+
73+
context "instance with tampered size" do
74+
let(:tampered_size) { 6000 }
75+
76+
subject(:tampered) do
77+
described_class.new(file, 0, tampered_size)
78+
end
79+
80+
context "when reading offset after the real file length" do
81+
let(:offset) { 5000 }
82+
let(:len) { 2 }
83+
it "returns nil" do
84+
expect(tampered.read(offset, len)).to be_nil
85+
end
86+
end
87+
end
88+
end
89+
90+
describe "#index" do
91+
let(:search) { 'MZ' }
92+
93+
it "returns index of first search occurrence" do
94+
expect(subject.index(search)).to eq(0)
95+
end
96+
97+
context "when offset out of range" do
98+
it "returns nil" do
99+
expect(subject.index(search, 6000)).to be_nil
100+
end
101+
end
102+
103+
context "when search string not found" do
104+
it "returns nil" do
105+
expect(subject.index(search, 4600)).to be_nil
106+
end
107+
end
108+
109+
context "instance with tampered size" do
110+
let(:tampered_size) { 6000 }
111+
112+
subject(:tampered) do
113+
described_class.new(file, 0, tampered_size)
114+
end
115+
116+
context "when searching offset after the real file length" do
117+
let(:offset) { 5000 }
118+
it "raises NoMethodError" do
119+
expect{ tampered.index(search, offset) }.to raise_error(NoMethodError)
120+
end
121+
end
122+
end
123+
end
124+
125+
describe "#subsource" do
126+
let(:offset) { 2 }
127+
let(:len) { 512 }
128+
129+
it "returns a new Rex::ImageSource::Disk" do
130+
expect(subject.subsource(offset, len)).to be_kind_of(described_class)
131+
end
132+
133+
it "returns a new Rex::ImageSource::Disk with same file" do
134+
expect(subject.subsource(offset, len).file).to eq(subject.file)
135+
end
136+
137+
it "returns a new Rex::ImageSource::Disk with provided size" do
138+
expect(subject.subsource(offset, len).size).to eq(len)
139+
end
140+
141+
it "returns a new Rex::ImageSource::Disk with file_offset added to the original" do
142+
expect(subject.subsource(offset, len).file_offset).to eq(offset + subject.file_offset)
143+
end
144+
end
145+
146+
describe "#close" do
147+
it "returns nil" do
148+
expect(subject.close).to be_nil
149+
end
150+
151+
it "closes the associated file" do
152+
expect(subject.file.closed?).to be_falsey
153+
subject.close
154+
expect(subject.file.closed?).to be_truthy
155+
end
156+
end
157+
end
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# -*- coding:binary -*-
2+
require 'spec_helper'
3+
4+
require 'rex/image_source/memory'
5+
6+
describe Rex::ImageSource::Memory do
7+
8+
let(:raw_data) { 'ABCDEFGHIJKLMNOP' }
9+
10+
subject do
11+
described_class.new(raw_data)
12+
end
13+
14+
it_should_behave_like 'Rex::ImageSource::ImageSource'
15+
16+
describe "#initialize" do
17+
subject(:memory_class) do
18+
described_class.allocate
19+
end
20+
21+
it "initializes size to data length" do
22+
memory_class.send(:initialize, raw_data)
23+
expect(memory_class.size).to eq(raw_data.length)
24+
end
25+
26+
it "initializes file_offset to 0 by default" do
27+
memory_class.send(:initialize, raw_data)
28+
expect(memory_class.file_offset).to eq(0)
29+
end
30+
31+
context "when using nil as data" do
32+
it "raises an error" do
33+
expect { memory_class.send(:initialize, nil) }.to raise_error(NoMethodError)
34+
end
35+
end
36+
end
37+
38+
describe "#read" do
39+
context "when offset is positive" do
40+
let(:offset) { 1 }
41+
let(:len) { 10 }
42+
43+
it "returns an String" do
44+
expect(subject.read(offset, len)).to be_a_kind_of(String)
45+
end
46+
47+
it "returns an String of provided length" do
48+
expect(subject.read(offset, len).length).to eq(10)
49+
end
50+
51+
it "returns an String with _raw_data contents starting at provided offset" do
52+
expect(subject.read(offset, len)).to start_with('BCD')
53+
end
54+
end
55+
56+
context "when offset is negative" do
57+
let(:offset) { -5 }
58+
let(:len) { 2 }
59+
60+
it "returns an String" do
61+
expect(subject.read(offset, len)).to be_a_kind_of(String)
62+
end
63+
64+
it "returns an String of provided length" do
65+
expect(subject.read(offset, len).length).to eq(2)
66+
end
67+
68+
it "offset is counted from the end of the _raw_data" do
69+
expect(subject.read(offset, len)).to eq('LM')
70+
end
71+
end
72+
73+
context "when offset is out of range" do
74+
let(:offset) { 20 }
75+
let(:len) { 2 }
76+
77+
it "returns nil" do
78+
expect(subject.read(offset, len)).to be_nil
79+
end
80+
end
81+
82+
context "when len is bigger than _raw_data" do
83+
let(:offset) { 0 }
84+
let(:len) { 20 }
85+
86+
it "returns an String" do
87+
expect(subject.read(offset, len)).to be_a_kind_of(String)
88+
end
89+
90+
it "returns an String truncated to available contents" do
91+
expect(subject.read(offset, len).length).to eq(raw_data.length)
92+
end
93+
end
94+
end
95+
96+
describe "#subsource" do
97+
let(:offset) { 2 }
98+
let(:len) { 10 }
99+
100+
it "returns a new Rex::ImageSource::Memory" do
101+
expect(subject.subsource(offset, len)).to be_kind_of(described_class)
102+
end
103+
104+
it "returns a new Rex::ImageSource::Memory with provided size" do
105+
expect(subject.subsource(offset, len).size).to eq(len)
106+
end
107+
108+
it "returns a new Rex::ImageSource::Memory with file_offset added to the original" do
109+
expect(subject.subsource(offset, len).file_offset).to eq(offset + subject.file_offset)
110+
end
111+
112+
it "returns a new Rex::ImageSource::Memory with rawdata from the original" do
113+
expect(subject.subsource(offset, len).rawdata).to eq(subject.rawdata[offset, len])
114+
end
115+
116+
context "when offset is out of range" do
117+
let(:offset) { 20 }
118+
let(:len) { 2 }
119+
120+
it "raises an error" do
121+
expect { subject.subsource(offset, len) }.to raise_error(NoMethodError)
122+
end
123+
end
124+
125+
context "when len is bigger than source rawdata" do
126+
let(:offset) { 2 }
127+
let(:len) { 20 }
128+
129+
it "returns a new Rex::ImageSource::Memory" do
130+
expect(subject.subsource(offset, len)).to be_kind_of(described_class)
131+
end
132+
133+
it "returns a new Rex::ImageSource::Memory with provided size truncated" do
134+
expect(subject.subsource(offset, len).size).to eq(14)
135+
end
136+
137+
it "returns a new Rex::ImageSource::Memory with file_offset added to the original" do
138+
expect(subject.subsource(offset, len).file_offset).to eq(offset + subject.file_offset)
139+
end
140+
141+
it "returns a new Rex::ImageSource::Memory with rawdata truncated" do
142+
expect(subject.subsource(offset, len).rawdata).to eq('CDEFGHIJKLMNOP')
143+
end
144+
end
145+
end
146+
147+
describe "#close" do
148+
it "returns nil" do
149+
expect(subject.close).to be_nil
150+
end
151+
end
152+
153+
describe "#index" do
154+
let(:found) { 'FG' }
155+
let(:not_found) { 'XYZ' }
156+
157+
context "when search available substring" do
158+
it "returns the index of the first occurrence" do
159+
expect(subject.index(found)).to eq(5)
160+
end
161+
162+
context "when using negative offset" do
163+
let(:offset) { -14 }
164+
it "returns the index of the first occurrence" do
165+
expect(subject.index(found, offset)).to eq(5)
166+
end
167+
end
168+
169+
context "when using positive offset" do
170+
let(:offset) { 1 }
171+
it "returns the index of the first occurrence" do
172+
expect(subject.index(found, offset)).to eq(5)
173+
end
174+
end
175+
end
176+
177+
context "when search not available substring" do
178+
it "returns nil" do
179+
expect(subject.index(not_found)).to be_nil
180+
end
181+
end
182+
183+
context "when using negative offset" do
184+
let(:offset) { -1 }
185+
it "start to search from offset from the end of the string" do
186+
expect(subject.index(found, offset)).to be_nil
187+
end
188+
end
189+
end
190+
191+
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
shared_examples_for "Rex::ImageSource::ImageSource" do
2+
3+
describe "#read_asciiz" do
4+
let(:offset) { 0 }
5+
6+
it "returns an String" do
7+
expect(subject.read_asciiz(offset)).to be_kind_of(String)
8+
end
9+
10+
it "returns a null free String" do
11+
expect(subject.read_asciiz(offset)).to_not include("\x00")
12+
end
13+
14+
context "when offset bigger than available data" do
15+
let(:offset) { 12345678 }
16+
17+
it "returns an empty String" do
18+
expect(subject.read_asciiz(offset)).to be_empty
19+
end
20+
end
21+
end
22+
23+
end

0 commit comments

Comments
 (0)