-
-
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?