ronin-code-asm 1.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +4 -0
- data/.editorconfig +11 -0
- data/.github/workflows/ruby.yml +31 -0
- data/.gitignore +11 -0
- data/.mailmap +1 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +44 -0
- data/Gemfile +25 -0
- data/README.md +166 -0
- data/Rakefile +39 -0
- data/data/os/freebsd/amd64/syscalls.yml +415 -0
- data/data/os/freebsd/x86/syscalls.yml +415 -0
- data/data/os/linux/amd64/syscalls.yml +306 -0
- data/data/os/linux/x86/syscalls.yml +339 -0
- data/gemspec.yml +26 -0
- data/lib/ronin/code/asm/archs/amd64.rb +100 -0
- data/lib/ronin/code/asm/archs/x86.rb +170 -0
- data/lib/ronin/code/asm/archs.rb +22 -0
- data/lib/ronin/code/asm/config.rb +33 -0
- data/lib/ronin/code/asm/immediate_operand.rb +84 -0
- data/lib/ronin/code/asm/instruction.rb +66 -0
- data/lib/ronin/code/asm/memory_operand.rb +119 -0
- data/lib/ronin/code/asm/os/freebsd.rb +35 -0
- data/lib/ronin/code/asm/os/linux.rb +35 -0
- data/lib/ronin/code/asm/os/os.rb +47 -0
- data/lib/ronin/code/asm/os.rb +57 -0
- data/lib/ronin/code/asm/program.rb +509 -0
- data/lib/ronin/code/asm/register.rb +111 -0
- data/lib/ronin/code/asm/shellcode.rb +75 -0
- data/lib/ronin/code/asm/syntax/att.rb +164 -0
- data/lib/ronin/code/asm/syntax/common.rb +241 -0
- data/lib/ronin/code/asm/syntax/intel.rb +150 -0
- data/lib/ronin/code/asm/syntax.rb +22 -0
- data/lib/ronin/code/asm/version.rb +28 -0
- data/lib/ronin/code/asm.rb +68 -0
- data/ronin-code-asm.gemspec +62 -0
- data/spec/asm_spec.rb +14 -0
- data/spec/config_spec.rb +10 -0
- data/spec/immediate_operand_spec.rb +79 -0
- data/spec/instruction_spec.rb +62 -0
- data/spec/memory_operand_spec.rb +80 -0
- data/spec/os_spec.rb +68 -0
- data/spec/program_spec.rb +439 -0
- data/spec/register_spec.rb +112 -0
- data/spec/shellcode_spec.rb +58 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/syntax/att_spec.rb +181 -0
- data/spec/syntax/common_spec.rb +42 -0
- data/spec/syntax/intel_spec.rb +174 -0
- metadata +143 -0
@@ -0,0 +1,439 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ronin/code/asm/program'
|
3
|
+
|
4
|
+
describe Ronin::Code::ASM::Program do
|
5
|
+
describe "#arch" do
|
6
|
+
it "must default to :x86" do
|
7
|
+
expect(subject.arch).to eq(:x86)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when :arch is :x86" do
|
12
|
+
subject { described_class.new(arch: :x86) }
|
13
|
+
|
14
|
+
it { expect(subject.word_size).to eq(4) }
|
15
|
+
|
16
|
+
describe "#stask_base" do
|
17
|
+
it "must be ebp" do
|
18
|
+
expect(subject.stack_base.name).to eq(:ebp)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#stask_pointer" do
|
23
|
+
it "must be esp" do
|
24
|
+
expect(subject.stack_pointer.name).to eq(:esp)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#stack_push" do
|
29
|
+
let(:value) { 0xff }
|
30
|
+
|
31
|
+
before { subject.stack_push(value) }
|
32
|
+
|
33
|
+
it "must add a 'push' instruction with a value" do
|
34
|
+
expect(subject.instructions[-1].name).to eq(:push)
|
35
|
+
expect(subject.instructions[-1].operands[0].value).to eq(value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#stack_pop" do
|
40
|
+
let(:register) { subject.register(:eax) }
|
41
|
+
|
42
|
+
before { subject.stack_pop(register) }
|
43
|
+
|
44
|
+
it "must add a 'pop' instruction with a register" do
|
45
|
+
expect(subject.instructions[-1].name).to eq(:pop)
|
46
|
+
expect(subject.instructions[-1].operands[0]).to eq(register)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#register_clear" do
|
51
|
+
let(:name) { :eax }
|
52
|
+
|
53
|
+
before { subject.register_clear(name) }
|
54
|
+
|
55
|
+
it "must add a 'xor' instruction with a registers" do
|
56
|
+
expect(subject.instructions[-1].name).to eq(:xor)
|
57
|
+
expect(subject.instructions[-1].operands[0].name).to eq(name)
|
58
|
+
expect(subject.instructions[-1].operands[1].name).to eq(name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#register_set" do
|
63
|
+
let(:name) { :eax }
|
64
|
+
let(:value) { 0xff }
|
65
|
+
|
66
|
+
before { subject.register_set(name,value) }
|
67
|
+
|
68
|
+
it "must add a 'xor' instruction with a registers" do
|
69
|
+
expect(subject.instructions[-1].name).to eq(:mov)
|
70
|
+
expect(subject.instructions[-1].operands[0].value).to eq(value)
|
71
|
+
expect(subject.instructions[-1].operands[1].name).to eq(name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#register_save" do
|
76
|
+
let(:name) { :eax }
|
77
|
+
|
78
|
+
before { subject.register_save(name) }
|
79
|
+
|
80
|
+
it "must add a 'xor' instruction with a registers" do
|
81
|
+
expect(subject.instructions[-1].name).to eq(:push)
|
82
|
+
expect(subject.instructions[-1].operands[0].name).to eq(name)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#register_save" do
|
87
|
+
let(:name) { :eax }
|
88
|
+
|
89
|
+
before { subject.register_load(name) }
|
90
|
+
|
91
|
+
it "must add a 'xor' instruction with a registers" do
|
92
|
+
expect(subject.instructions[-1].name).to eq(:pop)
|
93
|
+
expect(subject.instructions[-1].operands[0].name).to eq(name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "#interrupt" do
|
98
|
+
let(:number) { 0x0a }
|
99
|
+
|
100
|
+
before { subject.interrupt(number) }
|
101
|
+
|
102
|
+
it "must add an 'int' instruction with the interrupt number" do
|
103
|
+
expect(subject.instructions[-1].name).to eq(:int)
|
104
|
+
expect(subject.instructions[-1].operands[0].value).to eq(number)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#syscall" do
|
109
|
+
before { subject.syscall }
|
110
|
+
|
111
|
+
it "must add an 'int 0x80' instruction" do
|
112
|
+
expect(subject.instructions[-1].name).to eq(:int)
|
113
|
+
expect(subject.instructions[-1].operands[0].value).to eq(0x80)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when :os is :linux" do
|
118
|
+
subject { described_class.new(arch: :x86, os: :linux) }
|
119
|
+
|
120
|
+
it { expect(subject.syscalls).to_not be_empty }
|
121
|
+
end
|
122
|
+
|
123
|
+
context "when :os is :freebsd" do
|
124
|
+
subject { described_class.new(arch: :x86, os: :freebsd) }
|
125
|
+
|
126
|
+
it { expect(subject.syscalls).to_not be_empty }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "when :arch is :amd64" do
|
131
|
+
subject { described_class.new(arch: :amd64) }
|
132
|
+
|
133
|
+
it { expect(subject.word_size).to eq(8) }
|
134
|
+
|
135
|
+
describe "#syscall" do
|
136
|
+
before { subject.syscall }
|
137
|
+
|
138
|
+
it "must add a 'syscall' instruction" do
|
139
|
+
expect(subject.instructions[-1].name).to eq(:syscall)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "when :os is :linux" do
|
144
|
+
subject { described_class.new(arch: :amd64, os: :linux) }
|
145
|
+
|
146
|
+
it { expect(subject.syscalls).to_not be_empty }
|
147
|
+
end
|
148
|
+
|
149
|
+
context "when :os is :freebsd" do
|
150
|
+
subject { described_class.new(arch: :amd64, os: :freebsd) }
|
151
|
+
|
152
|
+
it { expect(subject.syscalls).to_not be_empty }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "#register?" do
|
157
|
+
it "must return true for existing registers" do
|
158
|
+
expect(subject.register?(:eax)).to be(true)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "must return false for unknown registers" do
|
162
|
+
expect(subject.register?(:foo)).to be(false)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "#register" do
|
167
|
+
it "must return a Register" do
|
168
|
+
expect(subject.register(:eax)).to be_kind_of(Ronin::Code::ASM::Register)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "must allocate the register" do
|
172
|
+
subject.register(:ebx)
|
173
|
+
|
174
|
+
expect(subject.allocated_registers).to include(:ebx)
|
175
|
+
end
|
176
|
+
|
177
|
+
context "when given an unknown register name" do
|
178
|
+
it "must raise an ArgumentError" do
|
179
|
+
expect {
|
180
|
+
subject.register(:foo)
|
181
|
+
}.to raise_error(ArgumentError)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#instruction" do
|
187
|
+
it "must return an Instruction" do
|
188
|
+
expect(subject.instruction(:hlt)).to be_kind_of(Ronin::Code::ASM::Instruction)
|
189
|
+
end
|
190
|
+
|
191
|
+
it "must append the new Instruction" do
|
192
|
+
subject.instruction(:push, 1)
|
193
|
+
|
194
|
+
expect(subject.instructions.last.name).to eq(:push)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "#byte" do
|
199
|
+
it "must return a ImmedateOperand" do
|
200
|
+
expect(subject.byte(1)).to be_kind_of(Ronin::Code::ASM::ImmediateOperand)
|
201
|
+
end
|
202
|
+
|
203
|
+
it "must have width of 1" do
|
204
|
+
expect(subject.byte(1).width).to eq(1)
|
205
|
+
end
|
206
|
+
|
207
|
+
context "when given a MemoryOperand" do
|
208
|
+
let(:register) { Ronin::Code::ASM::Register.new(:eax, 4) }
|
209
|
+
let(:memory_operand) { Ronin::Code::ASM::MemoryOperand.new(register) }
|
210
|
+
|
211
|
+
it "must return a MemoryOperand" do
|
212
|
+
expect(subject.byte(memory_operand)).to be_kind_of(
|
213
|
+
Ronin::Code::ASM::MemoryOperand
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "must have a width of 1" do
|
218
|
+
expect(subject.byte(memory_operand).width).to eq(1)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
describe "#word" do
|
224
|
+
it "must return a Ronin::Code::ASM::ImmediateOperand" do
|
225
|
+
expect(subject.word(1)).to be_kind_of(Ronin::Code::ASM::ImmediateOperand)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "must have width of 2" do
|
229
|
+
expect(subject.word(1).width).to eq(2)
|
230
|
+
end
|
231
|
+
|
232
|
+
context "when given a MemoryOperand" do
|
233
|
+
let(:register) { Ronin::Code::ASM::Register.new(:eax, 4) }
|
234
|
+
let(:memory_operand) { Ronin::Code::ASM::MemoryOperand.new(register) }
|
235
|
+
|
236
|
+
it "must return a MemoryOperand" do
|
237
|
+
expect(subject.word(memory_operand)).to be_kind_of(
|
238
|
+
Ronin::Code::ASM::MemoryOperand
|
239
|
+
)
|
240
|
+
end
|
241
|
+
|
242
|
+
it "must have a width of 2" do
|
243
|
+
expect(subject.word(memory_operand).width).to eq(2)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe "#dword" do
|
249
|
+
it "must return a Ronin::Code::ASM::ImmediateOperand" do
|
250
|
+
expect(subject.dword(1)).to be_kind_of(Ronin::Code::ASM::ImmediateOperand)
|
251
|
+
end
|
252
|
+
|
253
|
+
it "must have width of 4" do
|
254
|
+
expect(subject.dword(1).width).to eq(4)
|
255
|
+
end
|
256
|
+
|
257
|
+
context "when given a MemoryOperand" do
|
258
|
+
let(:register) { Ronin::Code::ASM::Register.new(:eax, 4) }
|
259
|
+
let(:memory_operand) { Ronin::Code::ASM::MemoryOperand.new(register) }
|
260
|
+
|
261
|
+
it "must return a MemoryOperand" do
|
262
|
+
expect(subject.dword(memory_operand)).to be_kind_of(
|
263
|
+
Ronin::Code::ASM::MemoryOperand
|
264
|
+
)
|
265
|
+
end
|
266
|
+
|
267
|
+
it "must have a width of 4" do
|
268
|
+
expect(subject.dword(memory_operand).width).to eq(4)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
describe "#qword" do
|
274
|
+
it "must return a Ronin::Code::ASM::ImmediateOperand" do
|
275
|
+
expect(subject.qword(1)).to be_kind_of(Ronin::Code::ASM::ImmediateOperand)
|
276
|
+
end
|
277
|
+
|
278
|
+
it "must have width of 8" do
|
279
|
+
expect(subject.qword(1).width).to eq(8)
|
280
|
+
end
|
281
|
+
|
282
|
+
context "when given a MemoryOperand" do
|
283
|
+
let(:register) { Ronin::Code::ASM::Register.new(:eax, 4) }
|
284
|
+
let(:memory_operand) { Ronin::Code::ASM::MemoryOperand.new(register) }
|
285
|
+
|
286
|
+
it "must return a MemoryOperand" do
|
287
|
+
expect(subject.qword(memory_operand)).to be_kind_of(
|
288
|
+
Ronin::Code::ASM::MemoryOperand
|
289
|
+
)
|
290
|
+
end
|
291
|
+
|
292
|
+
it "must have a width of 8" do
|
293
|
+
expect(subject.qword(memory_operand).width).to eq(8)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe "#label" do
|
299
|
+
let(:name) { :_start }
|
300
|
+
|
301
|
+
it "must return the label name" do
|
302
|
+
label = subject.label(name) { }
|
303
|
+
|
304
|
+
expect(label).to eq(name)
|
305
|
+
end
|
306
|
+
|
307
|
+
it "must add the label to the instructions" do
|
308
|
+
subject.label(name) { }
|
309
|
+
|
310
|
+
expect(subject.instructions.last).to eq(name)
|
311
|
+
end
|
312
|
+
|
313
|
+
it "must accept a block" do
|
314
|
+
subject.label(name) { push 2 }
|
315
|
+
|
316
|
+
expect(subject.instructions[-1].name).to eq(:push)
|
317
|
+
expect(subject.instructions[-2]).to eq(name)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "#method_missing" do
|
322
|
+
context "when called without a block" do
|
323
|
+
it "must add a new instruction" do
|
324
|
+
subject.pop
|
325
|
+
|
326
|
+
expect(subject.instructions[-1].name).to eq(:pop)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
context "when called with one argument and a block" do
|
331
|
+
it "must add a new label" do
|
332
|
+
subject._loop { mov eax, ebx }
|
333
|
+
|
334
|
+
expect(subject.instructions[-2]).to eq(:_loop)
|
335
|
+
expect(subject.instructions[-1].name).to eq(:mov)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
describe "#to_asm" do
|
341
|
+
subject do
|
342
|
+
described_class.new do
|
343
|
+
push eax
|
344
|
+
push ebx
|
345
|
+
push ecx
|
346
|
+
|
347
|
+
mov ebx, eax
|
348
|
+
mov ebx, eax+0
|
349
|
+
mov ebx, eax+4
|
350
|
+
mov ebx, eax+esi
|
351
|
+
mov ebx, eax+(esi*4)
|
352
|
+
mov ebx, eax+(esi*4)+10
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
it "must convert the program to Intel syntax" do
|
357
|
+
expect(subject.to_asm).to eq([
|
358
|
+
"BITS 32",
|
359
|
+
"section .text",
|
360
|
+
"_start:",
|
361
|
+
"\tpush\teax",
|
362
|
+
"\tpush\tebx",
|
363
|
+
"\tpush\tecx",
|
364
|
+
"\tmov\tebx,\teax",
|
365
|
+
"\tmov\tebx,\t[eax]",
|
366
|
+
"\tmov\tebx,\t[eax+0x4]",
|
367
|
+
"\tmov\tebx,\t[eax+esi]",
|
368
|
+
"\tmov\tebx,\t[eax+esi*0x4]",
|
369
|
+
"\tmov\tebx,\t[eax+esi*0x4+0xa]",
|
370
|
+
""
|
371
|
+
].join($/))
|
372
|
+
end
|
373
|
+
|
374
|
+
context "when given :att" do
|
375
|
+
it "must convert the program to ATT syntax" do
|
376
|
+
expect(subject.to_asm(:att)).to eq([
|
377
|
+
".code32",
|
378
|
+
".text",
|
379
|
+
"_start:",
|
380
|
+
"\tpushl\t%eax",
|
381
|
+
"\tpushl\t%ebx",
|
382
|
+
"\tpushl\t%ecx",
|
383
|
+
"\tmovl\t%eax,\t%ebx",
|
384
|
+
"\tmovl\t(%eax),\t%ebx",
|
385
|
+
"\tmovl\t0x4(%eax),\t%ebx",
|
386
|
+
"\tmovl\t(%eax,%esi),\t%ebx",
|
387
|
+
"\tmovl\t(%eax,%esi,4),\t%ebx",
|
388
|
+
"\tmovl\t0xa(%eax,%esi,4),\t%ebx",
|
389
|
+
""
|
390
|
+
].join($/))
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
describe "#assemble", integration: true do
|
396
|
+
subject do
|
397
|
+
described_class.new do
|
398
|
+
push eax
|
399
|
+
push ebx
|
400
|
+
push ecx
|
401
|
+
|
402
|
+
mov ebx, eax
|
403
|
+
mov ebx, eax+0
|
404
|
+
mov ebx, eax+4
|
405
|
+
mov ebx, eax+esi
|
406
|
+
mov ebx, eax+(esi*4)
|
407
|
+
mov ebx, eax+(esi*4)+10
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
let(:output) { Tempfile.new(['ronin-asm', '.o']).path }
|
412
|
+
|
413
|
+
before { subject.assemble(output) }
|
414
|
+
|
415
|
+
it "must write to the output file" do
|
416
|
+
expect(File.size(output)).to be > 0
|
417
|
+
end
|
418
|
+
|
419
|
+
context "when syntax: :intel is given" do
|
420
|
+
let(:output) { Tempfile.new(['ronin-asm', '.o']).path }
|
421
|
+
|
422
|
+
before { subject.assemble(output, syntax: :intel) }
|
423
|
+
|
424
|
+
it "must write to the output file" do
|
425
|
+
expect(File.size(output)).to be > 0
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
context "when syntax is unknown" do
|
430
|
+
let(:syntax) { :foo }
|
431
|
+
|
432
|
+
it do
|
433
|
+
expect {
|
434
|
+
subject.assemble(output, syntax: syntax)
|
435
|
+
}.to raise_error(ArgumentError,"unknown ASM syntax: #{syntax.inspect}")
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'ronin/code/asm/register'
|
4
|
+
|
5
|
+
describe Ronin::Code::ASM::Register do
|
6
|
+
let(:register) { described_class.new(:eax, 4) }
|
7
|
+
|
8
|
+
subject { register }
|
9
|
+
|
10
|
+
describe "#+" do
|
11
|
+
context "when given an Ronin::Code::ASM::MemoryOperand" do
|
12
|
+
let(:operand) do
|
13
|
+
Ronin::Code::ASM::MemoryOperand.new(nil,10,register,2)
|
14
|
+
end
|
15
|
+
|
16
|
+
subject { register + operand }
|
17
|
+
|
18
|
+
it { expect(subject).to be_kind_of(Ronin::Code::ASM::MemoryOperand) }
|
19
|
+
|
20
|
+
it "must set the base" do
|
21
|
+
expect(subject.base).to eq(register)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "must preserve the offset, index and scale" do
|
25
|
+
expect(subject.offset).to eq(operand.offset)
|
26
|
+
expect(subject.index).to eq(operand.index)
|
27
|
+
expect(subject.scale).to eq(operand.scale)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when given a Register" do
|
32
|
+
subject { register + register }
|
33
|
+
|
34
|
+
it { expect(subject).to be_kind_of(Ronin::Code::ASM::MemoryOperand) }
|
35
|
+
|
36
|
+
it "must set the base" do
|
37
|
+
expect(subject.base).to eq(register)
|
38
|
+
end
|
39
|
+
|
40
|
+
it { expect(subject.offset).to eq(0) }
|
41
|
+
|
42
|
+
it "must set the index" do
|
43
|
+
expect(subject.index).to eq(register)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when given an Integer" do
|
48
|
+
let(:offset) { 10 }
|
49
|
+
|
50
|
+
subject { register + offset }
|
51
|
+
|
52
|
+
it { expect(subject).to be_kind_of(Ronin::Code::ASM::MemoryOperand) }
|
53
|
+
|
54
|
+
it "must set the base" do
|
55
|
+
expect(subject.base).to eq(register)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "must set the offset" do
|
59
|
+
expect(subject.offset).to eq(offset)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "otherwise" do
|
64
|
+
it "must raise a TypeError" do
|
65
|
+
expect {
|
66
|
+
register + Object.new
|
67
|
+
}.to raise_error(TypeError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#-" do
|
73
|
+
let(:offset) { 10 }
|
74
|
+
|
75
|
+
subject { register - offset }
|
76
|
+
|
77
|
+
it { expect(subject).to be_kind_of(Ronin::Code::ASM::MemoryOperand) }
|
78
|
+
|
79
|
+
it "must set the base" do
|
80
|
+
expect(subject.base).to eq(register)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "must set a negative offset" do
|
84
|
+
expect(subject.offset).to eq(-offset)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#*" do
|
89
|
+
let(:scale) { 2 }
|
90
|
+
|
91
|
+
subject { register * scale }
|
92
|
+
|
93
|
+
it { expect(subject).to be_kind_of(Ronin::Code::ASM::MemoryOperand) }
|
94
|
+
|
95
|
+
it { expect(subject.base).to be_nil }
|
96
|
+
it { expect(subject.offset).to eq(0) }
|
97
|
+
|
98
|
+
it "must set the index" do
|
99
|
+
expect(subject.index).to eq(register)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "must set the scale" do
|
103
|
+
expect(subject.scale).to eq(scale)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#to_s" do
|
108
|
+
it "must return the register name" do
|
109
|
+
expect(subject.to_s).to eq(subject.name.to_s)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'ronin/code/asm/shellcode'
|
5
|
+
|
6
|
+
describe Ronin::Code::ASM::Shellcode do
|
7
|
+
describe "#assemble", integration: true do
|
8
|
+
subject do
|
9
|
+
described_class.new do
|
10
|
+
xor eax, eax
|
11
|
+
push eax
|
12
|
+
push 0x68732f2f
|
13
|
+
push 0x6e69622f
|
14
|
+
mov ebx, esp
|
15
|
+
push eax
|
16
|
+
push ebx
|
17
|
+
mov ecx, esp
|
18
|
+
xor edx, edx
|
19
|
+
mov al, 0xb
|
20
|
+
int 0x80
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:shellcode) { "1\xC0Ph//shh/bin\x89\xE3PS\x89\xE11\xD2\xB0\v\xCD\x80" }
|
25
|
+
|
26
|
+
it "must assemble down to raw machine code" do
|
27
|
+
expect(subject.assemble).to eq(shellcode)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "must return an ASCII-8bit encoded String" do
|
31
|
+
expect(subject.assemble.encoding).to eq(Encoding::ASCII_8BIT)
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with :output" do
|
35
|
+
let(:output) do
|
36
|
+
Tempfile.new(['ronin-shellcode-custom-path', '.bin']).path
|
37
|
+
end
|
38
|
+
|
39
|
+
it "must write to the custom path" do
|
40
|
+
expect(subject.assemble(output: output)).to eq(shellcode)
|
41
|
+
|
42
|
+
File.binread(output)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "with :syntax is :intel" do
|
47
|
+
it "assemble down to raw machine code" do
|
48
|
+
expect(subject.assemble(syntax: :intel)).to eq(shellcode)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with :syntax is :att" do
|
53
|
+
it "assemble down to raw machine code" do
|
54
|
+
expect(subject.assemble(syntax: :att)).to eq(shellcode)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|