-
Notifications
You must be signed in to change notification settings - Fork 10
/
vermilion.py
159 lines (110 loc) · 4.8 KB
/
vermilion.py
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
"""
Named after the Vermilion Flycatcher (Bird). Helping you catch bugs with a bird's eye view of RedLeaf ;)
"""
import traceback
import os
import pathlib
# *** DEFINITIONS & CONSTANTS ***
NEW_DOMAIN_LOADED = "redleaf_kernel::domain::load_domain::gdb_notify_new_domain_loaded"
LOAD_DOMAIN = "redleaf_kernel::domain::load_domain::load_domain"
REDLEAF_KERNEL_ENTRY = "redleaf_kernel::rust_main"
class DelayedExecute:
def __init__(self, command: str, to_string=False) -> None:
self.command: str = command
self.to_string = to_string
def __call__(self):
gdb.execute(self.command, to_string=self.to_string)
class DelayedWrite:
def __init__(self, string: str) -> None:
self.string: str = string
def __call__(self):
gdb.write(self.string)
gdb.flush()
# *** PRINTERS ***
def print_greeting():
print("--------------------------------------------")
print("| Vermilion |")
print("| For RedLeaf by the Mars Research Group |")
print("--------------------------------------------")
def print_frame_info():
newest_frame = gdb.newest_frame()
print(newest_frame)
print(newest_frame.name())
print(newest_frame.architecture())
print(newest_frame.type())
print(newest_frame.pc())
print(newest_frame.function())
# *** AUTOMATIC DOMAIN LOADING ***
def remove_domain_symbol_file(name: str):
"""
Removing the symbol file will set breakpoints that have been set with incorrect addresses into a pending state.
Adding the symbol file at the correct location afterwards will fix this.
"""
full_path = pathlib.Path(f"./domains/build/{name}").resolve()
gdb.post_event(DelayedExecute(f"remove-symbol-file {full_path}", to_string=True))
def load_domain_symbol_file(name: str, text_start: int):
if isinstance(text_start, gdb.Value):
text_start = int(text_start)
file_path = f"domains/build/{name}"
print(f"Adding Domain {file_path} @ 0x{text_start:02X}")
remove_domain_symbol_file(name)
# Command of interest: "add-symbol-file-from-memory address"
gdb.post_event(
DelayedExecute(f"add-symbol-file {file_path} {text_start}", to_string=True)
)
def handle_stop_event(event):
# print("handle_stop_event", event)
if isinstance(event, gdb.BreakpointEvent):
breakpoints = event.breakpoints
for bp in (b for b in breakpoints if b.is_valid()):
# print(
# f"{bp.enabled=}, {bp.silent=}, {bp.pending=}, {bp.number=}, {bp.type=}, {bp.temporary=}, {bp.location=}"
# )
if bp.location == NEW_DOMAIN_LOADED:
try:
# Load the domain
frame = gdb.newest_frame()
caller_frame = frame.older()
if not caller_frame.function():
print(
f"Vermilion Error: Caller Frame does not have a function! {caller_frame}"
)
gdb.post_event(DelayedExecute("bt"))
if caller_frame.function().name == LOAD_DOMAIN:
# print(
# frame.name(),
# caller_frame.name(),
# )
# print(caller_frame.read_var("name"))
domain_name = str(caller_frame.read_var("name"))[1:-1]
domain_start = caller_frame.read_var("_domain_start")
load_domain_symbol_file(domain_name, domain_start)
gdb.post_event(DelayedExecute("continue"))
else:
print(
f"Vermilion Error: {NEW_DOMAIN_LOADED} should only be called from {LOAD_DOMAIN} but was called from {caller_frame.function().name}"
)
except Exception as e:
print("Vermilion Error:", e)
traceback.print_exc()
# gdb.post_event(DelayedExecute("continue"))
# gdb.post_event(DelayedWrite("(gdc) "))
def setup_automatic_domain_loading():
# Create the Domain Load Breakpoint
bp = gdb.Breakpoint(NEW_DOMAIN_LOADED, internal=True)
bp.silent = True
gdb.events.stop.connect(handle_stop_event)
def load_all_domains_for_autocomplete():
"""
Adds the symbol files for every domain for autocomplete purposed. It is done without an address so any breakpoint set is incorrect.
"""
for file in pathlib.Path("./domains/build/").iterdir():
if file.suffix == "":
# Add the domain
gdb.post_event(DelayedExecute(f"add-symbol-file {file}", to_string=True))
# *** INITIALIZATION ***
def init():
print_greeting()
setup_automatic_domain_loading()
load_all_domains_for_autocomplete()
init()