From d1f44fd0c6cccedd8c5615612cf2a82400702ff5 Mon Sep 17 00:00:00 2001 From: "DoronZ doron88@gmail.com" Date: Sun, 10 Sep 2023 13:47:12 +0300 Subject: [PATCH 1/3] add macos support --- cfprefsmon/__main__.py | 119 ++++++++++++++++++++++++----------------- pyproject.toml | 2 +- requirements.txt | 1 + 3 files changed, 73 insertions(+), 49 deletions(-) diff --git a/cfprefsmon/__main__.py b/cfprefsmon/__main__.py index 6a56e18..9e38b4b 100644 --- a/cfprefsmon/__main__.py +++ b/cfprefsmon/__main__.py @@ -1,5 +1,9 @@ +from typing import Optional + import click -from pymobiledevice3.lockdown import create_using_usbmux +from maclog.log import get_logger +from pymobiledevice3.cli.cli_common import LockdownCommand +from pymobiledevice3.lockdown import LockdownClient from pymobiledevice3.services.os_trace import OsTraceService FORMAT = 'CFPreference[{domain}][{user}][{key}] = {value} # Process: {procname}' @@ -13,71 +17,90 @@ DEFAULT_USER = 'kCFPreferencesAnyUser' +PREFS = {} -@click.command() -@click.option('--udid') -@click.option('--unique', is_flag=True, help='output only unique entries') -@click.option('--color/--no-color', default=True, help='make colored output') -@click.option('--undefined', is_flag=True, help='filter only non-existing keys') -def cli(udid, unique, color, undefined): - lockdown = create_using_usbmux(serial=udid) - prefs = {} - for entry in OsTraceService(lockdown).syslog(): - if entry.label is None: - continue - if entry.label.subsystem != 'com.apple.defaults' or entry.label.category != 'User Defaults': - continue +def print_entry(message: str, filename: str, subsystem: Optional[str] = None, category: Optional[str] = None, + unique: bool = False, color: bool = False, undefined: bool = False) -> None: + if subsystem != 'com.apple.defaults' or category != 'User Defaults': + return - message = entry.message + if 'cfprefs' not in message.lower(): + return - if 'cfprefs' not in message.lower(): - continue + if not message.startswith(HAS_VALUE_PREFIX) and not message.startswith(NO_VALUE_PREFIX): + return - if not message.startswith(HAS_VALUE_PREFIX) and not message.startswith(NO_VALUE_PREFIX): - continue + # print(message) + user = DEFAULT_USER + value = None - # print(message) + key = message.split(FOR_KEY_PREFIX, 1)[1].split(FOR_KEY_SUFFIX, 1)[0].strip() + domain = message.rsplit(DOMAIN_PREFIX, 1)[1].split(',', 1)[0].strip() + procname = filename + has_value = False + + if USER_PREFIX in message: + user = message.rsplit(USER_PREFIX, 1)[1].split(',', 1)[0].strip() + + if not user: user = DEFAULT_USER - value = None - key = message.split(FOR_KEY_PREFIX, 1)[1].split(FOR_KEY_SUFFIX, 1)[0].strip() - domain = message.rsplit(DOMAIN_PREFIX, 1)[1].split(',', 1)[0].strip() - procname = entry.filename - has_value = False + if message.startswith(HAS_VALUE_PREFIX): + value = message.split(HAS_VALUE_PREFIX, 1)[1].split(FOR_KEY_PREFIX, 1)[0] + has_value = True - if USER_PREFIX in message: - user = message.rsplit(USER_PREFIX, 1)[1].split(',', 1)[0].strip() + if domain not in PREFS: + PREFS[domain] = {} - if not user: - user = DEFAULT_USER + if user not in PREFS[domain]: + PREFS[domain][user] = [] - if message.startswith(HAS_VALUE_PREFIX): - value = message.split(HAS_VALUE_PREFIX, 1)[1].split(FOR_KEY_PREFIX, 1)[0] - has_value = True + if unique and key in PREFS[domain][user]: + return - if domain not in prefs: - prefs[domain] = {} + PREFS[domain][user].append(key) - if user not in prefs[domain]: - prefs[domain][user] = [] + if color: + domain = click.style(domain, fg='yellow') + user = click.style(user, fg='bright_green') + key = click.style(key, fg='green') + procname = click.style(procname, fg='magenta') - if unique and key in prefs[domain][user]: - continue + if not has_value: + value = click.style(value, fg='red') - prefs[domain][user].append(key) + if (not undefined) or (undefined and not has_value): + print(FORMAT.format(domain=domain, user=user, key=key, value=value, procname=procname)) - if color: - domain = click.style(domain, fg='yellow') - user = click.style(user, fg='bright_green') - key = click.style(key, fg='green') - procname = click.style(procname, fg='magenta') - if not has_value: - value = click.style(value, fg='red') +@click.group() +def cli(): + pass - if (not undefined) or (undefined and not has_value): - print(FORMAT.format(domain=domain, user=user, key=key, value=value, procname=procname)) + +@cli.command() +@click.option('--unique', is_flag=True, help='output only unique entries') +@click.option('--color/--no-color', default=True, help='make colored output') +@click.option('--undefined', is_flag=True, help='filter only non-existing keys') +def host(unique, color, undefined): + """ Sniff on macOS host """ + for entry in get_logger(): + print_entry(entry.event_message, entry.process_image_path, entry.subsystem, entry.category, unique=unique, + color=color, undefined=undefined) + + +@cli.command(cls=LockdownCommand) +@click.option('--unique', is_flag=True, help='output only unique entries') +@click.option('--color/--no-color', default=True, help='make colored output') +@click.option('--undefined', is_flag=True, help='filter only non-existing keys') +def mobile(service_provider: LockdownClient, unique, color, undefined): + """ Sniff on connected iOS device """ + for entry in OsTraceService(service_provider).syslog(): + if entry.label is None: + continue + print_entry(entry.message, entry.filename, entry.label.subsystem, entry.label.category, unique=unique, + color=color, undefined=undefined) if __name__ == '__main__': diff --git a/pyproject.toml b/pyproject.toml index 09684e6..ba79438 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "cfprefsmon" version = "0.0.4" description = "Search for interesting internal preferences inside a connected iDevice" readme = "README.md" -requires-python = ">=3.7" +requires-python = ">=3.8" license = { file = "LICENSE" } keywords = ["ios", "cli", "preferences", "cfprefsd", "monitor"] authors = [ diff --git a/requirements.txt b/requirements.txt index 22abc9a..2f9ee12 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pymobiledevice3>=2.0.0 +maclog click From d3850baa494f30c7c7eb8ecdf4bbb2bbe2688fbc Mon Sep 17 00:00:00 2001 From: doronz Date: Sun, 10 Sep 2023 13:52:31 +0300 Subject: [PATCH 2/3] ci: remove python3.7 --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index d980db8..e689708 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11"] steps: - uses: actions/checkout@v3 From 4a1be91fc1137e587192be2d1a7ede4eefcea4fa Mon Sep 17 00:00:00 2001 From: doronz Date: Sun, 10 Sep 2023 13:56:09 +0300 Subject: [PATCH 3/3] docs: update README --- README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9b05cb0..98fcdfa 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,24 @@ # Description -Simple utility to search for interesting preferences in iDevices. +Simple utility to search for interesting preferences in macOS and connected iDevices. # Installation ```shell -python3 -m pip install -U --user cfprefsmon +python3 -m pip install -U cfprefsmon +``` + +# Usage + +``` +Usage: cfprefsmon [OPTIONS] COMMAND [ARGS]... + +Options: + --help Show this message and exit. + +Commands: + host Sniff on macOS host + mobile Sniff on connected iOS device ``` # Example @@ -14,7 +27,7 @@ In this example, where the value for each preference is `None`, this is probably on a jailbroken device. ``` -➜ cfprefmon git:(master) ✗ cfprefsmon +➜ cfprefmon git:(master) ✗ cfprefsmon mobile CFPreference[com.apple.springboard][kCFPreferencesAnyUser][SBDisableHomeButton] = 0 # Process: /System/Library/CoreServices/SpringBoard.app/SpringBoard CFPreference[com.apple.springboard][kCFPreferencesAnyUser][SBStoreDemoAppLock] = 0 # Process: /System/Library/CoreServices/SpringBoard.app/SpringBoard CFPreference[com.apple.springboard][kCFPreferencesAnyUser][ThermalLockoutEnabledBrickMode] = 0 # Process: /System/Library/CoreServices/SpringBoard.app/SpringBoard