Skip to content

Commit 416aa04

Browse files
committed
Add a script to install Magisk
1 parent b33cdb6 commit 416aa04

File tree

2 files changed

+241
-0
lines changed

2 files changed

+241
-0
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ running kernel module address. It relocates the bundled `getroot.elf` to the add
7575
of the `_CmdVersion` handler, writes it there, executes it via `b.ver` and `execve()`s
7676
a shell.
7777

78+
See the `install-magisk` script to install [Magisk](https://github.com/topjohnwu/Magisk)
79+
for a more permanent way of acquiring root. It does not seem to affect OTA updates in
80+
any way, except that it needs reinstallation after an update.
81+
7882
## cli
7983

8084
### Technical details

install-magisk

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
#!/bin/bash
2+
set -euxo pipefail
3+
4+
# Settings for adb and curl.
5+
export ANDROID_SERIAL="172.16.23.223:5555" # see adb help
6+
readonly CURL_ARGS=(-L) # follow location
7+
8+
# Root shell loader and temporary script path for run_as_root.
9+
readonly LOADER_PATH="/data/local/tmp/loader"
10+
readonly LOADER_BUILD_DIR="$(dirname "$(readlink -f "$0")")" # script directory
11+
readonly SCRIPT_TMP_PATH="/data/local/tmp/magiskinstall-tmp"
12+
13+
# Magisk Manager package name, version and URL
14+
readonly MAGISK_MANAGER_PACKAGE_NAME="com.topjohnwu.magisk"
15+
readonly MAGISK_MANAGER_MAIN_PACKAGE="${MAGISK_MANAGER_PACKAGE_NAME}/a.c"
16+
readonly MAGISK_MANAGER_VERSION="7.5.1"
17+
readonly MAGISK_MANAGER_APK_URL="https://github.com/topjohnwu/Magisk/releases/download/manager-v${MAGISK_MANAGER_VERSION}/MagiskManager-v${MAGISK_MANAGER_VERSION}.apk"
18+
19+
# boot, recovery and recoveryB partitions. boot is backed up to recoveryB before patching, whose
20+
# content should match recovery.
21+
readonly BOOT_PARTNO="5"
22+
readonly BOOT_DEV="/dev/mmcblk0p${BOOT_PARTNO}"
23+
readonly RECOVERY_PARTNO="4"
24+
readonly RECOVERY_DEV="/dev/mmcblk0p${RECOVERY_PARTNO}"
25+
readonly RECOVERY_B_PARTNO="8"
26+
readonly RECOVERY_B_DEV="/dev/mmcblk0p${RECOVERY_B_PARTNO}"
27+
28+
# Directory to place boot image in for patching from Magisk Manager.
29+
readonly DOWNLOAD_DIR="/sdcard/Download" # Something accessible from the Android UI
30+
readonly PATCH_DIR="${DOWNLOAD_DIR}/magiskinstall.$(date +%F_%T)"
31+
32+
# Directory where Magisk Manager places the patched output boot image.
33+
readonly MAGISK_PATCHED_PATH="${DOWNLOAD_DIR}/magisk_patched.img"
34+
35+
# Runs the passed arguments as root.
36+
run_as_root() {
37+
# Getting something escaped properly through adb shell, the loader's argv parsing, and then
38+
# the android shell is tricky, so generate a shell script and execute it on the device.
39+
local tmp="$(mktemp)"
40+
printf '#!/system/bin/sh\nPS4="(run_as_root) + "\nset -x\n' > "$tmp"
41+
printf '%q ' "$@" >> "$tmp" # Need to print "%q " individually; the entire format is repeated per arg
42+
printf '\n' >> "$tmp"
43+
chmod +x "$tmp"
44+
adb push "$tmp" "$SCRIPT_TMP_PATH" > /dev/null
45+
rm -f "$tmp"
46+
adb shell "$LOADER_PATH -trigger_exec $SCRIPT_TMP_PATH"
47+
}
48+
49+
# Tests whether run_as_root escapes arguments and returns the return code properly.
50+
test_run_as_root() {
51+
local have="$(run_as_root sh -c 'echo $1' "foo bar" "bar baz" "baz qux")"
52+
local want="bar baz"
53+
if [ "$have" != "$want" ]; then
54+
echo "ERROR: run_as_root broken: got >>>$have<<<, want >>>$want<<<" >&2
55+
exit 99
56+
fi
57+
58+
if run_as_root false; then
59+
echo "ERROR: 'run_as_root false' succeeded" >&2
60+
exit 99
61+
fi
62+
}
63+
64+
# Installs the root shell loader on the device.
65+
prepare_root() {
66+
if ! adb shell test -x "$LOADER_PATH"; then
67+
echo "A root shell loader is not installed. Building and installing."
68+
make -C "$LOADER_BUILD_DIR" loader
69+
adb push "$LOADER_BUILD_DIR"/loader "$LOADER_PATH"
70+
fi
71+
test_run_as_root
72+
}
73+
74+
# Returns the name of the given partition number from the device’s /proc/partinfo.
75+
get_partname() {
76+
local partno="$1"
77+
# ex.: part05: 00012800 0000a000 "boot"
78+
set -x
79+
run_as_root cat /proc/partinfo | \
80+
awk '/^part0*'"$partno"':/ { gsub("\"", ""); print $NF }'
81+
}
82+
83+
# Queries the user for a response and returns 0 for yes, 1 for no.
84+
ask_yesno() {
85+
local prompt="$1"
86+
local reply
87+
while true; do
88+
printf "%s [y/n]: " "$prompt"
89+
read reply
90+
case "$reply" in
91+
[yY])
92+
return 0
93+
;;
94+
[nN])
95+
return 1
96+
;;
97+
*)
98+
echo "Invalid response. Please enter 'y' or 'n'."
99+
;;
100+
esac
101+
done
102+
}
103+
104+
# Returns the versionName of an installed package.
105+
get_package_version() {
106+
local pkg="$1"
107+
adb shell pm dump "$pkg" | \
108+
sed -r -n -e 's/^[[:blank:]]*versionName=([^[:blank:]]+)/\1/;T;p'
109+
}
110+
111+
# Checks whether a given partition is an Android boot image.
112+
is_android_bootimg() {
113+
local part="$1"
114+
test "$(run_as_root dd if="$part" bs=8 count=1)" = 'ANDROID!'
115+
}
116+
117+
# (Re-)installs Magisk Manager.
118+
install_magisk_manager(){
119+
local tmp="$(mktemp)"
120+
curl "${CURL_ARGS[@]}" "$MAGISK_MANAGER_APK_URL" > "$tmp"
121+
if ! adb install -r "$tmp"; then
122+
if ! ask_yesno "Installation of Magisk Manager failed. Continue?"; then
123+
exit 1
124+
fi
125+
fi
126+
rm -f "$tmp"
127+
}
128+
129+
130+
### Main script starts here ###
131+
132+
echo "Preparing root environment..."
133+
prepare_root
134+
135+
echo "Checking Magisk Manager version..."
136+
mm_version="$(get_package_version "$MAGISK_MANAGER_PACKAGE_NAME")"
137+
if [ -z "$mm_version" ]; then
138+
echo "Magisk Manager does not seem to be installed. Installing..."
139+
install_magisk_manager
140+
elif [ "$mm_version" != "$MAGISK_MANAGER_VERSION" ]; then
141+
echo "Your Magisk Manager version '$mm_version' does not match expected '$MAGISK_MANAGER_VERSION'."
142+
if ask_yesno "Reinstall?"; then
143+
install_magisk_manager
144+
fi
145+
else
146+
echo "Installed Magisk Manager version $mm_version matches expected version."
147+
fi
148+
149+
echo "Sanity-checking partitions..."
150+
declare -rA want_partnames=(["$BOOT_PARTNO"]="boot"
151+
["$RECOVERY_PARTNO"]="recovery"
152+
["$RECOVERY_B_PARTNO"]="recoveryB")
153+
for partno in "${!want_partnames[@]}"; do
154+
want_name="${want_partnames[$partno]}"
155+
have_name="$(get_partname "$partno")"
156+
if [ "$have_name" != "$want_name" ]; then
157+
echo "Partition $partno is named '$have_name', expected '$want_name'. Aborting." >&2
158+
exit 1
159+
fi
160+
echo "Partition $partno has expected name '$want_name'."
161+
done
162+
163+
for dev in "$BOOT_DEV" "$RECOVERY_DEV" "$RECOVERY_B_DEV"; do
164+
if ! is_android_bootimg "$dev"; then
165+
echo "Device $dev does not appear to be an Android boot image. Aborting." >&2
166+
exit 1
167+
fi
168+
echo "Device $dev looks like an Android boot image."
169+
done
170+
171+
echo "Creating patch directory $PATCH_DIR..."
172+
adb shell mkdir "$PATCH_DIR"
173+
174+
orig_img="$PATCH_DIR/boot_orig.img"
175+
run_as_root dd if="$BOOT_DEV" of="$orig_img"
176+
run_as_root sh -c 'getprop > "$1"/getprop.txt' argv0 "$PATCH_DIR"
177+
178+
echo "Starting Magisk Manager..."
179+
adb shell am start "$MAGISK_MANAGER_MAIN_PACKAGE"
180+
181+
cat <<EOF
182+
183+
Magisk Manager has been started. To patch the boot image, do the following:
184+
185+
* Click on 'Install' in the 'Magisk is not installed' line. You will need to click 'Install'
186+
with a mouse; just selecting and 'clicking' the line with the TV remote WILL NOT WORK.
187+
188+
* Select 'Install', then 'Select and Patch a File'.
189+
190+
* Select $orig_img
191+
192+
* Wait until Magisk has finished patching.
193+
194+
* Press [Enter] here. You can close Magisk Manager on the TV.
195+
EOF
196+
read
197+
198+
if ! adb shell test -e "$MAGISK_PATCHED_PATH"; then
199+
echo "Patched image not found at '$MAGISK_PATCHED_PATH'. Aborting." >&2
200+
exit 1
201+
elif ! adb shell test "$MAGISK_PATCHED_PATH" -nt "$orig_img"; then
202+
echo "Patched image ($MAGISK_PATCHED_PATH) is not newer than $orig_img. Aborting." >&2
203+
exit 1
204+
fi
205+
206+
if adb shell cmp "$MAGISK_PATCHED_PATH" "$orig_img"; then
207+
echo "Patched image $MAGISK_PATCHED_PATH equal to original image $orig_img. Aborting." >&2
208+
exit 1
209+
fi
210+
211+
if ! is_android_bootimg "$MAGISK_PATCHED_PATH"; then
212+
echo "Patched image $MAGISK_PATCHED_PATH does not look like an Android boot image. Aborting." >&2
213+
exit 1
214+
fi
215+
216+
217+
patched_img="$PATCH_DIR/boot_patched.img"
218+
echo "Patched image $MAGISK_PATCHED_PATH looks sane, moving to $patched_img"
219+
adb shell mv "$MAGISK_PATCHED_PATH" "$patched_img"
220+
221+
if run_as_root cmp "$RECOVERY_DEV" "$RECOVERY_B_DEV" || \
222+
ask_yesno "Partitions 'recovery' and 'recoveryB' differ (after ~20MB seems normal). Do you still want to back up 'boot' to 'recoveryB'?"; then
223+
run_as_root dd if="$BOOT_DEV" of="$RECOVERY_B_DEV"
224+
echo "Backed up $BOOT_DEV (boot) to $RECOVERY_B_DEV (recoveryB). See README.md for recovery hints."
225+
else
226+
if ! ask_yesno "Not backing up boot partition. Are you sure?"; then
227+
exit 1
228+
fi
229+
fi
230+
231+
if ! ask_yesno "Do you want to write $patched_img to $BOOT_DEV? This is your last chance to quit."; then
232+
exit 1
233+
fi
234+
235+
run_as_root dd if="$patched_img" of="$BOOT_DEV"
236+
237+
echo "Boot partition ($BOOT_DEV) has been patched. Enjoy!"

0 commit comments

Comments
 (0)