-
-
Save TheFreeman193/46cf02661208efa81adf4ad3da92632b to your computer and use it in GitHub Desktop.
#!/system/bin/sh | |
# Copyright (C) MIT License 2024 Nicholas Bissell (TheFreeman193) | |
NL=" | |
" | |
SYSNMLN=65 | |
showHeader() { | |
echo " | |
========= Kernel release offset finder ========= | |
Buy me a coffee: https://ko-fi.com/nickbissell | |
===================== v2.1 ===================== | |
" >&2 | |
} | |
showUsage() { | |
showHeader 2>&1 | |
echo "Usage: | |
$0 --auto [--hex] [--quiet] [--patch new_value [--inplace]] | |
$0 [--kernel] kernel_file [--hex] [--quiet] [--patch new_value [--inplace]] | |
$0 --image boot_img [--hex] [--quiet] [--patch new_value [--inplace]] | |
$0 --boot boot_part [--hex] [--quiet] [--patch new_value [--inplace]] | |
--auto -a Detect boot partition and extract automatically | |
--kernel -k Get offset from an uncompressed kernel file (e.g. kernel) | |
--image -i Get offset from a boot image file (e.g. boot.img) | |
--boot -b Get offset from a boot partition (e.g. /dev/block/...) | |
--hex -x Return offset in hexadecimal format | |
--quiet -q Quiet operation - don't print operations to stderr | |
--patch -p Patch the partition/image/kernel with new_value | |
--inplace -n For --kernel/--image: Patch the original file instead of copying | |
kernel_file An uncompressed Linux kernel extracted with magiskboot | |
boot_img A boot image file containing the Linux kernel | |
boot_part A path to the boot partition of the device | |
new_value A new value for the non-numeric part of the release string | |
" | |
exit 0 | |
} | |
[ $# -eq 0 ] || [ -z "$1" ] && showUsage | |
if [ -f /data/adb/magisk/magisk ] || [ -f /data/adb/magisk/magisk32 ]; then | |
ROOTMODE="Magisk" | |
BBPATH="/data/adb/magisk/busybox" | |
MBPATH="/data/adb/magisk/magiskboot" | |
elif [ -f /data/adb/ksu/bin/ksud ]; then | |
ROOTMODE="KernelSU" | |
BBPATH="/data/adb/ksu/bin/busybox" | |
MBPATH="/data/adb/ksu/bin/magiskboot" | |
elif [ -f /data/adb/ap/bin/apd ]; then | |
ROOTMODE="APatch" | |
BBPATH="/data/adb/ap/bin/busybox" | |
MBPATH="/data/adb/magiskboot" | |
else | |
echo "ERROR: Magisk, KernelSU, or APatch is needed for this script. Make sure Magisk/KSU/AP is installed and you are running a root shell." >&2 | |
exit 2 | |
fi | |
if [ ! -f "$BBPATH" ] || [ ! -f "$MBPATH" ]; then | |
echo "ERROR: Critical $ROOTMODE components not found. Make sure $ROOTMODE is installed correctly and includes busybox and magiskboot." >&2 | |
[ "$ROOTMODE" = "APatch" ] && echo "$ROOTMODE users should get a copy of magiskboot from the Magisk APK, place it at /data/adb/magiskboot and chmod +x." | |
exit 2 | |
fi | |
for cmd in dd xxd strings stat sed grep; do | |
alias $cmd="$BBPATH $cmd" | |
done | |
readUtsFieldHex() { | |
[ -z "$SYSNMLN" ] && SYSNMLN=65 | |
dd if="$1" skip="$2" count="$SYSNMLN" iflag="skip_bytes,count_bytes" status=none | xxd -p -c "$SYSNMLN" | |
} | |
toUtsFieldHex() { | |
[ -z "$SYSNMLN" ] && SYSNMLN=65 | |
hexSYSNMLN="$((SYSNMLN * 2))" | |
hexPad="$(dd if=/dev/zero iflag=count_bytes count="$SYSNMLN" status=none | xxd -p -c "$SYSNMLN")" | |
echo "$(printf %s "$1" | xxd -p -c "$SYSNMLN")$hexPad" | cut -b "-$hexSYSNMLN" | |
} | |
findStringsInBin() { | |
strings -t d "$1" | grep -F "$2" | sed -r 's/^[[:space:]]*([0-9]+)[[:space:]]+.+$/\1/g' | grep -E "^[0-9]+$" | |
} | |
findUtsFieldOffset() { | |
file="$1" | |
value="$2" | |
[ -z "$SYSNMLN" ] && SYSNMLN=65 | |
[ $quietMode -eq 0 ] && echo "${NL}Looking for instances of '$value' in '$file'..." >&2 | |
curSys="$(uname -s | cut -b "-$SYSNMLN")" | |
curMachine="$(uname -m | cut -b "-$SYSNMLN")" | |
hexValue="$(toUtsFieldHex "$value")" | |
hexCurSys="$(toUtsFieldHex "$curSys")" | |
hexCurMachine="$(toUtsFieldHex "$curMachine")" | |
potentialOffsets="$(findStringsInBin "$file" "$value")" | |
validOffsets="" | |
[ $asHex -eq 0 ] && offsetFormat="%s%s... " || offsetFormat="%s0x%x... " | |
for offset in $potentialOffsets; do | |
[ $quietMode -eq 0 ] && printf "$offsetFormat" " Checking match at offset " "$offset" >&2 | |
if [ "$(readUtsFieldHex "$file" "$offset")" == "$hexValue" ]; then | |
found="" | |
sysOffset="$((offset - SYSNMLN * 5))" | |
endOffset="$((offset + SYSNMLN * 5))" | |
while [ "$sysOffset" -lt "$endOffset" ]; do | |
curHex="$(readUtsFieldHex "$file" "$sysOffset")" | |
if [ "$curHex" == "$hexCurSys" ]; then | |
found="sysname" | |
break 1 | |
elif [ "$curHex" == "$hexCurMachine" ]; then | |
found="machine" | |
break 1 | |
fi | |
sysOffset="$((sysOffset + SYSNMLN))" | |
done | |
if [ -n "$found" ]; then | |
validOffsets+="$offset$NL" | |
[ $quietMode -eq 0 ] && echo "Found UTS $found field!" >&2 | |
else | |
[ $quietMode -eq 0 ] && echo "Not a complete UTS structure" >&2 | |
fi | |
else | |
[ $quietMode -eq 0 ] && echo "Partial match/not UTS" >&2 | |
fi | |
done | |
if [ -n "$validOffsets" ]; then | |
echo "$validOffsets" | sed '/^$/d' | |
else | |
echo "${NL}No offsets with UTS format found for '$curRelease'" >&2 | |
return 1 | |
fi | |
} | |
findBootPath() { | |
[ $quietMode -eq 0 ] && echo "${NL}Detecting boot partition path..." >&2 | |
bootPath="/dev/block/bootdevice/by-name" | |
if [ ! -d "$bootPath/" ]; then | |
echo "ERROR: Boot partition directory not found." >&2 | |
return 1 | |
fi | |
if [ -e "$bootPath/boot" ]; then | |
echo "$bootPath/boot" | |
elif [ -e "$bootPath/boot_a" ] && [ -e "$bootPath/boot_b" ]; then | |
slot_suffix="$(getprop ro.boot.slot_suffix)" | |
if [ -n "$slot_suffix" ]; then | |
echo "$bootPath/boot$slot_suffix" | |
return 0 | |
fi | |
current_slot="$(getprop current-slot)" | |
if [ -n "$current_slot" ]; then | |
echo "$bootPath/boot$current_slot" | |
return 0 | |
fi | |
command -v bootctl && slot_number="$(bootctl get-current-slot)" | |
[ -n "$slot_number" ] && slot_suffix="$(bootctl get-suffix "$slot_number")" | |
if [ -n "$slot_suffix" ]; then | |
echo "$bootPath/boot$slot_suffix" | |
return 0 | |
fi | |
slot_suffix="$(cat /proc/cmdline | sed 's/ /\n/g' | grep -Ei 'slot_suffix|current-slot' | sed -r 's/.+=//')" | |
if [ -n "$slot_suffix" ]; then | |
echo "$bootPath/boot$slot_suffix" | |
return 0 | |
fi | |
echo "ERROR: Cannot get active slot." >&2 | |
return 1 | |
else | |
echo "ERROR: Unexpected boot partition configuration." >&2 | |
return 1 | |
fi | |
} | |
getOutFile() { | |
fileOut="$1.new" | |
ctr=1 | |
while [ -f "$fileOut" ]; do | |
ctr=$((ctr + 1)) | |
fileOut="$1.new$ctr" | |
done | |
echo "$fileOut" | |
} | |
patchKernel() { | |
if [ $# -ne 3 ]; then | |
echo "ERROR patchKernel: Invalid number of parameters!" >&2 | |
return 21 | |
fi | |
if [ ! -f "$1" ]; then | |
echo "ERROR patchKernel: Kernel file doesn't exist!" >&2 | |
return 22 | |
fi | |
if [ -z "$2" ]; then | |
echo "ERROR patchKernel: Replacement string is empty!" >&2 | |
return 23 | |
fi | |
if [ -z "$3" ]; then | |
echo "ERROR patchKernel: No replacement offsets provided!" >&2 | |
return 24 | |
fi | |
kSize="$(stat -c "%s" $1)" | |
maxOffset=$((kSize - SYSNMLN)) | |
maxLen=$((SYSNMLN - 1)) | |
[ $quietMode -eq 0 ] && echo "${NL}Patching kernel file '$1'..." >&2 | |
for offset in $3; do | |
if [ "$offset" -lt 1 ] || [ "$offset" -gt $maxOffset ]; then | |
echo "WARNING patchKernel: Offset $offset not between 0 and $maxOffset. Ignoring." >&2 | |
continue | |
fi | |
if [ $quietMode -eq 0 ]; then | |
[ $asHex -eq 1 ] && prettyOffset="$(printf "0x%x" "$offset")" || prettyOffset="$offset" | |
printf %s " Offset $prettyOffset: " >&2 | |
fi | |
curVal="$(dd if="$1" iflag="count_bytes,skip_bytes" skip="$offset" count="$SYSNMLN" status=none)" | |
versionPart="$(echo "$curVal" | sed -r 's/^([0-9.]+).*/\1/g')" | |
replRaw="$versionPart$2" | |
replSafe="${replRaw:0:$maxLen}" | |
dd if="/dev/zero" conv=notrunc iflag=count_bytes count="$SYSNMLN" oflag=seek_bytes seek="$offset" of="$1" status=none 2>/dev/null | |
printf %s "$replSafe" | dd conv=notrunc oflag="seek_bytes" seek="$offset" of="$1" status=none 2>/dev/null | |
readBack="$(dd if="$1" iflag="count_bytes,skip_bytes" skip="$offset" count="$SYSNMLN" status=none)" | |
if [ "$readBack" == "$replSafe" ]; then | |
[ $quietMode -eq 0 ] && echo "'$curVal' -> '$replSafe'" >&2 | |
else | |
echo "ERROR patchKernel: Read back '$readBack' doesn't match target value '$replSafe'!" >&2 | |
return 25 | |
fi | |
[ "${#replRaw}" -gt $maxLen ] && echo "WARNING patchKernel: Replacement string is longer than field length ($maxLen) and has been truncated." >&2 | |
done | |
return 0 | |
} | |
newStage() { | |
lastDir="$PWD" | |
stageDir="/data/local/tmp/kr_offset_$RANDOM" | |
mkdir "$stageDir" | |
if [ ! -d "$stageDir" ]; then | |
echo "ERROR: Couldn't create temporary directory '$stageDir'" >&2 | |
return 1 | |
fi | |
cd "$stageDir" | |
} | |
cleanupQuit() { | |
if [ -n "$stageDir" ] && [ -d "$stageDir" ]; then | |
[ -n "$lastDir" ] && [ -d "$lastDir" ] && cd "$lastDir" | |
rm -rf "$stageDir" | |
fi | |
[ -n "$1" ] && exit $1 || exit 0 | |
} | |
kernelFile="" | |
imageFile="" | |
bootPath="" | |
newValue="" | |
autoExtract=0 | |
asHex=0 | |
quietMode=0 | |
inPlace=0 | |
while [ $# -gt 0 ]; do | |
param="$1" | |
hasShifted=0 | |
case "$param" in | |
-h|-\?|--help) | |
showUsage | |
;; | |
--kernel|-k|-[aqxn]*k) | |
kernelFile="$(readlink -f "$2")" | |
shift; shift; hasShifted=1 | |
;; | |
--image|-i|-[aqxn]*i) | |
imageFile="$(readlink -f "$2")" | |
shift; shift; hasShifted=1 | |
;; | |
--boot|-b|-[aqxn]*b) | |
bootPath="$2" | |
shift; shift; hasShifted=1 | |
;; | |
--patch|-p|-[aqxn]*p) | |
newValue="$2" | |
shift; shift; hasShifted=1 | |
;; | |
--auto|-a) | |
autoExtract=1 | |
shift; continue | |
;; | |
--quiet|-q) | |
quietMode=1 | |
shift; continue | |
;; | |
--hex|-x) | |
asHex=1 | |
shift; continue | |
;; | |
--inplace|-n) | |
inPlace=1 | |
shift; continue | |
;; | |
*) | |
if echo "$1" | grep -Eqv '^-'; then | |
kernelFile="$(readlink -f "$1")" | |
shift; continue | |
fi | |
;; | |
esac | |
echo "$param" | grep -Eq '^-[qxn]*a[qxn]*[kibp]$' && autoExtract=1 | |
echo "$param" | grep -Eq '^-[qan]*x[qan]*[kibp]$' && asHex=1 | |
echo "$param" | grep -Eq '^-[axn]*q[axn]*[kibp]$' && quietMode=1 | |
echo "$param" | grep -Eq '^-[qax]*n[qax]*[kibp]$' && inPlace=1 | |
[ $hasShifted -eq 1 ] && continue | |
echo "ERROR: Couldn't parse parameter '$1'." >&2 | |
exit 1 | |
done | |
[ $quietMode -eq 0 ] && showHeader | |
primArgs=0 | |
mode=0 | |
[ -n "$kernelFile" ] && mode=1 && primArgs=$((primArgs + 1)) | |
[ -n "$imageFile" ] && mode=2 && primArgs=$((primArgs + 1)) | |
[ -n "$bootPath" ] && mode=3 && primArgs=$((primArgs + 1)) | |
[ $autoExtract -eq 1 ] && mode=4 && primArgs=$((primArgs + 1)) | |
if [ $primArgs -gt 1 ]; then | |
echo "ERROR: Too many parameters ($primArgs) - only 1 of --kernel, --image, --boot, --auto allowed." >&2 | |
exit 3 | |
fi | |
if [ $mode -eq 0 ]; then | |
echo "ERROR: You must either pass a kernel file, boot image, partition path, or use --auto." >&2 | |
exit 4 | |
fi | |
if [ $mode -ge 4 ]; then | |
bootPath="$(findBootPath)" || cleanupQuit 5 | |
fi | |
if [ $mode -ge 2 ]; then | |
newStage | |
[ $? -ne 0 ] && cleanupQuit 6 | |
fi | |
if [ $mode -ge 3 ]; then | |
if [ ! -e "$bootPath" ]; then | |
echo "ERROR: Boot partition path '$bootPath' not found." >&2 | |
cleanupQuit 7 | |
fi | |
imageFile="boot.img" | |
[ $quietMode -eq 0 ] && echo "${NL}Extracting boot image '$bootPath'..." >&2 | |
dd if="$bootPath" of="$imageFile" status=none | |
if [ ! -f "$imageFile" ]; then | |
echo "ERROR: Failed to extract boot image." >&2 | |
cleanupQuit 8 | |
fi | |
fi | |
if [ $mode -ge 2 ]; then | |
kernelFile="kernel" | |
[ $quietMode -eq 0 ] && echo "${NL}Unpacking boot image '$imageFile'..." >&2 | |
$MBPATH unpack "$imageFile" 2>/dev/null | |
if [ $? -ne 0 ] || [ ! -f "$kernelFile" ]; then | |
echo "ERROR: Failed to unpack boot image." >&2 | |
cleanupQuit 9 | |
fi | |
fi | |
if [ $mode -eq 1 ] && [ ! -f "$kernelFile" ]; then | |
echo "ERROR: Failed to unpack boot image." >&2 | |
cleanupQuit 10 | |
fi | |
curRelease="$(uname -r | cut -b "-$SYSNMLN")" | |
releaseOffsets="$(findUtsFieldOffset "$kernelFile" "$curRelease")" | |
[ $? -ne 0 ] || [ -z "$releaseOffsets" ] && cleanupQuit 0 | |
if [ -z "$newValue" ]; then | |
[ $quietMode -eq 0 ] && echo "" >&2 | |
if [ $asHex -eq 1 ]; then | |
for offset in $releaseOffsets; do | |
printf "0x%x\n" "$offset" | |
done | |
else | |
echo "$releaseOffsets" | |
fi | |
[ $quietMode -eq 0 ] && echo "" >&2 | |
cleanupQuit 0 | |
fi | |
if [ $mode -eq 1 ]; then | |
if [ $inPlace -eq 1 ]; then | |
kernelOut="$kernelFile" | |
else | |
kernelOut="$(getOutFile "$kernelFile")" | |
dd if="$kernelFile" of="$kernelOut" status=none | |
fi | |
patchKernel "$kernelOut" "$newValue" "$releaseOffsets" | |
res=$?; [ $res -ne 0 ] && cleanupQuit $res | |
echo "$kernelOut" | |
cleanupQuit 0 | |
fi | |
patchKernel "$kernelFile" "$newValue" "$releaseOffsets" | |
res=$?; [ $res -ne 0 ] && cleanupQuit $res | |
imageOut="$(getOutFile "$imageFile")" | |
[ $quietMode -eq 0 ] && echo "${NL}Packing new boot image '$imageOut'..." >&2 | |
$MBPATH repack "$imageFile" "$imageOut" 2>/dev/null | |
if [ $? -ne 0 ] || [ ! -f "$imageOut" ]; then | |
cleanupQuit 11 | |
fi | |
if [ $mode -eq 2 ]; then | |
if [ $inPlace -eq 1 ]; then | |
[ $quietMode -eq 0 ] && echo "${NL}Overwriting existing image '$imageFile'..." >&2 | |
mv -f "$imageOut" "$imageFile" | |
echo "$imageFile" | |
else | |
echo "$imageOut" | |
fi | |
cleanupQuit 0 | |
fi | |
[ $quietMode -eq 0 ] && echo "${NL}Flashing new boot image to '$bootPath'..." >&2 | |
srcHash="$(sha1sum -b "$imageOut")" | |
dd if="$imageOut" of="$bootPath" status=none | |
[ $? -ne 0 ] && cleanupQuit 12 | |
destHash="$(sha1sum -b "$bootPath")" | |
if [ "$destHash" != "$srcHash" ]; then | |
echo "ERROR: SHA-1 hash of flashed image '$bootPath' doesn't match source '$imageOut'!" >&2 | |
cleanupQuit 13 | |
fi | |
[ $quietMode -eq 0 ] && echo "${NL}Finished. Patched '$bootPath'.$NL" >&2 | |
echo "$bootPath" | |
[ $quietMode -eq 0 ] && echo "" >&2 | |
cleanupQuit 0 |
Hi
Where can I find
kr_patch.sh
?
kr_patch.sh
is just what I'm calling badabing2003's patch script.
For anyone reading this:
DO NOT RUN run it with bash kr_offset.sh
(yes, I'm dumb and it made me try to debug the script)
If ./kr_offset.sh
does not work, use sh kr_offset.sh
sh, NOT bash
Also: for kernels in the format of 4.14.190-lineageos-g27cc0d58ea9e
, remember that you're replacing everything after the kernel number, so in this case we're replacing -lineageos-g27cc0d58ea9e
with our new string.
Can you add support for kernelSU
Can you add support for kernelSU
@Edhic1 This turned out to be simpler than I expected as KSU ships with magiskboot
. The updated v2.1 script can search and patch KSU-modified kernels. It should also work with APatch-modified kernels so long as magiskboot
is provided in /data/adb
.
Hi
Where can I find
kr_patch.sh
?