Skip to content

Commit

Permalink
(feat) Add folder download functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
IPMegladon committed Sep 13, 2024
1 parent b38cec4 commit 41672c2
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 15 deletions.
124 changes: 112 additions & 12 deletions objection/commands/filemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@
_ls_cache = {}


def _should_download_folder(args: list) -> bool:
"""
Checks if --json is in the list of tokens received from the command line.
:param args:
:return:
"""

return len(args) > 0 and '--folder' in args


def cd(args: list) -> None:
"""
Change the current working directory of the device.
Expand Down Expand Up @@ -405,14 +416,16 @@ def download(args: list) -> None:
source = args[0]
destination = args[1] if len(args) > 1 else os.path.basename(source)

should_download_folder = _should_download_folder(args)

if device_state.platform == Ios:
_download_ios(source, destination)
_download_ios(source, destination, should_download_folder)

if device_state.platform == Android:
_download_android(source, destination)
_download_android(source, destination, should_download_folder)


def _download_ios(path: str, destination: str) -> None:
def _download_ios(path: str, destination: str, should_download_folder: bool, path_root: bool = True) -> None:
"""
Download a file from an iOS filesystem and store it locally.
Expand All @@ -428,27 +441,55 @@ def _download_ios(path: str, destination: str) -> None:

api = state_connection.get_api()

click.secho('Downloading {0} to {1}'.format(path, destination), fg='green', dim=True)
if path_root:
click.secho('Downloading {0} to {1}'.format(path, destination), fg='green', dim=True)

if not api.ios_file_readable(path):
click.secho('Unable to download file. File is not readable.', fg='red')
return

if not api.ios_file_path_is_file(path):
click.secho('Unable to download file. Target path is not a file.', fg='yellow')
if not should_download_folder:
click.secho('To download folders, specify --folder.', fg='yellow')
return

if os.path.exists(destination):
click.secho('The target path already exists.', fg='yellow')
return

os.makedirs(destination)

if path_root:
if not click.confirm('Do you want to download the full directory?', default=True):
click.secho('Download aborted.', fg='yellow')
return
click.secho('Downloading directory recursively...', fg='green')

data = api.ios_file_ls(path)
for name, _ in data['files'].items():
sub_path = device_state.platform.path_separator.join([path, name])
sub_destination = os.path.join(destination, name)

_download_ios(sub_path, sub_destination, True, False)
if path_root:
click.secho('Recursive download finished.', fg='green')

return

click.secho('Streaming file from device...', dim=True)
if path_root:
click.secho('Streaming file from device...', dim=True)
file_data = api.ios_file_download(path)

click.secho('Writing bytes to destination...', dim=True)
if path_root:
click.secho('Writing bytes to destination...', dim=True)

with open(destination, 'wb') as fh:
fh.write(bytearray(file_data['data']))

click.secho('Successfully downloaded {0} to {1}'.format(path, destination), bold=True)


def _download_android(path: str, destination: str) -> None:
def _download_android(path: str, destination: str, should_download_folder: bool, path_root: bool = True) -> None:
"""
Download a file from the Android filesystem and store it locally.
Expand All @@ -464,20 +505,47 @@ def _download_android(path: str, destination: str) -> None:

api = state_connection.get_api()

click.secho('Downloading {0} to {1}'.format(path, destination), fg='green', dim=True)
if path_root:
click.secho('Downloading {0} to {1}'.format(path, destination), fg='green', dim=True)

if not api.android_file_readable(path):
click.secho('Unable to download file. Target path is not readable.', fg='red')
return

if not api.android_file_path_is_file(path):
click.secho('Unable to download file. Target path is not a file.', fg='yellow')
if not should_download_folder:
click.secho('To download folders, specify --folder.', fg='yellow')
return

if os.path.exists(destination):
click.secho('The target path already exists.', fg='yellow')
return

os.makedirs(destination)

if path_root:
if not click.confirm('Do you want to download the full directory?', default=True):
click.secho('Download aborted.', fg='yellow')
return
click.secho('Downloading directory recursively...', fg='green')

data = api.android_file_ls(path)
for name, _ in data['files'].items():
sub_path = device_state.platform.path_separator.join([path, name])
sub_destination = os.path.join(destination, name)

_download_android(sub_path, sub_destination, True, False)
if path_root:
click.secho('Recursive download finished.', fg='green')
return

click.secho('Streaming file from device...', dim=True)
if path_root:
click.secho('Streaming file from device...', dim=True)
file_data = api.android_file_download(path)

click.secho('Writing bytes to destination...', dim=True)
if path_root:
click.secho('Writing bytes to destination...', dim=True)

with open(destination, 'wb') as fh:
fh.write(bytearray(file_data['data']))

Expand Down Expand Up @@ -837,3 +905,35 @@ def list_files_in_current_fm_directory() -> dict:
resp[file_name] = file_name

return resp


def list_content_in_current_fm_directory() -> dict:
"""
Return folders and files in the current working directory of the
Frida attached device.
"""

resp = {}

# check for existence based on the runtime
if device_state.platform == Ios:
response = _get_short_ios_listing()

elif device_state.platform == Android:
response = _get_short_android_listing()

# looks like we landed in an unknown runtime.
# just return.
else:
return resp

# loop the response to get entries.
for entry in response:
name, _ = entry

if ' ' in name:
resp[f"'{name}'"] = name
else:
resp[name] = name

return resp
7 changes: 4 additions & 3 deletions objection/console/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
'exec': filemanager.pwd_print,
},

'file': {
'filesystem': {
'meta': 'Work with files on the remote filesystem',
'commands': {
'cat': {
Expand All @@ -132,8 +132,9 @@
'exec': filemanager.upload
},
'download': {
'meta': 'Download a file',
'dynamic': filemanager.list_files_in_current_fm_directory,
'meta': 'Download a file or folder',
'flags': ['--folder'],
'dynamic': filemanager.list_content_in_current_fm_directory,
'exec': filemanager.download
},

Expand Down

0 comments on commit 41672c2

Please sign in to comment.