metasm 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hgtags +3 -0
  4. data/Gemfile +1 -0
  5. data/INSTALL +61 -0
  6. data/LICENCE +458 -0
  7. data/README +29 -21
  8. data/Rakefile +10 -0
  9. data/TODO +10 -12
  10. data/doc/code_organisation.txt +2 -0
  11. data/doc/core/DynLdr.txt +247 -0
  12. data/doc/core/ExeFormat.txt +43 -0
  13. data/doc/core/Expression.txt +220 -0
  14. data/doc/core/GNUExports.txt +27 -0
  15. data/doc/core/Ia32.txt +236 -0
  16. data/doc/core/SerialStruct.txt +108 -0
  17. data/doc/core/VirtualString.txt +145 -0
  18. data/doc/core/WindowsExports.txt +61 -0
  19. data/doc/core/index.txt +1 -0
  20. data/doc/style.css +6 -3
  21. data/doc/usage/debugger.txt +327 -0
  22. data/doc/usage/index.txt +1 -0
  23. data/doc/use_cases.txt +2 -2
  24. data/metasm.gemspec +22 -0
  25. data/{lib/metasm.rb → metasm.rb} +11 -3
  26. data/{lib/metasm → metasm}/compile_c.rb +13 -7
  27. data/metasm/cpu/arc.rb +8 -0
  28. data/metasm/cpu/arc/decode.rb +425 -0
  29. data/metasm/cpu/arc/main.rb +191 -0
  30. data/metasm/cpu/arc/opcodes.rb +588 -0
  31. data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
  32. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  33. data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
  34. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  35. data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
  36. data/metasm/cpu/arm/opcodes.rb +324 -0
  37. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  38. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  39. data/metasm/cpu/arm64.rb +15 -0
  40. data/metasm/cpu/arm64/debug.rb +38 -0
  41. data/metasm/cpu/arm64/decode.rb +289 -0
  42. data/metasm/cpu/arm64/encode.rb +41 -0
  43. data/metasm/cpu/arm64/main.rb +105 -0
  44. data/metasm/cpu/arm64/opcodes.rb +232 -0
  45. data/metasm/cpu/arm64/parse.rb +20 -0
  46. data/metasm/cpu/arm64/render.rb +95 -0
  47. data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
  48. data/metasm/cpu/bpf/decode.rb +142 -0
  49. data/metasm/cpu/bpf/main.rb +60 -0
  50. data/metasm/cpu/bpf/opcodes.rb +81 -0
  51. data/metasm/cpu/bpf/render.rb +41 -0
  52. data/metasm/cpu/cy16.rb +9 -0
  53. data/metasm/cpu/cy16/decode.rb +253 -0
  54. data/metasm/cpu/cy16/main.rb +63 -0
  55. data/metasm/cpu/cy16/opcodes.rb +78 -0
  56. data/metasm/cpu/cy16/render.rb +41 -0
  57. data/metasm/cpu/dalvik.rb +11 -0
  58. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
  59. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
  60. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
  61. data/metasm/cpu/ia32.rb +17 -0
  62. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
  63. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
  64. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
  65. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
  66. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  67. data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
  68. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  69. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
  70. data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
  71. data/metasm/cpu/mips.rb +14 -0
  72. data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
  73. data/metasm/cpu/mips/debug.rb +42 -0
  74. data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
  75. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  76. data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
  77. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
  78. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  79. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  80. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  81. data/metasm/cpu/msp430/decode.rb +247 -0
  82. data/metasm/cpu/msp430/main.rb +62 -0
  83. data/metasm/cpu/msp430/opcodes.rb +101 -0
  84. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  85. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  86. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  87. data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
  88. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
  89. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  90. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  91. data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
  92. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
  93. data/metasm/cpu/ppc/parse.rb +55 -0
  94. data/metasm/cpu/python.rb +8 -0
  95. data/metasm/cpu/python/decode.rb +136 -0
  96. data/metasm/cpu/python/main.rb +36 -0
  97. data/metasm/cpu/python/opcodes.rb +180 -0
  98. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  99. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
  100. data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
  101. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  102. data/metasm/cpu/x86_64.rb +15 -0
  103. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
  104. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  105. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
  106. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
  107. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
  108. data/metasm/cpu/x86_64/opcodes.rb +136 -0
  109. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
  110. data/metasm/cpu/x86_64/render.rb +35 -0
  111. data/metasm/cpu/z80.rb +9 -0
  112. data/metasm/cpu/z80/decode.rb +313 -0
  113. data/metasm/cpu/z80/main.rb +67 -0
  114. data/metasm/cpu/z80/opcodes.rb +224 -0
  115. data/metasm/cpu/z80/render.rb +59 -0
  116. data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
  117. data/{lib/metasm → metasm}/decode.rb +35 -4
  118. data/{lib/metasm → metasm}/decompile.rb +15 -16
  119. data/{lib/metasm → metasm}/disassemble.rb +201 -45
  120. data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
  121. data/{lib/metasm → metasm}/dynldr.rb +220 -133
  122. data/{lib/metasm → metasm}/encode.rb +10 -1
  123. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  124. data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
  125. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  126. data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
  127. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
  128. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
  129. data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
  130. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  131. data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
  132. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
  133. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
  134. data/metasm/exe_format/gb.rb +65 -0
  135. data/metasm/exe_format/javaclass.rb +424 -0
  136. data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
  137. data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
  138. data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
  139. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  140. data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
  141. data/metasm/exe_format/pyc.rb +167 -0
  142. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  143. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  144. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  145. data/metasm/exe_format/swf.rb +205 -0
  146. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  147. data/metasm/exe_format/zip.rb +335 -0
  148. data/metasm/gui.rb +13 -0
  149. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  150. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  151. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
  152. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  153. data/metasm/gui/dasm_graph.rb +1695 -0
  154. data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
  155. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  156. data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
  157. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  158. data/{lib/metasm → metasm}/gui/debug.rb +93 -27
  159. data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
  160. data/{lib/metasm → metasm}/gui/qt.rb +12 -2
  161. data/{lib/metasm → metasm}/gui/win32.rb +179 -42
  162. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  163. data/{lib/metasm → metasm}/main.rb +389 -264
  164. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  165. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  166. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  167. data/metasm/os/main.rb +330 -0
  168. data/{lib/metasm → metasm}/os/windows.rb +132 -42
  169. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  170. data/{lib/metasm → metasm}/parse.rb +26 -24
  171. data/{lib/metasm → metasm}/parse_c.rb +221 -116
  172. data/{lib/metasm → metasm}/preprocessor.rb +55 -40
  173. data/{lib/metasm → metasm}/render.rb +14 -38
  174. data/misc/hexdump.rb +2 -1
  175. data/misc/lint.rb +58 -0
  176. data/misc/txt2html.rb +9 -7
  177. data/samples/bindiff.rb +3 -4
  178. data/samples/dasm-plugins/bindiff.rb +15 -0
  179. data/samples/dasm-plugins/bookmark.rb +133 -0
  180. data/samples/dasm-plugins/c_constants.rb +57 -0
  181. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  182. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  183. data/samples/dasm-plugins/dasm_all.rb +70 -0
  184. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  185. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  186. data/samples/dasm-plugins/dump_text.rb +35 -0
  187. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  188. data/samples/dasm-plugins/findgadget.rb +75 -0
  189. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  190. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  191. data/samples/dasm-plugins/imm2off.rb +34 -0
  192. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  193. data/samples/dasm-plugins/patch_file.rb +95 -0
  194. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  195. data/samples/dasm-plugins/scanxrefs.rb +26 -0
  196. data/samples/dasm-plugins/selfmodify.rb +197 -0
  197. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  198. data/samples/dasmnavig.rb +1 -1
  199. data/samples/dbg-apihook.rb +24 -9
  200. data/samples/dbg-plugins/heapscan.rb +283 -0
  201. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  202. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  203. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  204. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  205. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  206. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  207. data/samples/dbg-plugins/trace_func.rb +214 -0
  208. data/samples/disassemble-gui.rb +35 -5
  209. data/samples/disassemble.rb +31 -6
  210. data/samples/dump_upx.rb +24 -12
  211. data/samples/dynamic_ruby.rb +12 -3
  212. data/samples/exeencode.rb +6 -5
  213. data/samples/factorize-headers-peimports.rb +1 -1
  214. data/samples/lindebug.rb +175 -381
  215. data/samples/metasm-shell.rb +1 -2
  216. data/samples/peldr.rb +2 -2
  217. data/tests/all.rb +1 -1
  218. data/tests/arc.rb +26 -0
  219. data/tests/dynldr.rb +22 -4
  220. data/tests/expression.rb +55 -0
  221. data/tests/graph_layout.rb +285 -0
  222. data/tests/ia32.rb +79 -26
  223. data/tests/mips.rb +9 -2
  224. data/tests/x86_64.rb +66 -18
  225. metadata +330 -218
  226. data/lib/metasm/arm/opcodes.rb +0 -177
  227. data/lib/metasm/gui.rb +0 -23
  228. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  229. data/lib/metasm/ia32.rb +0 -14
  230. data/lib/metasm/ia32/opcodes.rb +0 -873
  231. data/lib/metasm/ppc/parse.rb +0 -52
  232. data/lib/metasm/x86_64.rb +0 -12
  233. data/lib/metasm/x86_64/opcodes.rb +0 -118
  234. data/samples/gdbclient.rb +0 -583
  235. data/samples/rubstop.rb +0 -399
@@ -5,6 +5,7 @@
5
5
 
6
6
 
7
7
  require 'metasm/os/main'
8
+ require 'metasm/debug'
8
9
  require 'socket'
9
10
 
10
11
  module Metasm
@@ -23,9 +24,11 @@ class GdbClient
23
24
  def gdb_send(cmd, buf='')
24
25
  buf = cmd + buf
25
26
  buf = '$' << buf << '#' << gdb_csum(buf)
27
+ log "gdb_send #{buf.inspect}" if $DEBUG
26
28
 
