mirror of
https://github.com/frida/frida-python
synced 2026-06-08 14:16:17 +00:00
Avoid logging getting overwritten by the status message changing
This commit is contained in:
+28
-10
@@ -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():
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user