Skip to content

Instantly share code, notes, and snippets.

@Maxdamantus

Maxdamantus/init Secret

Last active October 26, 2021 07:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Maxdamantus/54d0bdb77523aa7d03120ebb1f10df4e to your computer and use it in GitHub Desktop.
Save Maxdamantus/54d0bdb77523aa7d03120ebb1f10df4e to your computer and use it in GitHub Desktop.
#!/init-root/bin/busybox sh
bb=/init-root/bin/busybox
run_chroot() {
(
$bb mkdir -p /mnt
exec 40<&0
# stage 0
exec 42<<'SETUP_SH'
set -x
bb=/init-root/bin/busybox
$bb mount -t tmpfs tmp /mnt
$bb cp -a /init-root/. /lib /mnt/
# /init not reused, but could be useful for debugging
$bb cp -a /init /mnt/
cd /mnt/
$bb mount --move . /
exec $bb chroot . /bin/busybox sh <&43 43<&-
SETUP_SH
# stage 1
exec 43<<'SETUP_SH'
set -x
for x in `busybox --list`; do
busybox ln -s busybox /bin/"$x"
done
mkdir -p /sys /proc /dev /dev/pts /etc /run
mount -t sysfs sys /sys
mount -t proc proc /proc
mount -t devpts devpts /dev/pts -o gid=5,noexec,nosuid
mdev -s
mkdir -p /etc
echo "root:x:0:0:root:/:/bin/sh" >/etc/passwd
echo "root:x:0:" >/etc/group
echo "/bin/sh" >/etc/shells
(umask 077; set +x; echo "root:$(cat /.secrets/passwd):::::::" >/etc/shadow)
chmod go-rw /.secrets/*
cat >/m-mount-root.sh <&44 44<&-
chmod +x /m-mount-root.sh
exec sh <&40 40<&-
SETUP_SH
# /m-mount-root.sh
# UNUSED
exec 44<<-'MOUNT_ROOT_SH'
#!/bin/sh
set -x -e
key_file=/proc/1/root/data/.cryptmmckey
retry=0
while :; do
test -e "$key_file" && break
if [ $((retry++)) -ge 200 ]; then
echo 'giving up waiting for key'
break
fi
sleep 0.1
done
cryptsetup open /dev/mmcblk0p2 cryptmmc "$key_file"
mkdir -p /mnt/root
mount -o ro,noatime /dev/mapper/cryptmmc /mnt/root
MOUNT_ROOT_SH
# /m-mount-root.sh
exec 44<<-'MOUNT_ROOT_SH'
#!/bin/sh
set -x -e
retry=0
while :; do
mount="$(grep '^[^ ]\+ /data .*\<rw\>' </proc/1/mounts)" && break
if [ $((retry++)) -ge 200 ]; then
echo 'giving up finding data mount'
exit 1
fi
sleep 0.1
done
mkdir -p /mnt/data /mnt/root
dev="$(echo "$mount" | cut -f1 -d' ' | sed 's:/dev/block/:/dev/:')"
mdev -s
test -e "$dev"
mount -o noatime "$dev" /mnt/data
mount -o bind /mnt/data/.debian/root /mnt/root
MOUNT_ROOT_SH
exec $bb unshare -m $bb sh <&42 42<&-
)
}
recovery() {
run_chroot <<'RECOVERY_SH'
set -x
# show that we're alive
echo 0 >/sys/class/backlight/backlight/bl_power
for x in `seq 0 20 250` `seq 250 -20 0` `seq 0 20 250`; do sleep 0.1 && echo $x >/sys/class/backlight/backlight/brightness; done
echo 'write to /dev/ipa'
echo 1 >/dev/ipa
sleep 3
mkdir /config
mount -t configfs configfs /config
cd /config
gadget=/config/usb_gadget/g1
mkdir $gadget
mkdir $gadget/functions/gsi.rndis
mkdir $gadget/configs/b.1
mkdir $gadget/configs/b.1/strings/0x409
echo rndis >$gadget/configs/b.1/strings/0x409/configuration
echo 0x2A70 >$gadget/idVendor
echo 0xF00E >$gadget/idProduct
# adb/fastboot uses f1, using something exclusive
(cd $gadget/configs/b.1 && ln -s ../../functions/gsi.rndis f5)
echo >$gadget/UDC
echo a600000.dwc3 >$gadget/UDC && break
dmesg
ipaddr=192.168.30.1
ifconfig rndis0 "$ipaddr" netmask 255.255.255.0
# reconfigure interface in case fastbootd activation removes it temporarily
(while sleep 5; do ifconfig rndis0 "$ipaddr" netmask 255.255.255.0; done) &
dropbear -r /.secrets/dropbear_key
ln -s /proc/1/root/fifo-continue /
echo '
echo ro.adb.secure=0 >>/proc/1/root/prop.default
echo >/fifo-continue
' >/stock-recovery
echo '
cat /proc/cmdline | sed '\''s/\(\<androidboot\.verifiedbootstate=\)[^ ]*\>/\1orange/'\'' >/proc/1/root/dev/.proc_cmdline
nsenter -t 1 -m -r/ -- mount --bind /proc/1/root/dev/.proc_cmdline /proc/1/root/proc/cmdline
' >/stock-unlock
(sleep 60 && [ -e /fifo-continue ] && exec >/fifo-continue) &
RECOVERY_SH
# wait for `stock-recovery` to be invoked, or above sleep to complete
$bb mkfifo /fifo-continue
# NOTE: fifo open needs to be in a child process (cat), since we are pid 1, so might get a SIGCHILD interrupt
$bb cat /fifo-continue >/dev/null
$bb rm /fifo-continue
}
boot() {
run_chroot <<'BOOT_SH'
set -x
precompiled_sepolicy=/odm/etc/selinux/precompiled_sepolicy
strace_1_bg() {
strace -p 1 -o strace-out -A "$@" &
strace_pid=$!
local retry=0
while ! grep $'^TracerPid:\t'"$strace_pid"'$' /proc/1/status; do
if [ $((retry++)) -ge 200 ]; then
echo 'giving up waiting for strace to attach'
break
fi
sleep 0.1
done
}
strace_1_kill() {
kill $strace_pid
while grep $'^TracerPid:\t'"$strace_pid"'$' /proc/1/status; do
if [ $((retry++)) -ge 200 ]; then
echo 'giving up waiting for strace to exit'
break
fi
sleep 0.1
done
}
strace_1_bg -P "$precompiled_sepolicy" --inject=faccessat:delay_exit=30s
(
while ! [ -e /proc/1/root/"$precompiled_sepolicy" ];
do sleep 0.1
done
cleanup_mounts=
for x in /proc/1/root/"$precompiled_sepolicy".*.sha256; do
nsenter -t 1 -m -r/ -- mount --bind /proc/1/root/dev/null "$x"
cleanup_mounts="$cleanup_mounts $x"
done
tmp=/proc/1/root/dev/.secilc-tmp
mkdir "$tmp"
mkfifo "$tmp"/log
cat <"$tmp"/log &
mkfifo "$tmp"/fence-trace-attached
echo catpid=$!
ls -lh /proc/1/root/
cp "$(realpath "$(which busybox)")" "$tmp"/busybox
cp /proc/1/root/system/bin/secilc "$tmp"/secilc-real
cat >"$tmp"/unconfined.cil <<'CIL'
(block debroot
(type m_unconfined)
(typepermissive m_unconfined)
(typeattributeset domain (m_unconfined))
; required at least for use of loop devices, not permissive
(allow kernel m_unconfined (fd (all)))
; required for ecryptfs
(allow unlabeled unlabeled (filesystem (associate)))
)
CIL
cat >"$tmp"/secilc-tmp <<'SECILC_SH'
#!/dev/.secilc-tmp/busybox sh
set -x
tmp=/dev/.secilc-tmp
exec >"$tmp"/log 2>&1
echo intercepted-secilc "$@"
echo "$tmp"/secilc-real "$@" >"$tmp"/cmd
for x in "$@"; do echo "$x"; done >"$tmp"/secilc-args
# wait until outer strace is attached, so setenforce will be delayed
read <"$tmp"/fence-trace-attached
exec "$tmp"/secilc-real "$@" "$tmp"/unconfined.cil
SECILC_SH
chmod +x "$tmp"/secilc-tmp
nsenter -t 1 -m -r/ -- mount --bind "$tmp"/secilc-tmp /proc/1/root/system/bin/secilc
cleanup_mounts="$cleanup_mounts /proc/1/root/system/bin/secilc"
# finished setting up secilc interceptor, resume init so it can execute it
strace_1_kill
# if init gets to the point where it's about to write `1` to enforce, we really want it to wait (eg, 30 seconds) until our `attr/current` loop has finished. generally it will probably finish by the time it's done a delayed read (1 second). prefer to do it during the `read` since the `write` will never actually happen if permissive mode is enabled.
strace_1_bg -P /sys/fs/selinux/enforce --inject=write:delay_enter=30s --inject=read:delay_exit=1s
(exec >"$tmp"/fence-trace-attached)
echo pid=$$
ls -lh /proc/self
cat /proc/self/attr/current
retry=0
while :; do
ctx='u:r:debroot.m_unconfined:s0'
echo -n "$ctx" >/proc/self/attr/current
grep -F "$ctx" </proc/self/attr/current && break
# give up eventually, so we don't end up running the entire OS under strace
if [ $((retry++)) -ge 200 ]; then
echo 'giving up setting context'
break
fi
sleep 0.1
done
# capture secilc args so that a second stage init can add extra policy if desired
cp "$tmp"/secilc-args /secilc-args
# clean up secilc injection stuff added into android's namespace/filesystem
rm -rf "$tmp"
for x in $cleanup_mounts; do
nsenter -t 1 -m -r/ -- umount "$x"
done
echo pid=$$
ls -lh /proc/self
cat /proc/self/attr/current
strace_1_kill
(
set -e
/m-mount-root.sh
# file will already be unlinked; capture the contents for debugging before the last references are lost
cp /proc/self/fd/1 /init-log
cd /mnt/root
exec >/dev/null 2>&1
echo /init-entered | sh ./enter
) && exit 0
# capture again, for debugging
cp /proc/self/fd/1 /init-log
dropbear -p 127.0.0.1:23 -r /.secrets/dropbear_key -P /dropbear.pid
) &
BOOT_SH
}
$bb mkdir -p /dev
# busybox opens /dev/null as stdin when backgrounding with `&`
$bb mknod /dev/null c 1 3
if false; then
$bb mkdir /sd
$bb mknod /dev_mmc b 179 1
$bb mknod /dev_kmsg c 1 11
$bb mount -t vfat -o rw,sync /dev_mmc /sd
$bb dmesg >/sd/bootout-test.txt
set -x
exec >/sd/bootout.txt 2>&1
echo mk
($bb cat <&5 2>&1 &) 5</dev_kmsg >/sd/kmsg.txt
$bb umount -l /sd
$bb dmesg
else
# NOTE: will avoid corruption of ssh connections
exec >/init-log 2>&1
fi
$bb [ -e /init-type-boot ] && boot
$bb [ -e /init-type-recovery ] && recovery
$bb test -e /init-sh-inject && . /init-sh/inject
$bb mv /init-orig /init || exit 1
$bb rm -rf /init-root
exec /init "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment