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
data/metasm/os/main.rb
ADDED
@@ -0,0 +1,330 @@
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
3
|
+
#
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
5
|
+
|
6
|
+
require 'metasm/main'
|
7
|
+
|
8
|
+
module Metasm
|
9
|
+
# this module regroups OS-related functions
|
10
|
+
# (eg. find_process, inject_shellcode)
|
11
|
+
# a 'class' just to be able to inherit from it...
|
12
|
+
class OS
|
13
|
+
# represents a running process with a few information, and defines methods to get more interaction (#memory, #debugger)
|
14
|
+
class Process
|
15
|
+
attr_accessor :pid, :path, :modules
|
16
|
+
class Module
|
17
|
+
attr_accessor :path, :addr, :size
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(pid=nil)
|
21
|
+
@pid = pid
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
mod = File.basename(path) rescue nil
|
26
|
+
"#{pid}: ".ljust(6) << (mod || '<unknown>')
|
27
|
+
end
|
28
|
+
def inspect
|
29
|
+
'<Process:' + ["pid: #@pid", modules.to_a.map { |m| " #{'%X' % m.addr} #{m.path}" }].join("\n") + '>'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# returns the Process whose pid is name (if name is an Integer) or first module path includes name (string)
|
34
|
+
def self.find_process(name)
|
35
|
+
case name
|
36
|
+
when nil
|
37
|
+
when Integer
|
38
|
+
list_processes.find { |pr| pr.pid == name }
|
39
|
+
else
|
40
|
+
list_processes.find { |pr| pr.path.to_s.include? name.to_s } or
|
41
|
+
(find_process(Integer(name)) if name =~ /^(0x[0-9a-f]+|[0-9]+)$/i)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# create a new debuggee process stopped at start
|
46
|
+
def self.create_process(path)
|
47
|
+
dbg = create_debugger(path)
|
48
|
+
pr = open_process(dbg.pid)
|
49
|
+
pr.debugger = dbg
|
50
|
+
pr.memory = dbg.memory
|
51
|
+
pr
|
52
|
+
end
|
53
|
+
|
54
|
+
# return 'winos' or 'linos' depending on the underlying OS
|
55
|
+
def self.shortname
|
56
|
+
case RUBY_PLATFORM
|
57
|
+
when /mswin|mingw|cygwin/i; 'winos'
|
58
|
+
when /linux/i; 'linos'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# return the platform-specific version
|
63
|
+
def self.current
|
64
|
+
case shortname
|
65
|
+
when 'winos'; WinOS
|
66
|
+
when 'linos'; LinOS
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# This class implements an objects that behaves like a regular string, but
|
72
|
+
# whose real data is dynamically fetched or generated on demand
|
73
|
+
# its size is immutable
|
74
|
+
# implements a page cache
|
75
|
+
# substrings are Strings (small substring) or another VirtualString
|
76
|
+
# (a kind of 'window' on the original VString, when the substring length is > 4096)
|
77
|
+
class VirtualString
|
78
|
+
# formats parameters for reading
|
79
|
+
def [](from, len=nil)
|
80
|
+
if not len and from.kind_of? Range
|
81
|
+
b = from.begin
|
82
|
+
e = from.end
|
83
|
+
b = b + length if b < 0
|
84
|
+
e = e + length if e < 0
|
85
|
+
len = e - b
|
86
|
+
len += 1 if not from.exclude_end?
|
87
|
+
from = b
|
88
|
+
end
|
89
|
+
from = from + length if from < 0
|
90
|
+
|
91
|
+
return nil if from > length or (from == length and not len)
|
92
|
+
len = length - from if len and from + len > length
|
93
|
+
return '' if len == 0
|
94
|
+
|
95
|
+
read_range(from, len)
|
96
|
+
end
|
97
|
+
|
98
|
+
# formats parameters for overwriting portion of the string
|
99
|
+
def []=(from, len, val=nil)
|
100
|
+
raise TypeError, 'cannot modify frozen virtualstring' if frozen?
|
101
|
+
|
102
|
+
if not val
|
103
|
+
val = len
|
104
|
+
len = nil
|
105
|
+
end
|
106
|
+
if not len and from.kind_of? Range
|
107
|
+
b = from.begin
|
108
|
+
e = from.end
|
109
|
+
b = b + length if b < 0
|
110
|
+
e = e + length if e < 0
|
111
|
+
len = e - b
|
112
|
+
len += 1 if not from.exclude_end?
|
113
|
+
from = b
|
114
|
+
elsif not len
|
115
|
+
len = 1
|
116
|
+
val = val.chr
|
117
|
+
end
|
118
|
+
from = from + length if from < 0
|
119
|
+
|
120
|
+
raise IndexError, 'Index out of string' if from > length
|
121
|
+
raise IndexError, 'Cannot modify virtualstring length' if val.length != len or from + len > length
|
122
|
+
|
123
|
+
write_range(from, val)
|
124
|
+
end
|
125
|
+
|
126
|
+
# returns the full raw data
|
127
|
+
def realstring
|
128
|
+
ret = ''
|
129
|
+
addr = 0
|
130
|
+
len = length
|
131
|
+
while len > @pagelength
|
132
|
+
ret << self[addr, @pagelength]
|
133
|
+
addr += @pagelength
|
134
|
+
len -= @pagelength
|
135
|
+
end
|
136
|
+
ret << self[addr, len]
|
137
|
+
end
|
138
|
+
|
139
|
+
# alias to realstring
|
140
|
+
# for bad people checking respond_to? :to_str (like String#<<)
|
141
|
+
# XXX alias does not work (not virtual (a la C++))
|
142
|
+
def to_str
|
143
|
+
realstring
|
144
|
+
end
|
145
|
+
|
146
|
+
# forwards unhandled messages to a frozen realstring
|
147
|
+
def method_missing(m, *args, &b)
|
148
|
+
if ''.respond_to? m
|
149
|
+
puts "Using VirtualString.realstring for #{m} from:", caller if $DEBUG
|
150
|
+
realstring.freeze.send(m, *args, &b)
|
151
|
+
else
|
152
|
+
super(m, *args, &b)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# avoid triggering realstring from method_missing if possible
|
157
|
+
def empty?
|
158
|
+
length == 0
|
159
|
+
end
|
160
|
+
|
161
|
+
# avoid triggering realstring from method_missing if possible
|
162
|
+
# heavily used in to find 0-terminated strings in ExeFormats
|
163
|
+
def index(chr, base=0)
|
164
|
+
return if base >= length or base <= -length
|
165
|
+
if i = self[base, 64].index(chr) or i = self[base, @pagelength].index(chr)
|
166
|
+
base + i
|
167
|
+
else
|
168
|
+
realstring.index(chr, base)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def rindex(chr, max=length)
|
173
|
+
return if max > length
|
174
|
+
if max > 64 and i = self[max-64, 64].rindex(chr)
|
175
|
+
max - 64 + i
|
176
|
+
elsif max > @pagelength and i = self[max-@pagelength, @pagelength].rindex(chr)
|
177
|
+
max - @pagelength + i
|
178
|
+
else
|
179
|
+
realstring.rindex(chr, max)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# '=~' does not go through method_missing
|
184
|
+
def =~(o)
|
185
|
+
realstring =~ o
|
186
|
+
end
|
187
|
+
|
188
|
+
# implements a read page cache
|
189
|
+
|
190
|
+
# the real address of our first byte
|
191
|
+
attr_accessor :addr_start
|
192
|
+
# our length
|
193
|
+
attr_accessor :length
|
194
|
+
# array of [addr, raw data], sorted by first == last accessed
|
195
|
+
attr_accessor :pagecache
|
196
|
+
# maximum length of self.pagecache (number of cached pages)
|
197
|
+
attr_accessor :pagecache_len
|
198
|
+
def initialize(addr_start, length)
|
199
|
+
@addr_start = addr_start
|
200
|
+
@length = length
|
201
|
+
@pagecache = []
|
202
|
+
@pagecache_len = 4
|
203
|
+
@pagelength ||= 4096 # must be (1 << x)
|
204
|
+
end
|
205
|
+
|
206
|
+
# returns wether a page is valid or not
|
207
|
+
def page_invalid?(addr)
|
208
|
+
cache_get_page(@addr_start+addr)[2]
|
209
|
+
end
|
210
|
+
|
211
|
+
# invalidates the page cache
|
212
|
+
def invalidate
|
213
|
+
@pagecache.clear
|
214
|
+
end
|
215
|
+
|
216
|
+
# returns the @pagelength-bytes page starting at addr
|
217
|
+
# return nil if the page is invalid/inaccessible
|
218
|
+
# addr is page-aligned by the caller
|
219
|
+
# addr is absolute
|
220
|
+
#def get_page(addr, len=@pagelength)
|
221
|
+
#end
|
222
|
+
|
223
|
+
# searches the cache for a page containing addr, updates if not found
|
224
|
+
def cache_get_page(addr)
|
225
|
+
addr &= ~(@pagelength-1)
|
226
|
+
i = 0
|
227
|
+
@pagecache.each { |c|
|
228
|
+
if addr == c[0]
|
229
|
+
# most recently used first
|
230
|
+
@pagecache.unshift @pagecache.delete_at(i) if i != 0
|
231
|
+
return c
|
232
|
+
end
|
233
|
+
i += 1
|
234
|
+
}
|
235
|
+
@pagecache.pop if @pagecache.length >= @pagecache_len
|
236
|
+
c = [addr]
|
237
|
+
p = get_page(addr)
|
238
|
+
c << p.to_s.ljust(@pagelength, "\0")
|
239
|
+
c << true if not p
|
240
|
+
@pagecache.unshift c
|
241
|
+
c
|
242
|
+
end
|
243
|
+
|
244
|
+
# reads a range from the page cache
|
245
|
+
# returns a new VirtualString (using dup) if the request is bigger than @pagelength bytes
|
246
|
+
def read_range(from, len)
|
247
|
+
from += @addr_start
|
248
|
+
if not len
|
249
|
+
base, page = cache_get_page(from)
|
250
|
+
page[from - base]
|
251
|
+
elsif len <= @pagelength
|
252
|
+
base, page = cache_get_page(from)
|
253
|
+
s = page[from - base, len]
|
254
|
+
if from+len-base > @pagelength # request crosses a page boundary
|
255
|
+
base, page = cache_get_page(from+len)
|
256
|
+
s << page[0, from+len-base]
|
257
|
+
end
|
258
|
+
s
|
259
|
+
else
|
260
|
+
# big request: return a new virtual page
|
261
|
+
dup(from, len)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# rewrites a segment of data
|
266
|
+
# the length written is the length of the content (a VirtualString cannot grow/shrink)
|
267
|
+
def write_range(from, content)
|
268
|
+
invalidate
|
269
|
+
rewrite_at(from + @addr_start, content)
|
270
|
+
end
|
271
|
+
|
272
|
+
# overwrites a section of the original data
|
273
|
+
#def rewrite_at(addr, content)
|
274
|
+
#end
|
275
|
+
end
|
276
|
+
|
277
|
+
# on-demand reading of a file
|
278
|
+
class VirtualFile < VirtualString
|
279
|
+
# returns a new VirtualFile of the whole file content (defaults readonly)
|
280
|
+
# returns a String if the file is small (<4096o) and readonly access
|
281
|
+
def self.read(path, mode='rb')
|
282
|
+
raise 'no filename specified' if not path
|
283
|
+
if sz = File.size(path) <= 4096 and (mode == 'rb' or mode == 'r')
|
284
|
+
File.open(path, mode) { |fd| fd.read }
|
285
|
+
else
|
286
|
+
File.open(path, mode) { |fd| new fd.dup, 0, sz }
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# the underlying file descriptor
|
291
|
+
attr_accessor :fd
|
292
|
+
|
293
|
+
# creates a new virtual mapping of a section of the file
|
294
|
+
# the file descriptor must be seekable
|
295
|
+
def initialize(fd, addr_start = 0, length = nil)
|
296
|
+
@fd = fd
|
297
|
+
if not length
|
298
|
+
@fd.seek(0, File::SEEK_END)
|
299
|
+
length = @fd.tell - addr_start
|
300
|
+
end
|
301
|
+
super(addr_start, length)
|
302
|
+
end
|
303
|
+
|
304
|
+
def dup(addr = @addr_start, len = @length)
|
305
|
+
self.class.new(@fd, addr, len)
|
306
|
+
end
|
307
|
+
|
308
|
+
# reads an aligned page from the file, at file offset addr
|
309
|
+
def get_page(addr, len=@pagelength)
|
310
|
+
@fd.pos = addr
|
311
|
+
@fd.read len
|
312
|
+
end
|
313
|
+
|
314
|
+
def page_invalid?(addr)
|
315
|
+
false
|
316
|
+
end
|
317
|
+
|
318
|
+
# overwrite a section of the file
|
319
|
+
def rewrite_at(addr, data)
|
320
|
+
@fd.pos = addr
|
321
|
+
@fd.write data
|
322
|
+
end
|
323
|
+
|
324
|
+
# returns the full content of the file
|
325
|
+
def realstring
|
326
|
+
@fd.pos = @addr_start
|
327
|
+
@fd.read(@length)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
@@ -4,6 +4,7 @@
|
|
4
4
|
# Licence is LGPL, see LICENCE in the top-level directory
|
5
5
|
|
6
6
|
require 'metasm/os/main'
|
7
|
+
require 'metasm/debug'
|
7
8
|
require 'metasm/dynldr'
|
8
9
|
|
9
10
|
module Metasm
|
@@ -125,6 +126,12 @@ typedef void *HMODULE;
|
|
125
126
|
#define DBG_CONTROL_C ((DWORD )0x40010005L)
|
126
127
|
#define DBG_CONTROL_BREAK ((DWORD )0x40010008L)
|
127
128
|
#define DBG_COMMAND_EXCEPTION ((DWORD )0x40010009L)
|
129
|
+
#define STATUS_WX86_CONTINUE ((DWORD )0x4000001DL)
|
130
|
+
#define STATUS_WX86_SINGLE_STEP ((DWORD )0x4000001EL)
|
131
|
+
#define STATUS_WX86_BREAKPOINT ((DWORD )0x4000001FL)
|
132
|
+
#define STATUS_WX86_EXCEPTION_CONTINUE ((DWORD )0x40000020L)
|
133
|
+
#define STATUS_WX86_EXCEPTION_LASTCHANCE ((DWORD )0x40000021L)
|
134
|
+
#define STATUS_WX86_EXCEPTION_CHAIN ((DWORD )0x40000022L)
|
128
135
|
#define STATUS_GUARD_PAGE_VIOLATION ((DWORD )0x80000001L)
|
129
136
|
#define STATUS_DATATYPE_MISALIGNMENT ((DWORD )0x80000002L)
|
130
137
|
#define STATUS_BREAKPOINT ((DWORD )0x80000003L)
|
@@ -416,7 +423,7 @@ typedef struct _CONTEXT_AMD64 {
|
|
416
423
|
|
417
424
|
XMMREG Vector[26];
|
418
425
|
DWORD64 VectorControl;
|
419
|
-
|
426
|
+
|
420
427
|
DWORD64 DebugControl;
|
421
428
|
DWORD64 LastBranchToRip;
|
422
429
|
DWORD64 LastBranchFromRip;
|
@@ -490,6 +497,10 @@ typedef struct _PROCESS_INFORMATION {
|
|
490
497
|
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
|
491
498
|
|
492
499
|
|
500
|
+
WINAPI
|
501
|
+
DWORD
|
502
|
+
GetVersion(VOID);
|
503
|
+
|
493
504
|
WINBASEAPI
|
494
505
|
HANDLE
|
495
506
|
WINAPI
|
@@ -1033,7 +1044,7 @@ OpenThreadToken (
|
|
1033
1044
|
__out HANDLE *TokenHandle);
|
1034
1045
|
EOS
|
1035
1046
|
SE_DEBUG_NAME = 'SeDebugPrivilege'
|
1036
|
-
|
1047
|
+
|
1037
1048
|
new_api_c <<EOS, 'ntdll'
|
1038
1049
|
#line #{__LINE__}
|
1039
1050
|
|
@@ -1170,11 +1181,12 @@ EOS
|
|
1170
1181
|
# convert a native function return value
|
1171
1182
|
# if the native does not have the zero_not_fail attribute, convert 0
|
1172
1183
|
# to nil, and print a message on stdout
|
1173
|
-
|
1184
|
+
def self.convert_ret_c2rb(fproto, ret)
|
1174
1185
|
@last_err_msg = nil
|
1175
1186
|
if ret == 0 and not fproto.has_attribute 'zero_not_fail'
|
1176
1187
|
# save error msg so that last_error_msg returns the same thing if called again
|
1177
1188
|
puts "WinAPI: error in #{fproto.name}: #{@last_err_msg = last_error_msg}" if $VERBOSE
|
1189
|
+
puts caller if $DEBUG
|
1178
1190
|
nil
|
1179
1191
|
else super(fproto, ret)
|
1180
1192
|
end
|
@@ -1254,12 +1266,12 @@ class WinOS < OS
|
|
1254
1266
|
def mappings
|
1255
1267
|
addr = 0
|
1256
1268
|
list = []
|
1257
|
-
info = WinAPI.alloc_c_struct("MEMORY_BASIC_INFORMATION#{
|
1269
|
+
info = WinAPI.alloc_c_struct("MEMORY_BASIC_INFORMATION#{WinAPI.host_cpu.size}")
|
1258
1270
|
path = [0xff].pack('C') * 512
|
1259
1271
|
|
1260
1272
|
hcache = heaps
|
1261
1273
|
|
1262
|
-
while WinAPI.virtualqueryex(handle, addr, info, info.
|
1274
|
+
while WinAPI.virtualqueryex(handle, addr, info, info.sizeof) != 0
|
1263
1275
|
addr += info.regionsize
|
1264
1276
|
next unless info.state & WinAPI::MEM_COMMIT > 0
|
1265
1277
|
|
@@ -1273,7 +1285,7 @@ class WinOS < OS
|
|
1273
1285
|
WinAPI::PAGE_EXECUTE_READWRITE => 'rwx',
|
1274
1286
|
WinAPI::PAGE_EXECUTE_WRITECOPY => 'rwx'
|
1275
1287
|
}[info[:protect] & 0xff]
|
1276
|
-
prot
|
1288
|
+
prot = prot.sub('r', '-') + 'g' if info[:protect] & WinAPI::PAGE_GUARD > 0
|
1277
1289
|
prot << 'p' if info[:type] & WinAPI::MEM_PRIVATE > 0
|
1278
1290
|
|
1279
1291
|
if h = hcache[info.baseaddress]
|
@@ -1301,7 +1313,7 @@ class WinOS < OS
|
|
1301
1313
|
@peb_base ||=
|
1302
1314
|
if WinAPI.respond_to?(:ntqueryinformationprocess)
|
1303
1315
|
pinfo = WinAPI.alloc_c_struct('PROCESS_BASIC_INFORMATION')
|
1304
|
-
if WinAPI.ntqueryinformationprocess(handle, WinAPI::PROCESSBASICINFORMATION, pinfo, pinfo.
|
1316
|
+
if WinAPI.ntqueryinformationprocess(handle, WinAPI::PROCESSBASICINFORMATION, pinfo, pinfo.sizeof, 0) == 0
|
1305
1317
|
pinfo.pebbaseaddress
|
1306
1318
|
end
|
1307
1319
|
else
|
@@ -1336,7 +1348,7 @@ class WinOS < OS
|
|
1336
1348
|
@teb_base ||=
|
1337
1349
|
if WinAPI.respond_to?(:ntqueryinformationthread)
|
1338
1350
|
tinfo = WinAPI.alloc_c_struct('THREAD_BASIC_INFORMATION')
|
1339
|
-
if WinAPI.ntqueryinformationthread(handle, WinAPI::THREADBASICINFORMATION, tinfo, tinfo.
|
1351
|
+
if WinAPI.ntqueryinformationthread(handle, WinAPI::THREADBASICINFORMATION, tinfo, tinfo.sizeof, 0) == 0
|
1340
1352
|
tinfo.tebbaseaddress
|
1341
1353
|
end
|
1342
1354
|
else
|
@@ -1373,10 +1385,12 @@ class WinOS < OS
|
|
1373
1385
|
@context ||= Context.new(self, :all)
|
1374
1386
|
if block_given?
|
1375
1387
|
suspend
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1388
|
+
begin
|
1389
|
+
@context.update
|
1390
|
+
yield @context
|
1391
|
+
ensure
|
1392
|
+
resume
|
1393
|
+
end
|
1380
1394
|
else
|
1381
1395
|
@context
|
1382
1396
|
end
|
@@ -1386,28 +1400,34 @@ class WinOS < OS
|
|
1386
1400
|
class Context
|
1387
1401
|
def initialize(thread, kind=:all)
|
1388
1402
|
@handle = thread.handle
|
1389
|
-
tg = thread.process ? thread.process.addrsz : 32
|
1390
|
-
|
1391
|
-
|
1403
|
+
tg = (thread.process ? thread.process.addrsz : 32)
|
1404
|
+
hcpu = WinAPI.host_cpu.shortname
|
1405
|
+
case hcpu
|
1406
|
+
when 'ia32', 'x64'
|
1392
1407
|
else raise "unsupported architecture #{tg}"
|
1393
1408
|
end
|
1394
1409
|
|
1395
1410
|
@getcontext = :getthreadcontext
|
1396
1411
|
@setcontext = :setthreadcontext
|
1397
1412
|
case tg
|
1398
|
-
when
|
1413
|
+
when 32
|
1399
1414
|
@context = WinAPI.alloc_c_struct('_CONTEXT_I386')
|
1400
1415
|
@context.contextflags = WinAPI::CONTEXT_I386_ALL
|
1401
|
-
if
|
1416
|
+
if hcpu == 'x64'
|
1402
1417
|
@getcontext = :wow64getthreadcontext
|
1403
1418
|
@setcontext = :wow64setthreadcontext
|
1404
1419
|
end
|
1405
|
-
when
|
1420
|
+
when 64
|
1406
1421
|
@context = WinAPI.alloc_c_struct('_CONTEXT_AMD64')
|
1407
1422
|
@context.contextflags = WinAPI::CONTEXT_AMD64_ALL
|
1408
1423
|
end
|
1409
1424
|
end
|
1410
1425
|
|
1426
|
+
# retrieve the actual context structure (so we can pass to API's like StackWalk64)
|
1427
|
+
def c_struct
|
1428
|
+
@context
|
1429
|
+
end
|
1430
|
+
|
1411
1431
|
# update the context to reflect the current thread reg values
|
1412
1432
|
# call only when the thread is suspended
|
1413
1433
|
def update
|
@@ -1418,15 +1438,17 @@ class WinOS < OS
|
|
1418
1438
|
case k.to_s
|
1419
1439
|
when /^[cdefgs]s$/i
|
1420
1440
|
@context["seg#{k}"]
|
1421
|
-
when /^st(\d
|
1441
|
+
when /^st(\d?)$/i
|
1422
1442
|
v = @context['st'][$1.to_i]
|
1423
|
-
buf = v.str[v.
|
1443
|
+
buf = v.str[v.stroff, 10]
|
1424
1444
|
# TODO check this, 'D' is 8byte wide
|
1425
1445
|
buf.unpack('D')[0]
|
1426
|
-
when /^
|
1446
|
+
# TODO when /^ymm(\d+)$/i
|
1447
|
+
when /^xmm(\d+)$/i
|
1427
1448
|
v = @context['xmm'][$1.to_i]
|
1428
1449
|
(v.hi << 64) | v.lo
|
1429
|
-
when /^mmx?(\d
|
1450
|
+
when /^mmx?(\d)$/i
|
1451
|
+
# XXX probably in st(0/7)
|
1430
1452
|
@context['xmm'][$1.to_i].lo
|
1431
1453
|
else
|
1432
1454
|
@context[k]
|
@@ -1437,15 +1459,17 @@ class WinOS < OS
|
|
1437
1459
|
case k.to_s
|
1438
1460
|
when /^[cdefgs]s$/i
|
1439
1461
|
@context["seg#{k}"] = v
|
1440
|
-
when /^st(\d
|
1462
|
+
when /^st(\d?)$/i
|
1441
1463
|
# TODO check this, 'D' is 8byte wide
|
1442
1464
|
buf = [v, 0, 0].pack('DCC')
|
1443
1465
|
@context['st'][$1.to_i][0, 10] = buf
|
1444
|
-
when /^
|
1466
|
+
# TODO when /^ymm(\d+)$/i
|
1467
|
+
when /^xmm(\d+)$/i
|
1445
1468
|
kk = @context['xmm'][$1.to_i]
|
1446
1469
|
kk.lo = v & ((1<<64)-1)
|
1447
1470
|
kk.hi = (v>>64) & ((1<<64)-1)
|
1448
|
-
when /^mmx?(\d
|
1471
|
+
when /^mmx?(\d)$/i
|
1472
|
+
# XXX st(7-$1) ?
|
1449
1473
|
@context['xmm'][$1.to_i].lo = v
|
1450
1474
|
else
|
1451
1475
|
@context[k] = v
|
@@ -1455,10 +1479,10 @@ class WinOS < OS
|
|
1455
1479
|
|
1456
1480
|
def method_missing(m, *a)
|
1457
1481
|
if m.to_s[-1] == ?=
|
1458
|
-
super(m, *a) if a.length != 1
|
1482
|
+
return super(m, *a) if a.length != 1
|
1459
1483
|
send '[]=', m.to_s[0...-1], a[0]
|
1460
1484
|
else
|
1461
|
-
super(m, *a) if a.length != 0
|
1485
|
+
return super(m, *a) if a.length != 0
|
1462
1486
|
send '[]', m
|
1463
1487
|
end
|
1464
1488
|
end
|
@@ -1649,6 +1673,11 @@ class << self
|
|
1649
1673
|
end
|
1650
1674
|
end
|
1651
1675
|
|
1676
|
+
# returns the [major, minor] version of the windows os
|
1677
|
+
def version
|
1678
|
+
v = WinAPI.getversion
|
1679
|
+
[v & 0xff, (v>>8) & 0xff]
|
1680
|
+
end
|
1652
1681
|
end # class << self
|
1653
1682
|
end
|
1654
1683
|
|
@@ -1699,6 +1728,8 @@ class WinDebugger < Debugger
|
|
1699
1728
|
attr_accessor :os_process, :os_thread,
|
1700
1729
|
:auto_fix_fs_bug,
|
1701
1730
|
# is current exception handled? (arg to pass to continuedbgevt)
|
1731
|
+
# if it has the special value :suspended, it means that the thread
|
1732
|
+
# is to be restarted through resume and not continuedbgevt
|
1702
1733
|
:continuecode
|
1703
1734
|
|
1704
1735
|
attr_accessor :callback_unloadlibrary, :callback_debugstring, :callback_ripevent
|
@@ -1747,6 +1778,7 @@ class WinDebugger < Debugger
|
|
1747
1778
|
@os_process = WinOS::Process.new(processinfo.dwprocessid, processinfo.hprocess)
|
1748
1779
|
@os_thread = WinOS::Thread.new(processinfo.dwthreadid, processinfo.hthread, @os_process)
|
1749
1780
|
initialize_osprocess
|
1781
|
+
check_target
|
1750
1782
|
end
|
1751
1783
|
|
1752
1784
|
# called whenever we receive a handle to a new process being debugged, after initialisation of @os_process
|
@@ -1830,17 +1862,46 @@ class WinDebugger < Debugger
|
|
1830
1862
|
end
|
1831
1863
|
|
1832
1864
|
def set_reg_value(r, v)
|
1833
|
-
|
1865
|
+
if @state == :running
|
1866
|
+
suspend
|
1867
|
+
ctx[r] = v
|
1868
|
+
resume
|
1869
|
+
else
|
1870
|
+
ctx[r] = v
|
1871
|
+
end
|
1834
1872
|
end
|
1835
1873
|
|
1836
1874
|
def do_continue(*a)
|
1837
1875
|
@cpu.dbg_disable_singlestep(self)
|
1838
|
-
|
1876
|
+
if @continuecode == :suspended
|
1877
|
+
resume
|
1878
|
+
else
|
1879
|
+
@state = :running
|
1880
|
+
WinAPI.continuedebugevent(@pid, @tid, @continuecode)
|
1881
|
+
end
|
1839
1882
|
end
|
1840
1883
|
|
1841
1884
|
def do_singlestep(*a)
|
1842
1885
|
@cpu.dbg_enable_singlestep(self)
|
1843
|
-
|
1886
|
+
if @continuecode == :suspended
|
1887
|
+
resume
|
1888
|
+
else
|
1889
|
+
@state = :running
|
1890
|
+
WinAPI.continuedebugevent(@pid, @tid, @continuecode)
|
1891
|
+
end
|
1892
|
+
end
|
1893
|
+
|
1894
|
+
def do_enable_bpm(bp)
|
1895
|
+
@bpm_info ||= WinAPI.alloc_c_struct("MEMORY_BASIC_INFORMATION#{WinAPI.host_cpu.size}")
|
1896
|
+
WinAPI.virtualqueryex(os_process.handle, bp.address, @bpm_info, @bpm_info.sizeof)
|
1897
|
+
# TODO save original page perms, check bpm type (:w -> vprotect(PAGE_READONLY)), handle multiple bpm on same page
|
1898
|
+
WinAPI.virtualprotectex(os_process.handle, bp.address, bp.internal[:len], @bpm_info[:protect] | WinAPI::PAGE_GUARD, @bpm_info)
|
1899
|
+
end
|
1900
|
+
|
1901
|
+
def do_disable_bpm(bp)
|
1902
|
+
@bpm_info ||= WinAPI.alloc_c_struct("MEMORY_BASIC_INFORMATION#{WinAPI.host_cpu.size}")
|
1903
|
+
WinAPI.virtualqueryex(os_process.handle, bp.address, @bpm_info, @bpm_info.sizeof)
|
1904
|
+
WinAPI.virtualprotectex(os_process.handle, bp.address, bp.internal[:len], @bpm_info[:protect] & ~WinAPI::PAGE_GUARD, @bpm_info)
|
1844
1905
|
end
|
1845
1906
|
|
1846
1907
|
def update_dbgev(ev)
|
@@ -1855,7 +1916,7 @@ class WinDebugger < Debugger
|
|
1855
1916
|
st = ev.exception
|
1856
1917
|
str = st.exceptionrecord
|
1857
1918
|
stf = st.dwfirstchance # non-zero = first chance
|
1858
|
-
|
1919
|
+
|
1859
1920
|
@state = :stopped
|
1860
1921
|
@info = "exception"
|
1861
1922
|
|
@@ -1866,7 +1927,7 @@ class WinDebugger < Debugger
|
|
1866
1927
|
# DWORD NumberParameters;
|
1867
1928
|
# ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
|
1868
1929
|
case str.exceptioncode
|
1869
|
-
when WinAPI::STATUS_ACCESS_VIOLATION
|
1930
|
+
when WinAPI::STATUS_ACCESS_VIOLATION, WinAPI::STATUS_GUARD_PAGE_VIOLATION
|
1870
1931
|
if @auto_fix_fs_bug and ctx.fs != 0x3b
|
1871
1932
|
# fix bug in xpsp1 where fs would get a random value in a debugee
|
1872
1933
|
log "wdbg: #{pid}:#{tid} fix fs bug" if $DEBUG
|
@@ -1882,11 +1943,11 @@ class WinDebugger < Debugger
|
|
1882
1943
|
addr = str.exceptioninformation[1]
|
1883
1944
|
evt_exception(:type => 'access violation', :st => str, :firstchance => stf,
|
1884
1945
|
:fault_addr => addr, :fault_access => mode)
|
1885
|
-
when WinAPI::STATUS_BREAKPOINT
|
1946
|
+
when WinAPI::STATUS_BREAKPOINT, WinAPI::STATUS_WX86_BREAKPOINT
|
1886
1947
|
# we must ack ntdll interrupts on process start
|
1887
1948
|
# but we should not mask process-generated exceptions by default..
|
1888
1949
|
evt_bpx
|
1889
|
-
when WinAPI::STATUS_SINGLE_STEP
|
1950
|
+
when WinAPI::STATUS_SINGLE_STEP, WinAPI::STATUS_WX86_SINGLE_STEP
|
1890
1951
|
evt_hwbp_singlestep
|
1891
1952
|
else
|
1892
1953
|
@status_name ||= WinAPI.cp.lexer.definition.keys.grep(/^STATUS_/).
|
@@ -1988,31 +2049,59 @@ class WinDebugger < Debugger
|
|
1988
2049
|
@dbg_eventstruct ||= WinAPI.alloc_c_struct('_DEBUG_EVENT')
|
1989
2050
|
if WinAPI.waitfordebugevent(@dbg_eventstruct, timeout) != 0
|
1990
2051
|
update_dbgev(@dbg_eventstruct)
|
2052
|
+
true
|
1991
2053
|
end
|
1992
2054
|
end
|
1993
2055
|
|
2056
|
+
def del_tid
|
2057
|
+
# tell Windows to release the THREAD object
|
2058
|
+
WinAPI.continuedebugevent(@pid, @tid, @continuecode)
|
2059
|
+
super()
|
2060
|
+
end
|
2061
|
+
|
2062
|
+
# do nothing, windows will send us a EXIT_PROCESS event
|
2063
|
+
def del_tid_notid
|
2064
|
+
nil while do_waitfordebug(10) and !@tid
|
2065
|
+
end
|
2066
|
+
|
2067
|
+
def del_pid
|
2068
|
+
# tell Windows to release the PROCESS object
|
2069
|
+
WinAPI.debugactiveprocessstop(@pid) if WinAPI.respond_to?(:debugactiveprocessstop)
|
2070
|
+
super()
|
2071
|
+
end
|
2072
|
+
|
1994
2073
|
def break
|
1995
2074
|
return if @state != :running
|
1996
|
-
|
1997
|
-
|
1998
|
-
else
|
1999
|
-
suspend
|
2000
|
-
end
|
2075
|
+
# debugbreak() will create a new thread to 0xcc, but wont touch existing threads
|
2076
|
+
suspend
|
2001
2077
|
end
|
2002
2078
|
|
2003
2079
|
def suspend
|
2004
2080
|
os_thread.suspend
|
2081
|
+
invalidate
|
2005
2082
|
@state = :stopped
|
2006
2083
|
@info = 'thread suspended'
|
2084
|
+
@continuecode = :suspended
|
2085
|
+
end
|
2086
|
+
|
2087
|
+
def resume
|
2088
|
+
@state = :running
|
2089
|
+
@info = nil
|
2090
|
+
os_thread.resume
|
2007
2091
|
end
|
2008
2092
|
|
2009
2093
|
def detach
|
2010
2094
|
del_all_breakpoints
|
2011
|
-
if WinAPI.respond_to? :debugactiveprocessstop
|
2012
|
-
WinAPI.debugactiveprocessstop(@pid)
|
2013
|
-
else
|
2095
|
+
if not WinAPI.respond_to? :debugactiveprocessstop
|
2014
2096
|
raise 'detach not supported'
|
2015
2097
|
end
|
2098
|
+
# handle pending bp events
|
2099
|
+
# TODO check_target needs the Breakpoint objects...
|
2100
|
+
#pid = @pid ; 50.times { check_target } ; self.pid = pid
|
2101
|
+
|
2102
|
+
# if we detach after a dbgevt and before calling continuedbgevent, the thread
|
2103
|
+
# may receive unhandled exceptions (eg BPX) and crash the process right after detach
|
2104
|
+
each_tid { do_continue if @state == :stopped }
|
2016
2105
|
del_pid
|
2017
2106
|
end
|
2018
2107
|
|
@@ -2021,6 +2110,7 @@ class WinDebugger < Debugger
|
|
2021
2110
|
end
|
2022
2111
|
|
2023
2112
|
def pass_current_exception(doit = true)
|
2113
|
+
return if @continuecode == :suspended
|
2024
2114
|
@continuecode = (doit ? WinAPI::DBG_EXCEPTION_NOT_HANDLED : WinAPI::DBG_CONTINUE)
|
2025
2115
|
end
|
2026
2116
|
end
|