mirror of
https://github.com/frida/frida-python
synced 2026-06-08 14:16:17 +00:00
@@ -32,3 +32,4 @@
|
||||
/src/frida/Makefile
|
||||
/src/frida/Makefile.in
|
||||
/stamp-h1
|
||||
.idea/
|
||||
|
||||
+185
-20
@@ -2,8 +2,12 @@ def main():
|
||||
from frida.application import ConsoleApplication
|
||||
from colorama import Fore, Style
|
||||
import json
|
||||
import platform
|
||||
try:
|
||||
import readline
|
||||
# Stupid hack to workaround oxs's shitty readline impl
|
||||
if platform.system() == "Darwin":
|
||||
import gnureadline as readline
|
||||
HAVE_READLINE = True
|
||||
except:
|
||||
HAVE_READLINE = False
|
||||
@@ -14,7 +18,16 @@ def main():
|
||||
def __init__(self):
|
||||
if HAVE_READLINE:
|
||||
readline.parse_and_bind("tab: complete")
|
||||
# force completor to run on first tab press
|
||||
readline.parse_and_bind("set show-all-if-unmodified on")
|
||||
readline.parse_and_bind("set show-all-if-ambiguous on")
|
||||
# Set our custom completer
|
||||
readline.set_completer(self.completer)
|
||||
self._idle = threading.Event()
|
||||
self._cond = threading.Condition()
|
||||
self._response = None
|
||||
self._completor_locals = []
|
||||
|
||||
super(REPLApplication, self).__init__(self._process_input)
|
||||
|
||||
def _usage(self):
|
||||
@@ -24,6 +37,7 @@ def main():
|
||||
return True
|
||||
|
||||
def _start(self):
|
||||
self._prompt_string = self._create_prompt()
|
||||
def on_message(message, data):
|
||||
self._reactor.schedule(lambda: self._process_message(message, data))
|
||||
self._script = self._session.create_script(self._create_repl_script())
|
||||
@@ -41,8 +55,7 @@ def main():
|
||||
self._script = None
|
||||
|
||||
def _create_repl_script(self):
|
||||
return """\
|
||||
|
||||
return """
|
||||
(function () {
|
||||
this.resume = function () {
|
||||
send({ name: '+resume' });
|
||||
@@ -66,17 +79,27 @@ def main():
|
||||
function onExpression(expression) {
|
||||
try {
|
||||
var result;
|
||||
eval("result = " + expression + ";");
|
||||
var sentRaw = false;
|
||||
if (result && result.hasOwnProperty('length')) {
|
||||
try {
|
||||
send({ name: '+result', payload: "OOB" }, result);
|
||||
sentRaw = true;
|
||||
} catch (e) {
|
||||
// If we passed in an object
|
||||
// TODO: Fix this gross nonsense
|
||||
if (typeof(expression) == "object"){
|
||||
if (expression.eval){
|
||||
eval("result = " + expression.eval + ";");
|
||||
}
|
||||
send({ name: '+silent_result', payload: result });
|
||||
// Otherwise expression is a string
|
||||
} else {
|
||||
eval("result = " + expression + ";");
|
||||
var sentRaw = false;
|
||||
if (result && result.hasOwnProperty('length')) {
|
||||
try {
|
||||
send({ name: '+result', payload: "OOB" }, result);
|
||||
sentRaw = true;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
if (!sentRaw) {
|
||||
send({ name: '+result', payload: result });
|
||||
}
|
||||
}
|
||||
if (!sentRaw) {
|
||||
send({ name: '+result', payload: result });
|
||||
}
|
||||
} catch (e) {
|
||||
send({ name: '+error', payload: e.toString() });
|
||||
@@ -87,11 +110,78 @@ def main():
|
||||
}).call(this);
|
||||
"""
|
||||
|
||||
def _synchronous_evaluate(self, text):
|
||||
self._reactor.schedule(lambda: self._send_expression({"eval": text}))
|
||||
self._cond.acquire()
|
||||
while self._response is None:
|
||||
self._cond.wait()
|
||||
response = self._response
|
||||
self._response = None
|
||||
self._cond.release()
|
||||
# TODO: This is super gross, need to actually fix this and unify conventions
|
||||
# once we know what we're doing with messages.
|
||||
if "payload" not in response[0]:
|
||||
response[0]['payload'] = None
|
||||
return response[0]['payload']
|
||||
|
||||
def _create_prompt(self):
|
||||
# Todo: Make this prompt less shitty and make sure all platforms are covered ;)
|
||||
device_type = self._device.type
|
||||
type_name = self._target[0]
|
||||
target = self._target[1]
|
||||
|
||||
if device_type == "local":
|
||||
if self._target[0] == "name":
|
||||
type_name = "ProcName"
|
||||
elif self._target[0] == "pid":
|
||||
type_name = "PID"
|
||||
prompt_string = "%s::%s::%s" % ("Local", type_name, target)
|
||||
|
||||
elif device_type == "tether" :
|
||||
prompt_string = "%s::%s::%s" % ("USB", self._device.name, target)
|
||||
|
||||
else:
|
||||
prompt_string = "%s::%s::%s" % (self._device.name, self._device.name, target)
|
||||
|
||||
return prompt_string
|
||||
|
||||
def completer(self, prefix, index):
|
||||
# If it's the first loop, do the init
|
||||
if index == 0:
|
||||
# If nothing is in the readline buffer
|
||||
if not prefix:
|
||||
# Give every key that doesn't start with a "_"
|
||||
self._completor_locals = [key for key in self._synchronous_evaluate("Object.keys(this)") if not key.startswith('_')]
|
||||
else:
|
||||
if prefix.endswith("."):
|
||||
thing_to_check = prefix.split(' ')[0][:-1]
|
||||
self._completor_locals = self._synchronous_evaluate("Object.keys(" + thing_to_check + ")")
|
||||
else:
|
||||
if "." in prefix:
|
||||
# grab the last statement
|
||||
target = prefix.split(' ')[-1]
|
||||
needle = target.split('.')[-1]
|
||||
# Chop off everything
|
||||
target = ".".join(target.split('.')[:-1])
|
||||
self._completor_locals = [target + "." + key for key in self._synchronous_evaluate("Object.keys(" + target + ")") if key.startswith(needle)]
|
||||
# Do stuff
|
||||
# Give every key that starts with the root prefix
|
||||
else:
|
||||
self._completor_locals = [key for key in self._synchronous_evaluate("Object.keys(this)") if key.startswith(prefix)]
|
||||
|
||||
try:
|
||||
return self._completor_locals[index]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def _process_input(self):
|
||||
if sys.version_info[0] >= 3:
|
||||
input_impl = input
|
||||
else:
|
||||
input_impl = raw_input
|
||||
|
||||
self._print_startup_message()
|
||||
|
||||
while True:
|
||||
self._idle.wait()
|
||||
expression = ""
|
||||
@@ -99,10 +189,12 @@ def main():
|
||||
while len(expression) == 0 or line.endswith("\\"):
|
||||
try:
|
||||
if len(expression) == 0:
|
||||
line = input_impl(">>> ")
|
||||
line = input_impl("[%s]" % self._prompt_string + "-> ")
|
||||
else:
|
||||
line = input_impl("... ")
|
||||
except EOFError:
|
||||
# An extra newline after EOF to exit the REPL cleanly
|
||||
print "\nThank you for using Frida!"
|
||||
return
|
||||
if len(line.strip()) > 0:
|
||||
if len(expression) > 0:
|
||||
@@ -111,8 +203,21 @@ def main():
|
||||
|
||||
if HAVE_READLINE:
|
||||
readline.add_history(expression)
|
||||
self._idle.clear()
|
||||
self._reactor.schedule(lambda: self._send_expression(expression))
|
||||
|
||||
if expression.endswith("?"):
|
||||
# Help feature
|
||||
self._print_help(expression)
|
||||
elif expression.startswith("%"):
|
||||
# "Magic" commands
|
||||
self._do_magic(expression)
|
||||
elif expression in ("quit", "q", "exit"):
|
||||
print "Thank you for using Frida!"
|
||||
return
|
||||
elif expression == "help":
|
||||
print "Frida help 1.0!"
|
||||
else:
|
||||
self._idle.clear()
|
||||
self._reactor.schedule(lambda: self._send_expression(expression))
|
||||
|
||||
def _send_expression(self, expression):
|
||||
self._script.post_message(expression)
|
||||
@@ -136,18 +241,79 @@ def main():
|
||||
output = Fore.RED + Style.BRIGHT + value + Style.RESET_ALL
|
||||
sys.stdout.write(output + "\n")
|
||||
sys.stdout.flush()
|
||||
self._idle.set()
|
||||
|
||||
handled = True
|
||||
elif name == '+resume':
|
||||
self._resume()
|
||||
self._idle.set()
|
||||
|
||||
handled = True
|
||||
elif name == "+silent_result":
|
||||
# TODO: This is hacky as shit, need to make it cleaner/better
|
||||
self._cond.acquire()
|
||||
self._response = (stanza, data)
|
||||
self._cond.notify()
|
||||
self._cond.release()
|
||||
|
||||
self._idle.set()
|
||||
handled = True
|
||||
|
||||
if not handled:
|
||||
print("message:", message, "data:", data)
|
||||
|
||||
def _print_startup_message(self):
|
||||
print """ _____
|
||||
(_____)
|
||||
| | Frida v3.0 - A world-class dynamic instrumentation framework
|
||||
| |
|
||||
|`-'| Commands:
|
||||
| | help -> Displays the help system
|
||||
| | object? -> Display information about 'object'
|
||||
| | exit/quit -> Exit
|
||||
| |
|
||||
| | More info at http://www.frida.re/docs/home/
|
||||
`._.'
|
||||
|
||||
"""
|
||||
|
||||
def _print_help(self, expression):
|
||||
# TODO: Figure out docstrings and implement here. This is real jankaty right now.
|
||||
help_text = ""
|
||||
if expression.endswith(".?"):
|
||||
expression = expression[:-2] + "?"
|
||||
|
||||
obj_to_identify = [x for x in expression.split(' ') if x.endswith("?")][0][:-1]
|
||||
obj_type = self._synchronous_evaluate("typeof(%s)" % obj_to_identify)
|
||||
|
||||
if obj_type == "function":
|
||||
|
||||
signature = self._synchronous_evaluate("%s.toString()" % obj_to_identify)
|
||||
clean_signature = signature.split("{")[0][:-1].split('function ')[-1]
|
||||
|
||||
if "[native code]" in signature:
|
||||
help_text += "Type: Function (native)\n"
|
||||
else:
|
||||
help_text += "Type: Function\n"
|
||||
|
||||
help_text += "Signature: %s\n" % clean_signature
|
||||
help_text += "Docstring: #TODO :)"
|
||||
elif obj_type == "object":
|
||||
help_text += "Type: Object\n"
|
||||
help_text += "Docstring: #TODO :)"
|
||||
|
||||
elif obj_type == "boolean":
|
||||
help_text += "Type: Boolean\n"
|
||||
help_text += "Docstring: #TODO :)"
|
||||
|
||||
elif obj_type == "string":
|
||||
help_text += "Type: Boolean\n"
|
||||
help_text += "Text: %s\n" % self._synchronous_evaluate("%s.toString()" % obj_to_identify)
|
||||
help_text += "Docstring: #TODO :)"
|
||||
|
||||
print help_text
|
||||
|
||||
|
||||
def _do_magic(self, expression):
|
||||
#TODO: add local file read capabilities i.e. %run /tmp/script.txt, and other stuff?
|
||||
print "You thought I was sleeping, didn't you. Acting."
|
||||
|
||||
def hexdump(src, length=16):
|
||||
try:
|
||||
xrange
|
||||
@@ -171,6 +337,5 @@ def main():
|
||||
app = REPLApplication()
|
||||
app.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ setup(
|
||||
"frida-ps = frida.ps:main",
|
||||
"frida-discover = frida.discoverer:main",
|
||||
"frida-trace = frida.tracer:main",
|
||||
"frida-repl = frida.repl:main"
|
||||
"frida = frida.repl:main"
|
||||
]
|
||||
},
|
||||
ext_modules=[Extension('_frida', [])],
|
||||
|
||||
Reference in New Issue
Block a user