Skip to content

Instantly share code, notes, and snippets.

@emonti
Last active October 13, 2015 02:08
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save emonti/4122904 to your computer and use it in GitHub Desktop.
Multi-arch bytecode disassembler using libLLVM
#!/usr/bin/env ruby
# author eric monti ~ nov 20, 2012
# license: DWTFYW
require 'rubygems'
require 'ffi'
class LLVMDisassembler
module C
extend FFI::Library
ffi_lib ['LLVM', 'LLVM-3.2svn', 'LLVM-3.1', 'LLVM-3.0']
# not currently used
callback :op_info_callback, [:ulong_long, :ulong_long, :ulong_long, :int, :pointer], :pointer
callback :symbol_lookup_callback, [:ulong_long, :pointer, :ulong_long, :pointer], :pointer
attach_function :create_disasm, :LLVMCreateDisasm, [:string, :pointer, :int, :op_info_callback, :symbol_lookup_callback], :pointer
attach_function :disasm_dispose, :LLVMDisasmDispose, [:pointer], :void
attach_function :disasm_instruction, :LLVMDisasmInstruction, [:pointer, :pointer, :ulong_long, :ulong_long, :pointer, :ulong], :ulong
end
def self.open(triple, &block)
new(triple) do |dis|
return block.call(dis)
end
end
def initialize(triple)
@dis = C.create_disasm(triple, nil, 0, nil, nil)
if block_given?
yield(self)
dispose()
end
end
def dispose
C.disasm_dispose(_dis)
@dis=nil
end
def get_instruction(bytes, pc=0)
insize = bytes.size
FFI::MemoryPointer.new(insize) do |bytes_ptr|
bytes_ptr.write_string_length(bytes, insize)
FFI::MemoryPointer.new(255) do |out_string|
retlen=C.disasm_instruction(_dis, bytes_ptr, insize, pc, out_string, 255)
if retlen > 0
return [out_string.read_string, bytes_ptr.read_array_of_uint8(retlen), retlen]
end
end
end
return nil
end
def get_instructions(bytes, pc=0)
off=0
insize = bytes.size
FFI::MemoryPointer.new(insize) do |bytes_ptr|
bytes_ptr.write_string_length(bytes, insize)
FFI::MemoryPointer.new(255) do |out_string|
while (off < insize)
offp = bytes_ptr+off
retlen=C.disasm_instruction(_dis, offp, insize-off, pc, out_string, 255)
if retlen > 0
yield(out_string.read_string, offp.read_array_of_uint8(retlen), retlen)
off+=retlen
else
break
end
end
end
end
return nil
end
private
def _dis
@dis || raise(FFI::NullPointerError, "This Disassembler has been disposed of and can no longer be used")
end
end
if __FILE__ == $0
arch = ARGV.shift || "i386"
off=0
LLVMDisassembler.open(arch) do |dis|
dis.get_instructions(STDIN.read) do |ins, byt, bytsz|
raw = byt.map{|b| b.to_s(16).rjust(2,'0') }.join(' ')
puts "#{off.to_s(16).rjust(8,'0')} #{raw.ljust(16)} #{ins}"
off+=bytsz
end
end
puts off.to_s(16).rjust(8,'0')
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment