setup.py
5.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# DExTer : Debugging Experience Tester
# ~~~~~~ ~ ~~ ~ ~~
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
from ctypes import *
from . import client
from . import control
from . import symbols
from .probe_process import probe_state
from .utils import *
class STARTUPINFOA(Structure):
_fields_ = [
('cb', c_ulong),
('lpReserved', c_char_p),
('lpDesktop', c_char_p),
('lpTitle', c_char_p),
('dwX', c_ulong),
('dwY', c_ulong),
('dwXSize', c_ulong),
('dwYSize', c_ulong),
('dwXCountChars', c_ulong),
('dwYCountChars', c_ulong),
('dwFillAttribute', c_ulong),
('wShowWindow', c_ushort),
('cbReserved2', c_ushort),
('lpReserved2', c_char_p),
('hStdInput', c_void_p),
('hStdOutput', c_void_p),
('hStdError', c_void_p)
]
class PROCESS_INFORMATION(Structure):
_fields_ = [
('hProcess', c_void_p),
('hThread', c_void_p),
('dwProcessId', c_ulong),
('dwThreadId', c_ulong)
]
def fetch_local_function_syms(Symbols, prefix):
syms = Symbols.get_all_functions()
def is_sym_in_src_dir(sym):
name, data = sym
symdata = Symbols.GetLineByOffset(data.Offset)
if symdata is not None:
srcfile, line = symdata
if prefix in srcfile:
return True
return False
syms = [x for x in syms if is_sym_in_src_dir(x)]
return syms
def break_on_all_but_main(Control, Symbols, main_offset):
mainfile, _ = Symbols.GetLineByOffset(main_offset)
prefix = '\\'.join(mainfile.split('\\')[:-1])
for name, rec in fetch_local_function_syms(Symbols, prefix):
if name == "main":
continue
bp = Control.AddBreakpoint2(offset=rec.Offset, enabled=True)
# All breakpoints are currently discarded: we just sys.exit for cleanup
return
def process_creator(binfile):
Kernel32 = WinDLL("Kernel32")
# Another flavour of process creation
startupinfoa = STARTUPINFOA()
startupinfoa.cb = sizeof(STARTUPINFOA)
startupinfoa.lpReserved = None
startupinfoa.lpDesktop = None
startupinfoa.lpTitle = None
startupinfoa.dwX = 0
startupinfoa.dwY = 0
startupinfoa.dwXSize = 0
startupinfoa.dwYSize = 0
startupinfoa.dwXCountChars = 0
startupinfoa.dwYCountChars = 0
startupinfoa.dwFillAttribute = 0
startupinfoa.dwFlags = 0
startupinfoa.wShowWindow = 0
startupinfoa.cbReserved2 = 0
startupinfoa.lpReserved2 = None
startupinfoa.hStdInput = None
startupinfoa.hStdOutput = None
startupinfoa.hStdError = None
processinformation = PROCESS_INFORMATION()
# 0x4 below specifies CREATE_SUSPENDED.
ret = Kernel32.CreateProcessA(binfile.encode("ascii"), None, None, None, False, 0x4, None, None, byref(startupinfoa), byref(processinformation))
if ret == 0:
raise Exception('CreateProcess running {}'.format(binfile))
return processinformation.dwProcessId, processinformation.dwThreadId, processinformation.hProcess, processinformation.hThread
def thread_resumer(hProcess, hThread):
Kernel32 = WinDLL("Kernel32")
# For reasons unclear to me, other suspend-references seem to be opened on
# the opened thread. Clear them all.
while True:
ret = Kernel32.ResumeThread(hThread)
if ret <= 0:
break
if ret < 0:
Kernel32.TerminateProcess(hProcess, 1)
raise Exception("Couldn't resume process after startup")
return
def setup_everything(binfile):
from . import client
from . import symbols
Client = client.Client()
created_pid, created_tid, hProcess, hThread = process_creator(binfile)
# Load lines as well as general symbols
sym_opts = Client.Symbols.GetSymbolOptions()
sym_opts |= symbols.SymbolOptionFlags.SYMOPT_LOAD_LINES
Client.Symbols.SetSymbolOptions(sym_opts)
Client.AttachProcess(created_pid)
# Need to enter the debugger engine to let it attach properly
Client.Control.WaitForEvent(timeout=1)
Client.SysObjects.set_current_thread(created_pid, created_tid)
Client.Control.Execute("l+t")
Client.Control.SetExpressionSyntax(cpp=True)
module_name = Client.Symbols.get_exefile_module_name()
offset = Client.Symbols.GetOffsetByName("{}!main".format(module_name))
breakpoint = Client.Control.AddBreakpoint2(offset=offset, enabled=True)
thread_resumer(hProcess, hThread)
Client.Control.SetExecutionStatus(control.DebugStatus.DEBUG_STATUS_GO)
# Problem: there is no guarantee that the client will ever reach main,
# something else exciting could happen in that time, the host system may
# be very loaded, and similar. Wait for some period, say, five seconds, and
# abort afterwards: this is a trade-off between spurious timeouts and
# completely hanging in the case of a environmental/programming error.
res = Client.Control.WaitForEvent(timeout=5000)
if res == S_FALSE:
Kernel32.TerminateProcess(hProcess, 1)
raise Exception("Debuggee did not reach main function in a timely manner")
break_on_all_but_main(Client.Control, Client.Symbols, offset)
# Set the default action on all exceptions to be "quit and detach". If we
# don't, dbgeng will merrily spin at the exception site forever.
filts = Client.Control.GetNumberEventFilters()
for x in range(filts[0], filts[0] + filts[1]):
Client.Control.SetExceptionFilterSecondCommand(x, "qd")
return Client, hProcess
def step_once(client):
client.Control.Execute("p")
try:
client.Control.WaitForEvent()
except Exception as e:
if client.Control.GetExecutionStatus() == control.DebugStatus.DEBUG_STATUS_NO_DEBUGGEE:
return None # Debuggee has gone away, likely due to an exception.
raise e
# Could assert here that we're in the "break" state
client.Control.GetExecutionStatus()
return probe_state(client)
def main_loop(client):
res = True
while res is not None:
res = step_once(client)
def cleanup(client, hProcess):
res = client.DetachProcesses()
Kernel32 = WinDLL("Kernel32")
Kernel32.TerminateProcess(hProcess, 1)