diff --git a/src/rpcclient/rpcclient/darwin/cfpreferences.py b/src/rpcclient/rpcclient/darwin/cfpreferences.py new file mode 100644 index 00000000..137c91fe --- /dev/null +++ b/src/rpcclient/rpcclient/darwin/cfpreferences.py @@ -0,0 +1,55 @@ +import typing + +from rpcclient.exceptions import RpcClientException + +kCFPreferencesCurrentUser = 'kCFPreferencesCurrentUser' +kCFPreferencesAnyUser = 'kCFPreferencesAnyUser' +kCFPreferencesCurrentHost = 'kCFPreferencesCurrentHost' +kCFPreferencesAnyHost = 'kCFPreferencesAnyHost' + + +class CFPreferences: + """ + API to the CFPreferences* functions - preferences managed by cfprefsd. + https://developer.apple.com/documentation/corefoundation/preferences_utilities?language=objc + """ + + def __init__(self, client): + """ + :param rpcclient.darwin.client.DarwinClient client: + """ + self._client = client + + def copy_key_list(self, application_id: str, username: str = kCFPreferencesCurrentUser, + hostname: str = kCFPreferencesCurrentHost) -> typing.Optional[typing.List[str]]: + application_id = self._client.cf(application_id) + username = self._client.cf(username) + hostname = self._client.cf(hostname) + return self._client.symbols.CFPreferencesCopyKeyList(application_id, username, hostname).py + + def copy_value(self, key: str, application_id: str, username: str = kCFPreferencesCurrentUser, + hostname: str = kCFPreferencesCurrentHost) -> typing.Optional[str]: + key = self._client.cf(key) + application_id = self._client.cf(application_id) + username = self._client.cf(username) + hostname = self._client.cf(hostname) + return self._client.symbols.CFPreferencesCopyValue(key, application_id, username, hostname).py + + def copy_all_values(self, application_id: str, username: str = kCFPreferencesCurrentUser, + hostname: str = kCFPreferencesCurrentHost) -> typing.Optional[typing.Mapping]: + result = {} + key_list = self.copy_key_list(application_id, username, hostname) + if not key_list: + raise RpcClientException(f'failed to get key list for: {application_id}/{username}/{hostname}') + for k in key_list: + result[k] = self.copy_value(k, application_id, username, hostname) + return result + + def set_value(self, key: str, value: str, application_id: str, username: str = kCFPreferencesCurrentUser, + hostname: str = kCFPreferencesCurrentHost): + key = self._client.cf(key) + value = self._client.cf(value) + application_id = self._client.cf(application_id) + username = self._client.cf(username) + hostname = self._client.cf(hostname) + self._client.symbols.CFPreferencesSetValue(key, value, application_id, username, hostname) diff --git a/src/rpcclient/rpcclient/darwin/client.py b/src/rpcclient/rpcclient/darwin/client.py index 9c6f78d2..c162aba6 100644 --- a/src/rpcclient/rpcclient/darwin/client.py +++ b/src/rpcclient/rpcclient/darwin/client.py @@ -1,22 +1,22 @@ -from collections import namedtuple -from functools import lru_cache import struct import typing +from collections import namedtuple +from functools import lru_cache from cached_property import cached_property from rpcclient.client import Client from rpcclient.darwin import objective_c_class +from rpcclient.darwin.consts import kCFNumberSInt64Type, kCFNumberDoubleType from rpcclient.darwin.fs import DarwinFs from rpcclient.darwin.media import DarwinMedia from rpcclient.darwin.network import DarwinNetwork from rpcclient.darwin.objective_c_symbol import ObjectiveCSymbol -from rpcclient.darwin.processes import DarwinProcesses from rpcclient.darwin.preferences import Preferences -from rpcclient.exceptions import RpcClientException +from rpcclient.darwin.processes import DarwinProcesses from rpcclient.darwin.structs import utsname -from rpcclient.darwin.consts import kCFNumberSInt64Type, kCFNumberDoubleType from rpcclient.darwin.symbol import DarwinSymbol +from rpcclient.exceptions import RpcClientException IsaMagic = namedtuple('IsaMagic', 'mask value') ISA_MAGICS = [ @@ -52,7 +52,7 @@ def __init__(self, sock, sysname: str, hostname: str, port: int = None): if self.uname.machine != 'x86_64': self.inode64 = True self.fs = DarwinFs(self) - self.prefs = Preferences(self) + self.preferences = Preferences(self) self.processes = DarwinProcesses(self) self.media = DarwinMedia(self) self.network = DarwinNetwork(self) @@ -75,6 +75,7 @@ def is_idevice(self): return self.uname.machine.startswith('i') def set_airplane_mode(self, mode: bool): + """ set whether the device should enter airplane mode (turns off baseband, bt, etc...) """ preferences = self.symbols.objc_getClass('RadiosPreferences').objc_call('new') preferences.objc_call('setAirplaneMode:', mode) preferences.objc_call('synchronize') diff --git a/src/rpcclient/rpcclient/darwin/preferences.py b/src/rpcclient/rpcclient/darwin/preferences.py index 9d66916b..1036d125 100644 --- a/src/rpcclient/rpcclient/darwin/preferences.py +++ b/src/rpcclient/rpcclient/darwin/preferences.py @@ -1,45 +1,13 @@ -import typing - -kCFPreferencesCurrentUser = 'kCFPreferencesCurrentUser' -kCFPreferencesAnyUser = 'kCFPreferencesAnyUser' -kCFPreferencesCurrentHost = 'kCFPreferencesCurrentHost' -kCFPreferencesAnyHost = 'kCFPreferencesAnyHost' +from rpcclient.darwin.cfpreferences import CFPreferences +from rpcclient.darwin.scpreferences import SCPreferences class Preferences: + """ Preferences utils """ + def __init__(self, client): """ :param rpcclient.darwin.client.DarwinClient client: """ - self._client = client - - def copy_key_list(self, application_id: str, username: str = kCFPreferencesCurrentUser, - hostname: str = kCFPreferencesCurrentHost) -> typing.Optional[typing.List[str]]: - application_id = self._client.cf(application_id) - username = self._client.cf(username) - hostname = self._client.cf(hostname) - return self._client.symbols.CFPreferencesCopyKeyList(application_id, username, hostname).py - - def copy_value(self, key: str, application_id: str, username: str = kCFPreferencesCurrentUser, - hostname: str = kCFPreferencesCurrentHost) -> typing.Optional[str]: - key = self._client.cf(key) - application_id = self._client.cf(application_id) - username = self._client.cf(username) - hostname = self._client.cf(hostname) - return self._client.symbols.CFPreferencesCopyValue(key, application_id, username, hostname).py - - def copy_all_values(self, application_id: str, username: str = kCFPreferencesCurrentUser, - hostname: str = kCFPreferencesCurrentHost) -> typing.Optional[typing.Mapping]: - result = {} - for k in self.copy_key_list(application_id, username, hostname): - result[k] = self.copy_value(k, application_id, username, hostname) - return result - - def set_value(self, key: str, value: str, application_id: str, username: str = kCFPreferencesCurrentUser, - hostname: str = kCFPreferencesCurrentHost): - key = self._client.cf(key) - value = self._client.cf(value) - application_id = self._client.cf(application_id) - username = self._client.cf(username) - hostname = self._client.cf(hostname) - self._client.symbols.CFPreferencesSetValue(key, value, application_id, username, hostname) + self.cf = CFPreferences(client) + self.sc = SCPreferences(client) diff --git a/src/rpcclient/rpcclient/darwin/scpreferences.py b/src/rpcclient/rpcclient/darwin/scpreferences.py new file mode 100644 index 00000000..ec718639 --- /dev/null +++ b/src/rpcclient/rpcclient/darwin/scpreferences.py @@ -0,0 +1,76 @@ +import typing + +from rpcclient.exceptions import RpcClientException + + +class SCPreferencesObject: + def __init__(self, client, ref): + self._client = client + self._ref = ref + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + try: + yield + finally: + self.release() + + @property + def keys(self) -> typing.List[str]: + return self._client.symbols.SCPreferencesCopyKeyList(self._ref).py + + def set(self, key: str, value): + if not self._client.symbols.SCPreferencesSetValue(self._ref, self._client.cf(key), self._client.cf(value)): + raise RpcClientException(f'SCPreferencesSetValue failed to set: {key}') + self._commit() + + def remove(self, key: str): + if not self._client.symbols.SCPreferencesRemoveValue(self._ref, self._client.cf(key)): + raise RpcClientException(f'SCPreferencesRemoveValue failed to remove: {key}') + self._commit() + + def get(self, key: str): + return self._client.symbols.SCPreferencesGetValue(self._ref, self._client.cf(key)).py + + def to_dict(self) -> typing.Mapping: + result = {} + for k in self.keys: + result[k] = self.get(k) + return result + + def release(self): + self._client.CFRelase(self._ref) + + def _commit(self): + if not self._client.symbols.SCPreferencesCommitChanges(self._ref): + raise RpcClientException('SCPreferencesCommitChanges failed') + self._client.symbols.SCPreferencesSynchronize(self._ref) + + +class SCPreferences: + """ + API to the SCPreferences* functions - preferences managed by SystemConfiguration framework. + https://developer.apple.com/documentation/systemconfiguration/scpreferences?language=objc + """ + + def __init__(self, client): + """ + :param rpcclient.darwin.client.DarwinClient client: + """ + self._client = client + + def get_preferences_object(self, preferences_id: str) -> SCPreferencesObject: + ref = self._client.symbols.SCPreferencesCreate(0, self._client.cf('rpcserver'), self._client.cf(preferences_id)) + if not ref: + raise RpcClientException(f'SCPreferencesCreate failed for: {preferences_id}') + return SCPreferencesObject(self._client, ref) + + def get_key_list(self, preferences_id: str) -> typing.List[str]: + with self.get_preferences_object(preferences_id) as o: + return o.keys + + def get_dict(self, preferences_id: str): + with self.get_preferences_object(preferences_id) as o: + return o.to_dict()