Add spawn support to ConsoleApplication

This commit is contained in:
Ole André Vadla Ravnås
2015-02-21 22:18:36 +01:00
parent be8bb74f93
commit 4d3d22f12e
4 changed files with 79 additions and 28 deletions
+70 -13
View File
@@ -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):
+3 -5
View File
@@ -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...")
+3 -5
View File
@@ -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):
+3 -5
View File
@@ -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)