fisk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ require "fisk"
2
+ require "fisk/helpers"
3
+
4
+ module Printer
5
+ fisk = Fisk.new
6
+
7
+ jitbuf = Fisk::Helpers.jitbuffer 4096
8
+
9
+ str = "fooooooooo\n"
10
+ wrapper = Fiddle::Pointer[str]
11
+
12
+ fisk.asm(jitbuf) do
13
+ push rbp
14
+ mov rdi, imm32(1) # File number for stdout
15
+ mov rsi, imm64(wrapper.to_i) # Address of the char * backing str
16
+ mov rdx, imm32(str.bytesize) # Number of bytes in the string
17
+ mov rax, imm32(0x02000004) # write syscall on macOS x86_64
18
+ syscall
19
+ pop rbp
20
+ ret
21
+ end
22
+
23
+ define_singleton_method :print!, &jitbuf.to_function([], Fiddle::TYPE_VOID)
24
+ end
25
+
26
+ Printer.print!
data/lib/fisk.rb ADDED
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fisk/machine/encoding"
4
+ require "fisk/machine"
5
+
6
+ class Fisk
7
+ module Registers
8
+ class Register < Struct.new(:name, :type, :value)
9
+ def works? type
10
+ type == self.name || type == self.type
11
+ end
12
+
13
+ def unknown_label?; false; end
14
+ end
15
+
16
+ EAX = Register.new "eax", "r32", 0
17
+ ECX = Register.new "ecx", "r32", 1
18
+ EDX = Register.new "edx", "r32", 2
19
+ EBX = Register.new "ebx", "r32", 3
20
+ ESP = Register.new "esp", "r32", 4
21
+ EBP = Register.new "ebp", "r32", 5
22
+ ESI = Register.new "esi", "r32", 6
23
+ EDI = Register.new "edi", "r32", 7
24
+
25
+ RAX = Register.new "rax", "r64", 0
26
+ RCX = Register.new "rcx", "r64", 1
27
+ RDX = Register.new "rdx", "r64", 2
28
+ RBX = Register.new "rbx", "r64", 3
29
+ RSP = Register.new "rsp", "r64", 4
30
+ RBP = Register.new "rbp", "r64", 5
31
+ RSI = Register.new "rsi", "r64", 6
32
+ RDI = Register.new "rdi", "r64", 7
33
+ 8.times do |i|
34
+ i += 8
35
+ const_set "R#{i}", Register.new("r#{i}", "r64", i)
36
+ end
37
+ end
38
+
39
+ class Operand < Struct.new(:value)
40
+ def type; value; end
41
+ def works? type; self.type == type; end
42
+ def unknown_label?; false; end
43
+ end
44
+
45
+ class M64 < Operand
46
+ def type
47
+ "m64"
48
+ end
49
+ end
50
+
51
+ def m64 x
52
+ M64.new x
53
+ end
54
+
55
+ class Imm8 < Operand
56
+ def type
57
+ "imm8"
58
+ end
59
+ end
60
+
61
+ class Imm32 < Operand
62
+ def type
63
+ "imm32"
64
+ end
65
+ end
66
+
67
+ class Imm64 < Operand
68
+ def type
69
+ "imm64"
70
+ end
71
+ end
72
+
73
+ class Rel8 < Operand
74
+ def type
75
+ "rel8"
76
+ end
77
+ end
78
+
79
+ class Rel32 < Operand
80
+ def type
81
+ "rel32"
82
+ end
83
+ end
84
+
85
+ class MOffs64 < Operand
86
+ def type
87
+ "moffs64"
88
+ end
89
+ end
90
+
91
+ class Lit < Operand
92
+ def type
93
+ value.to_s
94
+ end
95
+ end
96
+
97
+ attr_reader :position
98
+
99
+ def initialize
100
+ @instructions = []
101
+ @labels = {}
102
+ @position = 0
103
+ end
104
+
105
+ class UnknownLabel < Struct.new(:name, :assembler, :insn)
106
+ def works? type
107
+ type == "rel32"
108
+ end
109
+
110
+ def unknown_label?; true; end
111
+
112
+ def value
113
+ label = assembler.label_for(name)
114
+ label.position - (insn.position + insn.bytesize)
115
+ end
116
+ end
117
+
118
+ class Label < Struct.new(:name, :position)
119
+ end
120
+
121
+ def label_for name
122
+ @labels.fetch name
123
+ end
124
+
125
+ def label name
126
+ UnknownLabel.new(name, self)
127
+ end
128
+
129
+ def make_label name
130
+ @labels[name] = Label.new(name, position)
131
+ end
132
+
133
+ Registers.constants.grep(/^[A-Z0-9]*$/).each do |const|
134
+ val = Registers.const_get const
135
+ define_method(const.downcase) { val }
136
+ end
137
+
138
+ class Instruction
139
+ attr_reader :position
140
+
141
+ def initialize insn, operands, position
142
+ @insn = insn
143
+ @operands = operands
144
+ @position = position
145
+ end
146
+
147
+ def encodings
148
+ @insn.encodings
149
+ end
150
+
151
+ def encode buffer
152
+ encoding = @insn.encodings.first
153
+ encoding.encode buffer, @operands
154
+ end
155
+
156
+ def bytesize
157
+ @insn.encodings.first.bytesize
158
+ end
159
+ end
160
+
161
+ def gen name, params
162
+ insns = Machine.instruction_with_name(name)
163
+ forms = insns.forms.find_all do |insn|
164
+ if insn.operands.length == params.length
165
+ params.zip(insn.operands).all? { |want_op, have_op|
166
+ want_op.works?(have_op.type)
167
+ }
168
+ else
169
+ false
170
+ end
171
+ end
172
+
173
+ raise NotImplementedError, "couldn't find instruction #{name}" if forms.length == 0
174
+
175
+ insn = nil
176
+
177
+ insn = forms.first
178
+
179
+ insn = Instruction.new(insn, params, position)
180
+ @position += insn.bytesize
181
+ @instructions << insn
182
+ params.each { |param| param.insn = insn if param.unknown_label? }
183
+
184
+ self
185
+ end
186
+
187
+ Machine.instructions.keys.each do |insn|
188
+ define_method(insn.downcase) do |*params|
189
+ gen insn, params
190
+ end
191
+ end
192
+
193
+ def moffs64 val
194
+ MOffs64.new val
195
+ end
196
+
197
+ def imm8 val
198
+ Imm8.new val
199
+ end
200
+
201
+ def imm32 val
202
+ Imm32.new val
203
+ end
204
+
205
+ def imm64 val
206
+ Imm64.new val
207
+ end
208
+
209
+ def rel8 val
210
+ Rel8.new val
211
+ end
212
+
213
+ def rel32 val
214
+ Rel32.new val
215
+ end
216
+
217
+ def lit val
218
+ # to_s because we're getting the value from JSON as a string
219
+ Lit.new val
220
+ end
221
+
222
+ def asm buf = StringIO.new(''.b), &block
223
+ @position = 0
224
+ instance_eval(&block)
225
+ write_to_buffer buf
226
+ buf
227
+ end
228
+
229
+ def to_binary
230
+ io = StringIO.new ''.b
231
+ write_to_buffer io
232
+ io.string
233
+ end
234
+
235
+ private
236
+
237
+ def write_to_buffer buffer
238
+ @instructions.each { |insn| insn.encode buffer }
239
+ end
240
+ end
@@ -0,0 +1,73 @@
1
+ require "fiddle"
2
+
3
+ class Fisk
4
+ module Helpers
5
+ include Fiddle
6
+
7
+ class Fiddle::Function
8
+ def to_proc
9
+ this = self
10
+ lambda { |*args| this.call(*args) }
11
+ end
12
+ end unless Function.method_defined?(:to_proc)
13
+
14
+ # from sys/mman.h on macOS
15
+ PROT_READ = 0x01
16
+ PROT_WRITE = 0x02
17
+ PROT_EXEC = 0x04
18
+ MAP_PRIVATE = 0x0002
19
+
20
+ if RUBY_PLATFORM =~ /darwin/
21
+ MAP_ANON = 0x1000
22
+ else
23
+ MAP_ANON = 0x20
24
+ end
25
+
26
+ mmap_ptr = Handle::DEFAULT["mmap"]
27
+ mmap_func = Function.new mmap_ptr, [TYPE_VOIDP,
28
+ TYPE_SIZE_T,
29
+ TYPE_INT,
30
+ TYPE_INT,
31
+ TYPE_INT,
32
+ TYPE_INT], TYPE_VOIDP, name: "mmap"
33
+
34
+ memcpy_ptr = Handle::DEFAULT["memcpy"]
35
+ memcpy_func = Function.new memcpy_ptr, [TYPE_VOIDP,
36
+ TYPE_VOIDP,
37
+ TYPE_SIZE_T], TYPE_VOIDP, name: "memcpy"
38
+
39
+ # Expose the mmap system call
40
+ define_singleton_method :mmap, &mmap_func
41
+
42
+ # Expose the memcpy system call
43
+ define_singleton_method :memcpy, &memcpy_func
44
+
45
+ def self.mmap_jit size
46
+ ptr = mmap 0, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0
47
+ ptr.size = size
48
+ ptr
49
+ end
50
+
51
+ class JITBuffer
52
+ attr_reader :memory, :pos
53
+
54
+ def initialize memory
55
+ @memory = memory
56
+ @pos = 0
57
+ end
58
+
59
+ def putc byte
60
+ @memory[@pos] = byte
61
+ @pos += 1
62
+ end
63
+
64
+ def to_function params, ret
65
+ Fiddle::Function.new memory.to_i, params, ret
66
+ end
67
+ end
68
+
69
+ def self.jitbuffer size
70
+ JITBuffer.new mmap_jit size
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,35 @@
1
+ class Fisk
2
+ class Machine
3
+ Instructions = {}
4
+
5
+ Operand = Struct.new(:type, :input, :output)
6
+
7
+ def self.instructions
8
+ Instructions
9
+ end
10
+
11
+ def self.instruction_with_name name
12
+ Instructions[name]
13
+ end
14
+
15
+ class Form
16
+ attr_reader :operands, :encodings
17
+
18
+ def initialize operands, encodings
19
+ @operands = operands
20
+ @encodings = encodings
21
+ end
22
+ end
23
+
24
+ class Instruction
25
+ attr_reader :name, :forms
26
+
27
+ def initialize name, forms
28
+ @name = name
29
+ @forms = forms
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ require "fisk/machine/generated"
@@ -0,0 +1,78 @@
1
+ class Fisk
2
+ class Machine
3
+ class Encoding
4
+ private
5
+
6
+ def add_modrm buffer, operands, mode, reg, rm
7
+ reg = get_operand_value(reg, operands) & 0x7
8
+ rm = get_operand_value(rm, operands) & 0x7
9
+ buffer.putc ((mode << 6) | (reg << 3) | rm)
10
+ end
11
+
12
+ def add_immediate buffer, operands, value, size
13
+ value = get_operand_value(value, operands)
14
+ write_num buffer, value, size
15
+ end
16
+
17
+ def add_code_offset buffer, operands, value, size
18
+ value = get_operand_value(value, operands)
19
+ write_num buffer, value, size
20
+ end
21
+
22
+ def add_data_offset buffer, operands, value, size
23
+ value = get_operand_value(value, operands)
24
+ write_num buffer, value, size
25
+ end
26
+
27
+ def add_rex buffer, operands, mandatory, w, r, x, b
28
+ return if mandatory == false
29
+
30
+ rex = 0b0100
31
+ rex = (rex << 1) | check_rex(w, operands)
32
+ rex = (rex << 1) | check_rex(r, operands)
33
+ rex = (rex << 1) | check_rex(x, operands)
34
+ rex = (rex << 1) | check_rex(b, operands)
35
+ buffer.putc rex
36
+ end
37
+
38
+ def add_opcode buffer, operands, byte, addend
39
+ if addend
40
+ byte |= get_operand_value(addend, operands)
41
+ end
42
+
43
+ buffer.putc byte
44
+ end
45
+
46
+ def get_operand_value v, operands
47
+ case v
48
+ when /^#(\d+)$/
49
+ operands[$1.to_i].value
50
+ when /^(\d+)$/
51
+ $1.to_i
52
+ else
53
+ raise NotImplementedError
54
+ end
55
+ end
56
+
57
+ def check_rex v, operands
58
+ return 0 unless v
59
+
60
+ case v
61
+ when /^(\d+)$/
62
+ v.to_i
63
+ when /^#(\d+)$/
64
+ (operands[$1.to_i].value >> 3)
65
+ else
66
+ raise NotImplementedError, v
67
+ end
68
+ end
69
+
70
+ def write_num buffer, num, size
71
+ size.times {
72
+ buffer.putc(num & 0xFF)
73
+ num >>= 8
74
+ }
75
+ end
76
+ end
77
+ end
78
+ end