27
29
  5.times {
28
30
  @io.write buf
31
+ out = ''
29
32
  loop do
30
33
  break if not IO.select([@io], nil, nil, 0.2)
31
34
  raise Errno::EPIPE if not ack = @io.read(1)
@@ -33,12 +36,15 @@ class GdbClient
33
36
  when '+'
34
37
  return true
35
38
  when '-'
36
- puts "gdb_send: ack neg" if $DEBUG
39
+ log "gdb_send: ack neg" if $DEBUG
37
40
  break
38
41
  when nil
39
42
  return
43
+ else
44
+ out << ack
40
45
  end
41
46
  end
47
+ log "no ack, got #{out.inspect}" if out != ''
42
48
  }
43
49
 
44
50
  log "send error #{cmd.inspect} (no ack)"
@@ -62,8 +68,18 @@ class GdbClient
62
68
  buf = nil
63
69
 
64
70
  while @recv_ctx
65
- return unless IO.select([@io], nil, nil, timeout)
66
- raise Errno::EPIPE if not c = @io.read(1)
71
+ if !@recv_ctx[:rbuf]
72
+ return unless IO.select([@io], nil, nil, timeout)
73
+ if @io.kind_of?(UDPSocket)
74
+ raise Errno::EPIPE if not @recv_ctx[:rbuf] = @io.recvfrom(65536)[0]
75
+ else
76
+ raise Errno::EPIPE if not c = @io.read(1)
77
+ end
78
+ end
79
+ if @recv_ctx[:rbuf]
80
+ c = @recv_ctx[:rbuf].slice!(0, 1)
81
+ @recv_ctx.delete :rbuf if @recv_ctx[:rbuf] == ''
82
+ end
67
83
 
68
84
  case @recv_ctx[:state]
69
85
  when :nosync
@@ -107,11 +123,11 @@ class GdbClient
107
123
  end
108
124
  outstr << unhex($1)
109
125
  ret = gdb_readresp(timeout, outstr)
110
- outstr.split("\n").each { |e| log 'gdb: ' + e } if first
126
+ outstr.split("\n").each { |o| log 'gdb: ' + o } if first
111
127
  return ret
112
128
  end
113
129
 
114
- puts "gdb_readresp: got #{buf[0, 64].inspect}#{'...' if buf.length > 64}" if $DEBUG
130
+ log "gdb_readresp: got #{buf[0, 64].inspect}#{'...' if buf.length > 64}" if $DEBUG
115
131
  buf
116
132
  end
117
133
 
@@ -169,7 +185,7 @@ class GdbClient
169
185
  buf = gdb_msg('g')
170
186
  regs = unhex(unrle(buf)) if buf
171
187
  if not buf or regs.length < @regmsgsize
172
- raise "regs buffer recv is too short !"
188
+ raise "regs buffer recv is too short ! (#{regs.length} < #{@regmsgsize})"
173
189
  end
174
190
  end
175
191
  Hash[*@gdbregs.zip(@unpack_int[regs]).flatten]
@@ -232,9 +248,9 @@ class GdbClient
232
248
 
233
249
  case io
234
250
  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
251
+ when /^ser:(.*)/i; @io = File.open($1, 'rb+')
252
+ when /^udp:\[?(.*)\]?:(.*?)$/i; @io = UDPSocket.new ; @io.connect($1, $2)
253
+ when /^(?:tcp:)?\[?(..+)\]?:(.*?)$/i; @io = TCPSocket.open($1, $2)
238
254
  else raise "unknown target #{io.inspect}"
239
255
  end
240
256
 
@@ -242,6 +258,10 @@ class GdbClient
242
258
  end
243
259
 
244
260
  def gdb_setup
261
+ pnd = ''
262
+ pnd << @io.read(1) while IO.select([@io], nil, nil, 0.2)
263
+ log "startpending: #{pnd.inspect}" if pnd != ''
264
+
245
265
  gdb_msg('q', 'Supported')
246
266
  #gdb_msg('Hc', '-1')
247
267
  #gdb_msg('qC')
@@ -295,17 +315,22 @@ class GdbClient
295
315
 
296
316
  attr_accessor :logger, :quiet
297
317
  def log(s)
318
+ puts s if $DEBUG and logger
298
319
  return if quiet
299
- @logger ||= $stdout
300
- @logger.puts s
320
+ logger ? logger.log(s) : puts(s)
301
321
  end
302
322
 
303
323
 
324
+ attr_accessor :ptrsz
325
+
304
326
  # setup the various function used to pack ints & the reg list
305
327
  # according to a target CPU
306
328
  def setup_arch(cpu)
329
+ @ptrsz = cpu.size
330
+
307
331
  case cpu.shortname
308
- when 'ia32'
332
+ when /^ia32/
333
+ @ptrsz = 32
309
334
  @gdbregs = GDBREGS_IA32
310
335
  @regmsgsize = 4 * @gdbregs.length
311
336
  when 'x64'
@@ -314,6 +339,12 @@ class GdbClient
314
339
  when 'arm'
315
340
  @gdbregs = cpu.dbg_register_list
316
341
  @regmsgsize = 4 * @gdbregs.length
342
+ when 'arm64'
343
+ @gdbregs = cpu.dbg_register_list
344
+ @regmsgsize = 8 * @gdbregs.length
345
+ when 'mips'
346
+ @gdbregs = cpu.dbg_register_list
347
+ @regmsgsize = cpu.size/8 * @gdbregs.length
317
348
  else
318
349
  # we can still use readmem/kill and other generic commands
319
350
  # XXX serverside setregs may fail if we give an incorrect regbuf size
@@ -324,7 +355,7 @@ class GdbClient
324
355
 
325
356
  # yay life !
326
357
  # do as if cpu is littleendian, fixup at the end
327
- case cpu.size
358
+ case @ptrsz
328
359
  when 16
329
360
  @pack_netint = lambda { |i| i.pack('n*') }
330
361
  @unpack_netint = lambda { |s| s.unpack('n*') }
@@ -345,7 +376,7 @@ class GdbClient
345
376
  @pack_netint, @pack_int = @pack_int, @pack_netint
346
377
  @unpack_netint, @unpack_int = @unpack_int, @unpack_netint
347
378
  end
348
- else raise "GdbServer: unsupported cpu size #{cpu.size}"
379
+ else raise "GdbServer: unsupported cpu size #{@ptrsz}"
349
380
  end
350
381
 
351
382
  # if target cpu is bigendian, use netint everywhere
@@ -362,7 +393,7 @@ class GdbRemoteString < VirtualString
362
393
 
363
394
  def initialize(gdb, addr_start=0, length=nil)
364
395
  @gdb = gdb
365
- length ||= 1 << (@gdb.cpu.size rescue 32)
396
+ length ||= 1 << (@gdb.ptrsz || 32)
366
397
  @pagelength = 512
367
398
  super(addr_start, length)
368
399
  end
@@ -389,17 +420,55 @@ end
389
420
 
390
421
  # this class implements a high-level API using the gdb-server network debugging protocol
391
422
  class GdbRemoteDebugger < Debugger
392
- attr_accessor :gdb, :check_target_timeout
423
+ attr_accessor :gdb, :check_target_timeout, :reg_val_cache
393
424
  def initialize(url, cpu='Ia32')
425
+ super()
426
+ @tid_stuff_list << :reg_val_cache << :regs_dirty
394
427
  @gdb = GdbClient.new(url, cpu)
395
428
  @gdb.logger = self
396
- @cpu = @gdb.cpu
397
- @memory = GdbRemoteString.new(@gdb)
398
- @reg_val_cache = {}
399
- @regs_dirty = false
400
429
  # when checking target, if no message seen since this much seconds, send a 'status' query
401
430
  @check_target_timeout = 1
431
+ set_context(28, 28)
432
+ end
433
+
434
+ def check_pid(pid)
435
+ # return nil if pid == nil
436
+ pid
437
+ end
438
+ def check_tid(tid)
439
+ tid
440
+ end
441
+
442
+ def list_processes
443
+ [@pid].compact
444
+ end
445
+ def list_threads
446
+ [@tid].compact
447
+ end
448
+
449
+ def mappings
450
+ []
451
+ end
452
+
453
+ def modules
454
+ []
455
+ end
456
+
457
+
458
+ def initialize_newtid
402
459
  super()
460
+ @reg_val_cache = {}
461
+ @regs_dirty = false
462
+ end
463
+
464
+ attr_accessor :realmode
465
+ def initialize_cpu
466
+ @cpu = @gdb.cpu
467
+ @realmode = true if @cpu and @cpu.shortname =~ /^ia32_16/
468
+ end
469
+
470
+ def initialize_memory
471
+ @memory = GdbRemoteString.new(@gdb)
403
472
  end
404
473
 
405
474
  def invalidate
@@ -409,12 +478,24 @@ class GdbRemoteDebugger < Debugger
409
478
  end
410
479
 
411
480
  def get_reg_value(r)
481
+ r = r.to_sym
412
482
  return @reg_val_cache[r] || 0 if @state != :stopped
413
483
  sync_regs
414
484
  @reg_val_cache = @gdb.read_regs || {} if @reg_val_cache.empty?
485
+ if realmode
486
+ case r
487
+ when :eip; seg = :cs
488
+ when :esp; seg = :ss
489
+ else seg = :ds
490
+ end
491
+ # XXX seg override
492
+ return @reg_val_cache[seg].to_i*16 + @reg_val_cache[r].to_i
493
+ end
415
494
  @reg_val_cache[r] || 0
416
495
  end
417
496
  def set_reg_value(r, v)
497
+ r = r.to_sym
498
+ # XXX realmode
418
499
  @reg_val_cache[r] = v
419
500
  @regs_dirty = true
420
501
  end
@@ -426,37 +507,49 @@ class GdbRemoteDebugger < Debugger
426
507
 
427
508
  def do_check_target
428
509
  return if @state == :dead
510
+
511
+ # keep-alive on the connexion
429
512
  t = Time.now
430
513
  @last_check_target ||= t
431
514
  if @state == :running and t - @last_check_target > @check_target_timeout
432
515
  @gdb.io.write '$?#' << @gdb.gdb_csum('?')
433
516
  @last_check_target = t
434
517
  end
518
+
435
519
  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/
