metasm 1.0.1 → 1.0.2

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 (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