metasm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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