ronin-code-asm 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-code-asm - A Ruby DSL for crafting Assembly programs and shellcode.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-code-asm is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-code-asm is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-code-asm. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/code/asm/program'
|
22
|
+
require 'ronin/code/asm/shellcode'
|
23
|
+
require 'ronin/code/asm/version'
|
24
|
+
|
25
|
+
module Ronin
|
26
|
+
module Code
|
27
|
+
module ASM
|
28
|
+
#
|
29
|
+
# Creates a new Assembly Program.
|
30
|
+
#
|
31
|
+
# @param [Hash{Symbol => Object}] kwargs
|
32
|
+
# Additional keyword arguments for {Program#initialize}.
|
33
|
+
#
|
34
|
+
# @option kwargs [String, Symbol] :arch (:x86)
|
35
|
+
# The architecture of the Program.
|
36
|
+
#
|
37
|
+
# @option kwargs [Hash{Symbol => Object}] :variables
|
38
|
+
# Variables to set in the program.
|
39
|
+
#
|
40
|
+
# @yield []
|
41
|
+
# The given block will be evaluated within the program.
|
42
|
+
#
|
43
|
+
# @return [Program]
|
44
|
+
# The new Assembly Program.
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# ASM.new do
|
48
|
+
# mov 1, eax
|
49
|
+
# mov 1, ebx
|
50
|
+
# mov 2, ecx
|
51
|
+
#
|
52
|
+
# _loop do
|
53
|
+
# push ecx
|
54
|
+
# imul ebx, ecx
|
55
|
+
# pop ebx
|
56
|
+
#
|
57
|
+
# inc eax
|
58
|
+
# cmp ebx, 10
|
59
|
+
# jl :_loop
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
def ASM.new(**kwargs,&block)
|
64
|
+
Program.new(**kwargs,&block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gemspec = YAML.load_file('gemspec.yml')
|
7
|
+
|
8
|
+
gem.name = gemspec.fetch('name')
|
9
|
+
gem.version = gemspec.fetch('version') do
|
10
|
+
lib_dir = File.join(File.dirname(__FILE__),'lib')
|
11
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
12
|
+
|
13
|
+
require 'ronin/code/asm/version'
|
14
|
+
Ronin::Code::ASM::VERSION
|
15
|
+
end
|
16
|
+
|
17
|
+
gem.summary = gemspec['summary']
|
18
|
+
gem.description = gemspec['description']
|
19
|
+
gem.licenses = Array(gemspec['license'])
|
20
|
+
gem.authors = Array(gemspec['authors'])
|
21
|
+
gem.email = gemspec['email']
|
22
|
+
gem.homepage = gemspec['homepage']
|
23
|
+
gem.metadata = gemspec['metadata'] if gemspec['metadata']
|
24
|
+
|
25
|
+
glob = lambda { |patterns| gem.files & Dir[*patterns] }
|
26
|
+
|
27
|
+
gem.files = `git ls-files`.split($/)
|
28
|
+
gem.files = glob[gemspec['files']] if gemspec['files']
|
29
|
+
gem.files += Array(gemspec['generated_files'])
|
30
|
+
|
31
|
+
gem.executables = gemspec.fetch('executables') do
|
32
|
+
glob['bin/*'].map { |path| File.basename(path) }
|
33
|
+
end
|
34
|
+
gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
|
35
|
+
|
36
|
+
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
37
|
+
gem.test_files = glob[gemspec['test_files'] || 'spec/{**/}*_spec.rb']
|
38
|
+
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
39
|
+
|
40
|
+
gem.require_paths = Array(gemspec.fetch('require_paths') {
|
41
|
+
%w[ext lib].select { |dir| File.directory?(dir) }
|
42
|
+
})
|
43
|
+
|
44
|
+
gem.requirements = gemspec['requirements']
|
45
|
+
gem.required_ruby_version = gemspec['required_ruby_version']
|
46
|
+
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
47
|
+
gem.post_install_message = gemspec['post_install_message']
|
48
|
+
|
49
|
+
split = lambda { |string| string.split(/,\s*/) }
|
50
|
+
|
51
|
+
if gemspec['dependencies']
|
52
|
+
gemspec['dependencies'].each do |name,versions|
|
53
|
+
gem.add_dependency(name,split[versions])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if gemspec['development_dependencies']
|
58
|
+
gemspec['development_dependencies'].each do |name,versions|
|
59
|
+
gem.add_development_dependency(name,split[versions])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/asm_spec.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ronin/code/asm'
|
3
|
+
|
4
|
+
describe Ronin::Code::ASM do
|
5
|
+
it "must have a version" do
|
6
|
+
expect(subject.const_defined?('VERSION')).to eq(true)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe ".new" do
|
10
|
+
it "must return a new Ronin::Code::ASM::Program" do
|
11
|
+
expect(subject.new(arch: :x86)).to be_kind_of(Ronin::Code::ASM::Program)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'ronin/code/asm/immediate_operand'
|
4
|
+
|
5
|
+
describe Ronin::Code::ASM::ImmediateOperand do
|
6
|
+
let(:value) { 0xff }
|
7
|
+
|
8
|
+
describe "#initialize" do
|
9
|
+
context "with a width" do
|
10
|
+
let(:width) { 2 }
|
11
|
+
|
12
|
+
subject { described_class.new(value,width) }
|
13
|
+
|
14
|
+
it "must set the width" do
|
15
|
+
expect(subject.width).to eq(width)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#width" do
|
21
|
+
describe "default width for" do
|
22
|
+
context "0x100000000 .. 0xffffffffffffffff" do
|
23
|
+
subject { described_class.new(0xffffffffffffffff).width }
|
24
|
+
|
25
|
+
it { expect(subject).to be == 8 }
|
26
|
+
end
|
27
|
+
|
28
|
+
context "-0x800000000 .. -0x7fffffffffffffff" do
|
29
|
+
subject { described_class.new(-0x7fffffffffffffff).width }
|
30
|
+
|
31
|
+
it { expect(subject).to be == 8 }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "0x10000 .. 0xffffffff" do
|
35
|
+
subject { described_class.new(0xffffffff).width }
|
36
|
+
|
37
|
+
it { expect(subject).to be == 4 }
|
38
|
+
end
|
39
|
+
|
40
|
+
context "-0x80000 .. -0x7fffffff" do
|
41
|
+
subject { described_class.new(-0x7fffffff).width }
|
42
|
+
|
43
|
+
it { expect(subject).to be == 4 }
|
44
|
+
end
|
45
|
+
|
46
|
+
context "0x100 .. 0xffff" do
|
47
|
+
subject { described_class.new(0xffff).width }
|
48
|
+
|
49
|
+
it { expect(subject).to be == 2 }
|
50
|
+
end
|
51
|
+
|
52
|
+
context "-0x80 .. -0x7fff" do
|
53
|
+
subject { described_class.new(-0x7fff).width }
|
54
|
+
|
55
|
+
it { expect(subject).to be == 2 }
|
56
|
+
end
|
57
|
+
|
58
|
+
context "0x0 .. 0xff" do
|
59
|
+
subject { described_class.new(0xff).width }
|
60
|
+
|
61
|
+
it { expect(subject).to be == 1 }
|
62
|
+
end
|
63
|
+
|
64
|
+
context "0x0 .. -0x7f" do
|
65
|
+
subject { described_class.new(-0x7f).width }
|
66
|
+
|
67
|
+
it { expect(subject).to be == 1 }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#to_i" do
|
73
|
+
subject { described_class.new(value) }
|
74
|
+
|
75
|
+
it "must return the value" do
|
76
|
+
expect(subject.to_i).to eq(value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'ronin/code/asm/instruction'
|
4
|
+
require 'ronin/code/asm/register'
|
5
|
+
require 'ronin/code/asm/immediate_operand'
|
6
|
+
require 'ronin/code/asm/memory_operand'
|
7
|
+
|
8
|
+
describe Ronin::Code::ASM::Instruction do
|
9
|
+
let(:register) { Ronin::Code::ASM::Register.new(:eax, 4) }
|
10
|
+
let(:immediate) { Ronin::Code::ASM::ImmediateOperand.new(0xff, 1) }
|
11
|
+
|
12
|
+
describe "#initialize" do
|
13
|
+
let(:name) { :mov }
|
14
|
+
let(:operands) { [immediate, register] }
|
15
|
+
|
16
|
+
subject { described_class.new(name,operands) }
|
17
|
+
|
18
|
+
it "must set the name" do
|
19
|
+
expect(subject.name).to eq(:mov)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "must set the operands" do
|
23
|
+
expect(subject.operands).to eq(operands)
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when given an Integer operand" do
|
27
|
+
let(:integer) { 0xff }
|
28
|
+
|
29
|
+
subject { described_class.new(name, [integer, register]) }
|
30
|
+
|
31
|
+
it "must wrap the operand to in a Ronin::Code::ASM::ImmediateOperand" do
|
32
|
+
expect(subject.operands[0]).to be_kind_of(Ronin::Code::ASM::ImmediateOperand)
|
33
|
+
expect(subject.operands[0].value).to eq(integer)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when given a nil operand" do
|
38
|
+
subject { described_class.new(name, [nil, register]) }
|
39
|
+
|
40
|
+
it "must wrap the operand to in a Ronin::Code::ASM::ImmediateOperand" do
|
41
|
+
expect(subject.operands[0]).to be_kind_of(Ronin::Code::ASM::ImmediateOperand)
|
42
|
+
expect(subject.operands[0].value).to eq(0)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#width" do
|
48
|
+
subject { described_class.new(:mov, [immediate, register]) }
|
49
|
+
|
50
|
+
it "must return the maximum width of the operands" do
|
51
|
+
expect(subject.width).to eq(register.width)
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when one of the operands does not define #width" do
|
55
|
+
subject { described_class.new(:mov, [:label, register]) }
|
56
|
+
|
57
|
+
it "must ignore them" do
|
58
|
+
expect(subject.width).to eq(register.width)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'ronin/code/asm/memory_operand'
|
4
|
+
require 'ronin/code/asm/register'
|
5
|
+
|
6
|
+
describe Ronin::Code::ASM::MemoryOperand do
|
7
|
+
let(:register) { Ronin::Code::ASM::Register.new(:eax, 4) }
|
8
|
+
|
9
|
+
describe "#initialize" do
|
10
|
+
it { expect(subject.base).to be_nil }
|
11
|
+
it { expect(subject.offset).to eq(0) }
|
12
|
+
it { expect(subject.index).to be_nil }
|
13
|
+
it { expect(subject.scale).to eq(1) }
|
14
|
+
|
15
|
+
it "must only accept nil and a Register for base" do
|
16
|
+
expect {
|
17
|
+
described_class.new(Object.new)
|
18
|
+
}.to raise_error(TypeError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "must only accept Integers for offset" do
|
22
|
+
expect {
|
23
|
+
described_class.new(register,2.0)
|
24
|
+
}.to raise_error(TypeError)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "must only accept nil and a Register for index" do
|
28
|
+
expect {
|
29
|
+
described_class.new(register,0,Object.new)
|
30
|
+
}.to raise_error(TypeError)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "must only accept Integers for offset" do
|
34
|
+
expect {
|
35
|
+
described_class.new(register,0,nil,2.0)
|
36
|
+
}.to raise_error(TypeError)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#+" do
|
42
|
+
let(:operand) { described_class.new(register,4,register,2) }
|
43
|
+
|
44
|
+
subject { operand + 4 }
|
45
|
+
|
46
|
+
it "must add to offset" do
|
47
|
+
expect(subject.offset).to eq(8)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "must not change base, index or scale" do
|
51
|
+
expect(subject.base).to eq(operand.base)
|
52
|
+
expect(subject.index).to eq(operand.index)
|
53
|
+
expect(subject.scale).to eq(operand.scale)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#-" do
|
58
|
+
let(:operand) { described_class.new(register,4,register,2) }
|
59
|
+
|
60
|
+
subject { operand - 2 }
|
61
|
+
|
62
|
+
it "must subtract from offset" do
|
63
|
+
expect(subject.offset).to eq(2)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "must not change base, index or scale" do
|
67
|
+
expect(subject.base).to eq(operand.base)
|
68
|
+
expect(subject.index).to eq(operand.index)
|
69
|
+
expect(subject.scale).to eq(operand.scale)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#width" do
|
74
|
+
subject { described_class.new(register,10) }
|
75
|
+
|
76
|
+
it "must return the width of base" do
|
77
|
+
expect(subject.width).to eq(register.width)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/spec/os_spec.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ronin/code/asm/os'
|
3
|
+
|
4
|
+
describe Ronin::Code::ASM::OS do
|
5
|
+
describe "SYSCALLS" do
|
6
|
+
subject { described_class::SYSCALLS }
|
7
|
+
|
8
|
+
let(:data_dir) { Ronin::Code::ASM::Config::DATA_DIR }
|
9
|
+
|
10
|
+
it { expect(subject).to be_kind_of(Hash) }
|
11
|
+
|
12
|
+
it "must load syscalls for :freebsd and :amd64" do
|
13
|
+
expect(subject[:freebsd][:amd64]).to eq(
|
14
|
+
YAML.load_file(
|
15
|
+
File.join(data_dir,'os','freebsd','amd64','syscalls.yml')
|
16
|
+
)
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "must load syscalls for :freebsd and :x86" do
|
21
|
+
expect(subject[:freebsd][:amd64]).to eq(
|
22
|
+
YAML.load_file(
|
23
|
+
File.join(data_dir,'os','freebsd','x86','syscalls.yml')
|
24
|
+
)
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "must load syscalls for :linux and :amd64" do
|
29
|
+
expect(subject[:linux][:amd64]).to eq(
|
30
|
+
YAML.load_file(
|
31
|
+
File.join(data_dir,'os','linux','amd64','syscalls.yml')
|
32
|
+
)
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "must load syscalls for :linux and :x86" do
|
37
|
+
expect(subject[:linux][:x86]).to eq(
|
38
|
+
YAML.load_file(
|
39
|
+
File.join(data_dir,'os','linux','x86','syscalls.yml')
|
40
|
+
)
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe ".[]" do
|
46
|
+
context "when given :linux" do
|
47
|
+
it "must return #{described_class}::Linux" do
|
48
|
+
expect(subject[:linux]).to be(described_class::Linux)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when given :freebsd" do
|
53
|
+
it "must return #{described_class}::FreeBSD" do
|
54
|
+
expect(subject[:freebsd]).to be(described_class::FreeBSD)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when given an unknown Symbol" do
|
59
|
+
let(:name) { :foo }
|
60
|
+
|
61
|
+
it do
|
62
|
+
expect {
|
63
|
+
subject[name]
|
64
|
+
}.to raise_error(ArgumentError,"unknown OS name: #{name.inspect}")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|