Opdis 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.README ADDED
@@ -0,0 +1,8 @@
1
+ The Opdis Ruby extension is released under the GNU Public License version 3.0,
2
+ distributed in this package as LICENSE.
3
+
4
+ The intent of this license choice is not to restrict distribution, but for
5
+ compatibility with the distribution terms of opdis and GNU binutils.
6
+
7
+ Contact community@thoughtgang.org for alternative licensing arrangements if
8
+ the GPLv3 is too restrictive.
data/README ADDED
@@ -0,0 +1,101 @@
1
+ Opdis
2
+
3
+ A Ruby C extension (and gem) for the opdis library, available from
4
+ http://freshmeat.net/projects/opdis .
5
+
6
+ BUILD
7
+ -----
8
+
9
+ The standard C extension build process is used:
10
+
11
+ bash# ruby1.9 extconf.rb
12
+ bash# make
13
+
14
+ Note that the Ruby headers must be installed. On Ubuntu, these are in the
15
+ ruby-dev or ruby1.9-dev package.
16
+
17
+
18
+ The gem is built using the standard gem build command:
19
+
20
+ bash# gem build Opdis.gemspec
21
+
22
+
23
+ The top-level Makefile supports each of these builds with the commands
24
+ 'make' and 'make gem'.
25
+
26
+ bash# make
27
+ # builds C extension
28
+ bash# make gem
29
+ # builds the gem
30
+
31
+
32
+ BINUTILS AND SUPPORTED ARCHITECTURES
33
+ ------------------------------------
34
+
35
+ The implementation of binutils (and libopcodes) does not provide a way to
36
+ determine the supported platforms at compile time, unless the config.h file
37
+ used to build the binutils package is present.
38
+
39
+ The extconf.rb file has been modified in order to detect the architectures
40
+ supported by the local copy of binutils, and to allow the user to specify
41
+ which architectures they want supported.
42
+
43
+ It does this using the following steps:
44
+
45
+ 1. run objdump -i to get the supported architectures
46
+ 2. each line that matches one of binutils' known architectures is
47
+ added as a #define to CPPFLAGS
48
+ 3. if no architectures have been found, or if objdump failed to run,
49
+ default to the i386 architecture.
50
+
51
+ The binary used in step 1 can be specified by the user via the --with-objdump
52
+ flag. For example:
53
+
54
+ bash# cat /tmp/objdump.sh
55
+ #/bin/sh
56
+ echo 'arm'
57
+ echo 'sparc'
58
+ echo 'm68k'
59
+ bash# ruby1.9 extconf.rb --with-objdump=/tmp/objdump.sh
60
+ checking for init_disassemble_info() in -lopcodes... yes
61
+ Adding architecture 'arm'
62
+ Adding architecture 'sparc'
63
+ Adding architecture 'm68k'
64
+ creating Makefile
65
+
66
+ This makes it possible to force compilation of support for specific
67
+ architectures when there is no working objdump present. Note that libopcodes
68
+ must have been compiled with support for the architectures, or you will get
69
+ runtime errors.
70
+
71
+
72
+ EXAMPLES
73
+
74
+ Extended examples are provided in the 'examples' directory. The following
75
+ code snippets give a brief overview of using the BFD and Opdis extensions
76
+ together.
77
+
78
+ require 'BFD'
79
+ require 'Opdis'
80
+
81
+ Bfd::Target.new('/tmp/a.out') do |tgt|
82
+ Opdis::Disassembler.new do |dis|
83
+ dis.disasm_entry( tgt ) { |insn| puts insn }
84
+ end
85
+ end
86
+
87
+ tgt = Bfd::Target.new('/tmp/a.out')
88
+ tgt.sections.values.each { |s| puts s.name } }
89
+ Opdis::Disassembler.new do |dis|
90
+ dis.disasm_section( tgt.sections['.text.] ) { |i| puts i }
91
+ end
92
+
93
+ Opdis::Disassembler.architectures.each { |arch| puts arch }
94
+
95
+ Opdis::Disassembler.new( arch: 'x86_64_intel' ) do |dis|
96
+ dis.disassemble( [0x90, 0xCC, 0x90] ) { |insn| puts insn }
97
+ end
98
+
99
+ Opdis::Disassembler.new( arch: 'x86_intel' ) do |dis|
100
+ dis.disassemble( "\x90\xCC\x90" ) { |insn| puts insn }
101
+ end
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ # Opdis Example: Array
3
+ # Linear disassembly of array of bytes
4
+ # Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
5
+
6
+ require 'Opdis'
7
+
8
+ def disasm_bytes( arch, bytes )
9
+ Opdis::Disassembler.new( :arch => arch ) do |dis|
10
+ dis.disassemble( bytes ) { |i| puts i }
11
+ end
12
+ end
13
+
14
+ def hex_to_array( bytes )
15
+ bytes.collect { |b| b.hex }
16
+ end
17
+
18
+ if __FILE__ == $0
19
+ raise "Usage: #{$0} ARCH BYTE [BYTE...]" if ARGV.length < 2
20
+
21
+ arch = ARGV.shift
22
+ disasm_bytes( arch, hex_to_array(ARGV) )
23
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # Opdis Example: BFD Entry Point
3
+ # Control-flow disassembly of a BFD object file from its entry point
4
+ # Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
5
+
6
+ require 'BFD'
7
+ require 'Opdis'
8
+
9
+ def disasm_entry( tgt )
10
+ Opdis::Disassembler.new() do |dis|
11
+ # NOTE: This will print instructions as they are disassembled
12
+ # (i.e. not in order)
13
+ dis.disasm_entry( tgt ) { |i| puts i }
14
+ end
15
+ end
16
+
17
+ if __FILE__ == $0
18
+ raise "Usage: #{$0} FILE [FILE...]" if ARGV.length == 0
19
+
20
+ ARGV.each do |filename|
21
+ Bfd::Target.new(filename) { |f| disasm_entry( f ) }
22
+ end
23
+
24
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # Opdis Example: BFD Section
3
+ # Linear disassembly of a section in a BFD object file
4
+ # Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
5
+
6
+ require 'BFD'
7
+ require 'Opdis'
8
+
9
+ def disasm_section( sec )
10
+ puts "Section not found in BFD" if not sec
11
+
12
+ puts "#{sec.name}:"
13
+ Opdis::Disassembler.new() do |dis|
14
+ dis.disasm_section( sec ) { |i| puts "\t#{i}" }
15
+ end
16
+ end
17
+
18
+ if __FILE__ == $0
19
+ raise "Usage: #{$0} FILE SECTION [SECTION...]" if ARGV.length == 0
20
+
21
+ Bfd::Target.new(ARGV.shift) do |tgt|
22
+ ARGV.each { |name| disasm_section( tgt.sections[name] ) }
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # Opdis Example: BFD Symbol
3
+ # Disassembly of a BFD object file from named symbols
4
+ # Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
5
+
6
+ require 'BFD'
7
+ require 'Opdis'
8
+
9
+ def disasm_symbol( sym )
10
+ raise "Symbol not found in BFD" if not sym
11
+ raise "Symbol #{sym.name} has no VMA!" if not sym.value
12
+
13
+ puts "#{sym.name}:"
14
+ Opdis::Disassembler.new() do |dis|
15
+ # NOTE: This will print instructions as they are disassembled
16
+ # i.e. not necessarily in order
17
+ dis.disasm_symbol( sym ) { |i| puts "\t#{i}" }
18
+ end
19
+ end
20
+
21
+ if __FILE__ == $0
22
+ raise "Usage: #{$0} FILE SYMBOL [SYMBOL...]" if ARGV.length == 0
23
+
24
+ Bfd::Target.new(ARGV.shift) do |tgt|
25
+ ARGV.each { |name| disasm_symbol( tgt.symbols[name] ) }
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # Opdis Example: String
3
+ # Linear disassembly of string of bytes
4
+ # Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
5
+
6
+ require 'Opdis'
7
+
8
+ def disasm_bytes( arch, bytes )
9
+
10
+ Opdis::Disassembler.new( :arch => arch ) do |dis|
11
+ dis.disassemble( bytes ) { |i| puts i }
12
+ end
13
+
14
+ end
15
+
16
+ def hex_to_string( bytes )
17
+ bytes.collect { |b| b.hex }.pack( 'C' * bytes.length )
18
+ end
19
+
20
+ if __FILE__ == $0
21
+ raise "Usage: #{$0} ARCH BYTE [BYTE...]" if ARGV.length < 2
22
+
23
+ arch = ARGV.shift
24
+ disasm_bytes( arch, hex_to_string(ARGV) )
25
+ end
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+ # Opdis Example: Decoder
3
+ # Custom Instruction Decoder used in linear disassembly of array of bytes
4
+ # Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
5
+
6
+ require 'BFD'
7
+ require 'Opdis'
8
+
9
+ # ----------------------------------------------------------------------
10
+ # Decoder that wraps Generic Decoder. This bypasses the X86Decoder processing.
11
+ class CustomDecoder < Opdis::InstructionDecoder
12
+
13
+ def decode(insn, hash)
14
+ # TODO
15
+ end
16
+
17
+ end
18
+
19
+ # ----------------------------------------------------------------------
20
+ # print an instruction in the standard disasm listing format:
21
+ # VMA 8_hex_bytes instruction
22
+ def print_insn(insn)
23
+ hex_str = insn.bytes.collect { |b| "%02X" % b }.join(' ')
24
+ puts "%08X %-23.23s %s" % [ insn.vma, hex_str, insn.ascii ]
25
+ end
26
+
27
+ # ----------------------------------------------------------------------
28
+ def disasm_bytes( bytes )
29
+ # custom decoder to use in disassembler
30
+ decoder = CustomDecoder.new
31
+
32
+ Opdis::Disassembler.new( :insn_decoder => decoder ) do |dis|
33
+
34
+ dis.disasm_entry( bytes ) { |i| print_insn(i) }
35
+
36
+ end
37
+ end
38
+
39
+ # ----------------------------------------------------------------------
40
+ if __FILE__ == $0
41
+ raise "Usage: #{$0} ARCH BYTE [BYTE...]" if ARGV.length < 2
42
+
43
+ arch = ARGV.shift
44
+ disasm_bytes( arch, ARGV.collect { |b| b.hex } )
45
+ end
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
3
+ # Linear disassembly of a section in a BFD object file
4
+
5
+ require 'Opdis'
6
+
7
+ def disasm_file( file, arch, offset, length )
8
+ if not length
9
+ file.seek( 0, IO::SEEK_END )
10
+ length = file.tell - offset
11
+ file.rewind
12
+ end
13
+
14
+ Opdis::Disassembler.new( :arch => arch ) do |dis|
15
+ opts = { :vma => offset, :buffer_vma => 0, :length=> length }
16
+ dis.disassemble( file, opts ) do |i|
17
+ puts "%08X\t%s" % [i.vma, i.to_s]
18
+ end
19
+ end
20
+ end
21
+
22
+ if __FILE__ == $0
23
+ raise "Usage: #{$0} FILE ARCH [OFFSET] [LENGTH]" if ARGV.length == 0
24
+
25
+ filename = ARGV.shift
26
+ arch = ARGV.shift
27
+ offset = ARGV.length > 0 ? ARGV.shift.to_i : 0
28
+ length = ARGV.length > 0 ? ARGV.shift.to_i : nil
29
+
30
+ File.open(filename, 'rb') { |f| disasm_file( f, arch, offset, length ) }
31
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
3
+ # Print available libopcodes options to STDOUT
4
+
5
+ require 'Opdis'
6
+
7
+ if __FILE__ == $0
8
+
9
+ Opdis::Disassembler.options.each { |line| puts line }
10
+
11
+ end
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env ruby
2
+ # Opdis Example: Resolver
3
+ # Custom Address Resolver used in control-flow disassembly of BFD entry point
4
+ # Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
5
+
6
+ require 'BFD'
7
+ require 'Opdis'
8
+
9
+ # ----------------------------------------------------------------------
10
+ class Opdis::ImmediateOperand
11
+ # return immediate operand value
12
+ def resolve( ign )
13
+ return @vma
14
+ end
15
+ end
16
+
17
+ # ----------------------------------------------------------------------
18
+ class Opdis::AddressExpressionOperand
19
+ # attempt to resolve address expression [if stack-register only]
20
+ def resolve( vm )
21
+ # TODO: if base is stack or frame register, return item from vm.stack;
22
+ # otherwise, return nil (need to be able to deref mem addr for
23
+ # general addr expressions).
24
+ return nil
25
+ end
26
+ end
27
+
28
+ # ----------------------------------------------------------------------
29
+ class Opdis::AbsoluteAddressOperand
30
+ # attempt to resolve absolute address [segment reg + addr]
31
+ def resolve( vm )
32
+ # TODO: lookup segment register in VM; return reg value + addr
33
+ return nil
34
+ end
35
+ end
36
+
37
+ # ----------------------------------------------------------------------
38
+ class Opdis::RegisterOperand
39
+ # attempt to resolve register operand
40
+ def resolve( vm )
41
+ # TODO: lookup register name in vm.registers and return stored value
42
+ return nil
43
+ end
44
+ end
45
+
46
+ # ----------------------------------------------------------------------
47
+ include Opdis
48
+ class CustomResolver < AddressResolver
49
+ attr_reader :registers # reg name -> value
50
+ attr_reader :stack # array of pushed values
51
+ attr_reader :stack_ptr # index (into @stack) of 'top' of stack
52
+ attr_reader :frame_ptr # index (into @stack) of start of frame
53
+
54
+ def initialize()
55
+ @registers = {}
56
+ @stack = []
57
+ @stack_ptr = @frame_ptr = nil
58
+ end
59
+
60
+ # return VMA for target operand or nil
61
+ def resolve(insn)
62
+ return insn.target ? insn.target.resolve(self) : nil
63
+ end
64
+
65
+ # -- Stack Management --
66
+ def stack_push( val )
67
+ @stack.push val
68
+ @stack_ptr = @stack.length - 1
69
+ end
70
+
71
+ def stack_pop()
72
+ val = @stack.push
73
+ @stack_ptr = @stack.length - 1
74
+ return val
75
+ end
76
+
77
+ def stack_frame()
78
+ @frame_ptr = @stack_ptr
79
+ end
80
+
81
+ def stack_unframe()
82
+ @stack_ptr = @frame_ptr
83
+ end
84
+
85
+ def stack_adjust( val )
86
+ if val < 0
87
+ val.abs.times { @stack.push 0 }
88
+ else
89
+ val.abs.times { @stack.pop }
90
+ end
91
+
92
+ @stack_ptr = @stack.length - 1
93
+ end
94
+
95
+ # -- Register Management --
96
+ # -- Instruction Handlers --
97
+ # call insn
98
+ def call(insn, ign, ignn)
99
+ stack_push insn.vma + insn.size
100
+ end
101
+
102
+ # return insn
103
+ def ret(insn, ign, ignn)
104
+ stack_pop
105
+ end
106
+
107
+ # push insn
108
+ def push(ign, op, ignn)
109
+ # TODO: stack_push op.value
110
+ end
111
+
112
+ # pop insn
113
+ def pop(ign, op, ignn)
114
+ val = stack_pop
115
+ # TODO: if dest is reg, move val to reg, otherwise discard
116
+ end
117
+
118
+ # frame insn
119
+ def frame(ign, ignn, ignnn)
120
+ stack_frame
121
+ end
122
+
123
+ # unframe insn
124
+ def unframe(ign, ignn, ignnn)
125
+ stack_unframe
126
+ end
127
+
128
+ # load/store insn
129
+ def lost(ign, src, dest)
130
+ # TODO: if dest is addr_expr for stack, set stack item to reg.value
131
+ # if dest is reg, fill reg with src.value
132
+ end
133
+
134
+ INSN_HANDLERS = [
135
+ [Instruction::CAT_CFLOW, Instruction::FLG_CALL, :call],
136
+ [Instruction::CAT_CFLOW, Instruction::FLG_CALLCC, :call],
137
+ [Instruction::CAT_CFLOW, Instruction::FLG_RET, :ret],
138
+ [Instruction::CAT_STACK, Instruction::FLG_PUSH, :push],
139
+ [Instruction::CAT_STACK, Instruction::FLG_POP, :pop],
140
+ [Instruction::CAT_STACK, Instruction::FLG_FRAME, :frame],
141
+ [Instruction::CAT_STACK, Instruction::FLG_UNFRAME, :unframe],
142
+ #[Instruction::CAT_MATH, Instruction::FLG_ADD, :add],
143
+ #[Instruction::CAT_MATH, Instruction::FLG_SUB, :sub],
144
+ [Instruction::CAT_LOADSTORE, nil, :lost]
145
+ ]
146
+
147
+ # Modify stack/insn contents based on insn
148
+ def process(insn)
149
+ INSN_HANDLERS.each do |hdlr|
150
+ if insn.category == hdlr[0] && (not hdlr[1] or insn.flags & hdlr[1])
151
+ # Invoke handler method with instruction and src, dest operands
152
+ self.send( hdlr[2], insn, insn.src, insn.dest )
153
+ end
154
+ end
155
+ end
156
+
157
+ end
158
+
159
+ # ----------------------------------------------------------------------
160
+ # print an instruction in the standard disasm listing format:
161
+ # VMA 8_hex_bytes instruction
162
+ def print_insn(insn)
163
+ hex_str = insn.bytes.bytes.collect { |b| "%02X" % b }.join(' ')
164
+ puts "%08X %-23.23s %s" % [ insn.vma, hex_str, insn.ascii ]
165
+ end
166
+
167
+ # ----------------------------------------------------------------------
168
+ def disasm_entry( tgt )
169
+ # custom resolver to use in disassembler
170
+ vm = CustomResolver.new
171
+
172
+ Disassembler.new( :resolver => vm ) do |dis|
173
+
174
+ dis.disasm_entry( tgt ) do |insn|
175
+ # Set register/stack contents based on insn
176
+ vm.process(insn)
177
+
178
+ # Print instructions in order of VMA
179
+ end.values.sort_by{ |i| i.vma }.each { |i| print_insn(i) }
180
+ end
181
+ end
182
+
183
+ # ----------------------------------------------------------------------
184
+ if __FILE__ == $0
185
+ raise "Usage: #{$0} FILE [FILE...]" if ARGV.length == 0
186
+
187
+ ARGV.each do |filename|
188
+ Bfd::Target.new(filename) { |f| disasm_entry( f ) }
189
+ end
190
+
191
+ end