Skip to content

Commit

Permalink
Copy command
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Chris Ellsworth authored and facebook-github-bot committed Apr 9, 2020
1 parent c238204 commit 0c9ee31
Showing 1 changed file with 116 additions and 0 deletions.
116 changes: 116 additions & 0 deletions commands/FBCopyCommands.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 0c9ee31

Please sign in to comment.