asmrepl 1.0.1 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66bd4fd2c44903afafbb5924f1910647aab05ff1d7f7f90cacf4d4d09964a8b8
4
- data.tar.gz: 0c3a99d4e03b71f43f1796144cf1af56c7ef17f43052227d510d5b4f5579db6f
3
+ metadata.gz: 4b7a42ce7b9651305349e0f3aa6eafe88755a7f5bce4224ea0fab31c7169a0cd
4
+ data.tar.gz: 95ef6c4ebe42d82341782e5014b21237f6ba436ce3b296041aec59c4fdc8ba38
5
5
  SHA512:
6
- metadata.gz: 41093a34308ffce3a1b2a5ee73ad599c85de6355ac843ba45ded9357228bdee7c0cd554c8ce478901209c62279a779da296e40062067b62e73cef37e3cd91b66
7
- data.tar.gz: 2214aa5766194481ceb6da8fd262e8eefb1cbeebb78a00ed3826805e1b35d07ac200626ab81cb39f2223d562019ed27f81a8d8ec7706ce6b8108bccf35301eaa
6
+ metadata.gz: affb18c181d063ddf30f48006d980753573ca797f51d49e51e231cb6fbc9a4ffc4ec0204e806b02c5df895426cdae04185cbe12f605ee755d4ec35529879954e
7
+ data.tar.gz: bdfa0987327ef236a1e4a9fd991e00d864bac78e7408185f1c94ec93cb04b20fe82bc7336a214422b1067bbc02a12c8ac69ac132cf4cf842653f9e7e1d45ed8c
data/README.md CHANGED
@@ -2,11 +2,6 @@
2
2
 
3
3
  This is a REPL for assembly language.
4
4
 
5
- ## Linux requirements
6
- ```
7
- $ sudo apt-get install libcapstone-dev
8
- ```
9
-
10
5
  ## Usage
11
6
 
12
7
  Install the gem:
@@ -27,16 +22,16 @@ When the REPL starts, it will display all register values and flags:
27
22
 
28
23
  ```
29
24
  ================== CPU STATE ===================
30
- rax 000000000000000000 r8 0x0000600001782be0
31
- rbx 000000000000000000 r9 0x00007fbf9b0068c0
32
- rcx 0x0000000109dae951 r10 000000000000000000
33
- rdx 0x000000000000000c r11 0x000000010999c000
34
- rdi 0x00007ff7b6b2bbf0 r12 000000000000000000
35
- rsi 0x00000001096315fd r13 0x00007ff7b6b2bdc0
36
- rbp 0x00007ff7b6b2bc40 r14 000000000000000000
37
- rsp 0x00007ff7b6b2bc38 r15 000000000000000000
38
-
39
- rip 0x000000010999c001
25
+ rax 000000000000000000 r8 0x00007f89d0f04640
26
+ rbx 000000000000000000 r9 0x0000000000000004
27
+ rcx 0x00007f89d0f04a50 r10 000000000000000000
28
+ rdx 0x..fc611d3f0aa2900d4 r11 0x00000001033a4000
29
+ rdi 0x00007ff7bd126148 r12 000000000000000000
30
+ rsi 000000000000000000 r13 0x00007ff7bd125dc0
31
+ rbp 0x00007ff7bd125c40 r14 000000000000000000
32
+ rsp 0x00007ff7bd125c38 r15 000000000000000000
33
+
34
+ rip 0x00000001033a4001
40
35
  rflags 0x0000000000000246
41
36
  cs 0x000000000000002b
42
37
  fs 000000000000000000
@@ -44,46 +39,58 @@ gs 000000000000000000
44
39
 
45
40
  FLAGS: ["PF", "ZF", "IF"]
46
41
 
47
- >>
42
+ (rip 0x00000001033a4001)>
48
43
  ```
49
44
 
50
45
  Then you can issue commands and inspect register values. Let's write to the
51
46
  `rax` register and inspect its value:
52
47
 
