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.
- data/.gitignore +8 -0
- data/.specopts +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +60 -0
- data/Rakefile +42 -0
- data/ffi-udis86.gemspec +88 -0
- data/lib/udis86.rb +4 -0
- data/lib/udis86/ffi.rb +30 -0
- data/lib/udis86/operand.rb +174 -0
- data/lib/udis86/operand_pointer.rb +36 -0
- data/lib/udis86/operand_value.rb +136 -0
- data/lib/udis86/types.rb +942 -0
- data/lib/udis86/ud.rb +563 -0
- data/lib/udis86/version.rb +6 -0
- data/spec/helpers/files.rb +11 -0
- data/spec/helpers/files/operands_index_scale +1 -0
- data/spec/helpers/files/operands_index_scale.s +2 -0
- data/spec/helpers/files/operands_memory +1 -0
- data/spec/helpers/files/operands_memory.s +2 -0
- data/spec/helpers/files/operands_offset +1 -0
- data/spec/helpers/files/operands_offset.s +2 -0
- data/spec/helpers/files/operands_simple +0 -0
- data/spec/helpers/files/operands_simple.s +2 -0
- data/spec/helpers/files/simple +1 -0
- data/spec/helpers/files/simple.s +4 -0
- data/spec/helpers/operands.rb +14 -0
- data/spec/operand_spec.rb +72 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/ud_spec.rb +195 -0
- data/spec/udis86_spec.rb +25 -0
- metadata +122 -0
data/lib/udis86/ud.rb
ADDED
@@ -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
|