Opdis 1.3.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/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