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