Skip to content

Commit d9379df

Browse files
authored
Merge pull request #10 from hazcod/feat/kext
Feature: kernel extension & system extension support
2 parents a015840 + cdc27b3 commit d9379df

File tree

2 files changed

+206
-12
lines changed

2 files changed

+206
-12
lines changed

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
# maclaunch
33

4-
Lists and controls your macOS startup items and their startup policy.
4+
Lists and controls all your macOS startup items and their startup policy.
55

66
Take back control of your macOS system!
77

@@ -31,8 +31,13 @@ Take back control of your macOS system!
3131

3232
## How does it work?
3333

34-
Lists XML/json/binary plist files in LaunchAgents and LaunchDaemons folders which are loaded by launchctl.
35-
When disabling an item, it uses launchctl to natively stop loading that service.
34+
maclaunch will list 3 distinct types of entries on your macOS system that can be persistently installed:
35+
36+
1. Configuration files for LaunchAgents and LaunchDaemons which are loaded by launchctl.
37+
2. Kernel extensions loaded in the kernel.
38+
3. System extensions loaded in userspace.
39+
40+
When disabling an item, it uses `launchctl`, `kextutil` or `systemextensionsctl` to natively stop loading that service.
3641
It does **not** alter the contents in any way or moves the file, so it should work with practically any service.
3742

3843
The name you provide can either be specific to that service or function as a filter to work on multiple services simultaneously.

maclaunch.sh

Lines changed: 198 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
#!/usr/bin/env bash
22

3-
#set -e
4-
#set -x
5-
63
startup_dirs=(/Library/LaunchAgents /Library/LaunchDaemons ~/Library/LaunchAgents ~/Library/LaunchDaemons)
74
system_dirs=(/System/Library/LaunchAgents /System/Library/LaunchDaemons)
85

@@ -12,6 +9,10 @@ YELLOW='\033[1;33m'
129
NC='\033[0m'
1310
BOLD='\033[1m'
1411

12+
#
13+
#--------------------------------------------------------------------------------------------------------------------------------------
14+
#
15+
1516
function join_by { local IFS="$1"; shift; echo "$*"; }
1617

