metasm 1.0.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.
Files changed (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. metadata +255 -0
@@ -0,0 +1,527 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2009 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+
7
+ require 'metasm/os/main'
8
+ require 'socket'
9
+
10
+ module Metasm
11
+ # lowlevel interface to the gdbserver protocol
12
+ class GdbClient
13
+ GDBREGS_IA32 = %w[eax ecx edx ebx esp ebp esi edi eip eflags cs ss ds es fs gs].map { |r| r.to_sym } # XXX [77] = 'orig_eax'
14
+ GDBREGS_X64 = %w[rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 rip rflags cs ss ds es fs gs].map { |r| r.to_sym }
15
+
16
+ # compute the hex checksum used in gdb protocol
17
+ def gdb_csum(buf)
18
+ '%02x' % (buf.unpack('C*').inject(0) { |cs, c| cs + c } & 0xff)
19
+ end
20
+
21
+ # send the buffer, waits ack
22
+ # return true on success
23
+ def gdb_send(cmd, buf='')
24
+ buf = cmd + buf
25
+ buf = '$' << buf << '#' << gdb_csum(buf)
26
+
27
+ 5.times {
28
+ @io.write buf
29
+ loop do
30
+ break if not IO.select([@io], nil, nil, 0.2)
31
+ raise Errno::EPIPE if not ack = @io.read(1)
32
+ case ack
33
+ when '+'
34
+ return true
35
+ when '-'
36
+ puts "gdb_send: ack neg" if $DEBUG
37
+ break
38
+ when nil
39
+ return
40
+ end
41
+ end
42
+ }
43
+
44
+ log "send error #{cmd.inspect} (no ack)"
45
+ false
46
+ end
47
+
48
+ def quiet_during
49
+ pq = quiet
50
+ @quiet = true
51
+ yield
52
+ ensure
53
+ @quiet = pq
54
+ end
55
+
56
+ # return buf, or nil on error / csum error
57
+ # waits IO.select(timeout) between each char
58
+ # outstr is used internally only to handle multiline output string
59
+ def gdb_readresp(timeout=nil, outstr=nil)
60
+ @recv_ctx ||= {}
61
+ @recv_ctx[:state] ||= :nosync
62
+ buf = nil
63
+
64
+ while @recv_ctx
65
+ return unless IO.select([@io], nil, nil, timeout)
66
+ raise Errno::EPIPE if not c = @io.read(1)
67
+
68
+ case @recv_ctx[:state]
69
+ when :nosync
70
+ if c == '$'
71
+ @recv_ctx[:state] = :data
72
+ @recv_ctx[:buf] = ''
73
+ end
74
+ when :data
75
+ if c == '#'
76
+ @recv_ctx[:state] = :csum1
77
+ @recv_ctx[:cs] = ''
78
+ else
79
+ @recv_ctx[:buf] << c
80
+ end
81
+ when :csum1
82
+ @recv_ctx[:cs] << c
83
+ @recv_ctx[:state] = :csum2
84
+ when :csum2
85
+ cs = @recv_ctx[:cs] << c
86
+ buf = @recv_ctx[:buf]
87
+ @recv_ctx = nil
88
+ if cs.downcase == gdb_csum(buf).downcase
89
+ @io.write '+'
90
+ else
91
+ log "transmit error"
92
+ @io.write '-'
93
+ return
94
+ end
95
+ end
96
+ end
97
+
98
+ case buf
99
+ when /^E(..)$/
100
+ e = $1.to_i(16)
101
+ log "error #{e} (#{PTrace::ERRNO.index(e)})"
102
+ return
103
+ when /^O([0-9a-fA-F]*)$/
104
+ if not outstr
105
+ first = true
106
+ outstr = ''
107
+ end
108
+ outstr << unhex($1)
109
+ ret = gdb_readresp(timeout, outstr)
110
+ outstr.split("\n").each { |e| log 'gdb: ' + e } if first
111
+ return ret
112
+ end
113
+
114
+ puts "gdb_readresp: got #{buf[0, 64].inspect}#{'...' if buf.length > 64}" if $DEBUG
115
+ buf
116
+ end
117
+
118
+ def gdb_msg(*a)
119
+ gdb_readresp if gdb_send(*a)
120
+ end
121
+
122
+ # rle: build the regexp that will match repetitions of a character, skipping counts leading to invalid char
123
+ rng = [3..(125-29)]
124
+ [?+, ?-, ?#, ?$].sort.each { |invalid|
125
+ invalid = invalid.unpack('C').first if invalid.kind_of? String
126
+ invalid -= 29
127
+ rng.each_with_index { |r, i|
128
+ if r.include? invalid
129
+ replace = [r.begin..invalid-1, invalid+1..r.end]
130
+ replace.delete_if { |r_| r_.begin > r_.end }
131
+ rng[i, 1] = replace
132
+ end
133
+ }
134
+ }
135
+ repet = rng.reverse.map { |r| "\\1{#{r.begin},#{r.end}}" }.join('|')
136
+ RLE_RE = /(.)(#{repet})/m
137
+
138
+ # rle-compress a buffer
139
+ # a character followed by '*' followed by 'x' is asc(x)-28 repetitions of the char
140
+ # eg '0* ' => '0' * (asc(' ') - 28) = '0000'
141
+ # for the count character, it must be 32 <= char < 126 and not be '+' '-' '#' or '$'
142
+ def rle(buf)
143
+ buf.gsub(RLE_RE) {
144
+ chr, len = $1, $2.length+1
145
+ chr + '*' + (len+28).chr
146
+ }
147
+ end
148
+ # decompress rle-encoded data
149
+ def unrle(buf) buf.gsub(/(.)\*(.)/) { $1 * ($2.unpack('C').first-28) } end
150
+ # send an integer as a long hex packed with leading 0 stripped
151
+ def hexl(int) @pack_netint[[int]].unpack('H*').first.sub(/^0+(.)/, '\\1') end
152
+ # send a binary buffer as a rle hex-encoded
153
+ def hex(buf) buf.unpack('H*').first end
154
+ # decode an rle hex-encoded buffer
155
+ def unhex(buf)
156
+ buf = buf[/^[a-fA-F0-9]*/]
157
+ buf = '0' + buf if buf.length & 1 == 1
158
+ [buf].pack('H*')
159
+ end
160
+
161
+ # retrieve remote regs
162
+ def read_regs
163
+ if buf = gdb_msg('g')
164
+ regs = unhex(unrle(buf))
165
+ p @unpack_int[regs].map { |v| '%x' % v } if $DEBUG
166
+ if regs.length < @regmsgsize
167
+ # retry once, was probably a response to something else
168
+ puts "bad regs size!" if $DEBUG
169
+ buf = gdb_msg('g')
170
+ regs = unhex(unrle(buf)) if buf
171
+ if not buf or regs.length < @regmsgsize
172
+ raise "regs buffer recv is too short !"
173
+ end
174
+ end
175
+ Hash[*@gdbregs.zip(@unpack_int[regs]).flatten]
176
+ end
177
+ end
178
+
179
+ # send the reg values
180
+ def send_regs(r = {})
181
+ return if r.empty?
182
+ regs = r.values_at(*@gdbregs)
183
+ gdb_msg('G', hex(@pack_int[regs]))
184
+ end
185
+
186
+ # read memory (small blocks prefered)
187
+ def getmem(addr, len)
188
+ return '' if len == 0
189
+ if mem = quiet_during { gdb_msg('m', hexl(addr) << ',' << hexl(len)) } and mem != ''
190
+ unhex(unrle(mem))
191
+ end
192
+ end
193
+
194
+ # write memory (small blocks prefered)
195
+ def setmem(addr, data)
196
+ len = data.length
197
+ return if len == 0
198
+ raise 'writemem error' if not gdb_msg('M', hexl(addr) << ',' << hexl(len) << ':' << rle(hex(data)))
199
+ end
200
+
201
+ def continue
202
+ gdb_send('c')
203
+ end
204
+
205
+ def singlestep
206
+ gdb_send('s')
207
+ end
208
+
209
+ def break
210
+ @io.write("\3")
211
+ end
212
+
213
+ def kill
214
+ gdb_send('k')
215
+ end
216
+
217
+ def detach
218
+ gdb_send('D')
219
+ end
220
+
221
+ # monitor, aka remote command
222
+ def rcmd(cmd)
223
+ gdb_msg('qRcmd,' + hex(cmd))
224
+ end
225
+
226
+ attr_accessor :io, :cpu, :gdbregs
227
+ def initialize(io, cpu='Ia32')
228
+ cpu = Metasm.const_get(cpu).new if cpu.kind_of? String
229
+ raise 'unknown cpu' if not cpu.kind_of? CPU
230
+ setup_arch(cpu)
231
+ @cpu = cpu
232
+
233
+ case io
234
+ when IO; @io = io
235
+ when /^udp:(.*):(.*?)$/i; @io = UDPSocket.new ; @io.connect($1, $2)
236
+ when /^(?:tcp:)?(.*):(.*?)$/i; @io = TCPSocket.open($1, $2) # XXX matches C:\fail
237
+ # TODO pipe, serial port, etc ; also check ipv6
238
+ else raise "unknown target #{io.inspect}"
239
+ end
240
+
241
+ gdb_setup
242
+ end
243
+
244
+ def gdb_setup
245
+ gdb_msg('q', 'Supported')
246
+ #gdb_msg('Hc', '-1')
247
+ #gdb_msg('qC')
248
+ if not gdb_msg('?')
249
+ log "nobody on the line, waiting for someone to wake up"
250
+ IO.select([@io], nil, nil, nil)
251
+ log "who's there ?"
252
+ end
253
+ end
254
+
255
+ def set_hwbp(type, addr, len=1, set=true)
256
+ set = (set ? 'Z' : 'z')
257
+ type = { 'r' => '3', 'w' => '2', 'x' => '1', 's' => '0' }[type.to_s] || raise("invalid bp type #{type.inspect}")
258
+ gdb_msg(set, type << ',' << hexl(addr) << ',' << hexl(len))
259
+ true
260
+ end
261
+
262
+ def unset_hwbp(type, addr, len=1)
263
+ set_hwbp(type, addr, len, false)
264
+ end
265
+
266
+ # use qSymbol to retrieve a symbol value (uint)
267
+ def request_symbol(name)
268
+ resp = gdb_msg('qSymbol:', hex(name))
269
+ if resp and a = resp.split(':')[1]
270
+ @unpack_netint[unhex(a)].first
271
+ end
272
+ end
273
+
274
+ def check_target(timeout=0)
275
+ return if not msg = gdb_readresp(timeout)
276
+ case msg[0]
277
+ when ?S
278
+ sig = unhex(msg[1, 2]).unpack('C').first
279
+ { :state => :stopped, :info => "signal #{sig} #{PTrace::SIGNAL[sig]}" }
280
+ when ?T
281
+ sig = unhex(msg[1, 2]).unpack('C').first
282
+ ret = { :state => :stopped, :info => "signal #{sig} #{PTrace::SIGNAL[sig]}" }
283
+ ret.update msg[3..-1].split(';').inject({}) { |h, s| k, v = s.split(':', 2) ; h.update k => (v || true) } # 'thread' -> pid
284
+ when ?W
285
+ code = unhex(msg[1, 2]).unpack('C').first
286
+ { :state => :dead, :info => "exited with code #{code}" }
287
+ when ?X
288
+ sig = unhex(msg[1, 2]).unpack('C').first
289
+ { :state => :dead, :info => "signal #{sig} #{PTrace::SIGNAL[sig]}" }
290
+ else
291
+ log "check_target: unhandled #{msg.inspect}"
292
+ { :state => :unknown }
293
+ end
294
+ end
295
+
296
+ attr_accessor :logger, :quiet
297
+ def log(s)
298
+ return if quiet
299
+ @logger ||= $stdout
300
+ @logger.puts s
301
+ end
302
+
303
+
304
+ # setup the various function used to pack ints & the reg list
305
+ # according to a target CPU
306
+ def setup_arch(cpu)
307
+ case cpu.shortname
308
+ when 'ia32'
309
+ @gdbregs = GDBREGS_IA32
310
+ @regmsgsize = 4 * @gdbregs.length
311
+ when 'x64'
312
+ @gdbregs = GDBREGS_X64
313
+ @regmsgsize = 8 * @gdbregs.length
314
+ when 'arm'
315
+ @gdbregs = cpu.dbg_register_list
316
+ @regmsgsize = 4 * @gdbregs.length
317
+ else
318
+ # we can still use readmem/kill and other generic commands
319
+ # XXX serverside setregs may fail if we give an incorrect regbuf size
320
+ puts "unsupported GdbServer CPU #{cpu.shortname}"
321
+ @gdbregs = [*0..32].map { |i| "r#{i}".to_sym }
322
+ @regmsgsize = 0
323
+ end
324
+
325
+ # yay life !
326
+ # do as if cpu is littleendian, fixup at the end
327
+ case cpu.size
328
+ when 16
329
+ @pack_netint = lambda { |i| i.pack('n*') }
330
+ @unpack_netint = lambda { |s| s.unpack('n*') }
331
+ @pack_int = lambda { |i| i.pack('v*') }
332
+ @unpack_int = lambda { |s| s.unpack('v*') }
333
+ when 32
334
+ @pack_netint = lambda { |i| i.pack('N*') }
335
+ @unpack_netint = lambda { |s| s.unpack('N*') }
336
+ @pack_int = lambda { |i| i.pack('V*') }
337
+ @unpack_int = lambda { |s| s.unpack('V*') }
338
+ when 64
339
+ bswap = lambda { |s| s.scan(/.{8}/m).map { |ss| ss.reverse }.join }
340
+ @pack_netint = lambda { |i| i.pack('Q*') }
341
+ @unpack_netint = lambda { |s| s.unpack('Q*') }
342
+ @pack_int = lambda { |i| bswap[i.pack('Q*')] }
343
+ @unpack_int = lambda { |s| bswap[s].unpack('Q*') }
344
+ if [1].pack('Q')[0] == ?\1 # ruby interpreter littleendian
345
+ @pack_netint, @pack_int = @pack_int, @pack_netint
346
+ @unpack_netint, @unpack_int = @unpack_int, @unpack_netint
347
+ end
348
+ else raise "GdbServer: unsupported cpu size #{cpu.size}"
349
+ end
350
+
351
+ # if target cpu is bigendian, use netint everywhere
352
+ if cpu.endianness == :big
353
+ @pack_int = @pack_netint
354
+ @unpack_int = @unpack_netint
355
+ end
356
+ end
357
+ end
358
+
359
+ # virtual string to access the remote process memory
360
+ class GdbRemoteString < VirtualString
361
+ attr_accessor :gdb
362
+
363
+ def initialize(gdb, addr_start=0, length=nil)
364
+ @gdb = gdb
365
+ length ||= 1 << (@gdb.cpu.size rescue 32)
366
+ @pagelength = 512
367
+ super(addr_start, length)
368
+ end
369
+
370
+ def dup(addr=@addr_start, len=@length)
371
+ self.class.new(@gdb, addr, len)
372
+ end
373
+
374
+ def rewrite_at(addr, data)
375
+ len = data.length
376
+ off = 0
377
+ while len > @pagelength
378
+ @gdb.setmem(addr+off, data[off, @pagelength])
379
+ off += @pagelength
380
+ len -= @pagelength
381
+ end
382
+ @gdb.setmem(addr+off, data[off, len])
383
+ end
384
+
385
+ def get_page(addr, len=@pagelength)
386
+ @gdb.getmem(addr, len)
387
+ end
388
+ end
389
+
390
+ # this class implements a high-level API using the gdb-server network debugging protocol
391
+ class GdbRemoteDebugger < Debugger
392
+ attr_accessor :gdb, :check_target_timeout
393
+ def initialize(url, cpu='Ia32')
394
+ @gdb = GdbClient.new(url, cpu)
395
+ @gdb.logger = self
396
+ @cpu = @gdb.cpu
397
+ @memory = GdbRemoteString.new(@gdb)
398
+ @reg_val_cache = {}
399
+ @regs_dirty = false
400
+ # when checking target, if no message seen since this much seconds, send a 'status' query
401
+ @check_target_timeout = 1
402
+ super()
403
+ end
404
+
405
+ def invalidate
406
+ sync_regs
407
+ @reg_val_cache.clear
408
+ super()
409
+ end
410
+
411
+ def get_reg_value(r)
412
+ return @reg_val_cache[r] || 0 if @state != :stopped
413
+ sync_regs
414
+ @reg_val_cache = @gdb.read_regs || {} if @reg_val_cache.empty?
415
+ @reg_val_cache[r] || 0
416
+ end
417
+ def set_reg_value(r, v)
418
+ @reg_val_cache[r] = v
419
+ @regs_dirty = true
420
+ end
421
+
422
+ def sync_regs
423
+ @gdb.send_regs(@reg_val_cache) if @regs_dirty and not @reg_val_cache.empty?
424
+ @regs_dirty = false
425
+ end
426
+
427
+ def do_check_target
428
+ return if @state == :dead
429
+ t = Time.now
430
+ @last_check_target ||= t
431
+ if @state == :running and t - @last_check_target > @check_target_timeout
432
+ @gdb.io.write '$?#' << @gdb.gdb_csum('?')
433
+ @last_check_target = t
434
+ end
435
+ return unless i = @gdb.check_target(0.01)
436
+ invalidate if i[:state] == :stopped and @state != :stopped
437
+ @state, @info = i[:state], i[:info]
438
+ @info = nil if @info =~ /TRAP/
439
+ end
440
+
441
+ def do_wait_target
442
+ return unless i = @gdb.check_target(nil)
443
+ invalidate if i[:state] == :stopped and @state != :stopped
444
+ @state, @info = i[:state], i[:info]
445
+ @info = nil if @info =~ /TRAP/
446
+ end
447
+
448
+ def do_continue(*a)
449
+ return if @state != :stopped
450
+ @state = :running
451
+ @info = 'continue'
452
+ @gdb.continue
453
+ @last_check_target = Time.now
454
+ end
455
+
456
+ def do_singlestep(*a)
457
+ return if @state != :stopped
458
+ @state = :running
459
+ @info = 'singlestep'
460
+ @gdb.singlestep
461
+ @last_check_target = Time.now
462
+ end
463
+
464
+ def break
465
+ @gdb.break
466
+ end
467
+
468
+ def kill(sig=nil)
469
+ # TODO signal nr
470
+ @gdb.kill
471
+ @state = :dead
472
+ @info = 'killed'
473
+ end
474
+
475
+ def detach
476
+ super() # remove breakpoints & stuff
477
+ @gdb.detach
478
+ @state = :dead
479
+ @info = 'detached'
480
+ end
481
+
482
+ # set to true to use the gdb msg to handle bpx, false to set 0xcc ourself
483
+ attr_accessor :gdb_bpx
484
+ def enable_bp(addr)
485
+ return if not b = @breakpoint[addr]
486
+ b.state = :active
487
+ case b.type
488
+ when :bpx
489
+ if gdb_bpx
490
+ @gdb.set_hwbp('s', addr, 1)
491
+ else
492
+ @cpu.dbg_enable_bp(self, addr, b)
493
+ end
494
+ when :hw
495
+ @gdb.set_hwbp(b.mtype, addr, b.mlen)
496
+ end
497
+ end
498
+
499
+ def disable_bp(addr)
500
+ return if not b = @breakpoint[addr]
501
+ b.state = :inactive
502
+ case b.type
503
+ when :bpx
504
+ if gdb_bpx
505
+ @gdb.unset_hwbp('s', addr, 1)
506
+ else
507
+ @cpu.dbg_disable_bp(self, addr, b)
508
+ end
509
+ when :hw
510
+ @gdb.unset_hwbp(b.mtype, addr, b.mlen)
511
+ end
512
+ end
513
+
514
+ def check_pre_run(*a)
515
+ sync_regs
516
+ super(*a)
517
+ end
518
+
519
+ def loadallsyms
520
+ puts 'loadallsyms unsupported'
521
+ end
522
+
523
+ def ui_command_setup(ui)
524
+ ui.new_command('monitor', 'send a remote command to run on the target') { |arg| @gdb.rcmd(arg) }
525
+ end
526
+ end
527
+ end