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.
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