From 475f5d8781c7494efb3a574e2a71081f9608337c Mon Sep 17 00:00:00 2001 From: Niels Hofmans Date: Thu, 3 Dec 2020 13:01:11 +0100 Subject: [PATCH 1/2] feat: add kext/sext listing support --- maclaunch.sh | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/maclaunch.sh b/maclaunch.sh index c76c8f0..073016b 100755 --- a/maclaunch.sh +++ b/maclaunch.sh @@ -50,6 +50,82 @@ function getScriptUser { grep 'UserName' -C1 "$scriptPath" | tail -n1 | cut -d '>' -f 2 | cut -d '<' -f 1 } +function getKernelExtensions { + kmutil showloaded --no-kernel-components --list-only --sort --show loaded 2>/dev/null | tr -s ' ' | grep -v 'com\.apple\.' +} + +function listKernelExtensions { + local filter="$1" + + getKernelExtensions | while IFS= read -r kextLine; do + + kextLoaded=$(echo "$kextLine" | cut -d ' ' -f 3) + kextName="$(echo "$kextLine" | cut -d ' ' -f 7)" + kextVersion="$(echo "$kextLine" | grep -o '\((.*)\)')" + + if [ "$filter" == "disabled" ] && [ "$kextLoaded" != "0" ]; then + continue + fi + + if [ "$filter" == "enabled" ] && [ "$kextLoaded" == "0" ]; then + continue + fi + + if [ -n "$filter" ] && [ "$filter" != "system" ] && [ "$filter" != "enabled" ] && [ "$filter" != "disabled" ]; then + if [[ "$kextName" != *"$filter"* ]]; then + continue + fi + fi + + kernelPath="$(kextfind -system-extensions "$kextName" 2>/dev/null)" + if [ -z "$kernelPath" ]; then + kernelPath="n/a" + fi + + local loaded + if [ "$kextLoaded" == "0" ]; then + loaded="${GREEN}${BOLD}disabled${NC}" + else + loaded="${RED}Always${NC}" + fi + + echo -e "${BOLD}> ${kextName}${NC} ${kextVersion}" + echo -e " Type : ${RED}kernel extension${NC}" + echo -e " User : ${RED}root${NC}" + echo -e " Launch: ${loaded}" + echo " File : ${kernelPath}" + + done +} + +function getSystemExtensions { + systemextensionsctl list 2>/dev/null | tail -n+2 | grep -v '^---' | grep -v '^enabled' | tr -s ' ' +} + +function listSystemExtensions { + local filter="$1" + + getSystemExtensions | while IFS= read -r extLine; do + + fullName="$(echo "$extLine" | cut -d$'\t' -f 4)" + extName="$(echo "$fullName" | cut -d ' ' -f 1)" + extVersion="$(echo "$fullName" | grep -o '\((.*)\)')" + + local loaded + if [ "$(echo "$extLine" | cut -d$'\t' -f 2)" == "*" ]; then + loaded="${ORANGE}enabled${NC}" + else + loaded="${GREEN}disabled${NC}" + fi + + echo -e "${BOLD}> ${extName}${NC} ${extVersion}" + echo -e " Type : system extension" + echo -e " User : $(whoami)" + echo -e " Launch: ${loaded}" + echo " File : n/a" + done +} + function listItems { local filter="$2" @@ -266,6 +342,8 @@ case "$1" in fi fi listItems "$1" "$2" + listKernelExtensions "$2" + listSystemExtensions "$2" ;; "disable") if [ $# -ne 2 ]; then From cdc27b32cbae49200cc84dd61314c1056fd3b5f9 Mon Sep 17 00:00:00 2001 From: Niels Hofmans Date: Thu, 3 Dec 2020 13:28:23 +0100 Subject: [PATCH 2/2] feat: add support for disabling/enabling kext/sext --- README.md | 11 +++-- maclaunch.sh | 131 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 129 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c826611..736349c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # maclaunch -Lists and controls your macOS startup items and their startup policy. +Lists and controls all your macOS startup items and their startup policy. Take back control of your macOS system! @@ -31,8 +31,13 @@ Take back control of your macOS system! ## How does it work? -Lists XML/json/binary plist files in LaunchAgents and LaunchDaemons folders which are loaded by launchctl. -When disabling an item, it uses launchctl to natively stop loading that service. +maclaunch will list 3 distinct types of entries on your macOS system that can be persistently installed: + +1. Configuration files for LaunchAgents and LaunchDaemons which are loaded by launchctl. +2. Kernel extensions loaded in the kernel. +3. System extensions loaded in userspace. + +When disabling an item, it uses `launchctl`, `kextutil` or `systemextensionsctl` to natively stop loading that service. It does **not** alter the contents in any way or moves the file, so it should work with practically any service. The name you provide can either be specific to that service or function as a filter to work on multiple services simultaneously. diff --git a/maclaunch.sh b/maclaunch.sh index 073016b..b24c082 100755 --- a/maclaunch.sh +++ b/maclaunch.sh @@ -1,8 +1,5 @@ #!/usr/bin/env bash -#set -e -#set -x - startup_dirs=(/Library/LaunchAgents /Library/LaunchDaemons ~/Library/LaunchAgents ~/Library/LaunchDaemons) system_dirs=(/System/Library/LaunchAgents /System/Library/LaunchDaemons) @@ -12,6 +9,10 @@ YELLOW='\033[1;33m' NC='\033[0m' BOLD='\033[1m' +# +#-------------------------------------------------------------------------------------------------------------------------------------- +# + function join_by { local IFS="$1"; shift; echo "$*"; } function usage { @@ -59,7 +60,7 @@ function listKernelExtensions { getKernelExtensions | while IFS= read -r kextLine; do - kextLoaded=$(echo "$kextLine" | cut -d ' ' -f 3) + kextLoaded="$(echo "$kextLine" | cut -d ' ' -f 3)" kextName="$(echo "$kextLine" | cut -d ' ' -f 7)" kextVersion="$(echo "$kextLine" | grep -o '\((.*)\)')" @@ -98,6 +99,56 @@ function listKernelExtensions { done } +function disableKernelExtensions { + local filter="$1" + + getKernelExtensions | while IFS= read -r kextLine; do + + kextLoaded="$(echo "$kextLine" | cut -d ' ' -f 3)" + kextName="$(echo "$kextLine" | cut -d ' ' -f 7)" + + if ! [[ "$kextName" =~ $filter ]]; then + continue + fi + + if [ "$kextLoaded" == "0" ]; then + #error "kernel extension is already unloaded" + continue + fi + + if ! kmutil load -b "$kextName" 1>/dev/null; then + error "could not disable kernel extension" + fi + + echo -e "${GREEN}Disabled ${STRONG}${kextName}${NC}" + done +} + +function enableKernelExtensions { + local filter="$1" + + getKernelExtensions | while IFS= read -r kextLine; do + + kextLoaded="$(echo "$kextLine" | cut -d ' ' -f 3)" + kextName="$(echo "$kextLine" | cut -d ' ' -f 7)" + + if ! [[ "$kextName" =~ $filter ]]; then + continue + fi + + if ! [ "$kextLoaded" == "0" ]; then + #error "kernel extension is already loaded" + continue + fi + + if ! kmutil unload -b "$kextName" 1>/dev/null; then + error "could not disable kernel extension" + fi + + echo -e "${GREEN}Enabled ${STRONG}${kextName}${NC}" + done +} + function getSystemExtensions { systemextensionsctl list 2>/dev/null | tail -n+2 | grep -v '^---' | grep -v '^enabled' | tr -s ' ' } @@ -111,6 +162,10 @@ function listSystemExtensions { extName="$(echo "$fullName" | cut -d ' ' -f 1)" extVersion="$(echo "$fullName" | grep -o '\((.*)\)')" + if [ -n "$filter" ] && ! [[ "$extName" =~ $filter ]]; then + continue + fi + local loaded if [ "$(echo "$extLine" | cut -d$'\t' -f 2)" == "*" ]; then loaded="${ORANGE}enabled${NC}" @@ -126,7 +181,52 @@ function listSystemExtensions { done } -function listItems { +function enableSystemExtensions { + local filter="$1" + + getSystemExtensions | while IFS= read -r extLine; do + + extName="$(echo "$extLine" | cut -d$'\t' -f 4 | cut -d ' ' -f 1)" + + if ! [[ "$extName" =~ $filter ]]; then + continue + fi + + if [ "$(echo "$extLine" | cut -d$'\t' -f 2)" == "*" ]; then + # error "this system extension is already enabled" + continue + fi + + #TODO: implement load system extension via CLI + error "enabling system extensions is not yet implemented" + done +} + +function disableSystemExtensions { + local filter="$1" + + getSystemExtensions | while IFS= read -r extLine; do + + extName="$(echo "$extLine" | cut -d$'\t' -f 4 | cut -d ' ' -f 1)" + + if ! [[ "$extName" =~ $filter ]]; then + continue + fi + + if ! [ "$(echo "$extLine" | cut -d$'\t' -f 2)" == "*" ]; then + # error "this system extension is already disabled" + continue + fi + + if ! systemextensionsctl uninstall '-' "$extName"; then + error "could not disable system extension" + fi + + echo -e "${GREEN}Enabled ${STRONG}${extName}${NC}" + done +} + +function listLaunchItems { local filter="$2" itemDirectories=("${startup_dirs[@]}") @@ -262,7 +362,7 @@ function listItems { done< <(find "${itemDirectories[@]}" -type f -iname '*.plist*' -print0 2>/dev/null) } -function enableItems { +function enableLaunchItems { disabled_items="$(launchctl print-disabled user/"$(id -u)")" while IFS= read -r -d '' startupFile; do @@ -296,7 +396,7 @@ function enableItems { done< <(find "${startup_dirs[@]}" "${system_dirs[@]}" \( -iname "*$1*.plist" -o -iname "*$1*.plist.disabled" \) -print0 2>/dev/null) } -function disableItems { +function disableLaunchItems { disabled_items="$(launchctl print-disabled user/"$(id -u)")" while IFS= read -r -d '' startupFile; do @@ -330,6 +430,10 @@ function disableItems { done< <(find "${startup_dirs[@]}" "${system_dirs[@]}" \( -iname "*$1*.plist" -o -iname "*$1*.plist.disabled" \) -print0 2>/dev/null) } +# +#-------------------------------------------------------------------------------------------------------------------------------------- +# + if [ $# -lt 1 ] || [ $# -gt 2 ]; then usage fi @@ -341,22 +445,29 @@ case "$1" in usage fi fi - listItems "$1" "$2" + listLaunchItems "$1" "$2" listKernelExtensions "$2" listSystemExtensions "$2" ;; + "disable") if [ $# -ne 2 ]; then usage fi - disableItems "$2" + disableLaunchItems "$2" + disableKernelExtensions "$2" + disableSystemExtensions "$2" ;; + "enable") if [ $# -ne 2 ]; then usage fi - enableItems "$2" + enableLaunchItems "$2" + enableKernelExtensions "$2" + enableSystemExtensions "$2" ;; + *) usage ;;