mush.sh 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  1. #!/bin/bash
  2. get_largest_cros_blockdev() {
  3. local largest size dev_name tmp_size remo
  4. size=0
  5. for blockdev in /sys/block/*; do
  6. dev_name="${blockdev##*/}"
  7. echo "$dev_name" | grep -q '^\(loop\|ram\)' && continue
  8. tmp_size=$(cat "$blockdev"/size)
  9. remo=$(cat "$blockdev"/removable)
  10. if [ "$tmp_size" -gt "$size" ] && [ "${remo:-0}" -eq 0 ]; then
  11. case "$(sfdisk -l -o name "/dev/$dev_name" 2>/dev/null)" in
  12. *STATE*KERN-A*ROOT-A*KERN-B*ROOT-B*)
  13. largest="/dev/$dev_name"
  14. size="$tmp_size"
  15. ;;
  16. esac
  17. fi
  18. done
  19. echo "$largest"
  20. }
  21. traps() {
  22. set +e
  23. trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
  24. trap 'echo \"${last_command}\" command failed with exit code $? - press a key to exit.' EXIT
  25. trap '' INT
  26. }
  27. mush_info() {
  28. echo -ne "\033]0;mush\007"
  29. if [ ! -f /mnt/stateful_partition/custom_greeting ]; then
  30. cat <<-EOF
  31. Welcome to mush, the murkmod developer shell.
  32. If you got here by mistake, don't panic! Just close this tab and carry on.
  33. This shell contains a list of utilities for performing various actions on a murkmodded chromebook.
  34. murkmod is now maintained completely independently from fakemurk. Don't report any bugs you encounter with it to the fakemurk developers.
  35. EOF
  36. else
  37. cat /mnt/stateful_partition/custom_greeting
  38. fi
  39. }
  40. doas() {
  41. ssh -t -p 1337 -i /rootkey -oStrictHostKeyChecking=no root@127.0.0.1 "$@"
  42. }
  43. runjob() {
  44. clear
  45. trap 'kill -2 $! >/dev/null 2>&1' INT
  46. (
  47. # shellcheck disable=SC2068
  48. $@
  49. )
  50. trap '' INT
  51. clear
  52. }
  53. swallow_stdin() {
  54. while read -t 0 notused; do
  55. read input
  56. done
  57. }
  58. edit() {
  59. if which nano 2>/dev/null; then
  60. doas nano "$@"
  61. else
  62. doas vi "$@"
  63. fi
  64. }
  65. locked_main() {
  66. traps
  67. mush_info
  68. while true; do
  69. echo -ne "\033]0;mush\007"
  70. cat <<-EOF
  71. (1) Emergency Revert & Re-Enroll
  72. (2) Soft Disable Extensions
  73. (3) Hard Disable Extensions
  74. (4) Hard Enable Extensions
  75. (5) Enter Admin Mode (Password-Protected)
  76. (6) Check for updates
  77. EOF
  78. swallow_stdin
  79. read -r -p "> (1-5): " choice
  80. case "$choice" in
  81. 1) runjob revert ;;
  82. 2) runjob softdisableext ;;
  83. 3) runjob harddisableext ;;
  84. 4) runjob hardenableext ;;
  85. 5) runjob prompt_passwd ;;
  86. 6) runjob do_updates && exit 0 ;;
  87. *) echo && echo "Invalid option, dipshit." && echo ;;
  88. esac
  89. done
  90. }
  91. main() {
  92. traps
  93. mush_info
  94. while true; do
  95. echo -ne "\033]0;mush\007"
  96. cat <<-EOF
  97. (1) Root Shell
  98. (2) Chronos Shell
  99. (3) Crosh
  100. (4) Plugins
  101. (5) Install plugins
  102. (6) Uninstall plugins
  103. (7) Powerwash
  104. (8) Emergency Revert & Re-Enroll
  105. (9) Soft Disable Extensions
  106. (10) Hard Disable Extensions
  107. (11) Hard Enable Extensions
  108. (12) Automagically Disable Extensions
  109. (13) Edit Pollen
  110. (14) Install Crouton
  111. (15) Start Crouton
  112. (16) Enable dev_boot_usb
  113. (17) Disable dev_boot_usb
  114. (18) Set mush password
  115. (19) Remove mush password
  116. (20) [EXPERIMENTAL] Update ChromeOS
  117. (21) [EXPERIMENTAL] Update Emergency Backup
  118. (22) [EXPERIMENTAL] Restore Emergency Backup Backup
  119. (23) [EXPERIMENTAL] Install Chromebrew
  120. (24) [EXPERIMENTAL] Install Gentoo Boostrap (dev_install)
  121. (25) Check for updates
  122. EOF
  123. swallow_stdin
  124. read -r -p "> (1-25): " choice
  125. case "$choice" in
  126. 1) runjob doas bash ;;
  127. 2) runjob doas "cd /home/chronos; sudo -i -u chronos" ;;
  128. 3) runjob /usr/bin/crosh.old ;;
  129. 4) runjob show_plugins ;;
  130. 5) runjob install_plugins ;;
  131. 6) runjob uninstall_plugins ;;
  132. 7) runjob powerwash ;;
  133. 8) runjob revert ;;
  134. 9) runjob softdisableext ;;
  135. 10) runjob harddisableext ;;
  136. 11) runjob hardenableext ;;
  137. 12) runjob autodisableexts ;;
  138. 13) runjob edit /etc/opt/chrome/policies/managed/policy.json ;;
  139. 14) runjob install_crouton ;;
  140. 15) runjob run_crouton ;;
  141. 16) runjob enable_dev_boot_usb ;;
  142. 17) runjob disable_dev_boot_usb ;;
  143. 18) runjob set_passwd ;;
  144. 19) runjob remove_passwd ;;
  145. 20) runjob attempt_chromeos_update ;;
  146. 21) runjob attempt_backup_update ;;
  147. 22) runjob attempt_restore_backup_backup ;;
  148. 23) runjob attempt_chromebrew_install ;;
  149. 24) runjob attempt_dev_install ;;
  150. 25) runjob do_updates && exit 0 ;;
  151. 26) runjob do_dev_updates && exit 0 ;;
  152. 101) runjob hard_disable_nokill ;;
  153. 111) runjob hard_enable_nokill ;;
  154. 112) runjob ext_purge ;;
  155. 113) runjob list_plugins ;;
  156. 114) runjob install_plugin_legacy ;;
  157. 115) runjob uninstall_plugin_legacy ;;
  158. 201) runjob api_read_file ;;
  159. 202) runjob api_write_file ;;
  160. 203) runjob api_append_file ;;
  161. 204) runjob api_touch_file ;;
  162. 205) runjob api_create_dir ;;
  163. 206) runjob api_rm_file ;;
  164. 207) runjob api_rm_dir ;;
  165. 208) runjob api_ls_dir ;;
  166. 209) runjob api_cd ;;
  167. *) echo && echo "Invalid option, dipshit." && echo ;;
  168. esac
  169. done
  170. }
  171. api_read_file() {
  172. echo "file to read?"
  173. read -r filename
  174. local contents=$( base64 $filename )
  175. echo "start content: $contents end content"
  176. }
  177. api_write_file() {
  178. echo "file to write to?"
  179. read -r filename
  180. echo "base64 contents?"
  181. read -r contents
  182. base64 -d <<< "$contents" > $filename
  183. }
  184. api_append_file() {
  185. echo "file to write to?"
  186. read -r filename
  187. echo "base64 contents to append?"
  188. read -r contents
  189. base64 -d <<< "$contents" >> $filename
  190. }
  191. api_touch_file() {
  192. echo "filename?"
  193. read -r filename
  194. touch $filename
  195. }
  196. api_create_dir() {
  197. echo "dirname?"
  198. read -r dirname
  199. mkdir -p $dirname
  200. }
  201. api_rm_file() {
  202. echo "filename?"
  203. read -r filename
  204. rm -f $filename
  205. }
  206. api_rm_dir() {
  207. echo "dirname?"
  208. read -r dirname
  209. rm -Rf $dirname
  210. }
  211. api_ls_dir() {
  212. echo "dirname? (or . for current dir)"
  213. read -r dirname
  214. ls $dirname
  215. }
  216. api_cd() {
  217. echo "dir?"
  218. read -r dirname
  219. cd $dirname
  220. }
  221. install_plugin_legacy() {
  222. local raw_url="https://raw.githubusercontent.com/rainestorme/murkmod/main/plugins"
  223. echo "Find a plugin you want to install here: "
  224. echo " https://github.com/rainestorme/murkmod/tree/main/plugins"
  225. echo "Enter the name of a plugin (including the .sh) to install it (or q to quit):"
  226. read -r plugin_name
  227. local plugin_url="$raw_url/$plugin_name"
  228. local plugin_info=$(curl -s $plugin_url)
  229. if [[ $plugin_info == *"Not Found"* ]]; then
  230. echo "Plugin not found"
  231. else
  232. echo "Installing..."
  233. doas "pushd /mnt/stateful_partition/murkmod/plugins && curl https://raw.githubusercontent.com/rainestorme/murkmod/main/plugins/$plugin_name -O && popd" > /dev/null
  234. echo "Installed $plugin_name"
  235. fi
  236. }
  237. uninstall_plugin_legacy() {
  238. local raw_url="https://raw.githubusercontent.com/rainestorme/murkmod/main/plugins"
  239. echo "Enter the name of a plugin (including the .sh) to uninstall it (or q to quit):"
  240. read -r plugin_name
  241. doas "rm -rf /mnt/stateful_partition/murkmod/plugins/$plugin_name"
  242. }
  243. list_plugins() {
  244. plugins_dir="/mnt/stateful_partition/murkmod/plugins"
  245. plugin_files=()
  246. while IFS= read -r -d '' file; do
  247. plugin_files+=("$file")
  248. done < <(find "$plugins_dir" -type f -name "*.sh" -print0)
  249. plugin_info=()
  250. for file in "${plugin_files[@]}"; do
  251. plugin_script=$file
  252. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=".*"' "$plugin_script" | cut -d= -f2-)
  253. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=".*"' "$plugin_script" | cut -d= -f2-)
  254. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=".*"' "$plugin_script" | cut -d= -f2-)
  255. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=".*"' "$plugin_script" | cut -d= -f2-)
  256. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=".*"' "$plugin_script" | cut -d= -f2-)
  257. # remove quotes from around each PLUGIN_* variable
  258. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  259. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  260. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  261. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  262. if grep -q "menu_plugin" "$plugin_script"; then
  263. plugin_info+=("$PLUGIN_FUNCTION,$PLUGIN_NAME,$PLUGIN_DESCRIPTION,$PLUGIN_AUTHOR,$PLUGIN_VERSION")
  264. fi
  265. done
  266. to_print=""
  267. # Print menu options
  268. for i in "${!plugin_info[@]}"; do
  269. to_print="$to_print[][]${plugin_info[$i]}"
  270. done
  271. echo "$to_print"
  272. }
  273. do_dev_updates() {
  274. echo "Welcome to the secret murkmod developer update menu!"
  275. echo "This utility allows you to install murkmod from a specific branch on the git repo."
  276. echo "If you were trying to update murkmod normally, then don't panic! Just enter 'main' at the prompt and everything will work normally."
  277. read -p "> (branch name, eg. main): " branch
  278. doas "MURKMOD_BRANCH=$branch bash <(curl -SLk https://raw.githubusercontent.com/rainestorme/murkmod/main/murkmod.sh)"
  279. exit
  280. }
  281. disable_ext() {
  282. local extid="$1"
  283. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 000 "/home/chronos/user/Extensions/$extid" && kill -9 $(pgrep -f "\-\-extension\-process") || "Extension ID $extid is invalid."
  284. }
  285. disable_ext_nokill() {
  286. local extid="$1"
  287. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 000 "/home/chronos/user/Extensions/$extid" || "Extension ID $extid is invalid."
  288. }
  289. enable_ext_nokill() {
  290. local extid="$1"
  291. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 777 "/home/chronos/user/Extensions/$extid" || "Invalid extension id."
  292. }
  293. ext_purge() {
  294. kill -9 $(pgrep -f "\-\-extension\-process")
  295. }
  296. hard_disable_nokill() {
  297. read -r -p "Enter extension ID > " extid
  298. disable_ext_nokill $extid
  299. }
  300. hard_enable_nokill() {
  301. read -r -p "Enter extension ID > " extid
  302. enable_ext_nokill $extid
  303. }
  304. autodisableexts() {
  305. echo "Disabling extensions..."
  306. disable_ext_nokill "haldlgldplgnggkjaafhelgiaglafanh" # GoGuardian
  307. disable_ext_nokill "dikiaagfielfbnbbopidjjagldjopbpa" # Clever Plus
  308. disable_ext_nokill "cgbbbjmgdpnifijconhamggjehlamcif" # Gopher Buddy
  309. disable_ext_nokill "inoeonmfapjbbkmdafoankkfajkcphgd" # Read and Write for Google Chrome
  310. disable_ext_nokill "enfolipbjmnmleonhhebhalojdpcpdoo" # Screenshot reader
  311. disable_ext_nokill "joflmkccibkooplaeoinecjbmdebglab" # Securly
  312. disable_ext_nokill "iheobagjkfklnlikgihanlhcddjoihkg" # Securly again
  313. disable_ext_nokill "adkcpkpghahmbopkjchobieckeoaoeem" # LightSpeed
  314. disable_ext_nokill "jcdhmojfecjfmbdpchihbeilohgnbdci" # Cisco Umbrella
  315. disable_ext_nokill "jdogphakondfdmcanpapfahkdomaicfa" # ContentKeeper Authenticator
  316. disable_ext_nokill "aceopacgaepdcelohobicpffbbejnfac" # Hapara
  317. disable_ext_nokill "kmffehbidlalibfeklaefnckpidbodff" # iBoss
  318. disable_ext_nokill "jaoebcikabjppaclpgbodmmnfjihdngk" # LightSpeed Classroom
  319. disable_ext_nokill "ghlpmldmjjhmdgmneoaibbegkjjbonbk" # Blocksi
  320. disable_ext_nokill "ddfbkhpmcdbciejenfcolaaiebnjcbfc" # Linewize
  321. disable_ext_nokill "jfbecfmiegcjddenjhlbhlikcbfmnafd" # Securly Classroom
  322. disable_ext_nokill "jjpmjccpemllnmgiaojaocgnakpmfgjg" # Impero
  323. disable_ext_nokill "feepmdlmhplaojabeoecaobfmibooaid" # OrbitNote
  324. disable_ext_nokill "dmhpekdihnngbkinliefnclgmgkpjeoo" # GoGuardian License
  325. disable_ext_nokill "modkadcjnbamppdpdkfoackjnhnfiogi" # MyMPS Chrome SSO
  326. ext_purge
  327. echo "Done."
  328. }
  329. set_passwd() {
  330. echo "Enter a new password to use for mush. This will be required to perform any future administrative actions, so make sure you write it down somewhere!"
  331. read -r -p " > " newpassword
  332. doas "touch /mnt/stateful_partition/murkmod/mush_password"
  333. doas "echo '$newpassword'> /mnt/stateful_partition/murkmod/mush_password"
  334. }
  335. remove_passwd() {
  336. echo "Removing password from mush..."
  337. doas "rm -f /mnt/stateful_partition/murkmod/mush_password"
  338. }
  339. prompt_passwd() {
  340. echo "Enter your password:"
  341. read -r -p " > " password
  342. stored_password=$(cat /mnt/stateful_partition/murkmod/mush_password)
  343. if [ "$password" == "$stored_password" ]; then
  344. main
  345. return
  346. else
  347. echo "Incorrect password."
  348. read -r -p "Press enter to continue." throwaway
  349. fi
  350. }
  351. disable_dev_boot_usb() {
  352. echo "Disabling dev_boot_usb"
  353. sed -i 's/\(dev_boot_usb=\).*/\10/' /usr/bin/crossystem
  354. }
  355. enable_dev_boot_usb() {
  356. echo "Enabling dev_boot_usb"
  357. sed -i 's/\(dev_boot_usb=\).*/\11/' /usr/bin/crossystem
  358. }
  359. do_updates() {
  360. doas "bash <(curl -SLk https://raw.githubusercontent.com/rainestorme/murkmod/main/murkmod.sh)"
  361. exit
  362. }
  363. show_plugins() {
  364. plugins_dir="/mnt/stateful_partition/murkmod/plugins"
  365. plugin_files=()
  366. while IFS= read -r -d '' file; do
  367. plugin_files+=("$file")
  368. done < <(find "$plugins_dir" -type f -name "*.sh" -print0)
  369. plugin_info=()
  370. for file in "${plugin_files[@]}"; do
  371. plugin_script=$file
  372. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=".*"' "$plugin_script" | cut -d= -f2-)
  373. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=".*"' "$plugin_script" | cut -d= -f2-)
  374. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=".*"' "$plugin_script" | cut -d= -f2-)
  375. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=".*"' "$plugin_script" | cut -d= -f2-)
  376. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=".*"' "$plugin_script" | cut -d= -f2-)
  377. # remove quotes from around each PLUGIN_* variable
  378. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  379. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  380. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  381. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  382. if grep -q "menu_plugin" "$plugin_script"; then
  383. plugin_info+=("$PLUGIN_FUNCTION (provided by $PLUGIN_NAME)")
  384. fi
  385. done
  386. # Print menu options
  387. for i in "${!plugin_info[@]}"; do
  388. printf "%s. %s\n" "$((i+1))" "${plugin_info[$i]}"
  389. done
  390. # Prompt user for selection
  391. read -p "> Select a plugin (or q to quit): " selection
  392. if [ "$selection" = "q" ]; then
  393. return 0
  394. fi
  395. # Validate user's selection
  396. if ! [[ "$selection" =~ ^[1-9][0-9]*$ ]]; then
  397. echo "Invalid selection. Please enter a number between 0 and ${#plugin_info[@]}"
  398. return 1
  399. fi
  400. if ((selection < 1 || selection > ${#plugin_info[@]})); then
  401. echo "Invalid selection. Please enter a number between 0 and ${#plugin_info[@]}"
  402. return 1
  403. fi
  404. # Get plugin function name and corresponding file
  405. selected_plugin=${plugin_info[$((selection-1))]}
  406. selected_file=${plugin_files[$((selection-1))]}
  407. # Execute the plugin
  408. bash <(cat $selected_file) # weird syntax due to noexec mount
  409. return 0
  410. }
  411. install_plugins() {
  412. clear
  413. echo "Fetching plugin information..."
  414. json=$(curl -s "https://api.github.com/repos/rainestorme/murkmod/contents/plugins")
  415. file_contents=()
  416. download_urls=()
  417. for entry in $(echo "$json" | jq -c '.[]'); do
  418. # Check if the entry is a file (type equals "file")
  419. if [[ $(echo "$entry" | jq -r '.type') == "file" ]]; then
  420. # Get the download URL for the file
  421. download_url=$(echo "$entry" | jq -r '.download_url')
  422. # Fetch the content of the file and append it to the array
  423. file_contents+=("$(curl -s "$download_url")")
  424. download_urls+=("$download_url")
  425. fi
  426. done
  427. plugin_info=()
  428. for content in "${file_contents[@]}"; do
  429. # Create a temporary file to hold the content
  430. tmp_file=$(mktemp)
  431. echo "$content" > "$tmp_file"
  432. # Extract information from the file
  433. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=.*' "$tmp_file" | cut -d= -f2-)
  434. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=.*' "$tmp_file" | cut -d= -f2-)
  435. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=.*' "$tmp_file" | cut -d= -f2-)
  436. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=.*' "$tmp_file" | cut -d= -f2-)
  437. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=.*' "$tmp_file" | cut -d= -f2-)
  438. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  439. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  440. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  441. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  442. # Add information to the plugin_info array
  443. plugin_info+=(" $PLUGIN_NAME (version $PLUGIN_VERSION by $PLUGIN_AUTHOR) \n $PLUGIN_DESCRIPTION")
  444. # Remove the temporary file
  445. rm "$tmp_file"
  446. done
  447. clear
  448. echo "Available plugins (press q to exit):"
  449. selected_option=0
  450. while true; do
  451. for i in "${!plugin_info[@]}"; do
  452. if [ $i -eq $selected_option ]; then
  453. printf " -> "
  454. else
  455. printf " "
  456. fi
  457. printf "${plugin_info[$i]}"
  458. # see if the plugin is already installed - get the filename of the plugin from its download url
  459. filename=$(echo "${download_urls[$i]}" | rev | cut -d/ -f1 | rev)
  460. if [ -f "/mnt/stateful_partition/murkmod/plugins/$filename" ]; then
  461. echo " (installed)"
  462. else
  463. echo
  464. fi
  465. done
  466. # Read a single character from the user
  467. read -s -n 1 key
  468. # Check the pressed key and update the selected option
  469. case "$key" in
  470. "q") break ;; # Exit the loop if the user presses 'q'
  471. "A") ((selected_option--)) ;; # Arrow key up
  472. "B") ((selected_option++)) ;; # Arrow key down
  473. "") clear
  474. echo "Using URL: ${download_urls[$selected_option]}"
  475. echo "Installing plugin..."
  476. doas "pushd /mnt/stateful_partition/murkmod/plugins && curl ${download_urls[$selected_option]} -O && popd" > /dev/null
  477. echo "Done!"
  478. ;;
  479. esac
  480. # Ensure the selected option stays within bounds
  481. ((selected_option = selected_option < 0 ? 0 : selected_option))
  482. ((selected_option = selected_option >= ${#plugin_info[@]} ? ${#plugin_info[@]} - 1 : selected_option))
  483. # Clear the screen for the next iteration
  484. clear
  485. echo "Available plugins (press q to exit):"
  486. done
  487. }
  488. uninstall_plugins() {
  489. clear
  490. plugins_dir="/mnt/stateful_partition/murkmod/plugins"
  491. plugin_files=()
  492. while IFS= read -r -d '' file; do
  493. plugin_files+=("$file")
  494. done < <(find "$plugins_dir" -type f -name "*.sh" -print0)
  495. plugin_info=()
  496. for file in "${plugin_files[@]}"; do
  497. plugin_script=$file
  498. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=.*' "$plugin_script" | cut -d= -f2-)
  499. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=.*' "$plugin_script" | cut -d= -f2-)
  500. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=.*' "$plugin_script" | cut -d= -f2-)
  501. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=.*' "$plugin_script" | cut -d= -f2-)
  502. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=.*' "$plugin_script" | cut -d= -f2-)
  503. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  504. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  505. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  506. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  507. plugin_info+=("$PLUGIN_NAME (version $PLUGIN_VERSION by $PLUGIN_AUTHOR)")
  508. done
  509. if [ ${#plugin_info[@]} -eq 0 ]; then
  510. echo "No plugins installed."
  511. read -r -p "Press enter to continue." throwaway
  512. return
  513. fi
  514. while true; do
  515. echo "Installed plugins:"
  516. for i in "${!plugin_info[@]}"; do
  517. echo "$(($i+1)). ${plugin_info[$i]}"
  518. done
  519. echo "0. Exit back to mush"
  520. read -r -p "Enter a number to uninstall a plugin, or 0 to exit: " choice
  521. if [ "$choice" -eq 0 ]; then
  522. clear
  523. return
  524. fi
  525. index=$(($choice-1))
  526. if [ "$index" -lt 0 ] || [ "$index" -ge ${#plugin_info[@]} ]; then
  527. echo "Invalid choice."
  528. continue
  529. fi
  530. plugin_file="${plugin_files[$index]}"
  531. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=".*"' "$plugin_file" | cut -d= -f2-)
  532. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=".*"' "$plugin_file" | cut -d= -f2-)
  533. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=".*"' "$plugin_file" | cut -d= -f2-)
  534. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=".*"' "$plugin_file" | cut -d= -f2-)
  535. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=".*"' "$plugin_file" | cut -d= -f2-)
  536. # remove quotes
  537. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  538. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  539. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  540. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  541. plugin_name="$PLUGIN_NAME (version $PLUGIN_VERSION by $PLUGIN_AUTHOR)"
  542. read -r -p "Are you sure you want to uninstall $plugin_name? [y/n] " confirm
  543. if [ "$confirm" == "y" ]; then
  544. doas rm "$plugin_file"
  545. echo "$plugin_name uninstalled."
  546. unset plugin_info[$index]
  547. plugin_info=("${plugin_info[@]}")
  548. fi
  549. done
  550. }
  551. powerwash() {
  552. echo "Are you sure you wanna powerwash? This will remove all user accounts and data, but won't remove fakemurk."
  553. sleep 2
  554. echo "(Press enter to continue, ctrl-c to cancel)"
  555. swallow_stdin
  556. read -r
  557. doas rm -f /stateful_unfucked
  558. doas reboot
  559. exit
  560. }
  561. revert() {
  562. echo "This option will re-enroll your chromebook and restore it to its exact state before fakemurk was run. This is useful if you need to quickly go back to normal."
  563. echo "This is *permanent*. You will not be able to fakemurk again unless you re-run everything from the beginning."
  564. echo "Are you sure - 100% sure - that you want to continue? (press enter to continue, ctrl-c to cancel)"
  565. swallow_stdin
  566. read -r
  567. printf "Setting kernel priority in 3 (this is your last chance to cancel)..."
  568. sleep 1
  569. printf "2..."
  570. sleep 1
  571. echo "1..."
  572. sleep 1
  573. echo "Setting kernel priority"
  574. DST=$(get_largest_cros_blockdev)
  575. if doas "((\$(cgpt show -n \"$DST\" -i 2 -P) > \$(cgpt show -n \"$DST\" -i 4 -P)))"; then
  576. doas cgpt add "$DST" -i 2 -P 0
  577. doas cgpt add "$DST" -i 4 -P 1
  578. else
  579. doas cgpt add "$DST" -i 4 -P 0
  580. doas cgpt add "$DST" -i 2 -P 1
  581. fi
  582. echo "Setting vpd..."
  583. doas vpd -i RW_VPD -s check_enrollment=1
  584. doas vpd -i RW_VPD -s block_devmode=1
  585. doas crossystem.old block_devmode=1
  586. echo "Setting stateful unfuck flag..."
  587. rm -f /stateful_unfucked
  588. echo "Done. Press enter to reboot"
  589. swallow_stdin
  590. read -r
  591. echo "Bye!"
  592. sleep 2
  593. doas reboot
  594. sleep 1000
  595. echo "Your chromebook should have rebooted by now. If your chromebook doesn't reboot in the next couple of seconds, press Esc+Refresh to do it manually."
  596. }
  597. harddisableext() { # calling it "hard disable" because it only reenables when you press
  598. read -r -p "Enter extension ID > " extid
  599. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 000 "/home/chronos/user/Extensions/$extid" && kill -9 $(pgrep -f "\-\-extension\-process") || "Invalid extension id."
  600. }
  601. hardenableext() {
  602. read -r -p "Enter extension ID > " extid
  603. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 777 "/home/chronos/user/Extensions/$extid" && kill -9 $(pgrep -f "\-\-extension\-process") || "Invalid extension id."
  604. }
  605. softdisableext() {
  606. echo "Extensions will stay disabled until you press Ctrl+c or close this tab"
  607. while true; do
  608. kill -9 $(pgrep -f "\-\-extension\-process") 2>/dev/null
  609. sleep 0.5
  610. done
  611. }
  612. # https://chromium.googlesource.com/chromiumos/docs/+/master/lsb-release.md
  613. lsbval() {
  614. local key="$1"
  615. local lsbfile="${2:-/etc/lsb-release}"
  616. if ! echo "${key}" | grep -Eq '^[a-zA-Z0-9_]+$'; then
  617. return 1
  618. fi
  619. sed -E -n -e \
  620. "/^[[:space:]]*${key}[[:space:]]*=/{
  621. s:^[^=]+=[[:space:]]*::
  622. s:[[:space:]]+$::
  623. p
  624. }" "${lsbfile}"
  625. }
  626. install_crouton() {
  627. # check if crouuton is already installed. if so, prompt the user to delete their old chroot
  628. if [ -f /mnt/stateful_partition/crouton_installed ] ; then
  629. read -p "Crouton is already installed. Would you like to delete your old chroot and create a new one? (y/N) " yn
  630. case $yn in
  631. [yY] ) doas "rm -rf /mnt/stateful_partition/crouton/chroots && rm -f /mnt/stateful_partition/crouton_installed";;
  632. [nN] ) return;;
  633. * ) return;;
  634. esac
  635. fi
  636. echo "Installing Crouton..."
  637. # if this is before v107, then we don't want to use the silence branch - audio is still supported
  638. local local_version=$(lsbval GOOGLE_RELEASE)
  639. if (( ${local_version%%\.*} <= 107 )); then
  640. doas "bash <(curl -SLk https://goo.gl/fd3zc) -r bullseye -t xfce"
  641. else
  642. # theoretically we could copy or link the includes for cras, but im not entirely sure how to do that
  643. # CROUTON_BRANCH=longliveaudiotools supports audio at the versions we're looking at, but it's experimental and tends to be broken
  644. # ig we can prompt the user?
  645. echo "Your version of ChromeOS is too recent to support the current main branch of Crouton. You can either install Crouton without audio support, or install the experimental audio branch. Which would you like to do?"
  646. echo "1. Install without audio support"
  647. echo "2. Install with experimental audio support (may be extremely broken)"
  648. read -r -p "> (1-2): " choice
  649. if [ "$choice" == "1" ]; then
  650. doas "CROUTON_BRANCH=silence bash <(curl -SLk https://goo.gl/fd3zc) -r bullseye -t xfce"
  651. elif [ "$choice" == "2" ]; then
  652. doas "CROUTON_BRANCH=longliveaudiotools bash <(curl -SLk https://goo.gl/fd3zc) -r bullseye -t xfce"
  653. else
  654. echo "Invalid option, defaulting to silence branch"
  655. doas "CROUTON_BRANCH=silence bash <(curl -SLk https://goo.gl/fd3zc) -r bullseye -t xfce"
  656. fi
  657. fi
  658. doas "bash <(echo 'touch /mnt/stateful_partition/crouton_installed')" # idfk about the syntax but it seems to work so im not complaining
  659. }
  660. run_crouton() {
  661. if [ -f /mnt/stateful_partition/crouton_installed ] ; then
  662. echo "Use Crtl+Shift+Alt+Forward and Ctrl+Shift+Alt+Back to toggle between desktops"
  663. doas "startxfce4"
  664. else
  665. echo "Install Crouton first!"
  666. read -p "Press enter to continue."
  667. fi
  668. }
  669. get_booted_kernnum() {
  670. if doas "((\$(cgpt show -n \"$dst\" -i 2 -P) > \$(cgpt show -n \"$dst\" -i 4 -P)))"; then
  671. echo -n 2
  672. else
  673. echo -n 4
  674. fi
  675. }
  676. opposite_num() {
  677. if [ "$1" == "2" ]; then
  678. echo -n 4
  679. elif [ "$1" == "4" ]; then
  680. echo -n 2
  681. elif [ "$1" == "3" ]; then
  682. echo -n 5
  683. elif [ "$1" == "5" ]; then
  684. echo -n 3
  685. else
  686. return 1
  687. fi
  688. }
  689. attempt_chromeos_update(){
  690. read -p "Do you want to use the default ChromeOS bootsplash? [y/N] " use_orig_bootsplash
  691. case "$use_orig_bootsplash" in
  692. [yY][eE][sS]|[yY])
  693. USE_ORIG_SPLASH="1"
  694. ;;
  695. *)
  696. USE_ORIG_SPLASH="0"
  697. ;;
  698. esac
  699. local builds=$(curl https://chromiumdash.appspot.com/cros/fetch_serving_builds?deviceCategory=Chrome%20OS)
  700. local release_board=$(lsbval CHROMEOS_RELEASE_BOARD)
  701. local board=${release_board%%-*}
  702. local hwid=$(jq "(.builds.$board[] | keys)[0]" <<<"$builds")
  703. local hwid=${hwid:1:-1}
  704. local latest_milestone=$(jq "(.builds.$board[].$hwid.pushRecoveries | keys) | .[length - 1]" <<<"$builds")
  705. local remote_version=$(jq ".builds.$board[].$hwid[$latest_milestone].version" <<<"$builds")
  706. local remote_version=${remote_version:1:-1}
  707. local local_version=$(lsbval GOOGLE_RELEASE)
  708. if (( ${remote_version%%\.*} > ${local_version%%\.*} )); then
  709. echo "Updating to ${remote_version}. THIS MAY DELETE ALL USER DATA! Press enter to confirm, Ctrl+C to cancel."
  710. read -r
  711. echo "Dumping emergency revert backup to stateful (this might take a while)..."
  712. echo "Finding correct partitions..."
  713. local dst=$(get_largest_cros_blockdev)
  714. local tgt_kern=$(opposite_num $(get_booted_kernnum))
  715. local tgt_root=$(( $tgt_kern + 1 ))
  716. local kerndev=${dst}p${tgt_kern}
  717. local rootdev=${dst}p${tgt_root}
  718. echo "Dumping kernel..."
  719. doas dd if=$kerndev of=/mnt/stateful_partition/murkmod/kern_backup.img bs=4M status=progress
  720. echo "Dumping rootfs..."
  721. doas dd if=$rootdev of=/mnt/stateful_partition/murkmod/root_backup.img bs=4M status=progress
  722. echo "Creating restore flag..."
  723. doas touch /mnt/stateful_partition/restore-emergency-backup
  724. doas chmod 777 /mnt/stateful_partition/restore-emergency-backup
  725. echo "Backups complete, actually updating now..."
  726. # read choice
  727. local reco_dl=$(jq ".builds.$board[].$hwid.pushRecoveries[$latest_milestone]" <<< "$builds")
  728. local tmpdir=/mnt/stateful_partition/update_tmp/
  729. doas mkdir $tmpdir
  730. echo "Downloading ${remote_version} from ${reco_dl}..."
  731. curl "${reco_dl:1:-1}" | doas "dd of=$tmpdir/image.zip status=progress"
  732. echo "Unzipping update binary..."
  733. cat $tmpdir/image.zip | gunzip | doas "dd of=$tmpdir/image.bin status=progress"
  734. doas rm -f $tmpdir/image.zip
  735. echo "Invoking image patcher..."
  736. if [ "$USE_ORIG_SPLASH" == 0 ]; then
  737. doas image_patcher.sh "$tmpdir/image.bin"
  738. else
  739. doas image_patcher.sh "$tmpdir/image.bin" cros
  740. fi
  741. local loop=$(doas losetup -f | tr -d '\r' | tail -1)
  742. doas losetup -P "$loop" "$tmpdir/image.bin"
  743. echo "Performing update..."
  744. printf "Overwriting partitions in 3 (this is your last chance to cancel)..."
  745. sleep 1
  746. printf "2..."
  747. sleep 1
  748. echo "1..."
  749. sleep 1
  750. echo "Installing kernel patch to ${kerndev}..."
  751. doas dd if="${loop}p4" of="$kerndev" status=progress
  752. echo "Installing root patch to ${rootdev}..."
  753. doas dd if="${loop}p3" of="$rootdev" status=progress
  754. echo "Setting kernel priority..."
  755. doas cgpt add "$dst" -i 4 -P 0
  756. doas cgpt add "$dst" -i 2 -P 0
  757. doas cgpt add "$dst" -i "$tgt_kern" -P 1
  758. echo "Setting crossystem and vpd block_devmode..."
  759. doas crossystem.old block_devmode=0
  760. doas vpd -i RW_VPD -s block_devmode=0
  761. echo "Cleaning up..."
  762. doas rm -Rf $tmpdir
  763. read -p "Done! Press enter to continue."
  764. else
  765. echo "Update not required."
  766. read -p "Press enter to continue."
  767. fi
  768. }
  769. attempt_backup_update(){
  770. local builds=$(curl https://chromiumdash.appspot.com/cros/fetch_serving_builds?deviceCategory=Chrome%20OS)
  771. local release_board=$(lsbval CHROMEOS_RELEASE_BOARD)
  772. local board=${release_board%%-*}
  773. local hwid=$(jq "(.builds.$board[] | keys)[0]" <<<"$builds")
  774. local hwid=${hwid:1:-1}
  775. local latest_milestone=$(jq "(.builds.$board[].$hwid.pushRecoveries | keys) | .[length - 1]" <<<"$builds")
  776. local remote_version=$(jq ".builds.$board[].$hwid[$latest_milestone].version" <<<"$builds")
  777. local remote_version=${remote_version:1:-1}
  778. read -p "Do you want to make a backup of your backup, just in case? (Y/n) " yn
  779. case $yn in
  780. [yY] ) do_backup=true ;;
  781. [nN] ) do_backup=false ;;
  782. * ) do_backup=true ;;
  783. esac
  784. echo "Updating to ${remote_version}. THIS CAN POSSIBLY DAMAGE YOUR EMERGENCY BACKUP! Press enter to confirm, Ctrl+C to cancel."
  785. read -r
  786. echo "Finding correct partitions..."
  787. local dst=$(get_largest_cros_blockdev)
  788. local tgt_kern=$(opposite_num $(get_booted_kernnum))
  789. local tgt_root=$(( $tgt_kern + 1 ))
  790. local kerndev=${dst}p${tgt_kern}
  791. local rootdev=${dst}p${tgt_root}
  792. if [ "$do_backup" = true ] ; then
  793. echo "Dumping emergency revert backup to stateful (this might take a while)..."
  794. echo "Dumping kernel..."
  795. doas dd if=$kerndev of=/mnt/stateful_partition/murkmod/kern_backup.img bs=4M status=progress
  796. echo "Dumping rootfs..."
  797. doas dd if=$rootdev of=/mnt/stateful_partition/murkmod/root_backup.img bs=4M status=progress
  798. echo "Backups complete, actually updating now..."
  799. fi
  800. # read choice
  801. local reco_dl=$(jq ".builds.$board[].$hwid.pushRecoveries[$latest_milestone]" <<< "$builds")
  802. local tmpdir=/mnt/stateful_partition/update_tmp/
  803. doas mkdir $tmpdir
  804. echo "Downloading ${remote_version} from ${reco_dl}..."
  805. curl "${reco_dl:1:-1}" | doas "dd of=$tmpdir/image.zip status=progress"
  806. echo "Unzipping update binary..."
  807. cat $tmpdir/image.zip | gunzip | doas "dd of=$tmpdir/image.bin status=progress"
  808. doas rm -f $tmpdir/image.zip
  809. echo "Creating loop device..."
  810. local loop=$(doas losetup -f | tr -d '\r')
  811. doas losetup -P "$loop" "$tmpdir/image.bin"
  812. printf "Overwriting backup in 3 (this is your last chance to cancel)..."
  813. sleep 1
  814. printf "2..."
  815. sleep 1
  816. echo "1..."
  817. sleep 1
  818. echo "Performing update..."
  819. echo "Installing kernel patch to ${kerndev}..."
  820. doas dd if="${loop}p4" of="$kerndev" status=progress
  821. echo "Installing root patch to ${rootdev}..."
  822. doas dd if="${loop}p3" of="$rootdev" status=progress
  823. echo "Setting crossystem and vpd block_devmode..." # idrk why, but it can't hurt to be safe
  824. doas crossystem.old block_devmode=0
  825. doas vpd -i RW_VPD -s block_devmode=0
  826. echo "Cleaning up..."
  827. doas rm -Rf $tmpdir
  828. read -p "Done! Press enter to continue."
  829. }
  830. attempt_restore_backup_backup() {
  831. echo "Looking for backup files..."
  832. dst=$(get_largest_cros_blockdev)
  833. tgt_kern=$(opposite_num $(get_booted_kernnum))
  834. tgt_root=$(( $tgt_kern + 1 ))
  835. kerndev=${dst}p${tgt_kern}
  836. rootdev=${dst}p${tgt_root}
  837. if [ -f /mnt/stateful_partition/murkmod/kern_backup.img ] && [ -f /mnt/stateful_partition/murkmod/root_backup.img ]; then
  838. echo "Backup files found!"
  839. echo "Restoring kernel..."
  840. dd if=/mnt/stateful_partition/murkmod/kern_backup.img of=$kerndev bs=4M status=progress
  841. echo "Restoring rootfs..."
  842. dd if=/mnt/stateful_partition/murkmod/root_backup.img of=$rootdev bs=4M status=progress
  843. echo "Removing backup files..."
  844. rm /mnt/stateful_partition/murkmod/kern_backup.img
  845. rm /mnt/stateful_partition/murkmod/root_backup.img
  846. echo "Restored successfully!"
  847. read -p "Press enter to continue."
  848. else
  849. echo "Missing backup image, aborting!"
  850. read -p "Press enter to continue."
  851. fi
  852. }
  853. attempt_install_chromebrew() {
  854. doas 'sudo -i -u chronos curl -Ls git.io/vddgY | bash' # kinda works now with cros_debug
  855. read -p 'Press enter to exit'
  856. }
  857. attempt_dev_install() {
  858. doas 'dev_install'
  859. }
  860. if [ "$0" = "$BASH_SOURCE" ]; then
  861. stty sane
  862. if [ -f /mnt/stateful_partition/murkmod/mush_password ]; then
  863. locked_main
  864. else
  865. main
  866. fi
  867. fi