53
48
  ```
54
- >> mov rax, 5
55
- >> rax
49
+ (rip 0x00000001033a4001)> mov rax, 5
50
+ =============== REGISTER CHANGES ===============
51
+ rax 000000000000000000 => 0x0000000000000005
52
+
53
+ (rip 0x00000001033a4009)> rax
56
54
  0x0000000000000005
57
- >>
55
+ (rip 0x00000001033a4009)>
58
56
  ```
59
57
 
60
58
  Now let's write to the `rbx` register and add the two values:
61
59
 
62
60
  ```
63
- >> mov rbx, 3
64
- >> add rax, rbx
65
- >> rax
61
+ (rip 0x00000001033a4009)> mov rbx, 3
62
+ =============== REGISTER CHANGES ===============
63
+ rbx 000000000000000000 => 0x0000000000000003
64
+
65
+ (rip 0x00000001033a4011)> add rax, rbx
66
+ =============== REGISTER CHANGES ===============
67
+ rax 0x0000000000000005 => 0x0000000000000008
68
+ rflags 0x0000000000000246 => 0x0000000000000202
69
+
70
+ FLAGS: ["IF"]
71
+
72
+ (rip 0x00000001033a4015)> rax
66
73
  0x0000000000000008
67
- >> rbx
74
+ (rip 0x00000001033a4015)> rbx
68
75
  0x0000000000000003
69
- >>
76
+ (rip 0x00000001033a4015)>
70
77
  ```
71
78
 
72
79
  Finally, lets check all values in the CPU:
73
80
 
74
81
  ```
75
- >> cpu
82
+ (rip 0x00000001033a4015)> cpu
76
83
  ================== CPU STATE ===================
77
- rax 0x0000000000000008 r8 0x0000600001d848a0
78
- rbx 0x0000000000000003 r9 0x00007fced316f850
79
- rcx 0x00000001017da951 r10 000000000000000000
80
- rdx 0x000000000000000c r11 0x00000001013cc000
81
- rdi 0x00007ff7bf0fdbf0 r12 000000000000000000
82
- rsi 0x000000010105f5fd r13 0x00007ff7bf0fddc0
83
- rbp 0x00007ff7bf0fdc40 r14 000000000000000000
84
- rsp 0x00007ff7bf0fdc38 r15 000000000000000000
85
-
86
- rip 0x00000001013cc029
84
+ rax 0x0000000000000008 r8 0x00007f89d0f04640
85
+ rbx 0x0000000000000003 r9 0x0000000000000004
86
+ rcx 0x00007f89d0f04a50 r10 000000000000000000
87
+ rdx 0x..fc611d3f0aa2900d4 r11 0x00000001033a4000
88
+ rdi 0x00007ff7bd126148 r12 000000000000000000
89
+ rsi 000000000000000000 r13 0x00007ff7bd125dc0
90
+ rbp 0x00007ff7bd125c40 r14 000000000000000000
91
+ rsp 0x00007ff7bd125c38 r15 000000000000000000
92
+
93
+ rip 0x00000001033a4015
87
94
  rflags 0x0000000000000202
88
95
  cs 0x000000000000002b
89
96
  fs 000000000000000000
@@ -91,5 +98,5 @@ gs 000000000000000000
91
98
 
92
99
  FLAGS: ["IF"]
93
100
 
94
- >>
101
+ (rip 0x00000001033a4015)>
95
102
  ```
data/asmrepl.gemspec CHANGED
@@ -20,5 +20,5 @@ Gem::Specification.new do |s|
20
20
  s.add_development_dependency 'minitest', '~> 5.14'
21
21
  s.add_development_dependency 'crabstone', '~> 4.0'
22
22
  s.add_development_dependency 'rake', '~> 13.0'
23
- s.add_dependency 'fisk', '~> 2'
23
+ s.add_dependency 'fisk', '~> 2.3.1'
24
24
  end
@@ -11,7 +11,11 @@ module ASMREPL
11
11
  l = if possibles.any? { |form| form.operands[1].type == n.to_s }
