Avoid logging getting overwritten by the status message changing

This commit is contained in:
Ole André Vadla Ravnås
2015-09-25 19:32:23 +02:00
parent 65cbaaabe4
commit 4de31ce812
6 changed files with 79 additions and 56 deletions
+28 -10
View File
@@ -12,7 +12,7 @@ import threading
import time
import colorama
from colorama import Style
from colorama import Fore, Style
import frida
@@ -55,6 +55,11 @@ def await_enter(reactor):
except KeyboardInterrupt:
print('')
class ConsoleState:
EMPTY = 1
STATUS = 2
TEXT = 3
class ConsoleApplication(object):
def __init__(self, run_until_return=await_enter, on_stop=None):
colorama.init()
@@ -99,7 +104,7 @@ class ConsoleApplication(object):
self._resumed = False
self._reactor = Reactor(run_until_return, on_stop)
self._exit_status = None
self._status_updated = False
self._console_state = ConsoleState.EMPTY
if self._device_id is not None and self._device_type is not None:
parser.error("-D cannot be used with -U and -R")
@@ -206,9 +211,7 @@ class ConsoleApplication(object):
self._session = self._device.attach(attach_target)
if self._enable_debugger:
self._session.enable_debugger()
self._update_status("")
print("Debugger listening on port 5858\n")
self._update_status("Attaching...")
self._print("Debugger listening on port 5858\n")
self._session.on('detached', self._schedule_on_session_detached)
except Exception as e:
if spawning:
@@ -222,25 +225,40 @@ class ConsoleApplication(object):
def _show_message_if_no_device(self):
if self._device is None:
print("Waiting for USB device to appear...")
self._print("Waiting for USB device to appear...")
def _on_device_lost(self):
if self._exit_status is not None:
return
print("Device disconnected.")
self._print("Device disconnected.")
self._exit(1)
def _on_session_detached(self):
print("Target process terminated.")
self._print("Target process terminated.")
self._exit(1)
def _clear_status(self):
if self._console_state == ConsoleState.STATUS:
print("\033[A" + (80 * " "))
def _update_status(self, message):
if self._status_updated:
if self._console_state == ConsoleState.STATUS:
cursor_position = "\033[A"
else:
cursor_position = ""
print("%-80s" % (cursor_position + Style.BRIGHT + message + Style.RESET_ALL,))
self._status_updated = True
self._console_state = ConsoleState.STATUS
def _print(self, *args, **kwargs):
print(*args, **kwargs)
self._console_state = ConsoleState.TEXT
def _log(self, level, text):
if level == 'info':
self._print(text)
else:
color = Fore.RED if level == 'error' else Fore.YELLOW
self._print(color + Style.BRIGHT + text + Style.RESET_ALL)
def find_device(type):
for device in frida.enumerate_devices():
+8 -8
View File
@@ -170,7 +170,7 @@ def main():
self._discoverer.start(self._session, self)
def _stop(self):
print("Stopping...")
self._print("Stopping...")
self._discoverer.dispose()
self._discoverer = None
@@ -180,17 +180,17 @@ def main():
def on_sample_result(self, module_functions, dynamic_functions):
for module, functions in module_functions.items():
print(module.name)
print("\t%-10s\t%s" % ("Calls", "Function"))
self._print(module.name)
self._print("\t%-10s\t%s" % ("Calls", "Function"))
for function, count in sorted(functions, key=lambda item: item[1], reverse=True):
print("\t%-10d\t%s" % (count, function))
print("")
self._print("\t%-10d\t%s" % (count, function))
self._print("")
if len(dynamic_functions) > 0:
print("Dynamic functions:")
print("\t%-10s\t%s" % ("Calls", "Function"))
self._print("Dynamic functions:")
self._print("\t%-10s\t%s" % ("Calls", "Function"))
for function, count in sorted(dynamic_functions, key=lambda item: item[1], reverse=True):
print("\t%-10d\t%s" % (count, function))
self._print("\t%-10d\t%s" % (count, function))
self._results_received.set()
+3 -3
View File
@@ -22,13 +22,13 @@ def main():
header_format = "%-" + str(id_column_width) + "s " + \
"%-" + str(type_column_width) + "s " + \
"%-" + str(name_column_width) + "s"
print(header_format % ("Id", "Type", "Name"))
print("%s %s %s" % (id_column_width * "-", type_column_width * "-", name_column_width * "-"))
self._print(header_format % ("Id", "Type", "Name"))
self._print("%s %s %s" % (id_column_width * "-", type_column_width * "-", name_column_width * "-"))
line_format = "%-" + str(id_column_width) + "s " + \
"%-" + str(type_column_width) + "s " + \
"%-" + str(name_column_width) + "s"
for device in sorted(devices, key=cmp_to_key(compare_devices)):
print(line_format % (device.id, device.type, device.name))
self._print(line_format % (device.id, device.type, device.name))
self._exit(0)
def compare_devices(a, b):
+7 -7
View File
@@ -41,16 +41,16 @@ def main():
header_format = "%" + str(pid_column_width) + "s " + \
"%-" + str(name_column_width) + "s " + \
"%-" + str(identifier_column_width) + "s"
print(header_format % ("PID", "Name", "Identifier"))
print("%s %s %s" % (pid_column_width * "-", name_column_width * "-", identifier_column_width * "-"))
self._print(header_format % ("PID", "Name", "Identifier"))
self._print("%s %s %s" % (pid_column_width * "-", name_column_width * "-", identifier_column_width * "-"))
line_format = "%" + str(pid_column_width) + "s " + \
"%-" + str(name_column_width) + "s " + \
"%-" + str(identifier_column_width) + "s"
for app in sorted(applications, key=cmp_to_key(compare_applications)):
if app.pid == 0:
print(line_format % ("-", app.name, app.identifier))
self._print(line_format % ("-", app.name, app.identifier))
else:
print(line_format % (app.pid, app.name, app.identifier))
self._print(line_format % (app.pid, app.name, app.identifier))
else:
try:
processes = self._device.enumerate_processes()
@@ -61,11 +61,11 @@ def main():
pid_column_width = max(map(lambda p: len("%d" % p.pid), processes))
name_column_width = max(map(lambda p: len(p.name), processes))
header_format = "%" + str(pid_column_width) + "s %s"
print(header_format % ("PID", "Name"))
print("%s %s" % (pid_column_width * "-", name_column_width * "-"))
self._print(header_format % ("PID", "Name"))
self._print("%s %s" % (pid_column_width * "-", name_column_width * "-"))
line_format = "%" + str(pid_column_width) + "d %s"
for process in sorted(processes, key=cmp_to_key(compare_processes)):
print(line_format % (process.pid, process.name))
self._print(line_format % (process.pid, process.name))
self._exit(0)
def compare_applications(a, b):
+20 -17
View File
@@ -56,7 +56,7 @@ def main():
if self._spawned_argv is not None:
self._update_status("Spawned `{command}`. Use %resume to let the main thread start executing!".format(command=" ".join(self._spawned_argv)))
else:
sys.stdout.write("\033[A")
self._clear_status()
self._ready.set()
def _on_stop(self):
@@ -74,6 +74,7 @@ def main():
def _load_script(self):
self._seqno += 1
script = self._session.create_script(name="repl%d" % self._seqno, source=self._create_repl_script())
script.set_log_handler(self._log)
self._unload_script()
self._script = script
def on_message(message, data):
@@ -125,7 +126,7 @@ def main():
eventloop.close()
except EOFError:
# An extra newline after EOF to exit the REPL cleanly
print("\nThank you for using Frida!")
self._print("\nThank you for using Frida!")
return
except KeyboardInterrupt:
line = ""
@@ -140,17 +141,16 @@ def main():
self._print_help(expression)
except JavaScriptError as e:
error = e.error
sys.stdout.write(Fore.RED + Style.BRIGHT + error['name'] + Style.RESET_ALL + ": " + error['message'] + "\n")
sys.stdout.flush()
self._print(Fore.RED + Style.BRIGHT + error['name'] + Style.RESET_ALL + ": " + error['message'])
except frida.InvalidOperationError:
return
elif expression.startswith("%"):
self._do_magic(expression[1:].rstrip())
elif expression in ("exit", "quit", "q"):
print("Thank you for using Frida!")
self._print("Thank you for using Frida!")
return
elif expression == "help":
print("Help: #TODO :)")
self._print("Help: #TODO :)")
else:
self._eval_and_print(expression)
@@ -168,11 +168,10 @@ def main():
output = Fore.RED + Style.BRIGHT + error['name'] + Style.RESET_ALL + ": " + error['message']
except frida.InvalidOperationError:
return
sys.stdout.write(output + "\n")
sys.stdout.flush()
self._print(output)
def _print_startup_message(self):
print(""" _____
self._print(""" _____
(_____)
| | Frida {version} - A world-class dynamic instrumentation framework
| |
@@ -219,7 +218,7 @@ def main():
help_text += "Text: %s\n" % self._evaluate("%s.toString()" % obj_to_identify)[1]
help_text += "Docstring: #TODO :)"
print(help_text)
self._print(help_text)
# Negative means at least abs(val) - 1
_magic_command_args = {
@@ -238,8 +237,8 @@ def main():
required_args = self._magic_command_args.get(command)
if required_args == None:
print("Unknown command: {}".format(command))
print("Valid commands: {}".format(", ".join(self._magic_command_args.keys())))
self._print("Unknown command: {}".format(command))
self._print("Valid commands: {}".format(", ".join(self._magic_command_args.keys())))
return
atleast_args = False
@@ -249,7 +248,7 @@ def main():
if (not atleast_args and len(args) != required_args) or \
(atleast_args and len(args) < required_args):
print("{cmd} command expects {atleast}{n} argument{s}".format(
self._print("{cmd} command expects {atleast}{n} argument{s}".format(
cmd=command, atleast='atleast ' if atleast_args else '', n=required_args, s='' if required_args == 1 else ' '))
return
@@ -289,7 +288,7 @@ def main():
if result[0] is None:
return True
else:
print("Failed to load script: {error}".format(error=result[0]))
self._print("Failed to load script: {error}".format(error=result[0]))
return False
def _create_prompt(self):
@@ -331,13 +330,17 @@ def main():
raise JavaScriptError(stanza['payload'])
def _process_message(self, message, data):
if message['type'] == 'send':
message_type = message['type']
if message_type == 'send':
stanza = message['payload']
with self._response_cond:
self._response_data = (stanza, data)
self._response_cond.notify()
elif message_type == 'error':
text = message.get('stack', message['description'])
self._log('error', text)
else:
print("message:", message, "data:", data)
self._print("message:", message, "data:", data)
def _create_repl_script(self):
user_script = ""
@@ -485,7 +488,7 @@ def main():
continue
yield Completion(key, -len(before_dot))
except Exception as e:
print(e)
self._print(e)
def _get_keys(self, code):
return sorted(
+13 -11
View File
@@ -67,8 +67,9 @@ class TracerProfile(object):
def __init__(self, spec):
self._spec = spec
def resolve(self, session):
def resolve(self, session, log_handler=None):
script = session.create_script(name="profile-resolver", source=self._create_resolver_script())
script.set_log_handler(log_handler)
result = [None, None]
completed = threading.Event()
def on_message(message, data):
@@ -922,11 +923,12 @@ function regExpEscape(s) {
"""
class Tracer(object):
def __init__(self, reactor, repository, profile):
def __init__(self, reactor, repository, profile, log_handler=None):
self._reactor = reactor
self._repository = repository
self._profile = profile
self._script = None
self._log_handler = log_handler
def start_trace(self, session, ui):
def on_create(*args):
@@ -955,9 +957,10 @@ class Tracer(object):
self._reactor.schedule(lambda: self._process_message(message, data, ui))
ui.on_trace_progress('resolve')
working_set = self._profile.resolve(session)
working_set = self._profile.resolve(session, log_handler=self._log_handler)
ui.on_trace_progress('instrument')
self._script = session.create_script(name="tracer", source=self._create_trace_script())
self._script.set_log_handler(self._log_handler)
self._script.on('message', on_message)
self._script.load()
for chunk in [working_set[i:i+1000] for i in range(0, len(working_set), 1000)]:
@@ -1399,11 +1402,11 @@ def main():
return True
def _start(self):
self._tracer = Tracer(self._reactor, FileRepository(), self._profile)
self._tracer = Tracer(self._reactor, FileRepository(), self._profile, log_handler=self._log)
self._targets = self._tracer.start_trace(self._session, self)
def _stop(self):
print("Stopping...")
self._print("Stopping...")
self._tracer.stop()
self._tracer = None
@@ -1428,24 +1431,23 @@ def main():
self._resume()
def on_trace_error(self, error):
print(Fore.RED + Style.BRIGHT + "Error" + Style.RESET_ALL + ": " + error['message'])
self._print(Fore.RED + Style.BRIGHT + "Error" + Style.RESET_ALL + ": " + error['message'])
def on_trace_events(self, events):
self._status_updated = False
no_attributes = Style.RESET_ALL
for timestamp, thread_id, depth, target_address, message in events:
indent = depth * " | "
attributes = self._get_attributes(thread_id)
if thread_id != self._last_event_tid:
print("%s /* TID 0x%x */%s" % (attributes, thread_id, Style.RESET_ALL))
self._print("%s /* TID 0x%x */%s" % (attributes, thread_id, Style.RESET_ALL))
self._last_event_tid = thread_id
print("%6d ms %s%s%s%s" % (timestamp, attributes, indent, message, no_attributes))
self._print("%6d ms %s%s%s%s" % (timestamp, attributes, indent, message, no_attributes))
def on_trace_handler_create(self, function, handler, source):
print("%s: Auto-generated handler at \"%s\"" % (function, source))
self._print("%s: Auto-generated handler at \"%s\"" % (function, source))
def on_trace_handler_load(self, function, handler, source):
print("%s: Loaded handler at \"%s\"" % (function, source))
self._print("%s: Loaded handler at \"%s\"" % (function, source))
def _get_attributes(self, thread_id):
attributes = self._attributes_by_thread_id.get(thread_id, None)