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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.hgtags +3 -0
- data/Gemfile +1 -0
- data/INSTALL +61 -0
- data/LICENCE +458 -0
- data/README +29 -21
- data/Rakefile +10 -0
- data/TODO +10 -12
- data/doc/code_organisation.txt +2 -0
- data/doc/core/DynLdr.txt +247 -0
- data/doc/core/ExeFormat.txt +43 -0
- data/doc/core/Expression.txt +220 -0
- data/doc/core/GNUExports.txt +27 -0
- data/doc/core/Ia32.txt +236 -0
- data/doc/core/SerialStruct.txt +108 -0
- data/doc/core/VirtualString.txt +145 -0
- data/doc/core/WindowsExports.txt +61 -0
- data/doc/core/index.txt +1 -0
- data/doc/style.css +6 -3
- data/doc/usage/debugger.txt +327 -0
- data/doc/usage/index.txt +1 -0
- data/doc/use_cases.txt +2 -2
- data/metasm.gemspec +22 -0
- data/{lib/metasm.rb → metasm.rb} +11 -3
- data/{lib/metasm → metasm}/compile_c.rb +13 -7
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +425 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
- data/metasm/cpu/arm/opcodes.rb +324 -0
- data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
- data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
- data/metasm/cpu/arm64.rb +15 -0
- data/metasm/cpu/arm64/debug.rb +38 -0
- data/metasm/cpu/arm64/decode.rb +289 -0
- data/metasm/cpu/arm64/encode.rb +41 -0
- data/metasm/cpu/arm64/main.rb +105 -0
- data/metasm/cpu/arm64/opcodes.rb +232 -0
- data/metasm/cpu/arm64/parse.rb +20 -0
- data/metasm/cpu/arm64/render.rb +95 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
- data/metasm/cpu/bpf/decode.rb +142 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +41 -0
- data/metasm/cpu/cy16.rb +9 -0
- data/metasm/cpu/cy16/decode.rb +253 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +41 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
- data/metasm/cpu/mips.rb +14 -0
- data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
- data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
- data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
- data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
- data/metasm/cpu/msp430/decode.rb +247 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
- data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
- data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
- data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
- data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
- data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
- data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +136 -0
- data/metasm/cpu/python/main.rb +36 -0
- data/metasm/cpu/python/opcodes.rb +180 -0
- data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
- data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
- data/metasm/cpu/x86_64/opcodes.rb +136 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +313 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +59 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
- data/{lib/metasm → metasm}/decode.rb +35 -4
- data/{lib/metasm → metasm}/decompile.rb +15 -16
- data/{lib/metasm → metasm}/disassemble.rb +201 -45
- data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
- data/{lib/metasm → metasm}/dynldr.rb +220 -133
- data/{lib/metasm → metasm}/encode.rb +10 -1
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
- data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
- data/metasm/exe_format/pyc.rb +167 -0
- data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
- data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
- data/metasm/exe_format/shellcode_rwx.rb +114 -0
- data/metasm/exe_format/swf.rb +205 -0
- data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
- data/metasm/exe_format/zip.rb +335 -0
- data/metasm/gui.rb +13 -0
- data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
- data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
- data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1695 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +93 -27
- data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
- data/{lib/metasm → metasm}/gui/qt.rb +12 -2
- data/{lib/metasm → metasm}/gui/win32.rb +179 -42
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +389 -264
- data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
- data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
- data/{lib/metasm → metasm}/os/linux.rb +628 -151
- data/metasm/os/main.rb +330 -0
- data/{lib/metasm → metasm}/os/windows.rb +132 -42
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +26 -24
- data/{lib/metasm → metasm}/parse_c.rb +221 -116
- data/{lib/metasm → metasm}/preprocessor.rb +55 -40
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +2 -1
- data/misc/lint.rb +58 -0
- data/misc/txt2html.rb +9 -7
- data/samples/bindiff.rb +3 -4
- data/samples/dasm-plugins/bindiff.rb +15 -0
- data/samples/dasm-plugins/bookmark.rb +133 -0
- data/samples/dasm-plugins/c_constants.rb +57 -0
- data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
- data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
- data/samples/dasm-plugins/dasm_all.rb +70 -0
- data/samples/dasm-plugins/demangle_cpp.rb +31 -0
- data/samples/dasm-plugins/deobfuscate.rb +251 -0
- data/samples/dasm-plugins/dump_text.rb +35 -0
- data/samples/dasm-plugins/export_graph_svg.rb +86 -0
- data/samples/dasm-plugins/findgadget.rb +75 -0
- data/samples/dasm-plugins/hl_opcode.rb +32 -0
- data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
- data/samples/dasm-plugins/imm2off.rb +34 -0
- data/samples/dasm-plugins/match_libsigs.rb +93 -0
- data/samples/dasm-plugins/patch_file.rb +95 -0
- data/samples/dasm-plugins/scanfuncstart.rb +36 -0
- data/samples/dasm-plugins/scanxrefs.rb +26 -0
- data/samples/dasm-plugins/selfmodify.rb +197 -0
- data/samples/dasm-plugins/stringsxrefs.rb +28 -0
- data/samples/dasmnavig.rb +1 -1
- data/samples/dbg-apihook.rb +24 -9
- data/samples/dbg-plugins/heapscan.rb +283 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
- data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
- data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
- data/samples/dbg-plugins/heapscan/winheap.h +174 -0
- data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
- data/samples/dbg-plugins/trace_func.rb +214 -0
- data/samples/disassemble-gui.rb +35 -5
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +12 -3
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +175 -381
- data/samples/metasm-shell.rb +1 -2
- data/samples/peldr.rb +2 -2
- data/tests/all.rb +1 -1
- data/tests/arc.rb +26 -0
- data/tests/dynldr.rb +22 -4
- data/tests/expression.rb +55 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +79 -26
- data/tests/mips.rb +9 -2
- data/tests/x86_64.rb +66 -18
- metadata +330 -218
- data/lib/metasm/arm/opcodes.rb +0 -177
- data/lib/metasm/gui.rb +0 -23
- data/lib/metasm/gui/dasm_graph.rb +0 -1354
- data/lib/metasm/ia32.rb +0 -14
- data/lib/metasm/ia32/opcodes.rb +0 -873
- data/lib/metasm/ppc/parse.rb +0 -52
- data/lib/metasm/x86_64.rb +0 -12
- data/lib/metasm/x86_64/opcodes.rb +0 -118
- data/samples/gdbclient.rb +0 -583
- 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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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 { |
|
|
126
|
+
outstr.split("\n").each { |o| log 'gdb: ' + o } if first
|
|
111
127
|
return ret
|
|
112
128
|
end
|
|
113
129
|
|
|
114
|
-
|
|
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 /^
|
|
236
|
-
when /^
|
|
237
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 #{
|
|
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.
|
|
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
|
-
|
|
437
|
-
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
@
|
|
564
|
+
@gdb.kill
|
|
473
565
|
end
|
|
474
566
|
|
|
475
567
|
def detach
|
|
476
|
-
|
|
477
|
-
|
|
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
|
|
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',
|
|
580
|
+
@gdb.set_hwbp('s', b.address, 1)
|
|
491
581
|
else
|
|
492
|
-
@cpu.dbg_enable_bp(self,
|
|
582
|
+
@cpu.dbg_enable_bp(self, b)
|
|
493
583
|
end
|
|
494
|
-
when :
|
|
495
|
-
@gdb.set_hwbp(b.
|
|
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
|
|
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',
|
|
595
|
+
@gdb.unset_hwbp('s', b.address, 1)
|
|
506
596
|
else
|
|
507
|
-
@cpu.dbg_disable_bp(self,
|
|
597
|
+
@cpu.dbg_disable_bp(self, b)
|
|
508
598
|
end
|
|
509
|
-
when :
|
|
510
|
-
@gdb.unset_hwbp(b.
|
|
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
|
-
|
|
516
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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 (
|
|
32
|
-
# anything else: try to attach if pid is numeric
|
|
33
|
-
def initialize(target, do_attach=true)
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
171
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
456
|
+
};
|
|
382
457
|
EOS
|
|
383
458
|
|
|
384
459
|
def sys_ptrace(req, pid, addr, data)
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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[
|
|
468
|
+
sys_ptrace(COMMAND[:TRACEME], 0, 0, 0)
|
|
393
469
|
end
|
|
394
470
|
|
|
395
471
|
def peektext(addr)
|
|
396
|
-
sys_ptrace(COMMAND[
|
|
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[
|
|
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[
|
|
407
|
-
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
502
|
+
sys_ptrace(COMMAND[:GETREGS], @pid, 0, buf)
|
|
425
503
|
buf
|
|
426
504
|
end
|
|
427
505
|
def setregs(buf)
|
|
428
|
-
|
|
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[
|
|
513
|
+
sys_ptrace(COMMAND[:GETFPREGS], @pid, 0, buf)
|
|
434
514
|
buf
|
|
435
515
|
end
|
|
436
516
|
def setfpregs(buf)
|
|
437
|
-
|
|
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[
|
|
524
|
+
sys_ptrace(COMMAND[:GETFPXREGS], @pid, 0, buf)
|
|
443
525
|
buf
|
|
444
526
|
end
|
|
445
527
|
def setfpxregs(buf)
|
|
446
|
-
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
546
|
+
sys_ptrace(COMMAND[:CONT], @pid, 0, sig)
|
|
464
547
|
end
|
|
465
548
|
|
|
466
549
|
def kill
|
|
467
|
-
sys_ptrace(COMMAND[
|
|
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[
|
|
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[
|
|
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[
|
|
565
|
+
sys_ptrace(COMMAND[:SYSCALL], @pid, 0, sig)
|
|
483
566
|
end
|
|
484
567
|
|
|
485
568
|
def attach
|
|
486
|
-
sys_ptrace(COMMAND[
|
|
569
|
+
sys_ptrace(COMMAND[:ATTACH], @pid, 0, 0)
|
|
487
570
|
end
|
|
488
571
|
|
|
489
572
|
def detach
|
|
490
|
-
sys_ptrace(COMMAND[
|
|
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[
|
|
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[
|
|
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[
|
|
506
|
-
|
|
599
|
+
sys_ptrace(COMMAND[:GETSIGINFO], @pid, 0, siginfo.str)
|
|
600
|
+
siginfo
|
|
507
601
|
end
|
|
508
602
|
|
|
509
|
-
def setsiginfo(si
|
|
603
|
+
def setsiginfo(si=siginfo)
|
|
510
604
|
si = si.str if si.respond_to?(:str)
|
|
511
|
-
sys_ptrace(COMMAND[
|
|
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
|
-
|
|
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
|
-
|
|
648
|
-
|
|
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
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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
|
-
|
|
722
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
|
1189
|
+
def hack_x64_32
|
|
772
1190
|
log "WARNING: debugging a 64bit process from a 32bit debugger is a very bad idea !"
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
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
|
|
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
|
-
#
|
|
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
|
-
|
|
819
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
944
|
-
|
|
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
|
-
|
|
951
|
-
|
|
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
|
-
|
|
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
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
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
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
-
|
|
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
|