12
12
  fisk.lit(n)
13
13
  else
14
- fisk.imm(n)
14
+ if r.size == 64
15
+ fisk.imm32(n)
16
+ else
17
+ fisk.imm(n)
18
+ end
15
19
  end
16
20
  fisk.gen_with_insn insn, [r, l]
17
21
  in [:command, [:instruction, insn], [:register, r], [:register, r2]]
@@ -0,0 +1,24 @@
1
+ require "crabstone"
2
+
3
+ class Crabstone::Binding::Instruction
4
+ class << self
5
+ alias :old_release :release
6
+ end
7
+
8
+ # Squelch error in crabstone
9
+ def self.release obj
10
+ nil
11
+ end
12
+ end
13
+
14
+ module ASMREPL
15
+ module Disasm
16
+ def self.disasm buffer
17
+ binary = buffer.memory[0, buffer.pos]
18
+ cs = Crabstone::Disassembler.new(Crabstone::ARCH_X86, Crabstone::MODE_64)
19
+ cs.disasm(binary, buffer.memory.to_i).each {|i|
20
+ puts "%s %s" % [i.mnemonic, i.op_str]
21
+ }
22
+ end
23
+ end
24
+ end
data/lib/asmrepl/linux.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "fisk/helpers"
2
+ require "asmrepl/thread_state"
2
3
 
3
4
  module ASMREPL
4
5
  module Linux
@@ -44,13 +45,13 @@ module ASMREPL
44
45
 
45
46
  # x86_64-linux-gnu/sys/ptrace.h
46
47
  PTRACE_GETREGS = 12
48
+ PTRACE_SETREGS = 13
47
49
 
48
50
  def self.traceme
49
51
  raise unless ptrace(PTRACE_TRACEME, 0, 0, 0).zero?
50
52
  end
51
53
 
52
- class ThreadState
53
- fields = (<<-eostruct).scan(/int ([^;]*);/).flatten
54
+ fields = (<<-eostruct).scan(/int ([^;]*);/).flatten
54
55
  struct user_regs_struct
55
56
  {
56
57
  __extension__ unsigned long long int r15;
@@ -81,82 +82,15 @@ struct user_regs_struct
81
82
  __extension__ unsigned long long int fs;
82
83
  __extension__ unsigned long long int gs;
83
84
  };
84
- eostruct
85
- fields.each_with_index do |field, i|
86
- define_method(field) do
87
- to_ptr[Fiddle::SIZEOF_INT64_T * i, Fiddle::SIZEOF_INT64_T].unpack1("l!")
88
- end
89
- end
90
-
91
- define_singleton_method(:sizeof) do
92
- fields.length * Fiddle::SIZEOF_INT64_T
93
- end
94
-
95
- def [] name
96
- idx = fields.index(name)
97
- return unless idx
98
- to_ptr[Fiddle::SIZEOF_INT64_T * idx, Fiddle::SIZEOF_INT64_T].unpack1("l!")
99
- end
100
-
101
- def self.malloc
102
- new Fiddle::Pointer.malloc sizeof
103
- end
104
-
105
- attr_reader :to_ptr
106
-
107
- def initialize buffer
108
- @to_ptr = buffer
109
- end
85
+ eostruct
110
86
 
111
- define_method(:fields) do
112
- fields
113
- end
114
-
115
- def to_s
116
- buf = ""
117
- fields.first(8).zip(fields.drop(8).first(8)).each do |l, r|
118
- buf << "#{l.ljust(3)} #{sprintf("%#018x", send(l))}"
119
- buf << " "
120
- buf << "#{r.ljust(3)} #{sprintf("%#018x", send(r))}\n"
121
- end
87
+ class ThreadState < ASMREPL::ThreadState.build(fields)
88
+ private
122
89
 
123
- buf << "\n"
90
+ def read_flags; eflags; end
124
91
 
