ffi-udis86 0.1.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,563 @@
1
+ require 'udis86/types'
2
+ require 'udis86/operand'
3
+ require 'udis86/ffi'
4
+
5
+ require 'ffi'
6
+
7
+ module FFI
8
+ module UDis86
9
+ class UD < FFI::Struct
10
+
11
+ include Enumerable
12
+
13
+ layout :inp_hook, :ud_input_callback,
14
+ :inp_curr, :uint8,
15
+ :inp_fill, :uint8,
16
+ :inp_file, :pointer,
17
+ :inp_ctr, :uint8,
18
+ :inp_buff, :pointer,
19
+ :inp_buff_end, :pointer,
20
+ :inp_end, :uint8,
21
+ :translator, :ud_translator_callback,
22
+ :insn_offset, :uint64,
23
+ :insn_hexcode, [:char, 32],
24
+ :insn_buffer, [:char, 64],
25
+ :insn_fill, :uint,
26
+ :dis_mode, :uint8,
27
+ :pc, :uint64,
28
+ :vendor, :uint8,
29
+ :mapen, :pointer,
30
+ :mnemonic, :ud_mnemonic_code,
31
+ :operand, [Operand, 3],
32
+ :error, :uint8,
33
+ :pfx_rex, :uint8,
34
+ :pfx_seg, :uint8,
35
+ :pfx_opr, :uint8,
36
+ :pfx_adr, :uint8,
37
+ :pfx_lock, :uint8,
38
+ :pfx_rep, :uint8,
39
+ :pfx_repe, :uint8,
40
+ :pfx_repne, :uint8,
41
+ :pfx_insn, :uint8,
42
+ :default64, :uint8,
43
+ :opr_mode, :uint8,
44
+ :adr_mode, :uint8,
45
+ :br_far, :uint8,
46
+ :br_near, :uint8,
47
+ :implicit_addr, :uint8,
48
+ :c1, :uint8,
49
+ :c2, :uint8,
50
+ :c3, :uint8,
51
+ :inp_cache, [NativeType::UINT8, 256],
52
+ :inp_sess, [NativeType::UINT8, 64],
53
+ :itab_entry, :pointer
54
+
55
+ #
56
+ # Creates a new disassembler object.
57
+ #
58
+ # @param [Hash] options
59
+ # Additional options.
60
+ #
61
+ # @option options [Integer] :mode (32)
62
+ # The mode of disassembly, can either 16, 32 or 64.
63
+ #
64
+ # @option options [Integer] :syntax (:intel)
65
+ # The assembly syntax the disassembler will emit, can be either
66
+ # `:att` or `:intel`.
67
+ #
68
+ # @option options [String] :buffer
69
+ # A buffer to disassemble.
70
+ #
71
+ # @option options [Symbol] :vendor
72
+ # Sets the vendor of whose instructions to choose from. Can be
73
+ # either `:amd` or `:intel`.
74
+ #
75
+ # @option options [Integer] :pc
76
+ # Initial value of the Program Counter (PC).
77
+ #
78
+ # @yield [ud]
79
+ # If a block is given, it will be used as an input callback to
80
+ # return the next byte to disassemble. When the block returns
81
+ # -1, the disassembler will stop processing input.
82
+ #
83
+ # @yieldparam [UD] ud
84
+ # The disassembler.
85
+ #
86
+ # @return [UD]
87
+ # The newly created disassembler.
88
+ #
89
+ def self.create(options={},&block)
90
+ ud = self.new
91
+ ud.init
92
+
93
+ ud.mode = (options[:mode] || 32)
94
+
95
+ if options[:buffer]
96
+ ud.input_buffer = options[:buffer]
97
+ end
98
+
99
+ ud.syntax = (options[:syntax] || :intel)
100
+
101
+ if options[:vendor]
102
+ ud.vendor = options[:vendor]
103
+ end
104
+
105
+ if options[:pc]
106
+ ud.pc = options[:pc]
107
+ end
108
+
109
+ ud.input_callback(&block) if block
110
+
111
+ return ud
112
+ end
113
+
114
+ #
115
+ # Opens a file and disassembles it.
116
+ #
117
+ # @param [String] path
118
+ # The path to the file.
119
+ #
120
+ # @param [Hash] options
121
+ # Additional dissaembly options.
122
+ #
123
+ # @option options [Integer] :mode (32)
124
+ # The mode of disassembly, can either 16, 32 or 64.
125
+ #
126
+ # @option options [Integer] :syntax (:intel)
127
+ # The assembly syntax the disassembler will emit, can be either
128
+ # `:att` or `:intel`.
129
+ #
130
+ # @option options [String] :buffer
131
+ # A buffer to disassemble.
132
+ #
133
+ # @option options [Symbol] :vendor
134
+ # Sets the vendor of whose instructions to choose from. Can be
135
+ # either `:amd` or `:intel`.
136
+ #
137
+ # @option options [Integer] :pc
138
+ # Initial value of the Program Counter (PC).
139
+ #
140
+ # @yield [ud]
141
+ # If a block is given, it will be passed the newly created
142
+ # UD object, configured to disassembler the file.
143
+ #
144
+ # @yieldparam [UD] ud
145
+ # The newly created disassembler.
146
+ #
147
+ def self.open(path,options={},&block)
148
+ File.open(path,'rb') do |file|
149
+ ud = self.create(options) do |ud|
150
+ if (b = file.getc)
151
+ b.ord
152
+ else
153
+ -1
154
+ end
155
+ end
156
+
157
+ block.call(ud) if block
158
+ end
159
+
160
+ return nil
161
+ end
162
+
163
+ #
164
+ # Initializes the disassembler.
165
+ #
166
+ # @return [UD]
167
+ # The initialized disassembler.
168
+ #
169
+ def init
170
+ UDis86.ud_init(self)
171
+ return self
172
+ end
173
+
174
+ #
175
+ # Returns the input buffer used by the disassembler.
176
+ #
177
+ # @return [String]
178
+ # The current contents of the input buffer.
179
+ #
180
+ def input_buffer
181
+ if @input_buffer
182
+ @input_buffer.get_bytes(0,@input_buffer.total)
183
+ else
184
+ ''
185
+ end
186
+ end
187
+
188
+ #
189
+ # Sets the contents of the input buffer for the disassembler.
190
+ #
191
+ # @param [Array<Integer>, String] data
192
+ # The new contents to use for the input buffer.
193
+ #
194
+ # @return [String]
195
+ # The new contents of the input buffer.
196
+ #
197
+ # @raise [RuntimeError]
198
+ # The given input buffer was neigther a String or an Array of bytes.
199
+ #
200
+ def input_buffer=(data)
201
+ data = data.to_s
202
+
203
+ @input_buffer = FFI::MemoryPointer.new(data.length)
204
+
205
+ if data.kind_of?(Array)
206
+ @input_buffer.put_array_of_uint8(0,data)
207
+ elsif data.kind_of?(String)
208
+ @input_buffer.put_bytes(0,data)
209
+ else
210
+ raise(RuntimeError,"input buffer must be either a String or an Array of bytes",caller)
211
+ end
212
+
213
+ UDis86.ud_set_input_buffer(self,@input_buffer,@input_buffer.total)
214
+ return data
215
+ end
216
+
217
+ #
218
+ # Sets the input callback for the disassembler.
219
+ #
220
+ # @yield [ud]
221
+ # If a block is given, it will be used to get the next byte of
222
+ # input to disassemble. When the block returns -1, the disassembler
223
+ # will stop processing input.
224
+ #
225
+ # @yieldparam [UD]
226
+ # The disassembler.
227
+ #
228
+ def input_callback(&block)
229
+ if block
230
+ @input_callback = Proc.new { |ptr| block.call(self) }
231
+
232
+ UDis86.ud_set_input_hook(self,@input_callback)
233
+ end
234
+
235
+ return @input_callback
236
+ end
237
+
238
+ #
239
+ # Returns the mode the disassembler is running in.
240
+ #
241
+ # @return [Integer]
242
+ # Returns either 16, 32 or 64.
243
+ #
244
+ def mode
245
+ self[:dis_mode]
246
+ end
247
+
248
+ #
249
+ # Sets the mode the disassembler will run in.
250
+ #
251
+ # @param [Integer] new_mode
252
+ # The mode the disassembler will run in. Can be either 16, 32 or 64.
253
+ #
254
+ # @return [Integer]
255
+ # The new mode of the disassembler.
256
+ #
257
+ def mode=(new_mode)
258
+ unless MODES.include?(new_mode)
259
+ raise(RuntimeError,"invalid disassembly mode #{new_mode}",caller)
260
+ end
261
+
262
+ UDis86.ud_set_mode(self,new_mode)
263
+ return new_mode
264
+ end
265
+
266
+ #
267
+ # Sets the assembly syntax that the disassembler will emit.
268
+ #
269
+ # @param [Symbol, String] new_syntax
270
+ # The new assembler syntax the disassembler will emit. Can be
271
+ # either `:att` or `:intel`.
272
+ #
273
+ # @return [Symbol]
274
+ # The new assembly syntax being used.
275
+ #
276
+ def syntax=(new_syntax)
277
+ new_syntax = new_syntax.to_s.downcase.to_sym
278
+ func_name = UDis86::SYNTAX[new_syntax]
279
+
280
+ unless func_name
281
+ raise(ArgumentError,"unknown syntax name #{new_syntax}",caller)
282
+ end
283
+
284
+ UDis86.ud_set_syntax(self,UDis86.method(func_name))
285
+ return new_syntax
286
+ end
287
+
288
+ #
289
+ # The vendor of whose instructions are to be choosen from during
290
+ # disassembly.
291
+ #
292
+ # @return [Symbol]
293
+ # The vendor name, may be either `:amd` or `:intel`.
294
+ #
295
+ def vendor
296
+ VENDORS[self[:vendor]]
297
+ end
298
+
299
+ #
300
+ # Sets the vendor, of whose instructions are to be choosen from
301
+ # during disassembly.
302
+ #
303
+ # @param [Symbol] new_vendor
304
+ # The new vendor to use, can be either `:amd` or `:intel`.
305
+ #
306
+ # @return [Symbol]
307
+ # The new vendor to use.
308
+ #
309
+ def vendor=(new_vendor)
310
+ UDis86.ud_set_vendor(self,VENDORS.index(new_vendor))
311
+ return new_vendor
312
+ end
313
+
314
+ #
315
+ # Returns the current value of the Program Counter (PC).
316
+ #
317
+ # @return [Integer]
318
+ # The value of the PC.
319
+ #
320
+ def pc
321
+ self[:pc]
322
+ end
323
+
324
+ #
325
+ # Sets the value of the Program Counter (PC).
326
+ #
327
+ # @param [Integer] new_pc
328
+ # The new value to use for the PC.
329
+ #
330
+ # @return [Integer]
331
+ # The new value of the PC.
332
+ #
333
+ def pc=(new_pc)
334
+ UDis86.ud_set_pc(self,new_pc)
335
+ return new_pc
336
+ end
337
+
338
+ #
339
+ # Causes the disassembler to skip a certain number of bytes in the
340
+ # input stream.
341
+ #
342
+ # @param [Integer] n
343
+ # The number of bytes to skip.
344
+ #
345
+ # @return [UD]
346
+ # The disassembler.
347
+ #
348
+ def skip(n)
349
+ UDis86.ud_input_skip(self,n)
350
+ return self
351
+ end
352
+
353
+ #
354
+ # The mnemonic code of the last disassembled instruction.
355
+ #
356
+ # @return [Symbol]
357
+ # The mnemonic code.
358
+ #
359
+ def mnemonic_code
360
+ self[:mnemonic]
361
+ end
362
+
363
+ #
364
+ # The mnemonic string of the last disassembled instruction.
365
+ #
366
+ # @return [Symbol]
367
+ # The mnemonic string.
368
+ #
369
+ def mnemonic
370
+ UDis86.ud_lookup_mnemonic(self[:mnemonic]).to_sym
371
+ end
372
+
373
+ #
374
+ # The 64-bit mode REX prefix of the last disassembled instruction.
375
+ #
376
+ # @return [Integer]
377
+ # The 64-bit REX prefix.
378
+ #
379
+ def rex_prefix
380
+ self[:pfx_rex]
381
+ end
382
+
383
+ #
384
+ # The segment register prefix of the last disassembled instruction.
385
+ #
386
+ # @return [Integer]
387
+ # The segment register prefix.
388
+ #
389
+ def segment_prefix
390
+ self[:pfx_seg]
391
+ end
392
+
393
+ #
394
+ # The operand-size prefix (66h) of the last disassembled instruction.
395
+ #
396
+ # @return [Integer]
397
+ # The operand-size prefix.
398
+ #
399
+ def operand_prefix
400
+ self[:pfx_opr]
401
+ end
402
+
403
+ #
404
+ # The address-size prefix (67h) of the last disassembled instruction.
405
+ #
406
+ # @return [Integer]
407
+ # The address-size prefix.
408
+ #
409
+ def address_prefix
410
+ self[:pfx_adr]
411
+ end
412
+
413
+ #
414
+ # The lock prefix of the last disassembled instruction.
415
+ #
416
+ # @return [Integer]
417
+ # The lock prefix.
418
+ #
419
+ def lock_prefix
420
+ self[:pfx_lock]
421
+ end
422
+
423
+ #
424
+ # The rep prefix of the last disassembled instruction.
425
+ #
426
+ # @return [Integer]
427
+ # The rep prefix.
428
+ #
429
+ def rep_prefix
430
+ self[:pfx_rep]
431
+ end
432
+
433
+ #
434
+ # The repe prefix of the last disassembled instruction.
435
+ #
436
+ # @return [Integer]
437
+ # The repe prefix.
438
+ #
439
+ def repe_prefix
440
+ self[:pfx_repe]
441
+ end
442
+
443
+ #
444
+ # The repne prefix of the last disassembled instruction.
445
+ #
446
+ # @return [Integer]
447
+ # The repne prefix.
448
+ #
449
+ def repne_prefix
450
+ self[:pfx_repne]
451
+ end
452
+
453
+ #
454
+ # Returns the assembly syntax for the last disassembled instruction.
455
+ #
456
+ # @return [String]
457
+ # The assembly syntax for the instruction.
458
+ #
459
+ def to_asm
460
+ UDis86.ud_insn_asm(self)
461
+ end
462
+
463
+ #
464
+ # Returns the hexadecimal representation of the disassembled
465
+ # instruction.
466
+ #
467
+ # @return [String]
468
+ # The hexadecimal form of the disassembled instruction.
469
+ #
470
+ def to_hex
471
+ UDis86.ud_insn_hex(self)
472
+ end
473
+
474
+ alias :to_s :to_asm
475
+
476
+ #
477
+ # Returns the operands for the last disassembled instruction.
478
+ #
479
+ # @return [Array<Operand>]
480
+ # The operands of the instruction.
481
+ #
482
+ def operands
483
+ self[:operand].entries.select do |operand|
484
+ [
485
+ :ud_op_reg,
486
+ :ud_op_mem,
487
+ :ud_op_ptr,
488
+ :ud_op_imm,
489
+ :ud_op_jimm,
490
+ :ud_op_const
491
+ ].include?(operand.type)
492
+ end
493
+ end
494
+
495
+ #
496
+ # Disassembles the next instruction in the input stream.
497
+ #
498
+ # @return [UD]
499
+ # The disassembler.
500
+ #
501
+ def next_insn
502
+ UDis86.ud_disassemble(self)
503
+ end
504
+
505
+ #
506
+ # Returns the number of bytes that were disassembled.
507
+ #
508
+ # @return [Integer]
509
+ # The number of bytes disassembled.
510
+ #
511
+ def insn_length
512
+ UDis86.ud_insn_len(self)
513
+ end
514
+
515
+ #
516
+ # Returns the starting offset of the disassembled instruction
517
+ # relative to the initial value of the Program Counter (PC).
518
+ #
519
+ # @return [Integer]
520
+ # The offset of the instruction.
521
+ #
522
+ def insn_offset
523
+ UDis86.ud_insn_off(self)
524
+ end
525
+
526
+ #
527
+ # Returns the pointer to the buffer holding the disassembled
528
+ # instruction bytes.
529
+ #
530
+ # @return [FFI::Pointer]
531
+ # The pointer to the instruction buffer.
532
+ #
533
+ def insn_ptr
534
+ UDis86.ud_insn_ptr(self)
535
+ end
536
+
537
+ #
538
+ # Reads each byte, disassembling each instruction.
539
+ #
540
+ # @yield [ud]
541
+ # If a block is given, it will be passed the disassembler after
542
+ # each instruction has been disassembled.
543
+ #
544
+ # @yieldparam [UD] ud
545
+ # The disassembler.
546
+ #
547
+ # @return [UD]
548
+ # The disassembler.
549
+ #
550
+ def disassemble(&block)
551
+ until UDis86.ud_disassemble(self) == 0
552
+ block.call(self) if block
553
+ end
554
+
555
+ return self
556
+ end
557
+
558
+ alias :disas :disassemble
559
+ alias :each :disassemble
560
+
561
+ end
562
+ end
563
+ end