asmrepl 1.0.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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