asmrepl 1.0.0

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.
@@ -0,0 +1,105 @@
1
+ require "fiddle"
2
+ require "fisk/helpers"
3
+ require "crabstone"
4
+ require "reline"
5
+
6
+ if RUBY_PLATFORM =~ /darwin/
7
+ require "asmrepl/macos"
8
+ else
9
+ require "asmrepl/linux"
10
+ end
11
+
12
+ class Crabstone::Binding::Instruction
13
+ class << self
14
+ alias :old_release :release
15
+ end
16
+
17
+ # Squelch error in crabstone
18
+ def self.release obj
19
+ nil
20
+ end
21
+ end
22
+
23
+ module ASMREPL
24
+ class REPL
25
+ include Fiddle
26
+
27
+ if RUBY_PLATFORM =~ /darwin/
28
+ CFuncs = MacOS
29
+ else
30
+ CFuncs = Linux
31
+ end
32
+
33
+ def initialize
34
+ size = 1024 * 16 # 16k is enough for anyone!
35
+ @buffer = CFuncs.jitbuffer(size)
36
+ CFuncs.memset(@buffer.memory, 0xCC, size)
37
+ @parser = ASMREPL::Parser.new
38
+ @assembler = ASMREPL::Assembler.new
39
+ end
40
+
41
+ def display_state state
42
+ puts " CPU STATE ".center(48, "=")
43
+ puts state
44
+ puts
45
+ puts "FLAGS: #{state.flags.inspect}"
46
+ puts
47
+ end
48
+
49
+ def start
50
+ pid = fork {
51
+ CFuncs.traceme
52
+ @buffer.to_function([], TYPE_INT).call
53
+ }
54
+
55
+ tracer = CFuncs::Tracer.new pid
56
+ should_cpu = true
57
+ while tracer.wait
58
+ state = tracer.state
59
+
60
+ # Show CPU state once on boot
61
+ if should_cpu
62
+ display_state state
63
+ should_cpu = false
64
+ end
65
+
66
+ # Move the JIT buffer to the current instruction pointer
67
+ pos = (state.rip - @buffer.memory.to_i)
68
+ @buffer.seek pos
69
+ use_history = true
70
+ loop do
71
+ cmd = nil
72
+ text = Reline.readmultiline(">> ", use_history) do |multiline_input|
73
+ if multiline_input =~ /\A\s*(\w+)\s*\Z/
74
+ register = $1
75
+ cmd = [:read, register]
76
+ else
77
+ cmd = :run
78
+ end
79
+ true
80
+ end
81
+
82
+ case cmd
83
+ in :run
84
+ break if text.chomp.empty?
85
+ binary = @assembler.assemble @parser.parse text.chomp
86
+ binary.bytes.each { |byte| @buffer.putc byte }
87
+ break
88
+ in [:read, "cpu"]
89
+ display_state state
90
+ in [:read, reg]
91
+ val = state[reg]
92
+ if val
93
+ puts sprintf("%#018x", state[reg])
94
+ else
95
+ puts "Unknown command: "
96
+ puts " " + text
97
+ end
98
+ else
99
+ end
100
+ end
101
+ tracer.continue
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,3 @@
1
+ module ASMREPL
2
+ VERSION = '1.0.0'
3
+ end
data/lib/asmrepl.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "asmrepl/parser"
2
+ require "asmrepl/assembler"
3
+ require "asmrepl/repl"
@@ -0,0 +1,116 @@
1
+ require "helper"
2
+
3
+ class ParserTest < ASMREPL::Test
4
+ def setup
5
+ super
6
+ @parser = ASMREPL::Parser.new
7
+ @assembler = ASMREPL::Assembler.new
8
+ end
9
+
10
+ def test_int3
11
+ assert_round_trip "int3 "
12
+ end
13
+
14
+ def test_and
15
+ assert_round_trip "and r9, 0xffff"
16
+ end
17
+
18
+ def test_negative
19
+ assert_round_trip "test qword ptr [r15], -9"
20
+ end
21
+
22
+ def test_mem_lhs
23
+ asm = disasm_for "mov [r15], r9"
24
+ assert_equal "mov qword ptr [r15], r9", asm
25
+ assert_round_trip "mov qword ptr [r15], r9"
26
+ end
27
+
28
+ def test_inc
29
+ assert_round_trip "inc r10"
30
+ assert_round_trip "inc qword ptr [r10]"
31
+ assert_round_trip "inc qword ptr [r10 + 8]"
32
+ end
33
+
34
+ def test_movabs
35
+ assert_round_trip "movabs r10, 0x6000014b1ae0"
36
+ end
37
+
38
+ def test_ret
39
+ assert_round_trip "ret "
40
+ end
41
+
42
+ def test_lea_rip
43
+ if RUBY_PLATFORM =~ /darwin/
44
+ assert_round_trip "lea rax, [rip]"
45
+ assert_round_trip "lea rax, [rip + 9]"
46
+ else
47
+ assert_round_trip "lea rax, qword ptr [rip]"
48
+ assert_round_trip "lea rax, qword ptr [rip + 9]"
49
+ end
50
+ end
51
+
52
+ def test_push_reg
53
+ assert_round_trip "push r13"
54
+ end
55
+
56
+ def test_int_4
57
+ assert_round_trip "int 4"
58
+ end
59
+
60
+ def test_shl_1
61
+ assert_round_trip "shl rax, 1"
62
+ end
63
+
64
+ def test_simple
65
+ assert_round_trip "mov rax, 0xff"
66
+ assert_round_trip "mov r8, 0xff"
67
+ end
68
+
69
+ def test_2_regs
70
+ assert_round_trip "mov r8, rax"
71
+ end
72
+
73
+ def test_memory_offset
74
+ assert_round_trip "mov r8, qword ptr [rax + 0x7b]"
75
+ assert_round_trip "mov r8, qword ptr [rax - 0x7b]"
76
+ end
77
+
78
+ def test_memory_defaults_64_offset
79
+ asm = disasm_for "mov rax, [rax + 0x7b]"
80
+ assert_equal "mov rax, qword ptr [rax + 0x7b]", asm
81
+
82
+ asm = disasm_for "mov rax, [rax - 0x7b]"
83
+ assert_equal "mov rax, qword ptr [rax - 0x7b]", asm
84
+ end
85
+
86
+ def test_memory_defaults_64
87
+ asm = disasm_for "mov rax, [rax]"
88
+ assert_equal "mov rax, qword ptr [rax]", asm
89
+ end
90
+
91
+ def test_memory_specify_width
92
+ assert_round_trip "mov r8, qword ptr [rax]"
93
+ assert_round_trip "mov r8d, dword ptr [r8]"
94
+ assert_round_trip "mov r8w, word ptr [r8]"
95
+ assert_round_trip "mov r8b, byte ptr [r8]"
96
+ end
97
+
98
+ def assert_round_trip asm
99
+ ast = @parser.parse asm
100
+ binary = @assembler.assemble ast
101
+ assert_equal asm, disasm(binary)
102
+ end
103
+
104
+ def disasm_for asm
105
+ ast = @parser.parse asm
106
+ binary = @assembler.assemble ast
107
+ disasm(binary)
108
+ end
109
+
110
+ def disasm binary
111
+ cs = Crabstone::Disassembler.new(Crabstone::ARCH_X86, Crabstone::MODE_64)
112
+ cs.disasm(binary, 0x0000).each {|i|
113
+ return "%s %s" % [i.mnemonic, i.op_str]
114
+ }
115
+ end
116
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,20 @@
1
+ require "asmrepl"
2
+ require "minitest"
3
+ require "minitest/autorun"
4
+ require "crabstone"
5
+
6
+ class Crabstone::Binding::Instruction
7
+ class << self
8
+ alias :old_release :release
9
+ end
10
+
11
+ # Squelch error in crabstone
12
+ def self.release obj
13
+ nil
14
+ end
15
+ end
16
+
17
+ module ASMREPL
18
+ class Test < Minitest::Test
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: asmrepl
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Patterson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-11-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: crabstone
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: fisk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2'
69
+ description: Tired of writing assembly and them assembling it? Now you can write assembly
70
+ and evaluate it!
71
+ email: tenderlove@ruby-lang.org
72
+ executables:
73
+ - asmrepl
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - CODE_OF_CONDUCT.md
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - asmrepl.gemspec
83
+ - bin/asmrepl
84
+ - lib/asmrepl.rb
85
+ - lib/asmrepl/assembler.rb
86
+ - lib/asmrepl/linux.rb
87
+ - lib/asmrepl/macos.rb
88
+ - lib/asmrepl/parser.rb
89
+ - lib/asmrepl/parser.tab.rb
90
+ - lib/asmrepl/parser.y
91
+ - lib/asmrepl/repl.rb
92
+ - lib/asmrepl/version.rb
93
+ - test/asmrepl_test.rb
94
+ - test/helper.rb
95
+ homepage: https://github.com/tenderlove/asmrepl
96
+ licenses:
97
+ - Apache-2.0
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.3.0.dev
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Write assembly in a REPL!
118
+ test_files:
119
+ - test/asmrepl_test.rb
120
+ - test/helper.rb