diff --git a/src/frida/application.py b/src/frida/application.py index 376f3d7..4acb360 100755 --- a/src/frida/application.py +++ b/src/frida/application.py @@ -2,6 +2,7 @@ import collections from optparse import OptionParser +import os import sys import threading import time @@ -27,6 +28,17 @@ class ConsoleApplication(object): action='store_const', const='tether', dest="device_type", default='local') parser.add_option("-R", "--remote", help="connect to remote device", action='store_const', const='remote', dest="device_type", default='local') + if self._needs_target(): + def store_target(option, opt_str, target_value, parser, target_type, *args, **kwargs): + if target_type == 'file': + target_value = [target_value] + setattr(parser.values, 'target', (target_type, target_value)) + parser.add_option("-f", "--file", help="spawn FILE", metavar="FILE", + type='string', action='callback', callback=store_target, callback_args=('file',)) + parser.add_option("-n", "--attach-name", help="attach to NAME", metavar="NAME", + type='string', action='callback', callback=store_target, callback_args=('name',)) + parser.add_option("-p", "--attach-pid", help="attach to PID", metavar="PID", + type='int', action='callback', callback=store_target, callback_args=('pid',)) self._add_options(parser) (options, args) = parser.parse_args() @@ -34,25 +46,35 @@ class ConsoleApplication(object): self._device_type = options.device_type self._device = None self._schedule_on_device_lost = lambda: self._reactor.schedule(self._on_device_lost) - self._target = None + self._spawned_pid = None self._process = None self._schedule_on_process_detached = lambda: self._reactor.schedule(self._on_process_detached) self._started = False + self._resumed = False self._reactor = Reactor(run_until_return) self._exit_status = None self._status_updated = False - self._initialize(parser, options, args) - - target_specifier = self._target_specifier(parser, options, args) - if target_specifier is not None: - try: - self._target = int(target_specifier) - except: - self._target = target_specifier + if self._needs_target(): + target = getattr(options, 'target', None) + if target is None: + if len(args) < 1: + parser.error("target file, process name or pid must be specified") + target = infer_target(args[0]) + args.pop(0) + target = expand_target(target) + if target[0] == 'file': + argv = target[1] + if not os.path.isfile(argv[0]): + parser.error("%s: file not found" % argv[0]) + argv.extend(args) + args = [] + self._target = target else: self._target = None + self._initialize(parser, options, args) + def run(self): mgr = frida.get_device_manager() on_devices_changed = lambda: self._reactor.schedule(self._try_start) @@ -66,6 +88,8 @@ class ConsoleApplication(object): self._process.off('detached', self._schedule_on_process_detached) self._process.detach() self._process = None + if self._spawned_pid is not None: + self._device.kill(self._spawned_pid) if self._device is not None: self._device.off('lost', self._schedule_on_device_lost) mgr.off('changed', on_devices_changed) @@ -78,8 +102,8 @@ class ConsoleApplication(object): def _initialize(self, parser, options, args): pass - def _target_specifier(self, parser, options, args): - return None + def _needs_target(self): + return False def _start(self): pass @@ -87,6 +111,13 @@ class ConsoleApplication(object): def _stop(self): pass + def _resume(self): + if self._resumed: + return + if self._spawned_pid is not None: + self._device.resume(self._spawned_pid) + self._resumed = True + def _exit(self, exit_status): self._exit_status = exit_status self._reactor.stop() @@ -100,8 +131,16 @@ class ConsoleApplication(object): self._device.on('lost', self._schedule_on_device_lost) if self._target is not None: try: - self._update_status("Attaching...") - self._process = self._device.attach(self._target) + target_type, target_value = self._target + if target_type == 'file': + argv = target_value + self._update_status("Spawning: %s" % " ".join(argv)) + self._spawned_pid = self._device.spawn(argv) + attach_target = self._spawned_pid + else: + attach_target = target_value + self._update_status("Attaching...") + self._process = self._device.attach(attach_target) self._process.on('detached', self._schedule_on_process_detached) except Exception as e: self._update_status("Failed to attach: %s" % e) @@ -138,6 +177,24 @@ def find_device(type): return device return None +def infer_target(target_value): + if target_value.startswith('.') or target_value.startswith(os.path.sep): + target_type = 'file' + target_value = [target_value] + else: + try: + target_value = int(target_value) + target_type = 'pid' + except: + target_type = 'name' + return (target_type, target_value) + +def expand_target(target): + target_type, target_value = target + if target_type == 'file': + target_value = [os.path.abspath(target_value[0])] + return (target_type, target_value) + class Reactor(object): def __init__(self, run_until_return): diff --git a/src/frida/discoverer.py b/src/frida/discoverer.py index fcf6c71..b551b61 100644 --- a/src/frida/discoverer.py +++ b/src/frida/discoverer.py @@ -153,15 +153,13 @@ def main(): class DiscovererApplication(ConsoleApplication, UI): def _usage(self): - return "usage: %prog [options] process-name-or-id" + return "usage: %prog [options] target" def _initialize(self, parser, options, args): self._discoverer = None - def _target_specifier(self, parser, options, args): - if len(args) != 1: - parser.error("process name or id must be specified") - return args[0] + def _needs_target(self): + return True def _start(self): self._update_status("Injecting script...") diff --git a/src/frida/repl.py b/src/frida/repl.py index e64a4f5..3087e10 100644 --- a/src/frida/repl.py +++ b/src/frida/repl.py @@ -17,12 +17,10 @@ def main(): super(REPLApplication, self).__init__(self._process_input) def _usage(self): - return "usage: %prog [options]" + return "usage: %prog [options] target" - def _target_specifier(self, parser, options, args): - if len(args) != 1: - parser.error("process name or id must be specified") - return args[0] + def _needs_target(self): + return True def _start(self): def on_message(message, data): diff --git a/src/frida/tracer.py b/src/frida/tracer.py index 26a0e5a..f69c15a 100644 --- a/src/frida/tracer.py +++ b/src/frida/tracer.py @@ -449,16 +449,14 @@ def main(): self._profile_builder = pb def _usage(self): - return "usage: %prog [options] process-name-or-id" + return "usage: %prog [options] target" def _initialize(self, parser, options, args): self._tracer = None self._profile = self._profile_builder.build() - def _target_specifier(self, parser, options, args): - if len(args) != 1: - parser.error("process name or id must be specified") - return args[0] + def _needs_target(self): + return True def _start(self): self._tracer = Tracer(self._reactor, FileRepository(), self._profile)