From 0c9ee3138371e6a36c3ddebfa4a7280e9ab6697c Mon Sep 17 00:00:00 2001 From: Chris Ellsworth Date: Thu, 9 Apr 2020 09:11:17 -0700 Subject: [PATCH] Copy command Summary: The `copy` command can be used to copy any `NSURL` or `NSData` to the host machine. The item will be copied to `/tmp/chisel_copy` and opened in the default application. If copying an `NSURL`, the file will retain its existing name and extension. If copying an `NSData`, a name will be generated with the `.data` extension. Options: - `--filename`: Set a name for the file to be copied. - `--no-open`: Prevent the file from being opened after copying. Reviewed By: kolinkrewinkel Differential Revision: D20922370 fbshipit-source-id: a4491f2ef03bf115fce37abdb79e2bd7269635a8 --- commands/FBCopyCommands.py | 116 +++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 commands/FBCopyCommands.py diff --git a/commands/FBCopyCommands.py b/commands/FBCopyCommands.py new file mode 100644 index 0000000..e36483a --- /dev/null +++ b/commands/FBCopyCommands.py @@ -0,0 +1,116 @@ +#!/usr/bin/python + +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +from __future__ import print_function +import os +import time + +import lldb +import errno +import fblldbbase as fb +import fblldbobjecthelpers as objectHelpers + + +def lldbcommands(): + return [FBCopyCommand()] + + +def _copyFromURL(url, preferredFilename, noOpen): + data = fb.evaluateObjectExpression( + '(id)[NSData dataWithContentsOfURL:(id){}]'.format(url) + ) + defaultFilename = fb.describeObject( + '(id)[[{} pathComponents] lastObject]'.format(url) + ) + _copyFromData(data, defaultFilename, preferredFilename, noOpen) + + +def _copyFromData(data, defaultFilename, preferredFilename, noOpen): + directory = '/tmp/chisel_copy/' + + path = directory + (preferredFilename or defaultFilename) + + try: + os.makedirs(directory) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(directory): + pass + else: + raise + + startAddress = fb.evaluateExpression('(void *)[(id)' + data + ' bytes]') + length = fb.evaluateExpression('(NSUInteger)[(id)' + data + ' length]') + + address = int(startAddress, 16) + length = int(length) + + if not (address or length): + print('Could not get data.') + return + + process = lldb.debugger.GetSelectedTarget().GetProcess() + error = lldb.SBError() + mem = process.ReadMemory(address, length, error) + + if error is not None and str(error) != 'success': + print(error) + else: + with open(path, 'wb') as file: + file.write(mem) + file.close() + print(path) + if not noOpen: + os.system('open ' + path) + + +def _copy(target, preferredFilename, noOpen): + target = '(' + target + ')' + + if objectHelpers.isKindOfClass(target, 'NSURL'): + _copyFromURL(target, preferredFilename, noOpen) + elif objectHelpers.isKindOfClass(target, 'NSData'): + _copyFromData( + target, + time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime()) + ".data", + preferredFilename, + noOpen + ) + else: + print('{} isn\'t supported. You can copy an NSURL or NSData.'.format( + objectHelpers.className(target) + )) + + +class FBCopyCommand(fb.FBCommand): + def name(self): + return 'copy' + + def description(self): + return 'Copy data to your Mac.' + + def options(self): + return [ + fb.FBCommandArgument( + short='-f', long='--filename', arg='filename', + help='The output filename.' + ), + fb.FBCommandArgument( + short='-n', long='--no-open', arg='noOpen', + boolean=True, default=False, + help='Do not open the file.' + ), + ] + + def args(self): + return [ + fb.FBCommandArgument( + arg='target', type='(id)', help='The object to copy.' + ) + ] + + def run(self, arguments, options): + _copy(arguments[0], options.filename, options.noOpen)