520
+ update_state(i)
521
+ true
439
522
  end
440
523
 
441
524
  def do_wait_target
442
525
  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/
526
+ update_state(i)
527
+ end
528
+
529
+ def update_state(i)
530
+ @info = (i[:info] if i[:info] !~ /TRAP/)
531
+ if i[:state] == :stopped and @state != :stopped
532
+ invalidate
533
+ @state = i[:state]
534
+ case @run_method
535
+ when :singlestep
536
+ evt_singlestep
537
+ else
538
+ evt_bpx # XXX evt_hwbp?
539
+ end
540
+ else
541
+ @state = i[:state]
542
+ end
446
543
  end
447
544
 
448
545
  def do_continue(*a)
449
- return if @state != :stopped
450
546
  @state = :running
451
- @info = 'continue'
452
547
  @gdb.continue
453
548
  @last_check_target = Time.now
454
549
  end
455
550
 
456
551
  def do_singlestep(*a)
457
- return if @state != :stopped
458
552
  @state = :running
459
- @info = 'singlestep'
460
553
  @gdb.singlestep
461
554
  @last_check_target = Time.now
462
555
  end
@@ -467,53 +560,52 @@ class GdbRemoteDebugger < Debugger
467
560
 
468
561
  def kill(sig=nil)
469
562
  # TODO signal nr
470
- @gdb.kill
471
563
  @state = :dead
472
- @info = 'killed'
564
+ @gdb.kill
473
565
  end
474
566
 
475
567
  def detach
476
- super() # remove breakpoints & stuff
477
- @gdb.detach
478
- @state = :dead
479
- @info = 'detached'
568
+ del_all_breakpoints
569
+ del_pid
480
570
  end
481
-
482
- # set to true to use the gdb msg to handle bpx, false to set 0xcc ourself
571
+
572
+ # set to true to use the gdb msg to handle bpx, false to set 0xcc manually ourself
483
573
  attr_accessor :gdb_bpx
484
- def enable_bp(addr)
485
- return if not b = @breakpoint[addr]
486
- b.state = :active
574
+ def do_enable_bp(b)
487
575
  case b.type
576
+ when :bpm
577
+ do_enable_bpm(b)
488
578
  when :bpx
489
579
  if gdb_bpx
490
- @gdb.set_hwbp('s', addr, 1)
580
+ @gdb.set_hwbp('s', b.address, 1)
491
581
  else
492
- @cpu.dbg_enable_bp(self, addr, b)
582
+ @cpu.dbg_enable_bp(self, b)
493
583
  end
494
- when :hw
495
- @gdb.set_hwbp(b.mtype, addr, b.mlen)
584
+ when :hwbp
585
+ @gdb.set_hwbp(b.internal[:type], b.address, b.internal[:len])
496
586
  end
497
587
  end
498
588
 
499
- def disable_bp(addr)
500
- return if not b = @breakpoint[addr]
501
- b.state = :inactive
589
+ def do_disable_bp(b)
502
590
  case b.type
591
+ when :bpm
592
+ do_disable_bpm(b)
503
593
  when :bpx
504
594
  if gdb_bpx
505
- @gdb.unset_hwbp('s', addr, 1)
595
+ @gdb.unset_hwbp('s', b.address, 1)
506
596
  else
507
- @cpu.dbg_disable_bp(self, addr, b)
597
+ @cpu.dbg_disable_bp(self, b)
508
598
  end
509
- when :hw
510
- @gdb.unset_hwbp(b.mtype, addr, b.mlen)
599
+ when :hwbp
600
+ @gdb.unset_hwbp(b.internal[:type], b.address, b.internal[:len])
511
601
  end
512
602
  end
513
603
 
514
604
  def check_pre_run(*a)
515
- sync_regs
516
- super(*a)
605
+ if ret = super(*a)
606
+ sync_regs
607
+ ret
608
+ end
517
609
  end
518
610
 
519
611
  def loadallsyms
@@ -258,7 +258,7 @@ libruby1.8.so.1.8
258
258
  ruby_current_node ruby_debug ruby_description ruby_digitmap ruby_dln_librefs ruby_dyna_vars ruby_errinfo ruby_eval_tree ruby_eval_tree_begin ruby_frame
259
259
  ruby_gc_stress ruby_ignorecase ruby_in_compile ruby_in_eval ruby_inplace_mode ruby_nerrs ruby_patchlevel ruby_platform ruby_release_date ruby_safe_level
260
260
  ruby_sandbox_restore ruby_sandbox_save ruby_scope ruby_sourcefile ruby_sourceline ruby_top_cref ruby_top_self ruby_verbose ruby_version ruby_yychar
261
- ruby_yydebug ruby_yylval
261
+ ruby_yydebug ruby_yylval rb_float_new_in_heap
262
262
  EOL
263
263
  curlibname = nil