125
- fields.drop(16).each do |reg|
126
- buf << "#{reg.ljust(8)} #{sprintf("%#018x", send(reg))}\n"
127
- end
128
- buf
129
- end
130
-
131
- FLAGS = [
132
- ['CF', 'Carry Flag'],
133
- [nil, 'Reserved'],
134
- ['PF', 'Parity Flag'],
135
- [nil, 'Reserved'],
136
- ['AF', 'Adjust Flag'],
137
- [nil, 'Reserved'],
138
- ['ZF', 'Zero Flag'],
139
- ['SF', 'Sign Flag'],
140
- ['TF', 'Trap Flag'],
141
- ['IF', 'Interrupt Enable Flag'],
142
- ['DF', 'Direction Flag'],
143
- ['OF', 'Overflow Flag'],
144
- ['IOPL_H', 'I/O privilege level High bit'],
145
- ['IOPL_L', 'I/O privilege level Low bit'],
146
- ['NT', 'Nested Task Flag'],
147
- [nil, 'Reserved'],
148
- ]
149
-
150
- def flags
151
- flags = eflags
152
- f = []
153
- FLAGS.each do |abbrv, _|
154
- if abbrv && flags & 1 == 1
155
- f << abbrv
156
- end
157
- flags >>= 1
158
- end
159
- f
92
+ def other_registers
93
+ super - ["orig_rax"]
160
94
  end
161
95
  end
162
96
 
@@ -176,6 +110,12 @@ struct user_regs_struct
176
110
  state
177
111
  end
178
112
 
113
+ def state= state
114
+ raise unless Linux.ptrace(PTRACE_SETREGS, @pid, 0, state).zero?
115
+
116
+ state
117
+ end
118
+
179
119
  def continue
180
120
  unless Linux.ptrace(Linux::PTRACE_CONT, @pid, 1, 0).zero?
181
121
  raise
data/lib/asmrepl/macos.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "fisk/helpers"
2
+ require "asmrepl/thread_state"
2
3
 
3
4
  module ASMREPL
4
5
  module MacOS
@@ -26,6 +27,7 @@ module ASMREPL
26
27
  make_function "task_for_pid", [TYPE_VOIDP, TYPE_INT, TYPE_VOIDP], TYPE_INT
27
28
  make_function "task_threads", [TYPE_VOIDP, TYPE_VOIDP, TYPE_VOIDP], TYPE_INT
28
29
  make_function "thread_get_state", [TYPE_VOIDP, TYPE_INT, TYPE_VOIDP, TYPE_VOIDP], TYPE_INT