1718
function usage {
@@ -50,7 +51,182 @@ function getScriptUser {
5051
grep '<key>UserName</key>' -C1 "$scriptPath" | tail -n1 | cut -d '>' -f 2 | cut -d '<' -f 1
5152
}
5253

53-
function listItems {
54+
function getKernelExtensions {
55+
kmutil showloaded --no-kernel-components --list-only --sort --show loaded 2>/dev/null | tr -s ' ' | grep -v 'com\.apple\.'
56+
}
57+
58+
function listKernelExtensions {
59+
local filter="$1"
60+
61+
getKernelExtensions | while IFS= read -r kextLine; do
62+
63+
kextLoaded="$(echo "$kextLine" | cut -d ' ' -f 3)"
64+
kextName="$(echo "$kextLine" | cut -d ' ' -f 7)"
65+
kextVersion="$(echo "$kextLine" | grep -o '\((.*)\)')"
66+
67+
if [ "$filter" == "disabled" ] && [ "$kextLoaded" != "0" ]; then
68+
continue
69+
fi
70+
71+
if [ "$filter" == "enabled" ] && [ "$kextLoaded" == "0" ]; then
72+
continue
73+
fi
74+
75+
if [ -n "$filter" ] && [ "$filter" != "system" ] && [ "$filter" != "enabled" ] && [ "$filter" != "disabled" ]; then
76+
if [[ "$kextName" != *"$filter"* ]]; then
77+
continue
78+
fi
79+
fi
80+
81+
kernelPath="$(kextfind -system-extensions "$kextName" 2>/dev/null)"
82+
if [ -z "$kernelPath" ]; then
83+
kernelPath="n/a"
84+
fi
85+
86+
local loaded
87+
if [ "$kextLoaded" == "0" ]; then
88+
loaded="${GREEN}${BOLD}disabled${NC}"
89+
else
90+
loaded="${RED}Always${NC}"
91+
fi
92+
93+
echo -e "${BOLD}> ${kextName}${NC} ${kextVersion}"
94+
echo -e " Type : ${RED}kernel extension${NC}"
95+
echo -e " User : ${RED}root${NC}"
96+
echo -e " Launch: ${loaded}"
97+
echo " File : ${kernelPath}"
98+
99+
done
100+
}
101+
102+
function disableKernelExtensions {
103+
local filter="$1"
104+
105+
getKernelExtensions | while IFS= read -r kextLine; do
106+
107+
kextLoaded="$(echo "$kextLine" | cut -d ' ' -f 3)"
108+
kextName="$(echo "$kextLine" | cut -d ' ' -f 7)"
109+
110+
if ! [[ "$kextName" =~ $filter ]]; then
111+
continue
112+
fi
113+
114+
if [ "$kextLoaded" == "0" ]; then
115+
#error "kernel extension is already unloaded"
116+
continue
117+
fi
118+
119+
if ! kmutil load -b "$kextName" 1>/dev/null; then
120+
error "could not disable kernel extension"
121+
fi
122+
123+
echo -e "${GREEN}Disabled ${STRONG}${kextName}${NC}"
124+
done
125+
}
126+
127+
function enableKernelExtensions {
128+
local filter="$1"
129+
130+
getKernelExtensions | while IFS= read -r kextLine; do
131+
132+
kextLoaded="$(echo "$kextLine" | cut -d ' ' -f 3)"
133+
kextName="$(echo "$kextLine" | cut -d ' ' -f 7)"
134+
135+
if ! [[ "$kextName" =~ $filter ]]; then
136+
continue
137+
fi
138+
139+
if ! [ "$kextLoaded" == "0" ]; then
140+
#error "kernel extension is already loaded"
141+
continue
142+
fi
143+
144+
if ! kmutil unload -b "$kextName" 1>/dev/null; then
145+
error "could not disable kernel extension"
146+
fi
147+
148+
echo -e "${GREEN}Enabled ${STRONG}${kextName}${NC}"
149+
done
150+
}
151+
152+
function getSystemExtensions {
153+
systemextensionsctl list 2>/dev/null | tail -n+2 | grep -v '^---' | grep -v '^enabled' | tr -s ' '
154+
}
155+
156+
function listSystemExtensions {
157+
local filter="$1"
158+
159+
getSystemExtensions | while IFS= read -r extLine; do
160+
161+
fullName="$(echo "$extLine" | cut -d$'\t' -f 4)"
162+
extName="$(echo "$fullName" | cut -d ' ' -f 1)"
163+
extVersion="$(echo "$fullName" | grep -o '\((.*)\)')"
164+
165+
if [ -n "$filter" ] && ! [[ "$extName" =~ $filter ]]; then
166+
continue
167+
fi
168+
169+
local loaded
170+
if [ "$(echo "$extLine" | cut -d$'\t' -f 2)" == "*" ]; then
171+
loaded="${ORANGE}enabled${NC}"
172+
else
173+
loaded="${GREEN}disabled${NC}"
174+
fi
175+
176+
echo -e "${BOLD}> ${extName}${NC} ${extVersion}"
177+
echo -e " Type : system extension"
178+
echo -e " User : $(whoami)"
179+
echo -e " Launch: ${loaded}"
180+
echo " File : n/a"
181+
done
182+
}
183+
184+
function enableSystemExtensions {
185+
local filter="$1"
186+
187+
getSystemExtensions | while IFS= read -r extLine; do
188+
189+
extName="$(echo "$extLine" | cut -d$'\t' -f 4 | cut -d ' ' -f 1)"
190+
191+
if ! [[ "$extName" =~ $filter ]]; then
192+
continue
193+
fi
194+
195+
if [ "$(echo "$extLine" | cut -d$'\t' -f 2)" == "*" ]; then
196+
# error "this system extension is already enabled"
197+
continue
198+
fi
199+
200+
#TODO: implement load system extension via CLI
201+
error "enabling system extensions is not yet implemented"
202+
done
203+
}
204+
205+
function disableSystemExtensions {
206+
local filter="$1"
207+
208+
getSystemExtensions | while IFS= read -r extLine; do
209+
210+
extName="$(echo "$extLine" | cut -d$'\t' -f 4 | cut -d ' ' -f 1)"
211+
212+
if ! [[ "$extName" =~ $filter ]]; then
213+
continue
214+
fi
215+
216+
if ! [ "$(echo "$extLine" | cut -d$'\t' -f 2)" == "*" ]; then
217+
# error "this system extension is already disabled"
218+
continue
219+
fi
220+
221+
if ! systemextensionsctl uninstall '-' "$extName"; then
222+
error "could not disable system extension"
223+
fi
224+
225+
echo -e "${GREEN}Enabled ${STRONG}${extName}${NC}"
226+
done
227+
}
228+
229+
function listLaunchItems {
54230
local filter="$2"
55231

56232
itemDirectories=("${startup_dirs[@]}")
@@ -186,7 +362,7 @@ function listItems {
186362
done< <(find "${itemDirectories[@]}" -type f -iname '*.plist*' -print0 2>/dev/null)
187363
}
188364

189-
function enableItems {
365+
function enableLaunchItems {
190366
disabled_items="$(launchctl print-disabled user/"$(id -u)")"
191367

192368
while IFS= read -r -d '' startupFile; do
@@ -220,7 +396,7 @@ function enableItems {
220396
done< <(find "${startup_dirs[@]}" "${system_dirs[@]}" \( -iname "*$1*.plist" -o -iname "*$1*.plist.disabled" \) -print0 2>/dev/null)
221397
}
222398

223-
function disableItems {
399+
function disableLaunchItems {
224400
disabled_items="$(launchctl print-disabled user/"$(id -u)")"
225401

226402
while IFS= read -r -d '' startupFile; do
@@ -254,6 +430,10 @@ function disableItems {
254430
done< <(find "${startup_dirs[@]}" "${system_dirs[@]}" \( -iname "*$1*.plist" -o -iname "*$1*.plist.disabled" \) -print0 2>/dev/null)
255431
}
256432

433+
#
434+
#--------------------------------------------------------------------------------------------------------------------------------------
435+
#
436+
257437
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
258438
usage
259439
fi
@@ -265,20 +445,29 @@ case "$1" in
265445
usage
266446
fi
267447
fi
268-
listItems "$1" "$2"
448+
listLaunchItems "$1" "$2"
449+
listKernelExtensions "$2"
450+
listSystemExtensions "$2"
269451
;;
452+
270453
"disable")
271454
if [ $# -ne 2 ]; then
272455
usage
273456
fi
274-
disableItems "$2"
457+
disableLaunchItems "$2"
458+
disableKernelExtensions "$2"
459+
disableSystemExtensions "$2"
275460
;;
461+
276462
"enable")
277463
if [ $# -ne 2 ]; then
278464
usage
279465
fi
280-
enableItems "$2"
466+
enableLaunchItems "$2"
467+
enableKernelExtensions "$2"
468+
enableSystemExtensions "$2"
281469
;;
470+
282471
*)
283472
usage
284473
;;

0 commit comments

Comments
 (0)