metasm 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.hgtags +3 -0
- data/Gemfile +1 -0
- data/INSTALL +61 -0
- data/LICENCE +458 -0
- data/README +29 -21
- data/Rakefile +10 -0
- data/TODO +10 -12
- data/doc/code_organisation.txt +2 -0
- data/doc/core/DynLdr.txt +247 -0
- data/doc/core/ExeFormat.txt +43 -0
- data/doc/core/Expression.txt +220 -0
- data/doc/core/GNUExports.txt +27 -0
- data/doc/core/Ia32.txt +236 -0
- data/doc/core/SerialStruct.txt +108 -0
- data/doc/core/VirtualString.txt +145 -0
- data/doc/core/WindowsExports.txt +61 -0
- data/doc/core/index.txt +1 -0
- data/doc/style.css +6 -3
- data/doc/usage/debugger.txt +327 -0
- data/doc/usage/index.txt +1 -0
- data/doc/use_cases.txt +2 -2
- data/metasm.gemspec +22 -0
- data/{lib/metasm.rb → metasm.rb} +11 -3
- data/{lib/metasm → metasm}/compile_c.rb +13 -7
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +425 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
- data/metasm/cpu/arm/opcodes.rb +324 -0
- data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
- data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
- data/metasm/cpu/arm64.rb +15 -0
- data/metasm/cpu/arm64/debug.rb +38 -0
- data/metasm/cpu/arm64/decode.rb +289 -0
- data/metasm/cpu/arm64/encode.rb +41 -0
- data/metasm/cpu/arm64/main.rb +105 -0
- data/metasm/cpu/arm64/opcodes.rb +232 -0
- data/metasm/cpu/arm64/parse.rb +20 -0
- data/metasm/cpu/arm64/render.rb +95 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
- data/metasm/cpu/bpf/decode.rb +142 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +41 -0
- data/metasm/cpu/cy16.rb +9 -0
- data/metasm/cpu/cy16/decode.rb +253 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +41 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
- data/metasm/cpu/mips.rb +14 -0
- data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
- data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
- data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
- data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
- data/metasm/cpu/msp430/decode.rb +247 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
- data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
- data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
- data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
- data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
- data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
- data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +136 -0
- data/metasm/cpu/python/main.rb +36 -0
- data/metasm/cpu/python/opcodes.rb +180 -0
- data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
- data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
- data/metasm/cpu/x86_64/opcodes.rb +136 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +313 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +59 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
- data/{lib/metasm → metasm}/decode.rb +35 -4
- data/{lib/metasm → metasm}/decompile.rb +15 -16
- data/{lib/metasm → metasm}/disassemble.rb +201 -45
- data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
- data/{lib/metasm → metasm}/dynldr.rb +220 -133
- data/{lib/metasm → metasm}/encode.rb +10 -1
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
- data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
- data/metasm/exe_format/pyc.rb +167 -0
- data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
- data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
- data/metasm/exe_format/shellcode_rwx.rb +114 -0
- data/metasm/exe_format/swf.rb +205 -0
- data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
- data/metasm/exe_format/zip.rb +335 -0
- data/metasm/gui.rb +13 -0
- data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
- data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
- data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1695 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +93 -27
- data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
- data/{lib/metasm → metasm}/gui/qt.rb +12 -2
- data/{lib/metasm → metasm}/gui/win32.rb +179 -42
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +389 -264
- data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
- data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
- data/{lib/metasm → metasm}/os/linux.rb +628 -151
- data/metasm/os/main.rb +330 -0
- data/{lib/metasm → metasm}/os/windows.rb +132 -42
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +26 -24
- data/{lib/metasm → metasm}/parse_c.rb +221 -116
- data/{lib/metasm → metasm}/preprocessor.rb +55 -40
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +2 -1
- data/misc/lint.rb +58 -0
- data/misc/txt2html.rb +9 -7
- data/samples/bindiff.rb +3 -4
- data/samples/dasm-plugins/bindiff.rb +15 -0
- data/samples/dasm-plugins/bookmark.rb +133 -0
- data/samples/dasm-plugins/c_constants.rb +57 -0
- data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
- data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
- data/samples/dasm-plugins/dasm_all.rb +70 -0
- data/samples/dasm-plugins/demangle_cpp.rb +31 -0
- data/samples/dasm-plugins/deobfuscate.rb +251 -0
- data/samples/dasm-plugins/dump_text.rb +35 -0
- data/samples/dasm-plugins/export_graph_svg.rb +86 -0
- data/samples/dasm-plugins/findgadget.rb +75 -0
- data/samples/dasm-plugins/hl_opcode.rb +32 -0
- data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
- data/samples/dasm-plugins/imm2off.rb +34 -0
- data/samples/dasm-plugins/match_libsigs.rb +93 -0
- data/samples/dasm-plugins/patch_file.rb +95 -0
- data/samples/dasm-plugins/scanfuncstart.rb +36 -0
- data/samples/dasm-plugins/scanxrefs.rb +26 -0
- data/samples/dasm-plugins/selfmodify.rb +197 -0
- data/samples/dasm-plugins/stringsxrefs.rb +28 -0
- data/samples/dasmnavig.rb +1 -1
- data/samples/dbg-apihook.rb +24 -9
- data/samples/dbg-plugins/heapscan.rb +283 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
- data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
- data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
- data/samples/dbg-plugins/heapscan/winheap.h +174 -0
- data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
- data/samples/dbg-plugins/trace_func.rb +214 -0
- data/samples/disassemble-gui.rb +35 -5
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +12 -3
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +175 -381
- data/samples/metasm-shell.rb +1 -2
- data/samples/peldr.rb +2 -2
- data/tests/all.rb +1 -1
- data/tests/arc.rb +26 -0
- data/tests/dynldr.rb +22 -4
- data/tests/expression.rb +55 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +79 -26
- data/tests/mips.rb +9 -2
- data/tests/x86_64.rb +66 -18
- metadata +330 -218
- data/lib/metasm/arm/opcodes.rb +0 -177
- data/lib/metasm/gui.rb +0 -23
- data/lib/metasm/gui/dasm_graph.rb +0 -1354
- data/lib/metasm/ia32.rb +0 -14
- data/lib/metasm/ia32/opcodes.rb +0 -873
- data/lib/metasm/ppc/parse.rb +0 -52
- data/lib/metasm/x86_64.rb +0 -12
- data/lib/metasm/x86_64/opcodes.rb +0 -118
- data/samples/gdbclient.rb +0 -583
- data/samples/rubstop.rb +0 -399
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
|