264
264
  data.each_line { |l|
@@ -5,6 +5,7 @@
5
5
 
6
6
 
7
7
  require 'metasm/os/main'
8
+ require 'metasm/debug'
8
9
 
9
10
  module Metasm
10
11
  class PTrace
@@ -13,9 +14,11 @@ class PTrace
13
14
  def self.open(target)
14
15
  ptrace = new(target)
15
16
  return ptrace if not block_given?
16
- ret = yield ptrace
17
- ptrace.detach
18
- ret
17
+ begin
18
+ yield ptrace
19
+ ensure
20
+ ptrace.detach
21
+ end
19
22
  end
20
23
 
21
24
  # calls PTRACE_TRACEME on the current (ruby) process
@@ -28,41 +31,65 @@ class PTrace
28
31
  # values for do_attach:
29
32
  # :create => always fork+traceme+exec+wait
30
33
  # :attach => always attach
31
- # false/nil => same as attach, without actually calling PT_ATTACH (usefull if we're already tracing pid)
32
- # anything else: try to attach if pid is numeric (using Integer()), else create
33
- def initialize(target, do_attach=true)
34
- begin
35
- raise ArgumentError if do_attach == :create
34
+ # false/nil => same as attach, without actually calling PT_ATTACH (useful when the ruby process is already tracing pid)
35
+ # default/anything else: try to attach if pid is numeric, else create
36
+ def initialize(target, do_attach=true, &b)
37
+ case do_attach
38
+ when :create
39
+ init_create(target, &b)
40
+ when :attach
41
+ init_attach(target)
42
+ when :dup
43
+ raise ArgumentError unless target.kind_of?(PTrace)
44
+ @pid = target.pid
45
+ tweak_for_pid(@pid, target.tgcpu) # avoid re-parsing /proc/self/exe
46
+ when nil, false
36
47
  @pid = Integer(target)
37
48
  tweak_for_pid(@pid)
38
- return if not do_attach
39
- attach
40
- rescue ArgumentError, TypeError
41
- raise if do_attach == :attach or not do_attach
42
- did_exec = true
43
- if not @pid = fork
44
- tweak_for_pid(::Process.pid)
45
- traceme
46
- ::Process.exec(*target)
47
- exit!(0)
48
- end
49
+ else
50
+ t = begin; Integer(target)
51
+ rescue ArgumentError, TypeError
52
+ end
53
+ t ? init_attach(t) : init_create(target, &b)
49
54
  end
55
+ end
56
+
57
+ def init_attach(target)
58
+ @pid = Integer(target)
59
+ tweak_for_pid(@pid)
60
+ attach
50
61
  wait
51
- raise "could not exec #{target}" if $?.exited?
52
- tweak_for_pid(@pid) if did_exec
53
62
  puts "Ptrace: attached to #@pid" if $DEBUG
54
63
  end
55
64
 
65
+ def init_create(target, &b)
66
+ if not @pid = ::Process.fork
67
+ tweak_for_pid(::Process.pid)
68
+ traceme
69
+ b.call if b
70
+ ::Process.exec(*target)
71
+ exit!(0)
72
+ end
73
+ wait
74
+ raise "could not exec #{target}" if $?.exited?
75
+ tweak_for_pid(@pid)
76
+ puts "Ptrace: attached to new #@pid" if $DEBUG
77
+ end
78
+
56
79
  def wait
57
80
  ::Process.wait(@pid, ::Process::WALL)
58
81
  end
59
82
 
60
83
  attr_accessor :reg_off, :intsize, :syscallnr, :syscallreg
61
84
  attr_accessor :packint, :packuint, :host_intsize, :host_syscallnr
62
- # setup the variables according to the target
63
- def tweak_for_pid(pid=@pid)
85
+ attr_accessor :tgcpu
86
+ @@sys_ptrace = {}
87
+
88
+ # setup variables according to the target (ptrace interface, syscall nrs, ...)
89
+ def tweak_for_pid(pid=@pid, tgcpu=nil)
64
90
  # use these for our syscalls PTRACE
65
- case LinOS.open_process(::Process.pid).cpu.shortname
91
+ @@host_csn ||= LinOS.open_process(::Process.pid).cpu.shortname
92
+ case @@host_csn
66
93
  when 'ia32'
67
94
  @packint = 'l'
68
95
  @packuint = 'L'
@@ -78,9 +105,9 @@ class PTrace
78
105
  else raise 'unsupported architecture'
79
106
  end
80
107
 
108
+ @tgcpu = tgcpu || LinOS.open_process(pid).cpu
81
109
  # use these to interpret the child state
82
- tgcpu = LinOS.open_process(pid).cpu
83
- case tgcpu.shortname
110
+ case @tgcpu.shortname
84
111
  when 'ia32'
85
112
  @syscallreg = 'ORIG_EAX'
86
113
  @syscallnr = SYSCALLNR_I386
@@ -92,13 +119,53 @@ class PTrace
92
119
  else raise 'unsupported target architecture'
93
120
  end
94
121
 
95
- cp = tgcpu.new_cparser
96
- cp.parse SIGINFO_C
97
- @siginfo = cp.alloc_c_struct('siginfo')
98
-
99
122
  # buffer used in ptrace syscalls
100
123
  @buf = [0].pack(@packint)
101
- @bufptr = str_ptr(@buf)
124
+
125
+ @sys_ptrace = @@sys_ptrace[@host_syscallnr['ptrace']] ||= setup_sys_ptrace(@host_syscallnr['ptrace'])
126
+ end
127
+
128
+ def setup_sys_ptrace(sysnr)
129
+ moo = Class.new(DynLdr)
130
+ case @@host_csn
131
+ when 'ia32'
132
+ # XXX compat lin2.4 ?
133
+ asm = <<EOS
134
+ #define off 3*4
135
+ push ebx
136
+ push esi
137
+ mov eax, #{sysnr}
138
+ mov ebx, [esp+off]
139
+ mov ecx, [esp+off+4]
140
+ mov edx, [esp+off+8]
141
+ mov esi, [esp+off+12]
142
+ call gs:[10h]
143
+ pop esi
144
+ pop ebx
145
+ ret
146
+ EOS
147
+ when 'x64'
148
+ asm = <<EOS
149
+ #define off 3*8
150
+ mov rax, #{sysnr}
151
+ //mov rdi, rdi
152
+ //mov rsi, rdi
153
+ //mov rdx, rdx
154
+ mov r10, rcx
155
+ syscall
156
+ ret
157
+ EOS
158
+ else raise 'unsupported target architecture'
159
+ end
160
+
161
+ moo.new_func_asm 'long ptrace(unsigned long, unsigned long, unsigned long, unsigned long)', asm
162
+ moo
163
+ end
164
+
165
+ def host_csn; @@host_csn end
166
+
167
+ def dup
168
+ self.class.new(self, :dup)
102
169
  end
103
170
 
104
171
  def str_ptr(str)
@@ -130,6 +197,7 @@ class PTrace
130
197
  end
131
198
 
132
199
  def writemem(off, str)
200
+ str.force_encoding('binary') if str.respond_to?(:force_encoding)
133
201
  decal = off % @host_intsize
134
202
  if decal > 0
135
203
  off -= decal
@@ -146,29 +214,30 @@ class PTrace
146
214
  pokedata(off+i, str[i, @host_intsize])
147
215
  i += @host_intsize
148
216
  end
217
+ true
149
218
  end
150
219
 
151
220
  # linux/ptrace.h
152
221
  COMMAND = {
153
- 'TRACEME' => 0, 'PEEKTEXT' => 1,
154
- 'PEEKDATA' => 2, 'PEEKUSR' => 3,
155
- 'POKETEXT' => 4, 'POKEDATA' => 5,
156
- 'POKEUSR' => 6, 'CONT' => 7,
157
- 'KILL' => 8, 'SINGLESTEP' => 9,
158
- 'ATTACH' => 16, 'DETACH' => 17,
159
- 'SYSCALL' => 24,
222
+ :TRACEME => 0, :PEEKTEXT => 1,
223
+ :PEEKDATA => 2, :PEEKUSR => 3,
224
+ :POKETEXT => 4, :POKEDATA => 5,
225
+ :POKEUSR => 6, :CONT => 7,
226
+ :KILL => 8, :SINGLESTEP => 9,
227
+ :ATTACH => 16, :DETACH => 17,
228
+ :SYSCALL => 24,
160
229
 
161
230
  # arch/x86/include/ptrace-abi.h
162
- 'GETREGS' => 12, 'SETREGS' => 13,
163
- 'GETFPREGS' => 14, 'SETFPREGS' => 15,
164
- 'GETFPXREGS' => 18, 'SETFPXREGS' => 19,
165
- 'OLDSETOPTIONS' => 21, 'GET_THREAD_AREA' => 25,
166
- 'SET_THREAD_AREA' => 26, 'ARCH_PRCTL' => 30,
167
- 'SYSEMU' => 31, 'SYSEMU_SINGLESTEP'=> 32,
168
- 'SINGLEBLOCK' => 33,
231
+ :GETREGS => 12, :SETREGS => 13,
232
+ :GETFPREGS => 14, :SETFPREGS => 15,
233
+ :GETFPXREGS => 18, :SETFPXREGS => 19,
234
+ :OLDSETOPTIONS => 21, :GET_THREAD_AREA => 25,
235
+ :SET_THREAD_AREA => 26, :ARCH_PRCTL => 30,
236
+ :SYSEMU => 31, :SYSEMU_SINGLESTEP=> 32,
237
+ :SINGLEBLOCK => 33,
169
238
  # 0x4200-0x4300 are reserved for architecture-independent additions.
170
- 'SETOPTIONS' => 0x4200, 'GETEVENTMSG' => 0x4201,
171
- 'GETSIGINFO' => 0x4202, 'SETSIGINFO' => 0x4203
239
+ :SETOPTIONS => 0x4200, :GETEVENTMSG => 0x4201,
240
+ :GETSIGINFO => 0x4202, :SETSIGINFO => 0x4203
172
241
  }
173
242
 
174
243
  OPTIONS = {
@@ -176,14 +245,15 @@ class PTrace
176
245
  'TRACESYSGOOD' => 0x01, 'TRACEFORK' => 0x02,
177
246
  'TRACEVFORK' => 0x04, 'TRACECLONE' => 0x08,
178
247
  'TRACEEXEC' => 0x10, 'TRACEVFORKDONE'=> 0x20,
179
- 'TRACEEXIT' => 0x40
248
+ 'TRACEEXIT' => 0x40, 'TRACESECCOMP' => 0x80,
180
249
  }
181
250
 
182
251
  WAIT_EXTENDEDRESULT = {
183
252
  # Wait extended result codes for the above trace options.
184
253
  'EVENT_FORK' => 1, 'EVENT_VFORK' => 2,
185
254
  'EVENT_CLONE' => 3, 'EVENT_EXEC' => 4,
186
- 'EVENT_VFORK_DONE' => 5, 'EVENT_EXIT' => 6
255
+ 'EVENT_VFORK_DONE' => 5, 'EVENT_EXIT' => 6,
256
+ 'EVENT_SECCOMP' => 7,
187
257
  }
188
258
  WAIT_EXTENDEDRESULT.update WAIT_EXTENDEDRESULT.invert
189
259
 
@@ -261,10 +331,10 @@ class PTrace
261
331
  mknodat fchownat futimesat fstatat64 unlinkat renameat linkat symlinkat readlinkat fchmodat
262
332
  faccessat pselect6 ppoll unshare set_robust_list get_robust_list splice sync_file_range tee vmsplice
263
333
  move_pages getcpu epoll_pwait utimensat signalfd timerfd eventfd fallocate timerfd_settime
264
- timerfd_gettime signalfd4 eventfd2 epoll_create1 dup3 pipe2 inotify_init1 preadv pwritev
334
+ timerfd_gettime signalfd4 eventfd2 epoll_create1 dup3 pipe2 inotify_init1 preadv pwritev
265
335
  rt_tg_sigqueueinfo perf_counter_open].inject({}) { |h, sc| h.update sc => h.length }
266
336
  SYSCALLNR_I386.update SYSCALLNR_I386.invert
267
-
337
+
268
338
  SYSCALLNR_X86_64 = %w[read write open close stat fstat lstat poll lseek mmap mprotect munmap brk rt_sigaction
269
339
  rt_sigprocmask rt_sigreturn ioctl pread64 pwrite64 readv writev access pipe select sched_yield
270
340
  mremap msync mincore madvise shmget shmat shmctl dup dup2 pause nanosleep getitimer alarm
@@ -341,7 +411,7 @@ typedef unsigned __int32 __uid_t;
341
411
  typedef uintptr_t sigval_t;
342
412
  typedef long __clock_t;
343
413
 
344
- typedef struct siginfo {
414
+ struct siginfo {
345
415
  int si_signo;
346
416
  int si_errno;
347
417
  int si_code;
@@ -377,138 +447,162 @@ typedef struct siginfo {
377
447
  long int si_band; /* Band event for SIGPOLL. */
378
448
  int si_fd;
379
449
  } _sigpoll;
450
+ struct { /* SIGSYS under SECCOMP */
451
+ uintptr_t si_calladdr; /* calling insn address */
452
+ int si_syscall; /* triggering syscall nr */
453
+ int si_arch; /* AUDIT_ARCH_* for syscall */
454
+ } _sigsys;
380
455
  };
381
- } siginfo_t;
456
+ };
382
457
  EOS
383
458
 
384
459
  def sys_ptrace(req, pid, addr, data)
385
- data = str_ptr(data) if data.kind_of?(String)
386
- addr = [addr].pack(@packint).unpack(@packint).first
387
- data = [data].pack(@packint).unpack(@packint).first
388
- Kernel.syscall(@host_syscallnr['ptrace'], req, pid, addr, data)
460
+ ret = @sys_ptrace.ptrace(req, pid, addr, data)
461
+ if ret < 0 and ret > -256
462
+ raise SystemCallError.new("ptrace #{COMMAND.index(req) || req}", -ret)
463
+ end
464
+ ret
389
465
  end
390
466
 
391
467
  def traceme
392
- sys_ptrace(COMMAND['TRACEME'], 0, 0, 0)
468
+ sys_ptrace(COMMAND[:TRACEME], 0, 0, 0)
393
469
  end
394
470
 
395
471
  def peektext(addr)
396
- sys_ptrace(COMMAND['PEEKTEXT'], @pid, addr, @bufptr)
472
+ sys_ptrace(COMMAND[:PEEKTEXT], @pid, addr, @buf)
397
473
  @buf
398
474
  end
399
475
 
400
476
  def peekdata(addr)
401
- sys_ptrace(COMMAND['PEEKDATA'], @pid, addr, @bufptr)
477
+ sys_ptrace(COMMAND[:PEEKDATA], @pid, addr, @buf)
402
478
  @buf
403
479
  end
404
480
 
405
481
  def peekusr(addr)
406
- sys_ptrace(COMMAND['PEEKUSR'], @pid, @host_intsize*addr, @bufptr)
407
- bufval & ((1 << ([@host_intsize, @intsize].min*8)) - 1)
482
+ sys_ptrace(COMMAND[:PEEKUSR], @pid, @host_intsize*addr, @buf)
483
+ @peekmask ||= (1 << ([@host_intsize, @intsize].min*8)) - 1
484
+ bufval & @peekmask
408
485
  end
409
486
 