30
+ make_function "thread_set_state", [TYPE_VOIDP, TYPE_INT, TYPE_VOIDP, TYPE_INT], TYPE_INT
29
31
  make_function "mmap", [TYPE_VOIDP,
30
32
  TYPE_SIZE_T,
31
33
  TYPE_INT,
@@ -47,8 +49,7 @@ module ASMREPL
47
49
  raise unless ptrace(PT_TRACE_ME, 0, 0, 0).zero?
48
50
  end
49
51
 
50
- class ThreadState
51
- fields = (<<-eostruct).scan(/uint64_t ([^;]*);/).flatten
52
+ fields = (<<-eostruct).scan(/uint64_t ([^;]*);/).flatten
52
53
  struct x86_thread_state64_t {
53
54
  uint64_t rax;
54
55
  uint64_t rbx;
@@ -72,83 +73,12 @@ struct x86_thread_state64_t {
72
73
  uint64_t fs;
73
74
  uint64_t gs;
74
75
  }
75
- eostruct
76
- fields.each_with_index do |field, i|
77
- define_method(field) do
78
- to_ptr[Fiddle::SIZEOF_INT64_T * i, Fiddle::SIZEOF_INT64_T].unpack1("l!")
79
- end
80
- end
81
-
82
- define_singleton_method(:sizeof) do
83
- fields.length * Fiddle::SIZEOF_INT64_T
84
- end
85
-
86
- def [] name
87
- idx = fields.index(name)
88
- return unless idx
89
- to_ptr[Fiddle::SIZEOF_INT64_T * idx, Fiddle::SIZEOF_INT64_T].unpack1("l!")
90
- end
91
-
92
- def self.malloc
93
- new Fiddle::Pointer.malloc sizeof
94
- end
76
+ eostruct
95
77
 
96
- attr_reader :to_ptr
78
+ class ThreadState < ASMREPL::ThreadState.build(fields)
79
+ private
97
80
 
98
- def initialize buffer
99
- @to_ptr = buffer
100
- end
101
-
102
- define_method(:fields) do
103
- fields
104
- end
105
-
106
- def to_s
107
- buf = ""
108
- fields.first(8).zip(fields.drop(8).first(8)).each do |l, r|
109
- buf << "#{l.ljust(3)} #{sprintf("%#018x", send(l))}"
110
- buf << " "
111
- buf << "#{r.ljust(3)} #{sprintf("%#018x", send(r))}\n"
112
- end
113
-
114
- buf << "\n"
115
-
116
- fields.drop(16).each do |reg|
117
- buf << "#{reg.ljust(6)} #{sprintf("%#018x", send(reg))}\n"
118
- end
119
- buf
120
- end
121
-
122
- FLAGS = [
123
- ['CF', 'Carry Flag'],
124
- [nil, 'Reserved'],
125
- ['PF', 'Parity Flag'],
126
- [nil, 'Reserved'],
127
- ['AF', 'Adjust Flag'],
128
- [nil, 'Reserved'],
129
- ['ZF', 'Zero Flag'],
130
- ['SF', 'Sign Flag'],
131
- ['TF', 'Trap Flag'],
132
- ['IF', 'Interrupt Enable Flag'],
133
- ['DF', 'Direction Flag'],
134
- ['OF', 'Overflow Flag'],
135
- ['IOPL_H', 'I/O privilege level High bit'],
136
- ['IOPL_L', 'I/O privilege level Low bit'],
137
- ['NT', 'Nested Task Flag'],
138
- [nil, 'Reserved'],
139
- ]
140
-
141
- def flags
142
- flags = rflags
143
- f = []
144
- FLAGS.each do |abbrv, _|
145
- if abbrv && flags & 1 == 1
146
- f << abbrv
147
- end
148
- flags >>= 1
149
- end
150
- f
151
- end
81
+ def read_flags; rflags; end
152
82
  end
153
83
 
154
84
  PT_TRACE_ME = 0
@@ -176,9 +106,30 @@ struct x86_thread_state64_t {
176
106
  end
177
107
 
178
108
  def state
179
- # Probably should use this for something
180
- # count = thread_count[0]
109
+ 3.times do
110
+ # Probably should use this for something
111
+ # count = thread_count[0]
112
+
113
+ # I can't remember what header I found this in, but it's from a macOS header
114
+ # :sweat-smile:
115
+ x86_THREAD_STATE64_COUNT = ThreadState.sizeof / Fiddle::SIZEOF_INT
116
+
117
+ # Same here
118
+ x86_THREAD_STATE64 = 4
119
+
120
+ state_count = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT64_T)
121
+ state_count[0, Fiddle::SIZEOF_INT64_T] = [x86_THREAD_STATE64_COUNT].pack("l!")
181
122
 
123
+ state = ThreadState.malloc
124
+ if MacOS.thread_get_state(@thread, x86_THREAD_STATE64, state, state_count).zero?
125
+ return state
126
+ end
127
+ end
128
+
129
+ raise "Couldn't get CPU state"
130
+ end
131
+
132
+ def state= new_state
182
133
  # I can't remember what header I found this in, but it's from a macOS header
183
134
  # :sweat-smile:
184
135
  x86_THREAD_STATE64_COUNT = ThreadState.sizeof / Fiddle::SIZEOF_INT
@@ -186,13 +137,7 @@ struct x86_thread_state64_t {
186
137
  # Same here
187
138
  x86_THREAD_STATE64 = 4
188
139
 
189
- state_count = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT64_T)
190
- state_count[0, Fiddle::SIZEOF_INT64_T] = [x86_THREAD_STATE64_COUNT].pack("l!")
191
-
192
- state = ThreadState.malloc
193
- raise unless MacOS.thread_get_state(@thread, x86_THREAD_STATE64, state, state_count).zero?
194
-
195
- state
140
+ raise unless MacOS.thread_set_state(@thread, x86_THREAD_STATE64, new_state, x86_THREAD_STATE64_COUNT).zero?
196
141
  end
197
142
 
198
143
  def continue
data/lib/asmrepl/repl.rb CHANGED
@@ -9,6 +9,8 @@ else
9
9
  end
10
10
 
11
11
  module ASMREPL
12
+ MAXINT = 0xFFFFFFFFFFFFFFFF
13
+
12
14
  class REPL
13
15
  include Fiddle
14
16
 
@@ -27,13 +29,40 @@ module ASMREPL
27
29
  end
28
30
 
29
31
  def display_state state
30
- puts " CPU STATE ".center(48, "=")
32
+ puts bold(" CPU STATE ".center(48, "="))
31
33
  puts state
32
34
  puts
33
35
  puts "FLAGS: #{state.flags.inspect}"
34
36
  puts
35
37
  end
36
38
 
39
+ def display_state_change last_state, state
40
+ puts bold(" REGISTER CHANGES ".center(48, "="))
41
+ show_flags = false
42
+
43
+ state.fields.each do |field|
44
+ next if field == "rip"
45
+
46
+ if last_state[field] != state[field]
47
+ print "#{field.ljust(6)} "
48
+ print sprintf("%#018x", last_state[field] & MAXINT)
49
+ print " => "
50
+ puts bold(sprintf("%#018x", state[field] & MAXINT))
51
+ end
52
+ end
53
+
54
+ if last_state.flags != state.flags
55
+ puts
56
+ puts "FLAGS: #{state.flags.inspect}"
57
+ end
58
+
59
+ puts
60
+ end
61
+
62
+ def bold string
63
+ "\e[1m#{string}\e[0m"
64
+ end
65
+
37
66
  def start
38
67
  pid = fork {
39
68
  CFuncs.traceme
@@ -42,49 +71,95 @@ module ASMREPL
42
71
 
43
72
  tracer = CFuncs::Tracer.new pid
44
73
  should_cpu = true
74
+ last_state = nil
75
+
45
76
  while tracer.wait
46
77
  state = tracer.state
47
78
 
48
79
  # Show CPU state once on boot
49
- if should_cpu
80
+ if last_state.nil?
50
81
  display_state state
51
- should_cpu = false
82
+ last_state = state
83
+ else
84
+ display_state_change last_state, state
85
+ last_state = state
52
86
  end
53
87
 
54
- # Move the JIT buffer to the current instruction pointer
55
- pos = (state.rip - @buffer.memory.to_i)
56
- @buffer.seek pos
57
88
  use_history = true
58
- loop do
59
- cmd = nil
60
- text = Reline.readmultiline(">> ", use_history) do |multiline_input|
61
- if multiline_input =~ /\A\s*(\w+)\s*\Z/
62
- register = $1
63
- cmd = [:read, register]
64
- else
65
- cmd = :run
89
+ begin
90
+ loop do
91
+ cmd = nil
92
+ prompt = sprintf("(rip %#018x)> ", state.rip)
93
+ text = Reline.readmultiline(prompt, use_history) do |multiline_input|
94
+ case multiline_input
95
+ when /\Adisasm\Z/
96
+ cmd = :disasm
97
+ when /\A\s*(\w+)\s*\Z/
98
+ register = $1
99
+ cmd = [:read, register]
100
+ when /\A\s*(\w+)\s*=\s*(\d+)\Z/
101
+ register = $1
102
+ cmd = [:write, register, $2.to_i]
103
+ else
104
+ cmd = :run
105
+ end
106
+ true
66
107
  end
67
- true
68
- end
69
108
 
70
- case cmd
71
- in :run
72
- break if text.chomp.empty?
73
- binary = @assembler.assemble @parser.parse text.chomp
74
- binary.bytes.each { |byte| @buffer.putc byte }
75
- break
76
- in [:read, "cpu"]
77
- display_state state
78
- in [:read, reg]
79
- val = state[reg]
80
- if val
81
- puts sprintf("%#018x", state[reg])
109
+ case cmd
110
+ in :disasm
111
+ # disassembles the JIT buffer. This is just for development,
112
+ # I don't want to make a hard dependency on crabstone right now.
113
+ # If you want to use this, install crabstone
114
+ begin
115
+ require "asmrepl/disasm"
116
+ ASMREPL::Disasm.disasm @buffer
117
+ rescue
118
+ end
119
+ in :run
120
+ break if text.chomp.empty?
121
+ begin
122
+ parser_result = @parser.parse text.chomp
123
+ rescue
124
+ puts "Invalid intruction"
125
+ next
126
+ end
127
+
128
+ begin
129
+ binary = @assembler.assemble parser_result
130
+
131
+ # Move the JIT buffer to the current instruction pointer, but
132
+ # rewind RIP so that we write over the int3
133
+ pos = (state.rip - @buffer.memory.to_i - 1)
134
+ @buffer.seek pos
135
+ binary.bytes.each { |byte| @buffer.putc byte }
136
+ state.rip -= 1
137
+ tracer.state = state
138
+ rescue Fisk::Errors::InvalidInstructionError => e
139
+ # Print an error message when the instruction is invalid
140
+ puts e.message
141
+ next
142
+ end
143
+ break
144
+ in [:write, reg, val]
145
+ state[reg] = val
146
+ tracer.state = state
147
+ in [:read, "cpu"]
148
+ display_state state
149
+ in [:read, reg]
150
+ val = state[reg]
151
+ if val
152
+ puts sprintf("%#018x", state[reg])
153
+ else
154
+ puts "Unknown command: "
155
+ puts " " + text
156
+ end
82
157
  else
83
- puts "Unknown command: "
84
- puts " " + text
85
158
  end
86
- else
87
159
  end
160
+ rescue Interrupt
161
+ puts ""
162
+ exit 0
88
163
  end
89
164
  tracer.continue
90
165
  end
@@ -0,0 +1,108 @@
1
+ require "fiddle"
2
+
3
+ module ASMREPL
4
+ class ThreadState
5
+ def self.sizeof
6
+ fields.length * Fiddle::SIZEOF_INT64_T
7
+ end
8
+
9
+ def self.malloc
10
+ new Fiddle::Pointer.malloc sizeof
11
+ end
12
+
13
+ FLAGS = [
14
+ ['CF', 'Carry Flag'],
15
+ [nil, 'Reserved'],
16
+ ['PF', 'Parity Flag'],
17
+ [nil, 'Reserved'],
18
+ ['AF', 'Adjust Flag'],
19
+ [nil, 'Reserved'],
20
+ ['ZF', 'Zero Flag'],
21
+ ['SF', 'Sign Flag'],
22
+ ['TF', 'Trap Flag'],
23
+ ['IF', 'Interrupt Enable Flag'],
24
+ ['DF', 'Direction Flag'],
25
+ ['OF', 'Overflow Flag'],
26
+ ['IOPL_H', 'I/O privilege level High bit'],
27
+ ['IOPL_L', 'I/O privilege level Low bit'],
28
+ ['NT', 'Nested Task Flag'],
29
+ [nil, 'Reserved'],
30
+ ]
31
+
32
+ attr_reader :to_ptr
33
+
34
+ def initialize buffer
35
+ @to_ptr = buffer
36
+ end
37
+
38
+ def [] name
39
+ idx = fields.index(name)
40
+ return unless idx
41
+ to_ptr[Fiddle::SIZEOF_INT64_T * idx, Fiddle::SIZEOF_INT64_T].unpack1("l!")
42
+ end
43
+
44
+ def []= name, val
45
+ idx = fields.index(name)
46
+ return unless idx
47
+ to_ptr[Fiddle::SIZEOF_INT64_T * idx, Fiddle::SIZEOF_INT64_T] = [val].pack("l!")
48
+ end
49
+
50
+ def flags
51
+ flags = read_flags
52
+ f = []
53
+ FLAGS.each do |abbrv, _|
54
+ if abbrv && flags & 1 == 1
55
+ f << abbrv
56
+ end
57
+ flags >>= 1
58
+ end
59
+ f
60
+ end
61
+
62
+ def to_s
63
+ buf = ""
64
+ display_registers.first(8).zip(display_registers.drop(8)).each do |l, r|
65
+ buf << "#{l.ljust(3)} #{sprintf("%#018x", self[l] & MAXINT)}"
66
+ buf << " "
67
+ buf << "#{r.ljust(3)} #{sprintf("%#018x", self[r] & MAXINT)}\n"
68
+ end
69
+
70
+ buf << "\n"
71
+
72
+ other_registers.each do |reg|
73
+ buf << "#{reg.ljust(7)} #{sprintf("%#018x", self[reg] & MAXINT)}\n"
74
+ end
75
+ buf
76
+ end
77
+
78
+ def display_registers
79
+ %w{ rax rbx rcx rdx rdi rsi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 }
80
+ end
81
+
82
+ def other_registers
83
+ fields - display_registers
84
+ end
85
+
86
+ def self.build fields
87
+ Class.new(ThreadState) do
88
+ define_method(:fields) do
89
+ fields
90
+ end
91
+
92
+ define_singleton_method(:fields) do
93
+ fields
94
+ end
95
+
96
+ fields.each_with_index do |field, i|
97
+ define_method(field) do
98
+ to_ptr[Fiddle::SIZEOF_INT64_T * i, Fiddle::SIZEOF_INT64_T].unpack1("l!")
99
+ end
100
+
101
+ define_method("#{field}=") do |v|
102
+ to_ptr[Fiddle::SIZEOF_INT64_T * i, Fiddle::SIZEOF_INT64_T] = [v].pack("l!")
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -1,3 +1,3 @@
1
1
  module ASMREPL
2
- VERSION = '1.0.1'
2
+ VERSION = '1.2.0'
3
3
  end
data/lib/asmrepl.rb CHANGED
@@ -1,3 +1,11 @@
1
+ require "fiddle"
2
+
3
+ module Fiddle
4
+ unless Fiddle.const_defined?(:SIZEOF_INT64_T)
5
+ SIZEOF_INT64_T = Fiddle::SIZEOF_UINTPTR_T
6
+ end
7
+ end
8
+
1
9
  require "asmrepl/parser"
2
10
  require "asmrepl/assembler"
3
11
  require "asmrepl/repl"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asmrepl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Patterson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-29 00:00:00.000000000 Z
11
+ date: 2021-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '2'
61
+ version: 2.3.1
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '2'
68
+ version: 2.3.1
69
69
  description: Tired of writing assembly and them assembling it? Now you can write assembly
70
70
  and evaluate it!
71
71
  email: tenderlove@ruby-lang.org
@@ -83,12 +83,14 @@ files:
83
83
  - bin/asmrepl
84
84
  - lib/asmrepl.rb
85
85
  - lib/asmrepl/assembler.rb
86
+ - lib/asmrepl/disasm.rb
86
87
  - lib/asmrepl/linux.rb
87
88
  - lib/asmrepl/macos.rb
88
89
  - lib/asmrepl/parser.rb
89
90
  - lib/asmrepl/parser.tab.rb
90
91
  - lib/asmrepl/parser.y
91
92
  - lib/asmrepl/repl.rb
93
+ - lib/asmrepl/thread_state.rb
92
94
  - lib/asmrepl/version.rb
93
95
  - test/asmrepl_test.rb
94
96
  - test/helper.rb