Skip to content

feat: Add MacOS support to Objection #664

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion objection/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys

__version__ = '1.11.0'
__version__ = '1.11.1'

# helper containing a python 3 related warning
# if this is run with python 2
Expand Down
22 changes: 21 additions & 1 deletion objection/commands/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from tabulate import tabulate

from ..state.connection import state_connection
from ..state.device import device_state, Android, Ios
from ..state.device import device_state, Android, Ios, Macos


def get_environment(args: list = None) -> None:
Expand All @@ -22,6 +22,9 @@ def get_environment(args: list = None) -> None:
if device_state.platform == Android:
_get_android_environment()

if device_state.platform == Macos:
_get_macos_environment()


def _get_ios_environment() -> None:
"""
Expand Down Expand Up @@ -51,3 +54,20 @@ def _get_android_environment() -> None:

click.secho('')
click.secho(tabulate(paths.items(), headers=['Name', 'Path']))


def _get_macos_environment() -> None:
"""
Prints information about the macOS environment.

This includes the current OS version as well as directories
of interest for the current applications Documents, Library and
main application bundle.

:return:
"""

paths = state_connection.get_api().env_ios_paths()

click.secho('')
click.secho(tabulate(paths.items(), headers=['Name', 'Path']))
7 changes: 6 additions & 1 deletion objection/console/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def get_agent() -> Agent:
@click.group()
@click.option('--network', '-N', is_flag=True, help='Connect using a network connection instead of USB.',
show_default=True)
@click.option('--desktop', '-D', is_flag=True, help='Connect using a local connection for desktop.',
show_default=True)
@click.option('--host', '-h', default='127.0.0.1', show_default=True)
@click.option('--port', '-p', required=False, default=27042, show_default=True)
@click.option('--api-host', '-ah', default='127.0.0.1', show_default=True)
Expand All @@ -53,7 +55,7 @@ def get_agent() -> Agent:
@click.option('--foremost', '-f', required=False, is_flag=True, help='Use the current foremost application.')
@click.option('--debugger', required=False, default=False, is_flag=True, help='Enable the Chrome debug port.')
@click.option('--uid', required=False, default=None, help='Specify the uid to run as (Android only).')
def cli(network: bool, host: str, port: int, api_host: str, api_port: int,
def cli(network: bool, desktop: bool, host: str, port: int, api_host: str, api_port: int,
name: str, serial: str, debug: bool, spawn: bool, no_pause: bool,
foremost: bool, debugger: bool, uid: int) -> None:
"""
Expand All @@ -79,6 +81,9 @@ def cli(network: bool, host: str, port: int, api_host: str, api_port: int,
if serial:
state_connection.device_id = serial

if desktop:
state_connection.use_desktop()

# set api parameters
app_state.api_host = api_host
app_state.api_port = api_port
Expand Down
212 changes: 210 additions & 2 deletions objection/console/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@
},
},
# ios commands
'ios': {
'ios': {
'meta': 'Commands specific to iOS',
'commands': {
'info': {
Expand Down Expand Up @@ -742,7 +742,215 @@
},
}
},

# macos commands
'macos': {
'meta': 'Commands specific to iOS',
'commands': {
'info': {
'meta': 'Get macOS and application related information',
'commands': {
'binary': {
'meta': 'Get information about application binaries and dylibs',
'exec': binary.info
}
}
},
'keychain': {
'meta': 'Work with the macOS keychain',
'commands': {
'dump': {
'meta': 'Dump the keychain for the current app\'s entitlement group',
'flags': ['--json', '--smart'],
'exec': keychain.dump
},
'dump_raw': {
'meta': 'Dump raw, unprocessed keychain entries (advanced)',
'exec': keychain.dump_raw
},
'clear': {
'meta': 'Delete all keychain entries for the current app\'s entitlement group',
'exec': keychain.clear
},
'add': {
'meta': 'Add an entry to the iOS keychain',
'flags': ['--account', '--service', '--data'],
'exec': keychain.add
}
}
},
'plist': {
'meta': 'Work with macOS Plists',
'commands': {
'cat': {
'meta': 'Cat a plist',
'dynamic': filemanager.list_files_in_current_fm_directory,
'exec': plist.cat
}
}
},
'bundles': {
'meta': 'Work with macOS Bundles',
'commands': {
'list_frameworks': {
'meta': 'Lists all of the application\'s bundles that represent frameworks',
'flags': ['--include-apple-frameworks', '--full-path'],
'exec': bundles.show_frameworks
},
'list_bundles': {
'meta': 'Lists all of the application\'s non framework bundles',
'flags': ['--full-path'],
'exec': bundles.show_bundles
}
}
},
'nsuserdefaults': {
'meta': 'Work with NSUserDefaults',
'commands': {
'get': {
'meta': 'Get all of the entries',
'exec': nsuserdefaults.get
}
}
},
'nsurlcredentialstorage': {
'meta': 'Work with the shared NSURLCredentialStorage',
'commands': {
'dump': {
'meta': 'Dump all of the credentials in the shared NSURLCredentialStorage',
'exec': nsurlcredentialstorage.dump
}
}
},
'cookies': {
'meta': 'Work with shared cookies',
'commands': {
'get': {
'meta': 'Get the current apps shared cookies',
'flags': ['--json'],
'exec': cookies.get
}
}
},
'heap': {
'meta': 'Commands to work with the macOS heap',
'commands': {
'print': {
'meta': 'Print information about objects on the iOS heap',
'commands': {
'ivars': {
'meta': 'Print instance variables for an Objective-C object',
'flags': ['--to-utf8'],
'exec': ios_heap.ivars
},
'methods': {
'meta': 'Print instance methods for an Objective-C object',
'flags': ['--without-arguments'],
'exec': ios_heap.methods
}
}
},
'search': {
'meta': 'Search for information about the current macOS heap',
'commands': {
'instances': {
'meta': 'Search for live instances of a particular class',
'exec': ios_heap.instances
}
}
},
'execute': {
'meta': 'Execute methods on objects on the macOS heap',
'flags': ['--return-string'],
'exec': ios_heap.execute
},
'evaluate': {
'meta': 'Evaluate JavaScript on objects on the macOS heap',
'flags': ['--inline'],
'exec': ios_heap.evaluate
}
}
},
'hooking': {
'meta': 'Commands used for hooking methods in iOS',
'commands': {
'list': {
'meta': 'Lists various bits of information',
'commands': {
'classes': {
'meta': 'List classes available in the current application',
'exec': ios_hooking.show_ios_classes
},
'class_methods': {
'meta': 'List the methods in a class',
'flags': ['--include-parents'],
'exec': ios_hooking.show_ios_class_methods
}
}
},
'watch': {
'meta': 'Watch invocations of classes and methods',
'exec': ios_hooking.watch,
'flags': ['--dump-args', '--dump-backtrace', '--dump-return'],
},
'set': {
'meta': 'Set various values',
'commands': {
'return_value': {
'meta': 'Set a methods return value. Supports only boolean returns',
'exec': ios_hooking.set_method_return_value
}
}
},
'search': {
'meta': 'Search for various classes and or methods',
'exec': ios_hooking.search,
'flags': ['--json', '--only-classes']
},
'generate': {
'meta': 'Generate Frida hooks for macOS',
'commands': {
'class': {
'meta': 'A generic hook manager for Classes',
'exec': ios_generate.clazz
},
'simple': {
'meta': 'Simple hooks for each Class method',
'exec': ios_generate.simple
}
},
}
}
},
'pasteboard': {
'meta': 'Work with the macOS pasteboard',
'commands': {
'monitor': {
'meta': 'Monitor the macOS pasteboard',
'exec': pasteboard.monitor
}
}
},
'sslpinning': {
'meta': 'Work with macOS SSL pinning',
'commands': {
'disable': {
'meta': 'Attempt to disable SSL pinning in various iOS libraries/classes',
'flags': ['--quiet'],
'exec': ios_pinning.ios_disable
}
}
},
'monitor': {
'meta': 'Commands to work with macOS function monitoring',
'commands': {
'crypto': {
'meta': 'Monitor CommonCrypto operations',
'exec': ios_crypto.crypto_enable
}
},
},
}
},
'exit': {
'meta': 'Exit',
},
Expand Down
11 changes: 11 additions & 0 deletions objection/state/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def __init__(self) -> None:
"""

self.network = False
self.desktop = False
self.host = None
self.port = None
self.device_type = 'usb'
Expand Down Expand Up @@ -43,6 +44,16 @@ def use_network(self) -> None:
self.network = True
self.device_type = 'remote'

def use_desktop(self) -> None:
"""
Sets the values required to have a Network connection.

:return:
"""

self.desktop = True
self.device_type = 'local'

def get_comms_type(self) -> int:
"""
Returns the currently configured connection type.
Expand Down
7 changes: 7 additions & 0 deletions objection/state/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ class Ios(Device):
path_separator = '/'


class Macos(Device):
""" Represents macOS specific configurations. """

name = 'macos'
path_separator = '/'


class DeviceState(object):
""" A class representing the state of a device and its runtime. """

Expand Down
5 changes: 3 additions & 2 deletions objection/utils/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from objection.state.app import app_state
from objection.state.connection import state_connection
from objection.state.device import device_state, Ios, Android
from objection.state.device import device_state, Ios, Android, Macos
from objection.state.jobs import job_manager_state
from objection.utils.helpers import debug_print

Expand Down Expand Up @@ -288,7 +288,8 @@ def update_device_state(self):
device_state.set_platform(Ios)
elif params['os']['id'] == 'android':
device_state.set_platform(Android)

elif params['os']['id'] == 'macos':
device_state.set_platform(Macos)
# set os version
device_state.set_version(params['os']['version'])

Expand Down