410
487
  def poketext(addr, data)
411
- sys_ptrace(COMMAND['POKETEXT'], @pid, addr, data.unpack(@packint).first)
488
+ sys_ptrace(COMMAND[:POKETEXT], @pid, addr, data.unpack(@packint).first)
412
489
  end
413
490
 
414
491
  def pokedata(addr, data)
415
- sys_ptrace(COMMAND['POKEDATA'], @pid, addr, data.unpack(@packint).first)
492
+ sys_ptrace(COMMAND[:POKEDATA], @pid, addr, data.unpack(@packint).first)
416
493
  end
417
494
 
418
495
  def pokeusr(addr, data)
419
- sys_ptrace(COMMAND['POKEUSR'], @pid, @host_intsize*addr, data)
496
+ sys_ptrace(COMMAND[:POKEUSR], @pid, @host_intsize*addr, data)
420
497
  end
421
498
 
422
499
  def getregs(buf=nil)
500
+ buf = buf.str if buf.respond_to?(:str) # AllocCStruct
423
501
  buf ||= [0].pack('C')*512
424
- sys_ptrace(COMMAND['GETREGS'], @pid, 0, buf)
502
+ sys_ptrace(COMMAND[:GETREGS], @pid, 0, buf)
425
503
  buf
426
504
  end
427
505
  def setregs(buf)
428
- sys_ptrace(COMMAND['SETREGS'], @pid, 0, buf)
506
+ buf = buf.str if buf.respond_to?(:str)
507
+ sys_ptrace(COMMAND[:SETREGS], @pid, 0, buf)
429
508
  end
430
509
 
431
510
  def getfpregs(buf=nil)
511
+ buf = buf.str if buf.respond_to?(:str)
432
512
  buf ||= [0].pack('C')*1024
433
- sys_ptrace(COMMAND['GETFPREGS'], @pid, 0, buf)
513
+ sys_ptrace(COMMAND[:GETFPREGS], @pid, 0, buf)
434
514
  buf
435
515
  end
436
516
  def setfpregs(buf)
437
- sys_ptrace(COMMAND['SETFPREGS'], @pid, 0, buf)
517
+ buf = buf.str if buf.respond_to?(:str)
518
+ sys_ptrace(COMMAND[:SETFPREGS], @pid, 0, buf)
438
519
  end
439
520
 
440
521
  def getfpxregs(buf=nil)
522
+ buf = buf.str if buf.respond_to?(:str)
441
523
  buf ||= [0].pack('C')*512
442
- sys_ptrace(COMMAND['GETFPXREGS'], @pid, 0, buf)
524
+ sys_ptrace(COMMAND[:GETFPXREGS], @pid, 0, buf)
443
525
  buf
444
526
  end
445
527
  def setfpxregs(buf)
446
- sys_ptrace(COMMAND['SETFPXREGS'], @pid, 0, buf)
528
+ buf = buf.str if buf.respond_to?(:str)
529
+ sys_ptrace(COMMAND[:SETFPXREGS], @pid, 0, buf)
447
530
  end
448
531
 
449
532
  def get_thread_area(addr)
450
- sys_ptrace(COMMAND['GET_THREAD_AREA'], @pid, addr, @bufptr)
533
+ sys_ptrace(COMMAND[:GET_THREAD_AREA], @pid, addr, @buf)
451
534
  bufval
452
535
  end
453
536
  def set_thread_area(addr, data)
454
- sys_ptrace(COMMAND['SET_THREAD_AREA'], @pid, addr, data)
537
+ sys_ptrace(COMMAND[:SET_THREAD_AREA], @pid, addr, data)
455
538
  end
456
539
 
457
540
  def prctl(addr, data)
458
- sys_ptrace(COMMAND['ARCH_PRCTL'], @pid, addr, data)
541
+ sys_ptrace(COMMAND[:ARCH_PRCTL], @pid, addr, data)
459
542
  end
460
-
543
+
461
544
  def cont(sig = nil)
462
545
  sig ||= 0
463
- sys_ptrace(COMMAND['CONT'], @pid, 0, sig)
546
+ sys_ptrace(COMMAND[:CONT], @pid, 0, sig)
464
547
  end
465
548
 
466
549
  def kill
467
- sys_ptrace(COMMAND['KILL'], @pid, 0, 0)
550
+ sys_ptrace(COMMAND[:KILL], @pid, 0, 0)
468
551
  end
469
552
 
470
553
  def singlestep(sig = nil)
471
554
  sig ||= 0
472
- sys_ptrace(COMMAND['SINGLESTEP'], @pid, 0, sig)
555
+ sys_ptrace(COMMAND[:SINGLESTEP], @pid, 0, sig)
473
556
  end
474
557
 
475
558
  def singleblock(sig = nil)
476
559
  sig ||= 0
477
- sys_ptrace(COMMAND['SINGLEBLOCK'], @pid, 0, sig)
560
+ sys_ptrace(COMMAND[:SINGLEBLOCK], @pid, 0, sig)
478
561
  end
479
562
 
480
563
  def syscall(sig = nil)
481
564
  sig ||= 0
482
- sys_ptrace(COMMAND['SYSCALL'], @pid, 0, sig)
565
+ sys_ptrace(COMMAND[:SYSCALL], @pid, 0, sig)
483
566
  end
484
567
 
485
568
  def attach
486
- sys_ptrace(COMMAND['ATTACH'], @pid, 0, 0)
569
+ sys_ptrace(COMMAND[:ATTACH], @pid, 0, 0)
487
570
  end
488
571
 
489
572
  def detach
490
- sys_ptrace(COMMAND['DETACH'], @pid, 0, 0)
573
+ sys_ptrace(COMMAND[:DETACH], @pid, 0, 0)
491
574
  end
492
575
 
493
576
  def setoptions(*opt)
494
577
  opt = opt.inject(0) { |b, o| b |= o.kind_of?(Integer) ? o : OPTIONS[o] }
495
- sys_ptrace(COMMAND['SETOPTIONS'], @pid, 0, opt)
578
+ sys_ptrace(COMMAND[:SETOPTIONS], @pid, 0, opt)
496
579
  end
497
580
 
498
581
  # retrieve pid of cld for EVENT_CLONE/FORK, exitcode for EVENT_EXIT
499
582
  def geteventmsg
500
- sys_ptrace(COMMAND['GETEVENTMSG'], @pid, 0, @bufptr)
583
+ sys_ptrace(COMMAND[:GETEVENTMSG], @pid, 0, @buf)
501
584
  bufval
502
585
  end
503
586
 
587
+ def cp
588
+ @cp ||= @tgcpu.new_cparser
589
+ end
590
+
591
+ def siginfo
592
+ @siginfo ||= (
593
+ cp.parse SIGINFO_C if not cp.toplevel.struct['siginfo']
594
+ cp.alloc_c_struct('siginfo')
595
+ )
596
+ end
597
+
504
598
  def getsiginfo
505
- sys_ptrace(COMMAND['GETSIGINFO'], @pid, 0, @siginfo.str)
506
- @siginfo
599
+ sys_ptrace(COMMAND[:GETSIGINFO], @pid, 0, siginfo.str)
600
+ siginfo
507
601
  end
508
602
 
509
- def setsiginfo(si=@siginfo)
603
+ def setsiginfo(si=siginfo)
510
604
  si = si.str if si.respond_to?(:str)
511
- sys_ptrace(COMMAND['SETSIGINFO'], @pid, 0, si)
605
+ sys_ptrace(COMMAND[:SETSIGINFO], @pid, 0, si)
512
606
  end
513
607
  end
514
608
 
@@ -562,7 +656,7 @@ class LinOS < OS
562
656
  # read from /proc/pid/task/
563
657
  def threads
564
658
  Dir.entries("/proc/#{pid}/task/").grep(/^\d+$/).map { |tid| tid.to_i }
565
- rescue
659
+ rescue
566
660
  # TODO handle pthread stuff (eg 2.4 kernels)
567
661
  [pid]
568
662
  end
@@ -641,11 +735,29 @@ class LinuxRemoteString < VirtualString
641
735
  self.class.new(@pid, addr, len, dbg)
642
736
  end
643
737
 
644
- def do_ptrace
738
+ def do_ptrace(needproc)
645
739
  if dbg
646
740
  dbg.switch_context(@pid) {
647
- # XXX tid ?
648
- yield dbg.ptrace if dbg.state == :stopped
741
+ st = dbg.state
742
+ next if st != :stopped
743
+ if needproc
744
+ # we will try to access /proc/pid/mem
745
+ # if the main thread is still running, fallback to ptrace.readmem instead
746
+ pst = (dbg.tid == @pid ? st : dbg.tid_stuff[@pid][:state])
747
+ if pst != :stopped
748
+ savedreadfd = @readfd
749
+ @readfd = nil
750
+ begin
751
+ yield dbg.ptrace
752
+ ensure
753
+ @readfd = savedreadfd
754
+ end
755
+ else
756
+ yield dbg.ptrace
757
+ end
758
+ else
759
+ yield dbg.ptrace
760
+ end
649
761
  }
650
762
  else
651
763
  PTrace.open(@pid) { |ptrace| yield ptrace }
@@ -654,11 +766,12 @@ class LinuxRemoteString < VirtualString
654
766
 
655
767
  def rewrite_at(addr, data)
656
768
  # target must be stopped
657
- do_ptrace { |ptrace| ptrace.writemem(addr, data) }
769
+ wr = do_ptrace(false) { |ptrace| ptrace.writemem(addr, data) }
770
+ raise "couldn't ptrace_write at #{'%x' % addr}" if not wr
658
771
  end
659
772
 
660
773
  def get_page(addr, len=@pagelength)
