ronin-code-asm 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.document +4 -0
  3. data/.editorconfig +11 -0
  4. data/.github/workflows/ruby.yml +31 -0
  5. data/.gitignore +11 -0
  6. data/.mailmap +1 -0
  7. data/.rspec +1 -0
  8. data/.ruby-version +1 -0
  9. data/.yardopts +1 -0
  10. data/COPYING.txt +165 -0
  11. data/ChangeLog.md +44 -0
  12. data/Gemfile +25 -0
  13. data/README.md +166 -0
  14. data/Rakefile +39 -0
  15. data/data/os/freebsd/amd64/syscalls.yml +415 -0
  16. data/data/os/freebsd/x86/syscalls.yml +415 -0
  17. data/data/os/linux/amd64/syscalls.yml +306 -0
  18. data/data/os/linux/x86/syscalls.yml +339 -0
  19. data/gemspec.yml +26 -0
  20. data/lib/ronin/code/asm/archs/amd64.rb +100 -0
  21. data/lib/ronin/code/asm/archs/x86.rb +170 -0
  22. data/lib/ronin/code/asm/archs.rb +22 -0
  23. data/lib/ronin/code/asm/config.rb +33 -0
  24. data/lib/ronin/code/asm/immediate_operand.rb +84 -0
  25. data/lib/ronin/code/asm/instruction.rb +66 -0
  26. data/lib/ronin/code/asm/memory_operand.rb +119 -0
  27. data/lib/ronin/code/asm/os/freebsd.rb +35 -0
  28. data/lib/ronin/code/asm/os/linux.rb +35 -0
  29. data/lib/ronin/code/asm/os/os.rb +47 -0
  30. data/lib/ronin/code/asm/os.rb +57 -0
  31. data/lib/ronin/code/asm/program.rb +509 -0
  32. data/lib/ronin/code/asm/register.rb +111 -0
  33. data/lib/ronin/code/asm/shellcode.rb +75 -0
  34. data/lib/ronin/code/asm/syntax/att.rb +164 -0
  35. data/lib/ronin/code/asm/syntax/common.rb +241 -0
  36. data/lib/ronin/code/asm/syntax/intel.rb +150 -0
  37. data/lib/ronin/code/asm/syntax.rb +22 -0
  38. data/lib/ronin/code/asm/version.rb +28 -0
  39. data/lib/ronin/code/asm.rb +68 -0
  40. data/ronin-code-asm.gemspec +62 -0
  41. data/spec/asm_spec.rb +14 -0
  42. data/spec/config_spec.rb +10 -0
  43. data/spec/immediate_operand_spec.rb +79 -0
  44. data/spec/instruction_spec.rb +62 -0
  45. data/spec/memory_operand_spec.rb +80 -0
  46. data/spec/os_spec.rb +68 -0
  47. data/spec/program_spec.rb +439 -0
  48. data/spec/register_spec.rb +112 -0
  49. data/spec/shellcode_spec.rb +58 -0
  50. data/spec/spec_helper.rb +7 -0
  51. data/spec/syntax/att_spec.rb +181 -0
  52. data/spec/syntax/common_spec.rb +42 -0
  53. data/spec/syntax/intel_spec.rb +174 -0
  54. 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
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'ronin/code/asm/config'
3
+
4
+ describe Ronin::Code::ASM::Config do
5
+ describe "DATA_DIR" do
6
+ it "must be a directory" do
7
+ expect(File.directory?(subject::DATA_DIR)).to be(true)
8
+ end
9
+ end
10
+ end
@@ -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