mirror of https://github.com/snare/voltron.git
291 lines
11 KiB
Python
291 lines
11 KiB
Python
try:
|
|
import logging
|
|
blessed = None
|
|
import blessed
|
|
|
|
import voltron
|
|
from voltron.core import Server
|
|
from voltron.plugin import PluginManager
|
|
try:
|
|
import lldb
|
|
in_lldb = True
|
|
except ImportError:
|
|
in_lldb = False
|
|
try:
|
|
import gdb
|
|
in_gdb = True
|
|
except ImportError:
|
|
in_gdb = False
|
|
|
|
if "vtrace" in locals():
|
|
in_vdb = True
|
|
|
|
import os
|
|
import sys
|
|
|
|
def parent_directory(the_path):
|
|
return os.path.abspath(os.path.join(the_path, os.pardir))
|
|
|
|
def add_vdb_to_path(vtrace):
|
|
sys.path.append(parent_directory(parent_directory(vtrace.__file__)))
|
|
|
|
# don't pass over this line!
|
|
# in order for the *VDB adaptor plugin* (not this file) to import
|
|
# vdb stuff, the import path must be updated.
|
|
# this is it.
|
|
#
|
|
# since atm vdb imports everything relatively
|
|
# (typically its not installed), then we use this
|
|
# hack to extract the package that's in use.
|
|
add_vdb_to_path(vtrace)
|
|
import vtrace
|
|
else:
|
|
in_vdb = False
|
|
|
|
log = voltron.setup_logging('debugger')
|
|
|
|
class VoltronCommand (object):
|
|
"""
|
|
Parent class for common methods across all debugger hosts.
|
|
"""
|
|
def handle_command(self, command):
|
|
global log
|
|
if "status" in command:
|
|
self.status()
|
|
elif 'debug' in command:
|
|
if 'enable' in command:
|
|
log.setLevel(logging.DEBUG)
|
|
print("Debug logging enabled")
|
|
elif 'disable' in command:
|
|
log.setLevel(logging.INFO)
|
|
print("Debug logging disabled")
|
|
else:
|
|
enabled = "enabled" if log.getEffectiveLevel() == logging.DEBUG else "disabled"
|
|
print("Debug logging is currently " + enabled)
|
|
elif 'init' in command:
|
|
self.register_hooks()
|
|
elif 'stopped' in command or 'update' in command:
|
|
self.adaptor.update_state()
|
|
self.server.dispatch_queue()
|
|
else:
|
|
print("Usage: voltron <init|status|debug|update>")
|
|
|
|
def status(self):
|
|
if self.server is not None:
|
|
summs = self.server.client_summary()
|
|
print("The following listeners are active:")
|
|
listen = voltron.config['server']['listen']
|
|
if listen['domain']:
|
|
print(" domain socket ({})".format(voltron.env['sock']))
|
|
if listen['tcp']:
|
|
print(" TCP socket ({})".format(listen['tcp']))
|
|
if listen['http']:
|
|
print(" web server ({})".format(listen['http']))
|
|
print("There are {} clients attached:".format(len(summs)))
|
|
for summary in summs:
|
|
print(" " + summary)
|
|
else:
|
|
print("Server is not running (no inferior)")
|
|
|
|
if in_lldb:
|
|
class VoltronLLDBCommand (VoltronCommand):
|
|
"""
|
|
Debugger command class for LLDB
|
|
"""
|
|
def __init__(self, debugger, dict):
|
|
super(VoltronCommand, self).__init__()
|
|
|
|
# grab the debugger and command interpreter
|
|
self.debugger = debugger
|
|
self.ci = self.debugger.GetCommandInterpreter()
|
|
|
|
# install the voltron command handler
|
|
self.debugger.HandleCommand('command script add -f dbgentry.lldb_invoke voltron')
|
|
|
|
# load plugins
|
|
self.pm = voltron.plugin.pm
|
|
|
|
# set up an lldb adaptor and set it as the package-wide adaptor
|
|
self.adaptor = self.pm.debugger_plugin_for_host('lldb').adaptor_class()
|
|
voltron.debugger = self.adaptor
|
|
|
|
# register plugins now that we have a debugger
|
|
self.pm.register_plugins()
|
|
|
|
# start the server
|
|
self.server = Server()
|
|
self.server.start()
|
|
|
|
self.hook_idx = None
|
|
|
|
def invoke(self, debugger, command, result, dict):
|
|
self.handle_command(command)
|
|
|
|
def register_hooks(self):
|
|
try:
|
|
output = self.adaptor.command("target stop-hook list")
|
|
if 'voltron' not in output:
|
|
output = self.adaptor.command('target stop-hook add -o \'voltron stopped\'')
|
|
try:
|
|
# hahaha this sucks
|
|
self.hook_idx = int(res.GetOutput().strip().split()[2][1:])
|
|
except:
|
|
pass
|
|
print("Registered stop-hook")
|
|
except:
|
|
print("No targets")
|
|
|
|
def unregister_hooks(self):
|
|
cmd = 'target stop-hook delete {}'.format(self.hook_idx if self.hook_idx else '')
|
|
self.debugger.HandleCommand(cmd)
|
|
|
|
def __lldb_init_module(debugger, env_dict):
|
|
"""
|
|
Called by LLDB when the module is loaded
|
|
"""
|
|
if 'cmd' not in env_dict:
|
|
log.debug("Initialising LLDB command")
|
|
env_dict['cmd'] = VoltronLLDBCommand(debugger, env_dict)
|
|
print(blessed.Terminal().bold_red("Voltron loaded."))
|
|
print("Run `voltron init` after you load a target.")
|
|
env_dict['cmd'].adaptor.host.HandleCommand("script import voltron")
|
|
|
|
def lldb_invoke(debugger, command, result, env_dict):
|
|
"""
|
|
Called when the voltron command is invoked within LLDB
|
|
"""
|
|
env_dict['cmd'].invoke(debugger, command, result, env_dict)
|
|
|
|
if in_gdb:
|
|
class VoltronGDBCommand (VoltronCommand, gdb.Command):
|
|
"""
|
|
Debugger command class for GDB
|
|
"""
|
|
def __init__(self):
|
|
super(VoltronCommand, self).__init__("voltron", gdb.COMMAND_NONE, gdb.COMPLETE_NONE)
|
|
|
|
# load plugins
|
|
self.pm = PluginManager()
|
|
|
|
# set up a gdb adaptor and set it as the package-wide adaptor
|
|
self.adaptor = self.pm.debugger_plugin_for_host('gdb').adaptor_class()
|
|
voltron.debugger = self.adaptor
|
|
|
|
# register plugins now that we have a debugger
|
|
self.pm.register_plugins()
|
|
|
|
# server is started and stopped with the inferior to avoid GDB hanging on exit
|
|
self.server = None
|
|
|
|
def invoke(self, arg, from_tty):
|
|
self.handle_command(arg)
|
|
|
|
def register_hooks(self):
|
|
gdb.events.stop.connect(self.stop_handler)
|
|
gdb.events.exited.connect(self.stop_and_exit_handler)
|
|
gdb.events.cont.connect(self.cont_handler)
|
|
|
|
def unregister_hooks(self):
|
|
gdb.events.stop.disconnect(self.stop_handler)
|
|
gdb.events.exited.disconnect(self.stop_and_exit_handler)
|
|
gdb.events.cont.disconnect(self.cont_handler)
|
|
|
|
def stop_handler(self, event):
|
|
self.adaptor.update_state()
|
|
self.server.dispatch_queue()
|
|
log.debug('Inferior stopped')
|
|
|
|
def exit_handler(self, event):
|
|
log.debug('Inferior exited')
|
|
self.server.stop()
|
|
|
|
def stop_and_exit_handler(self, event):
|
|
log.debug('Inferior stopped and exited')
|
|
self.stop_handler(event)
|
|
self.exit_handler(event)
|
|
|
|
def cont_handler(self, event):
|
|
log.debug('Inferior continued')
|
|
if self.server == None or self.server.is_running == False:
|
|
self.server = Server()
|
|
self.server.start()
|
|
|
|
if __name__ == "__main__":
|
|
log.debug('Initialising GDB command')
|
|
inst = VoltronGDBCommand()
|
|
print(blessed.Terminal().bold_red("Voltron loaded."))
|
|
|
|
if in_vdb:
|
|
class VoltronVDBCommand(VoltronCommand, vtrace.Notifier):
|
|
"""
|
|
Debugger command class for VDB
|
|
"""
|
|
def __init__(self, vdb, vtrace):
|
|
"""
|
|
vdb is the debugger instance
|
|
vtrace is the vtrace module?
|
|
"""
|
|
super(VoltronCommand, self).__init__()
|
|
self._vdb = vdb
|
|
self._vtrace = vtrace
|
|
|
|
self.pm = PluginManager()
|
|
|
|
self.adaptor = self.pm.debugger_plugin_for_host('vdb').adaptor_class(self._vdb, self._vtrace)
|
|
voltron.debugger = self.adaptor
|
|
|
|
self.pm.register_plugins()
|
|
|
|
self.server = Server()
|
|
self.server.start()
|
|
|
|
def invoke(self, arg, from_tty):
|
|
self.handle_command(arg)
|
|
|
|
def register_hooks(self):
|
|
self._vdb.registerNotifier(vtrace.NOTIFY_ALL, self)
|
|
|
|
def unregister_hooks(self):
|
|
self._vdb.deregisterNotifier(vtrace.NOTIFY_ALL, self)
|
|
|
|
def notify(self, event, trace):
|
|
if event == self._vtrace.NOTIFY_DETACH:
|
|
self.exit_handler(event)
|
|
elif event == self._vtrace.NOTIFY_EXIT:
|
|
self.exit_handler(event)
|
|
elif event == self._vtrace.NOTIFY_BREAK:
|
|
self.stop_handler(event)
|
|
elif event == self._vtrace.NOTIFY_STEP:
|
|
self.stop_handler(event)
|
|
elif event == self._vtrace.NOTIFY_CONTINUE:
|
|
self.cont_handler(event)
|
|
|
|
def stop_handler(self, event):
|
|
self.adaptor.update_state()
|
|
log.debug('Inferior stopped')
|
|
|
|
def exit_handler(self, event):
|
|
log.debug('Inferior exited')
|
|
self.server.stop()
|
|
# vdb doesn't signal STOP/BREAK on exit, so we
|
|
# clear an outstanding Wait requests
|
|
self.adaptor.update_state()
|
|
|
|
def cont_handler(self, event):
|
|
log.debug('Inferior continued')
|
|
|
|
# wb: i have no idea if this __name__ test is actually correct
|
|
# but __builtin__ is its value when run from vdbbin
|
|
if __name__ == "__builtin__":
|
|
log.debug('Initialising VDB command')
|
|
inst = VoltronVDBCommand(db, vtrace)
|
|
inst.register_hooks()
|
|
print(blessed.Terminal().bold_red("Voltron loaded."))
|
|
|
|
|
|
except Exception as e:
|
|
msg = "Exception {} raised while loading Voltron: {}".format(type(e), str(e))
|
|
if blessed:
|
|
msg = blessed.Terminal().bold_red(msg)
|
|
print(msg)
|