661
- do_ptrace { |ptrace|
774
+ do_ptrace(true) { |ptrace|
662
775
  begin
663
776
  if readfd and addr < (1<<63)
664
777
  # 1<<63: ruby seek = 'too big to fit longlong', linux read = EINVAL
@@ -675,6 +788,305 @@ class LinuxRemoteString < VirtualString
675
788
  end
676
789
  end
677
790
 
791
+ class PTraceContext_Ia32 < PTrace
792
+ C_STRUCT = <<EOS
793
+ struct user_regs_struct_ia32 {
794
+ unsigned __int32 ebx;
795
+ unsigned __int32 ecx;
796
+ unsigned __int32 edx;
797
+ unsigned __int32 esi;
798
+ unsigned __int32 edi;
799
+ unsigned __int32 ebp;
800
+ unsigned __int32 eax;
801
+ unsigned __int32 ds;
802
+ unsigned __int32 es;
803
+ unsigned __int32 fs;
804
+ unsigned __int32 gs;
805
+ unsigned __int32 orig_eax;
806
+ unsigned __int32 eip;
807
+ unsigned __int32 cs;
808
+ unsigned __int32 eflags;
809
+ unsigned __int32 esp;
810
+ unsigned __int32 ss;
811
+ };
812
+
813
+ struct user_fxsr_struct_ia32 {
814
+ unsigned __int16 cwd;
815
+ unsigned __int16 swd;
816
+ unsigned __int16 twd;
817
+ unsigned __int16 fop;
818
+ unsigned __int32 fip;
819
+ unsigned __int32 fcs;
820
+ unsigned __int32 foo;
821
+ unsigned __int32 fos;
822
+ unsigned __int32 mxcsr;
823
+ unsigned __int32 reserved;
824
+ unsigned __int32 st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */
825
+ unsigned __int32 xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */
826
+ unsigned __int32 padding[56];
827
+ };
828
+ EOS
829
+
830
+ def initialize(ptrace, pid=ptrace.pid)
831
+ super(ptrace, :dup)
832
+ @pid = pid
833
+ @cp = ptrace.cp
834
+ init
835
+ end
836
+
837
+ def init
838
+ @gpr = @@gpr_ia32 ||= [:ebx, :ecx, :edx, :esi, :edi, :ebp, :eax,
839
+ :ds, :es, :fs, :gs, :orig_eax, :eip, :cs, :eflags,
840
+ :esp, :ss].inject({}) { |h, r| h.update r => true }
841
+ @gpr_peek = @@gpr_peek_ia32 ||= (0..7).inject({}) { |h, i|
842
+ h.update "dr#{i}".to_sym => REGS_I386["DR#{i}"] }
843
+ @gpr_sub = @@gpr_sub_ia32 ||= gpr_sub_init
844
+ @xmm = @@xmm_ia32 ||= [:cwd, :swd, :twd, :fop, :fip, :fcs, :foo,
845
+ :fos, :mxcsr].inject({}) { |h, r| h.update r => true }
846
+ @cp.parse C_STRUCT if not @cp.toplevel.struct['user_regs_struct_ia32']
847
+ @gpr_st = @xmm_st = nil
848
+ end
849
+
850
+ # :bh => [:ebx, 0xff, 8]
851
+ # XXX similar to Reg.symbolic... DRY
852
+ def gpr_sub_init
853
+ ret = {}
854
+ %w[a b c d].each { |r|
855
+ b = "e#{r}x".to_sym
856
+ ret["#{r}x".to_sym] = [b, 0xffff]
857
+ ret["#{r}l".to_sym] = [b, 0xff]
858
+ ret["#{r}h".to_sym] = [b, 0xff, 8]
859
+ }
860
+ %w[sp bp si di].each { |r|
861
+ b = "e#{r}".to_sym
862
+ ret[r.to_sym] = [b, 0xffff]
863
+ }
864
+ ret[:orig_rax] = [:orig_eax, 0xffff_ffff]
865
+ ret
866
+ end
867
+
868
+ def do_getregs
869
+ st = cp.alloc_c_struct('user_regs_struct_ia32')
870
+ getregs(st)
871
+ st
872
+ end
873
+
874
+ def do_setregs(st=@gpr_st)
875
+ setregs(st)
876
+ end
877
+
878
+ def do_getxmm
879
+ st = cp.alloc_c_struct('user_fxsr_struct_ia32')
880
+ getfpxregs(st)
881
+ st
882
+ end
883
+
884
+ def do_setxmm(st=@xmm_st)
885
+ setfpxregs(st)
886
+ end
887
+
888
+ def get_reg(r)
889
+ r = r.downcase if r == 'ORIG_EAX' or r == 'ORIG_RAX'
890
+ rs = r.to_sym
891
+ if @gpr[rs]
892
+ @gpr_st ||= do_getregs
893
+ @gpr_st[rs]
894
+ elsif o = @gpr_peek[rs]
895
+ peekusr(o)
896
+ elsif o = @gpr_sub[rs]
897
+ v = get_reg(o[0])
898
+ v >>= o[2] if o[2]
899
+ v &= o[1]
900
+ elsif @xmm[rs]
901
+ @xmm_st ||= do_getxmm
902
+ @xmm_st[rs]
903
+ else
904
+ case r.to_s
905
+ when /^st(\d?)$/i
906
+ i = $1.to_i
907
+ @xmm_st ||= do_getxmm
908
+ fu = @xmm_st.st_space
909
+ [fu[4*i], fu[4*i+1], fu[4*i+2]].pack('L*').unpack('D').first # XXX
910
+ when /^mmx?(\d)$/i
911
+ i = $1.to_i
912
+ @xmm_st ||= do_getxmm
913
+ fu = @xmm_st.st_space
914
+ fu[4*i] | (fu[4*i + 1] << 32)
915
+ when /^xmm(\d+)$/i
916
+ i = $1.to_i
917
+ @xmm_st ||= do_getxmm
918
+ fu = @xmm_st.xmm_space
919
+ fu[4*i] | (fu[4*i + 1] << 32) | (fu[4*i + 2] << 64) | (fu[4*i + 3] << 96)
920
+ # TODO when /^ymm(\d+)$/i
921
+ else raise "unknown register name #{r}"
922
+ end
923
+ end
924
+ end
925
+
926
+ def set_reg(r, v)
927
+ r = r.downcase if r == 'ORIG_EAX' or r == 'ORIG_RAX'
928
+ rs = r.to_sym
929
+ if @gpr[rs]
930
+ @gpr_st ||= do_getregs
931
+ @gpr_st[rs] = v
932
+ do_setregs
933
+ elsif o = @gpr_peek[rs]
934
+ pokeusr(o, v)
935
+ elsif o = @gpr_sub[rs]
936
+ vo = get_reg(o[0])
937
+ msk = o[1]
938
+ v &= o[1]
939
+ if o[2]
940
+ msk <<= o[2]
941
+ v <<= o[2]
942
+ end
943
+ v |= vo & ~msk
944
+ set_reg(o[0], v)
945
+ elsif @xmm[rs]
946
+ @xmm_st ||= do_getxmm
947
+ @xmm_st[rs] = v
948
+ do_setxmm
949
+ else
950
+ case r.to_s
951
+ when /^st(\d?)$/i
952
+ i = $1.to_i
953
+ @xmm_st ||= do_getxmm
954
+ fu = @xmm_st.st_space
955
+ fu[4*i], fu[4*i+1], fu[4*i+2] = [v, -1].pack('DL').unpack('L*') # XXX
956
+ do_setxmm
957
+ when /^mmx?(\d)$/i
958
+ i = $1.to_i
959
+ @xmm_st ||= do_getxmm
960
+ fu = @xmm_st.st_space
961
+ fu[4*i] = v & 0xffff_ffff
962
+ fu[4*i + 1] = (v >> 32) & 0xffff_ffff
963
+ do_setxmm
964
+ when /^xmm(\d+)$/i
965
+ i = $1.to_i
966
+ @xmm_st ||= do_getxmm
967
+ fu = @xmm_st.xmm_space
968
+ fu[4*i] = v & 0xffff_ffff
969
+ fu[4*i + 1] = (v >> 32) & 0xffff_ffff
970
+ fu[4*i + 2] = (v >> 64) & 0xffff_ffff
971
+ fu[4*i + 3] = (v >> 96) & 0xffff_ffff
972
+ do_setxmm
973
+ # TODO when /^ymm(\d+)$/i
974
+ else raise "unknown register name #{r}"
975
+ end
976
+ end
977
+ end
978
+ end
979
+
980
+ class PTraceContext_X64 < PTraceContext_Ia32
981
+ C_STRUCT = <<EOS
982
+ struct user_regs_struct_x64 {
983
+ unsigned __int64 r15;
984
+ unsigned __int64 r14;
985
+ unsigned __int64 r13;
986
+ unsigned __int64 r12;
987
+ unsigned __int64 rbp;
988
+ unsigned __int64 rbx;
989
+ unsigned __int64 r11;
990
+ unsigned __int64 r10;
991
+ unsigned __int64 r9;
992
+ unsigned __int64 r8;
993
+ unsigned __int64 rax;
994
+ unsigned __int64 rcx;
995
+ unsigned __int64 rdx;
996
+ unsigned __int64 rsi;
997
+ unsigned __int64 rdi;
998
+ unsigned __int64 orig_rax;
999
+ unsigned __int64 rip;
1000
+ unsigned __int64 cs;
1001
+ unsigned __int64 rflags;
1002
+ unsigned __int64 rsp;
1003
+ unsigned __int64 ss;
1004
+ unsigned __int64 fs_base;
1005
+ unsigned __int64 gs_base;
1006
+ unsigned __int64 ds;
1007
+ unsigned __int64 es;
1008
+ unsigned __int64 fs;
1009
+ unsigned __int64 gs;
1010
+ };
1011
+
1012
+ struct user_i387_struct_x64 {
1013
+ unsigned __int16 cwd;
1014
+ unsigned __int16 swd;
1015
+ unsigned __int16 twd; /* Note this is not the same as the 32bit/x87/FSAVE twd */
1016
+ unsigned __int16 fop;
1017
+ unsigned __int64 rip;
1018
+ unsigned __int64 rdp;
1019
+ unsigned __int32 mxcsr;
1020
+ unsigned __int32 mxcsr_mask;
1021
+ unsigned __int32 st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */
1022
+ unsigned __int32 xmm_space[64]; /* 16*16 bytes for each XMM-reg = 256 bytes */
1023
+ unsigned __int32 padding[24];
1024
+ // YMM ?
1025
+ };
1026
+ EOS
1027
+
1028
+ def init
1029
+ @gpr = @@gpr_x64 ||= [:r15, :r14, :r13, :r12, :rbp, :rbx, :r11,
1030
+ :r10, :r9, :r8, :rax, :rcx, :rdx, :rsi, :rdi, :orig_rax,
1031
+ :rip, :cs, :rflags, :rsp, :ss, :fs_base, :gs_base, :ds,
1032
+ :es, :fs, :gs].inject({}) { |h, r| h.update r => true }
1033
+ @gpr_peek = @@gpr_peek_x64 ||= (0..7).inject({}) { |h, i|
1034
+ h.update "dr#{i}".to_sym => REGS_X86_64["DR#{i}"] }
1035
+ @gpr_sub = @@gpr_sub_x64 ||= gpr_sub_init
1036
+ @xmm = @@xmm_x64 ||= [:cwd, :swd, :twd, :fop, :rip, :rdp, :mxcsr,
1037
+ :mxcsr_mask].inject({}) { |h, r| h.update r => true }
1038
+ @cp.parse C_STRUCT if not @cp.toplevel.struct['user_regs_struct_x64']
1039
+ @gpr_st = @xmm_st = nil
1040
+ end
1041
+
1042
+ def gpr_sub_init
1043
+ ret = {}
1044
+ %w[a b c d].each { |r|
1045
+ b = "r#{r}x".to_sym
1046
+ ret["e#{r}x".to_sym] = [b, 0xffff_ffff]
1047
+ ret[ "#{r}x".to_sym] = [b, 0xffff]
1048
+ ret[ "#{r}l".to_sym] = [b, 0xff]
1049
+ ret[ "#{r}h".to_sym] = [b, 0xff, 8]
1050
+ }
1051
+ %w[sp bp si di].each { |r|
1052
+ b = "r#{r}".to_sym
1053
+ ret["e#{r}".to_sym] = [b, 0xffff_ffff]
1054
+ ret[ "#{r}".to_sym] = [b, 0xffff]
1055
+ ret["#{r}l".to_sym] = [b, 0xff]
1056
+ }
1057
+ (8..15).each { |i|
1058
+ b = "r#{i}".to_sym
1059
+ ret["r#{i}d"] = [b, 0xffff_ffff]
1060
+ ret["r#{i}w"] = [b, 0xffff]
1061
+ ret["r#{i}b"] = [b, 0xff]
1062
+ }
1063
+ ret[:eip] = [:rip, 0xffff_ffff]
1064
+ ret[:eflags] = [:rflags, 0xffff_ffff]
1065
+ ret[:orig_eax] = [:orig_rax, 0xffff_ffff]
1066
+ ret
1067
+ end
1068
+
1069
+ def do_getregs
1070
+ st = cp.alloc_c_struct('user_regs_struct_x64')
1071
+ getregs(st)
1072
+ st
1073
+ end
1074
+
1075
+ def do_setregs(st=@gpr_st)
1076
+ setregs(st)
1077
+ end
1078
+
1079
+ def do_getxmm
1080
+ st = cp.alloc_c_struct('user_i387_struct_x64')
1081
+ getfpregs(st)
1082
+ st
1083
+ end
1084
+
1085
+ def do_setxmm(st=@xmm_st)
1086
+ setfpregs(st)
1087
+ end
1088
+ end
1089
+
678
1090
  module ::Process
679
1091
  WALL = 0x40000000 if not defined? WALL
680
1092
  WCLONE = 0x80000000 if not defined? WCLONE
@@ -683,10 +1095,10 @@ end
683
1095
  # this class implements a high-level API over the ptrace debugging primitives
684
1096
  class LinDebugger < Debugger
685
1097
  # ptrace is per-process or per-thread ?
686
- attr_accessor :ptrace, :continuesignal, :has_pax_mprotect, :target_syscall
1098
+ attr_accessor :ptrace, :continuesignal, :has_pax_mprotect, :target_syscall, :cached_waitpid
687
1099
  attr_accessor :callback_syscall, :callback_branch, :callback_exec
688
1100
 
689
- def initialize(pidpath=nil)
1101
+ def initialize(pidpath=nil, &b)
690
1102
  super()
691
1103
  @pid_stuff_list << :has_pax_mprotect << :ptrace << :breaking << :os_process
692
1104
  @tid_stuff_list << :continuesignal << :saved_csig << :ctx << :target_syscall
@@ -696,15 +1108,14 @@ class LinDebugger < Debugger
696
1108
 
697
1109
  @callback_syscall = lambda { |i| log "syscall #{i[:syscall]}" }
698
1110
  @callback_exec = lambda { |i| log "execve #{os_process.path}" }
1111
+ @cached_waitpid = []
699
1112
 
700
1113
  return if not pidpath
701
1114
 
702
- begin
703
- pid = Integer(pidpath)
704
- attach(pid)
705
- rescue ArgumentError
706
- create_process(pidpath)
707
- end
1115
+ t = begin; Integer(pidpath)
1116
+ rescue ArgumentError, TypeError
1117
+ end
1118
+ t ? attach(t) : create_process(pidpath, &b)
708
1119
  end
709
1120
 
710
1121
  def shortname; 'lindbg'; end
@@ -715,11 +1126,18 @@ class LinDebugger < Debugger
715
1126
  set_context(pt.pid, pt.pid) # swapout+init_newpid
716
1127
  log "attached #@pid"
717
1128
  list_threads.each { |tid| attach_thread(tid) if tid != @pid }
1129
+ set_tid @pid
718
1130
  end
719
1131
 
720
1132
  # create a process and debug it
721
- def create_process(path)
722
- pt = PTrace.new(path, :create)
1133
+ # if given a block, the block is run in the context of the ruby subprocess
1134
+ # after the fork() and before exec()ing the target binary
1135
+ # you can use it to eg tweak file descriptors:
1136
+ # tg_stdin_r, tg_stdin_w = IO.pipe
1137
+ # create_process('/bin/cat') { tg_stdin_w.close ; $stdin.reopen(tg_stdin_r) }
1138
+ # tg_stdin_w.write 'lol'
1139
+ def create_process(path, &b)
1140
+ pt = PTrace.new(path, :create, &b)
723
1141
  # TODO save path, allow restart etc
724
1142
  set_context(pt.pid, pt.pid) # swapout+init_newpid
725
1143
  log "attached #@pid"
@@ -727,10 +1145,10 @@ class LinDebugger < Debugger
727
1145
 
728
1146
  def initialize_cpu
729
1147
  @cpu = os_process.cpu
730
- # need to init @ptrace here, before init_dasm calls gui.swapin
1148
+ # need to init @ptrace here, before init_dasm calls gui.swapin XXX this stinks
731
1149
  @ptrace = PTrace.new(@pid, false)
732
1150
  if @cpu.size == 64 and @ptrace.reg_off['EAX']
733
- hack_64_32
1151
+ hack_x64_32
734
1152
  end
735
1153
  set_tid @pid
736
1154
  set_thread_options
@@ -764,19 +1182,18 @@ class LinDebugger < Debugger
764
1182
  os_process.modules
765
1183
  end
766
1184
 
767
- # we're a 32bit process debugging a 64bit target
1185
+ # We're a 32bit process debugging a 64bit target
768
1186
  # the ptrace kernel interface we use only allow us a 32bit-like target access
769
- # with this we advertize the cpu as having eax..edi registers (the only one we
1187
+ # With this we advertize the cpu as having eax..edi registers (the only one we
770
1188
  # can access), while still decoding x64 instructions (whose addr < 4G)
771
- def hack_64_32
1189
+ def hack_x64_32
772
1190
  log "WARNING: debugging a 64bit process from a 32bit debugger is a very bad idea !"
773
- @cpu.instance_eval {
774
- ia32 = Ia32.new
775
- @dbg_register_pc = ia32.dbg_register_pc
776
- @dbg_register_flags = ia32.dbg_register_flags
777
- @dbg_register_list = ia32.dbg_register_list
778
- @dbg_register_size = ia32.dbg_register_size
779
- }
1191
+ ia32 = Ia32.new
1192
+ @cpu.instance_variable_set('@dbg_register_pc', ia32.dbg_register_pc)
1193
+ @cpu.instance_variable_set('@dbg_register_sp', ia32.dbg_register_sp)
1194
+ @cpu.instance_variable_set('@dbg_register_flags', ia32.dbg_register_flags)
1195
+ @cpu.instance_variable_set('@dbg_register_list', ia32.dbg_register_list)
1196
+ @cpu.instance_variable_set('@dbg_register_size', ia32.dbg_register_size)
780
1197
  end
781
1198
 
782
1199
  # attach a thread of the current process
@@ -784,9 +1201,16 @@ class LinDebugger < Debugger
784
1201
  set_tid tid
785
1202
  @ptrace.pid = tid
786
1203
  @ptrace.attach
787
- @state = :stopped # no need to wait()
1204
+ @state = :stopped
1205
+ # store this waitpid so that we can return it in a future check_target
1206
+ ::Process.waitpid(tid, ::Process::WALL)
1207
+ # XXX can $? be safely stored?
1208
+ @cached_waitpid << [tid, $?.dup]
788
1209
  log "attached thread #{tid}"
789
1210
  set_thread_options
1211
+ rescue Errno::ESRCH
1212
+ # raced, thread quitted already
1213
+ del_tid
790
1214
  end
791
1215
 
792
1216
  # set the debugee ptrace options (notify clone/exec/exit, and fork/vfork depending on @trace_children)
@@ -807,27 +1231,23 @@ class LinDebugger < Debugger
807
1231
  super()
808
1232
  end
809
1233
 
810
- # a hash of the current thread context
811
- # TODO keys = :gpr, :fpu, :xmm, :dr ; val = AllocCStruct
812
- # include accessors for st0/xmm12 (@ptrace.getfpregs.unpack etc)
1234
+ # current thread register values accessor
813
1235
  def ctx
814
- @ctx ||= {}
1236
+ @ctx ||= case @ptrace.host_csn
1237
+ when 'ia32'; PTraceContext_Ia32.new(@ptrace, @tid)
1238
+ when 'x64'; PTraceContext_X64.new(@ptrace, @tid)
1239
+ else raise '8==D'
1240
+ end
815
1241
  end
816
1242
 
817
1243
  def get_reg_value(r)
818
- raise "bad register #{r}" if not k = @ptrace.reg_off[r.to_s.upcase]
819
- return ctx[r] || 0 if @state != :stopped
820
- @ptrace.pid = @tid
821
- ctx[r] ||= @ptrace.peekusr(k)
1244
+ return 0 if @state != :stopped
1245
+ ctx.get_reg(r)
822
1246
  rescue Errno::ESRCH
823
1247
  0
824
1248
  end
825
1249
  def set_reg_value(r, v)
826
- raise "bad register #{r}" if not k = @ptrace.reg_off[r.to_s.upcase]
827
- ctx[r] = v
828
- return if @state != :stopped
829
- @ptrace.pid = @tid
830
- @ptrace.pokeusr(k, v)
1250
+ ctx.set_reg(r, v)
831
1251
  end
832
1252
 
833
1253
  def update_waitpid(status)
@@ -851,14 +1271,14 @@ class LinDebugger < Debugger
851
1271
  end
852
1272
  elsif status.stopped?
853
1273
  sig = status.stopsig & 0x7f
854
- signame = PTrace::SIGNAL[sig]
1274
+ signame = PTrace::SIGNAL[sig]
855
1275
  if signame == 'TRAP'
856
1276
  if status.stopsig & 0x80 > 0
857
1277
  # XXX int80 in x64 => syscallnr32 ?
858
1278
  evt_syscall info.update(:syscall => @ptrace.syscallnr[get_reg_value(@ptrace.syscallreg)])
859
1279
 
860
1280
  elsif (status >> 16) > 0
861
- case o = PTrace::WAIT_EXTENDEDRESULT[status >> 16]
1281
+ case PTrace::WAIT_EXTENDEDRESULT[status >> 16]
862
1282
  when 'EVENT_FORK', 'EVENT_VFORK'
863
1283
  # parent notification of a fork
864
1284
  # child receives STOP (may have already happened)
@@ -870,6 +1290,7 @@ class LinDebugger < Debugger
870
1290
  resume_badbreak
871
1291
 
872
1292
  when 'EVENT_EXIT'
1293
+ @ptrace.pid = @tid
873
1294
  info.update :exitcode => @ptrace.geteventmsg
874
1295
  if @tid == @pid
875
1296
  evt_endprocess info
@@ -885,6 +1306,7 @@ class LinDebugger < Debugger
885
1306
  end
886
1307
 
887
1308
  else
1309
+ @ptrace.pid = @tid
888
1310
  si = @ptrace.getsiginfo
889
1311
  case si.si_code
890
1312
  when PTrace::SIGINFO['BRKPT'],
@@ -915,6 +1337,7 @@ class LinDebugger < Debugger
915
1337
  elsif signame == 'STOP' and @breaking
916
1338
  @state = :stopped
917
1339
  @info = 'break'
1340
+ @breaking.call if @breaking.kind_of? Proc
918
1341
  @breaking = nil
919
1342
 
920
1343
  else
@@ -923,6 +1346,7 @@ class LinDebugger < Debugger
923
1346
  if signame == 'SEGV'
924
1347
  # need more data on access violation (for bpm)
925
1348
  info.update :type => 'access violation'
1349
+ @ptrace.pid = @tid
926
1350
  si = @ptrace.getsiginfo
927
1351
  access = case si.si_code
928
1352
  when PTrace::SIGINFO['MAPERR']; :r # XXX write access to unmapped => ?
@@ -937,36 +1361,53 @@ class LinDebugger < Debugger
937
1361
  evt_exception info.update(:type => "unknown wait #{status.inspect}")
938
1362
  end
939
1363
  end
940
-
1364
+
941
1365
  def set_tid_findpid(tid)
942
1366
  return if tid == @tid
943
- if tid != @pid and pr = list_processes.find { |p| p.threads.include? tid }
944
- set_pid pr.pid
1367
+ if tid != @pid and !@tid_stuff[tid]
1368
+ if kv = @pid_stuff.find { |k, v| v[:tid_stuff] and v[:tid_stuff][tid] }
1369
+ set_pid kv[0]
1370
+ elsif pr = list_processes.find { |p| p.threads.include?(tid) }
1371
+ set_pid pr.pid
1372
+ end
945
1373
  end
946
1374
  set_tid tid
947
1375
  end
948
1376
 
949
1377
  def do_check_target
950
- return unless t = ::Process.waitpid(-1, ::Process::WNOHANG | ::Process::WALL)
951
- # XXX all threads may have stopped, wait them now ?
1378
+ if @cached_waitpid.empty?
1379
+ t = ::Process.waitpid(-1, ::Process::WNOHANG | ::Process::WALL)
1380
+ st = $?
1381
+ else
1382
+ t, st = @cached_waitpid.shift
1383
+ end
1384
+ return if not t
952
1385
  set_tid_findpid t
953
- update_waitpid $?
1386
+ update_waitpid st
1387
+ true
954
1388
  rescue ::Errno::ECHILD
955
1389
  end
956
1390
 
957
1391
  def do_wait_target
958
- t = ::Process.waitpid(-1, ::Process::WALL)
1392
+ if @cached_waitpid.empty?
1393
+ t = ::Process.waitpid(-1, ::Process::WALL)
1394
+ st = $?
1395
+ else
1396
+ t, st = @cached_waitpid.shift
1397
+ end
959
1398
  set_tid_findpid t
960
- update_waitpid $?
1399
+ update_waitpid st
961
1400
  rescue ::Errno::ECHILD
962
1401
  end
963
1402
 
964
1403
  def do_continue
1404
+ @state = :running
965
1405
  @ptrace.pid = tid
966
1406
  @ptrace.cont(@continuesignal)
967
1407
  end
968
1408
 
969
1409
  def do_singlestep(*a)
1410
+ @state = :running
970
1411
  @ptrace.pid = tid
971
1412
  @ptrace.singlestep(@continuesignal)
972
1413
  end
@@ -975,10 +1416,21 @@ class LinDebugger < Debugger
975
1416
  # regexp allowed to wait a specific syscall
976
1417
  def syscall(arg=nil)
977
1418
  arg = nil if arg and arg.strip == ''
978
- return if not check_pre_run(:syscall, arg)
979
- @target_syscall = arg
980
- @ptrace.pid = @tid
981
- @ptrace.syscall(@continuesignal)
1419
+ if b = check_breakpoint_cause and b.hash_shared.find { |bb| bb.state == :active }
1420
+ singlestep_bp(b) {
1421
+ next if not check_pre_run(:syscall, arg)
1422
+ @target_syscall = arg
1423
+ @state = :running
1424
+ @ptrace.pid = @tid
1425
+ @ptrace.syscall(@continuesignal)
1426
+ }
1427
+ else
1428
+ return if not check_pre_run(:syscall, arg)
1429
+ @target_syscall = arg
1430
+ @state = :running
1431
+ @ptrace.pid = @tid
1432
+ @ptrace.syscall(@continuesignal)
1433
+ end
982
1434
  end
983
1435
 
984
1436
  def syscall_wait(*a, &b)
@@ -990,9 +1442,19 @@ class LinDebugger < Debugger
990
1442
  def singleblock
991
1443
  # record as singlestep to avoid evt_singlestep -> evt_exception
992
1444
  # step or block doesn't matter much here anyway
993
- return if not check_pre_run(:singlestep)
994
- @ptrace.pid = @tid
995
- @ptrace.singleblock(@continuesignal)
1445
+ if b = check_breakpoint_cause and b.hash_shared.find { |bb| bb.state == :active }
1446
+ singlestep_bp(b) {
1447
+ next if not check_pre_run(:singlestep)
1448
+ @state = :running
1449
+ @ptrace.pid = @tid
1450
+ @ptrace.singleblock(@continuesignal)
1451
+ }
1452
+ else
1453
+ return if not check_pre_run(:singlestep)
1454
+ @state = :running
1455
+ @ptrace.pid = @tid
1456
+ @ptrace.singleblock(@continuesignal)
1457
+ end
996
1458
  end
997
1459
 
998
1460
  def singleblock_wait(*a, &b)
@@ -1034,8 +1496,8 @@ class LinDebugger < Debugger
1034
1496
  # calling continue() here will loop back to TRAP+INFO_EXEC
1035
1497
  end
1036
1498
 
1037
- def break
1038
- @breaking = true
1499
+ def break(&b)
1500
+ @breaking = b || true
1039
1501
  kill 'STOP'
1040
1502
  end
1041
1503
 
@@ -1059,7 +1521,7 @@ class LinDebugger < Debugger
1059
1521
  when nil, ''; 9
1060
1522
  when Integer; sig
1061
1523
  when String
1062
- sig = sig.upcase.sub(/^SIG_?/, '')
1524
+ sig = sig.upcase.sub(/^SIG_?/, '')
1063
1525
  PTrace::SIGNAL[sig] || Integer(sig)
1064
1526
  else raise "unhandled signal #{sig.inspect}"
1065
1527
  end
@@ -1067,10 +1529,25 @@ class LinDebugger < Debugger
1067
1529
 
1068
1530
  # stop debugging the current process
1069
1531
  def detach
1532
+ if @state == :running
1533
+ # must be stopped so we can rm bps
1534
+ self.break { detach }
1535
+ mypid = @pid
1536
+ wait_target
1537
+
1538
+ # after syscall(), wait will return once for interrupted syscall,
1539
+ # and we need to wait more for the break callback to kick in
1540
+ if @pid == mypid and @state == :stopped and @info =~ /syscall/
1541
+ do_continue
1542
+ check_target
1543
+ end
1544
+
1545
+ return
1546
+ end
1070
1547
  del_all_breakpoints
1071
1548
  each_tid {
1072
1549
  @ptrace.pid = @tid
1073
- @ptrace.detach
1550
+ @ptrace.detach rescue nil
1074
1551
  @delete_thread = true
1075
1552
  }
1076
1553
  del_pid