mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-10 06:02:09 +00:00
Merge remote-tracking branch
'origin/GP-4858_Dan_gdbCatchpointIsAProblem--SQUASHED' (Closes #6822)
This commit is contained in:
commit
6f75337bf2
@ -1,17 +1,17 @@
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from contextlib import contextmanager
|
||||
import inspect
|
||||
@ -1200,12 +1200,13 @@ def ghidra_trace_put_environment(*, is_mi, **kwargs):
|
||||
put_environment()
|
||||
|
||||
|
||||
def put_regions():
|
||||
def put_regions(regions=None):
|
||||
inf = gdb.selected_inferior()
|
||||
try:
|
||||
regions = util.REGION_INFO_READER.get_regions()
|
||||
except Exception:
|
||||
regions = []
|
||||
if regions is None:
|
||||
try:
|
||||
regions = util.REGION_INFO_READER.get_regions()
|
||||
except Exception:
|
||||
regions = []
|
||||
if len(regions) == 0 and gdb.selected_thread() is not None:
|
||||
regions = [util.REGION_INFO_READER.full_mem()]
|
||||
mapper = STATE.trace.memory_mapper
|
||||
@ -1229,6 +1230,7 @@ def put_regions():
|
||||
regobj.insert()
|
||||
STATE.trace.proxy_object_path(
|
||||
MEMORY_PATTERN.format(infnum=inf.num)).retain_values(keys)
|
||||
return regions
|
||||
|
||||
|
||||
@cmd('ghidra trace put-regions', '-ghidra-trace-put-regions', gdb.COMMAND_DATA,
|
||||
|
@ -5,7 +5,7 @@
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@ -33,11 +33,10 @@ GhidraHookPrefix()
|
||||
|
||||
|
||||
class HookState(object):
|
||||
__slots__ = ('installed', 'mem_catchpoint', 'batch', 'skip_continue', 'in_break_w_cont')
|
||||
__slots__ = ('installed', 'batch', 'skip_continue', 'in_break_w_cont')
|
||||
|
||||
def __init__(self):
|
||||
self.installed = False
|
||||
self.mem_catchpoint = None
|
||||
self.batch = None
|
||||
self.skip_continue = False
|
||||
self.in_break_w_cont = False
|
||||
@ -64,7 +63,7 @@ class InferiorState(object):
|
||||
def __init__(self):
|
||||
self.first = True
|
||||
# For things we can detect changes to between stops
|
||||
self.regions = False
|
||||
self.regions = []
|
||||
self.modules = False
|
||||
self.threads = False
|
||||
self.breaks = False
|
||||
@ -107,13 +106,10 @@ class InferiorState(object):
|
||||
print(f"Couldn't record page with SP: {e}")
|
||||
self.visited.add(hashable_frame)
|
||||
# NB: These commands (put_modules/put_regions) will fail if the process is running
|
||||
if first or self.regions or self.threads or self.modules:
|
||||
# Sections, memory syscalls, or stack allocations
|
||||
commands.put_modules()
|
||||
self.modules = False
|
||||
commands.put_regions()
|
||||
self.regions = False
|
||||
elif first or self.modules:
|
||||
regions_changed, regions = util.REGION_INFO_READER.have_changed(self.regions)
|
||||
if regions_changed:
|
||||
self.regions = commands.put_regions(regions)
|
||||
if first or self.modules:
|
||||
commands.put_modules()
|
||||
self.modules = False
|
||||
if first or self.breaks:
|
||||
@ -252,14 +248,6 @@ def on_frame_selected():
|
||||
commands.activate()
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_syscall_memory():
|
||||
inf = gdb.selected_inferior()
|
||||
if inf.num not in INF_STATES:
|
||||
return
|
||||
INF_STATES[inf.num].regions = True
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_memory_changed(event):
|
||||
inf = gdb.selected_inferior()
|
||||
@ -287,7 +275,8 @@ def on_register_changed(event):
|
||||
# For now, just record the lot
|
||||
HOOK_STATE.ensure_batch()
|
||||
with trace.open_tx("Register {} changed".format(event.regnum)):
|
||||
commands.putreg(event.frame, util.get_register_descs(event.frame.architecture()))
|
||||
commands.putreg(event.frame, util.get_register_descs(
|
||||
event.frame.architecture()))
|
||||
|
||||
|
||||
@log_errors
|
||||
@ -319,12 +308,9 @@ def check_for_continue(event):
|
||||
HOOK_STATE.in_break_w_cont = False
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_stop(event):
|
||||
if hasattr(event, 'breakpoints') and HOOK_STATE.mem_catchpoint in event.breakpoints:
|
||||
HOOK_STATE.skip_continue = True
|
||||
return
|
||||
if check_for_continue(event):
|
||||
HOOK_STATE.skip_continue = True
|
||||
return
|
||||
@ -416,8 +402,6 @@ def on_breakpoint_created(b):
|
||||
|
||||
@log_errors
|
||||
def on_breakpoint_modified(b):
|
||||
if b == HOOK_STATE.mem_catchpoint:
|
||||
return
|
||||
inf = gdb.selected_inferior()
|
||||
notify_others_breaks(inf)
|
||||
if inf.num not in INF_STATES:
|
||||
@ -466,20 +450,6 @@ def on_before_prompt():
|
||||
HOOK_STATE.end_batch()
|
||||
|
||||
|
||||
# This will be called by a catchpoint
|
||||
class GhidraTraceEventMemoryCommand(gdb.Command):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('hooks-ghidra event-memory', gdb.COMMAND_NONE)
|
||||
|
||||
def invoke(self, argument, from_tty):
|
||||
self.dont_repeat()
|
||||
on_syscall_memory()
|
||||
|
||||
|
||||
GhidraTraceEventMemoryCommand()
|
||||
|
||||
|
||||
def cmd_hook(name):
|
||||
|
||||
def _cmd_hook(func):
|
||||
@ -556,21 +526,6 @@ def install_hooks():
|
||||
# Respond to user-driven state changes: (Not target-driven)
|
||||
gdb.events.memory_changed.connect(on_memory_changed)
|
||||
gdb.events.register_changed.connect(on_register_changed)
|
||||
# Respond to target-driven memory map changes:
|
||||
# group:memory is actually a bit broad, but will probably port better
|
||||
# One alternative is to name all syscalls that cause a change....
|
||||
# Ones we could probably omit:
|
||||
# msync,
|
||||
# (Deals in syncing file-backed pages to disk.)
|
||||
# mlock, munlock, mlockall, munlockall, mincore, madvise,
|
||||
# (Deal in paging. Doesn't affect valid addresses.)
|
||||
# mbind, get_mempolicy, set_mempolicy, migrate_pages, move_pages
|
||||
# (All NUMA stuff)
|
||||
#
|
||||
if HOOK_STATE.mem_catchpoint is not None:
|
||||
HOOK_STATE.mem_catchpoint.enabled = True
|
||||
else:
|
||||
HOOK_STATE.mem_catchpoint = util.MEM_CATCHPOINT_SETTER.install_catchpoint()
|
||||
|
||||
gdb.events.cont.connect(on_cont)
|
||||
gdb.events.stop.connect(on_stop)
|
||||
@ -605,7 +560,6 @@ def remove_hooks():
|
||||
|
||||
gdb.events.memory_changed.disconnect(on_memory_changed)
|
||||
gdb.events.register_changed.disconnect(on_register_changed)
|
||||
HOOK_STATE.mem_catchpoint.enabled = False
|
||||
|
||||
gdb.events.cont.disconnect(on_cont)
|
||||
gdb.events.stop.disconnect(on_stop)
|
||||
|
@ -1,17 +1,17 @@
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from collections import namedtuple
|
||||
import bisect
|
||||
@ -245,6 +245,14 @@ class RegionInfoReader(object):
|
||||
sizeptr = int(gdb.parse_and_eval('sizeof(void*)')) * 8
|
||||
return Region(0, 1 << sizeptr, 0, None, 'full memory')
|
||||
|
||||
def have_changed(self, regions):
|
||||
if len(regions) == 1 and regions[0].objfile == 'full memory':
|
||||
return False, None
|
||||
new_regions = self.get_regions()
|
||||
if new_regions == regions:
|
||||
return False, None
|
||||
return True, new_regions
|
||||
|
||||
|
||||
class RegionInfoReaderV8(RegionInfoReader):
|
||||
cmd = REGIONS_CMD
|
||||
@ -344,41 +352,6 @@ def _choose_breakpoint_location_info_reader():
|
||||
BREAKPOINT_LOCATION_INFO_READER = _choose_breakpoint_location_info_reader()
|
||||
|
||||
|
||||
class MemCatchpointSetterV8(object):
|
||||
def install_catchpoint(self):
|
||||
return object()
|
||||
|
||||
|
||||
class MemCatchpointSetterV11(object):
|
||||
def install_catchpoint(self):
|
||||
breaks_before = set(gdb.breakpoints())
|
||||
try:
|
||||
gdb.execute("""
|
||||
catch syscall group:memory
|
||||
commands
|
||||
silent
|
||||
hooks-ghidra event-memory
|
||||
cont
|
||||
end
|
||||
""")
|
||||
return (set(gdb.breakpoints()) - breaks_before).pop()
|
||||
except Exception as e:
|
||||
print(f"Error setting memory catchpoint: {e}")
|
||||
return object()
|
||||
|
||||
|
||||
def _choose_mem_catchpoint_setter():
|
||||
if GDB_VERSION.major >= 11:
|
||||
return MemCatchpointSetterV11()
|
||||
if GDB_VERSION.major >= 8:
|
||||
return MemCatchpointSetterV8()
|
||||
else:
|
||||
raise gdb.GdbError(
|
||||
"GDB version not recognized by ghidragdb: " + GDB_VERSION.full)
|
||||
|
||||
|
||||
MEM_CATCHPOINT_SETTER = _choose_mem_catchpoint_setter()
|
||||
|
||||
def set_bool_param_by_api(name, value):
|
||||
gdb.set_parameter(name, value)
|
||||
|
||||
@ -418,11 +391,11 @@ def get_register_descs(arch, group='all'):
|
||||
if hasattr(arch, "registers"):
|
||||
try:
|
||||
return arch.registers(group)
|
||||
except ValueError: # No such group, or version too old
|
||||
except ValueError: # No such group, or version too old
|
||||
return arch.registers()
|
||||
else:
|
||||
descs = []
|
||||
try:
|
||||
try:
|
||||
regset = gdb.execute(
|
||||
f"info registers {group}", to_string=True).strip().split('\n')
|
||||
except Exception as e:
|
||||
@ -433,7 +406,8 @@ def get_register_descs(arch, group='all'):
|
||||
tokens = line.strip().split()
|
||||
descs.append(RegisterDesc(tokens[0]))
|
||||
return descs
|
||||
|
||||
|
||||
|
||||
def selected_frame():
|
||||
try:
|
||||
return gdb.selected_frame()
|
||||
|
Loading…
Reference in New Issue
Block a user