From 755a3d6a3956b18bd58e3ecfdc9d0ee03b2e78b3 Mon Sep 17 00:00:00 2001 From: Fabio Belavenuto Date: Mon, 18 Jul 2022 10:30:58 -0300 Subject: [PATCH 1/3] Added "Define custom MAC" to cmdline menu --- files/board/arpl/overlayfs/opt/arpl/menu.sh | 32 +++++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/files/board/arpl/overlayfs/opt/arpl/menu.sh b/files/board/arpl/overlayfs/opt/arpl/menu.sh index f72fef66..2377b6f7 100755 --- a/files/board/arpl/overlayfs/opt/arpl/menu.sh +++ b/files/board/arpl/overlayfs/opt/arpl/menu.sh @@ -180,6 +180,7 @@ function addonMenu() { done < <(availableAddons "${PLATFORM}" "${KVER}") if [ ! -f "${TMP_PATH}/menu" ] ; then dialog --backtitle "`backtitle`" --msgbox "No available addons to add" 0 0 + NEXT="e" continue fi dialog --backtitle "`backtitle`" --menu "Select an addon" 0 0 0 \ @@ -249,12 +250,13 @@ function cmdlineMenu() { while IFS="=" read KEY VALUE; do [ -n "${KEY}" ] && CMDLINE["${KEY}"]="${VALUE}" done < <(readConfigMap "cmdline" "${USER_CONFIG_FILE}") - echo "a \"Add/edit an cmdline item\"" > "${TMP_PATH}/menu" - echo "d \"Delete cmdline item(s)\"" >> "${TMP_PATH}/menu" - echo "s \"Show user cmdline\"" >> "${TMP_PATH}/menu" - echo "m \"Show model/build cmdline\"" >> "${TMP_PATH}/menu" - echo "u \"Show SATA(s) # ports and drives\"" >> "${TMP_PATH}/menu" - echo "e \"Exit\"" >> "${TMP_PATH}/menu" + echo "a \"Add/edit an cmdline item\"" > "${TMP_PATH}/menu" + echo "d \"Delete cmdline item(s)\"" >> "${TMP_PATH}/menu" + echo "c \"Define a custom MAC\"" >> "${TMP_PATH}/menu" + echo "s \"Show user cmdline\"" >> "${TMP_PATH}/menu" + echo "m \"Show model/build cmdline\"" >> "${TMP_PATH}/menu" + echo "u \"Show SATA(s) # ports and drives\"" >> "${TMP_PATH}/menu" + echo "e \"Exit\"" >> "${TMP_PATH}/menu" # Loop menu while true; do dialog --backtitle "`backtitle`" --menu "Choose a option" 0 0 0 \ @@ -296,6 +298,24 @@ function cmdlineMenu() { deleteConfigKey "cmdline.${I}" "${USER_CONFIG_FILE}" done ;; + c) + dialog --backtitle "`backtitle`" --title "User cmdline" \ + --inputbox "Type a custom MAC address" 0 0 "${CMDLINE['mac1']}"\ + 2>${TMP_PATH}/resp + [ $? -ne 0 ] && continue + MAC1="`sed 's/://g' <"${TMP_PATH}/resp"`" + if [ -z "${MAC1}" ]; then + unset CMDLINE["mac1"] + unset CMDLINE["netif_num"] + deleteConfigKey "cmdline.mac1" "${USER_CONFIG_FILE}" + deleteConfigKey "cmdline.netif_num" "${USER_CONFIG_FILE}" + else + CMDLINE["mac1"]="${MAC1}" + CMDLINE["netif_num"]=1 + writeConfigKey "cmdline.mac1" "${MAC1}" "${USER_CONFIG_FILE}" + writeConfigKey "cmdline.netif_num" "1" "${USER_CONFIG_FILE}" + fi + ;; s) ITEMS="" for KEY in ${!CMDLINE[@]}; do From 9edb1cfe0fb7e7f014909ea397fef3c8e1647394 Mon Sep 17 00:00:00 2001 From: Fabio Belavenuto Date: Mon, 18 Jul 2022 10:46:03 -0300 Subject: [PATCH 2/3] Adding title into boot screen --- files/board/arpl/overlayfs/opt/arpl/boot.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/files/board/arpl/overlayfs/opt/arpl/boot.sh b/files/board/arpl/overlayfs/opt/arpl/boot.sh index 224136aa..84583403 100755 --- a/files/board/arpl/overlayfs/opt/arpl/boot.sh +++ b/files/board/arpl/overlayfs/opt/arpl/boot.sh @@ -9,12 +9,13 @@ loaderIsConfigured || die "Loader is not configured!" # Print text centralized, if variable ${COLUMNS} is defined clear +TITLE="Welcome to Automated Redpill Loader v${ARPL_VERSION}" +printf "\033[1;44m%*s\n" $COLUMNS "" +printf "\033[1;44m%*s\033[A\n" $COLUMNS "" +printf "\033[1;32m%*s\033[0m\n" $(((${#TITLE}+$COLUMNS)/2)) "${TITLE}" +printf "\033[1;44m%*s\033[0m\n" $COLUMNS "" TITLE="BOOTING..." -if [ -z "${COLUMNS}" ]; then - echo -e "\033[1;33m${TITLE}\033[0m" -else - printf "\033[1;33m%*s\033[0m\n" $(((${#TITLE}+${COLUMNS})/2)) "${TITLE}" -fi +printf "\033[1;33m%*s\033[0m\n" $(((${#TITLE}+${COLUMNS})/2)) "${TITLE}" # Check if DSM zImage changed, patch it if necessary ZIMAGE_HASH="`readConfigKey "zimage-hash" "${USER_CONFIG_FILE}"`" From ff422707b70d2d471176b373f034c74e2fe7afe0 Mon Sep 17 00:00:00 2001 From: Fabio Belavenuto Date: Mon, 18 Jul 2022 13:15:06 -0300 Subject: [PATCH 3/3] Replaced php scripts for alternatives Add kpatch C code for patch DSM kernel Fix ramdisk-common-init-script (wrong merge) --- .gitignore | 1 + Taskfile.yaml | 6 + .../board/arpl/overlayfs/opt/arpl/common.php | 245 ------------- files/board/arpl/overlayfs/opt/arpl/crc32.php | 8 - files/board/arpl/overlayfs/opt/arpl/kpatch | Bin 0 -> 141968 bytes .../opt/arpl/patch-boot_params-check.php | 141 -------- .../opt/arpl/patch-ramdisk-check.php | 85 ----- .../patch/ramdisk-common-init-script.patch | 2 +- .../overlayfs/opt/arpl/vmlinux-to-bzImage.sh | 5 +- .../arpl/overlayfs/opt/arpl/zimage-patch.sh | 9 +- kpatch/Makefile | 12 + kpatch/main.c | 333 ++++++++++++++++++ 12 files changed, 358 insertions(+), 489 deletions(-) delete mode 100755 files/board/arpl/overlayfs/opt/arpl/common.php delete mode 100755 files/board/arpl/overlayfs/opt/arpl/crc32.php create mode 100755 files/board/arpl/overlayfs/opt/arpl/kpatch delete mode 100755 files/board/arpl/overlayfs/opt/arpl/patch-boot_params-check.php delete mode 100755 files/board/arpl/overlayfs/opt/arpl/patch-ramdisk-check.php create mode 100644 kpatch/Makefile create mode 100644 kpatch/main.c diff --git a/.gitignore b/.gitignore index 6c3ac2f5..cc7824ef 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ test.sh docker/Dockerfile docker/cache *.bak +*.o diff --git a/Taskfile.yaml b/Taskfile.yaml index bcb6559c..33afc022 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -20,3 +20,9 @@ tasks: dir: addons cmds: - ./compile-addons.sh {{.CLI_ARGS}} + + compile-kpatch: + dir: kpatch + cmds: + - make clean all + - mv kpatch ../files/board/arpl/overlayfs/opt/arpl/ diff --git a/files/board/arpl/overlayfs/opt/arpl/common.php b/files/board/arpl/overlayfs/opt/arpl/common.php deleted file mode 100755 index 8fcae197..00000000 --- a/files/board/arpl/overlayfs/opt/arpl/common.php +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env php - $num) { - if ($num === null) { - $searchSeq[] = null; - } elseif (is_array($num) && count($num) == 2 && is_int($num[0]) && is_int($num[1]) && $num[0] >= 0 && - $num[0] <= 255 && $num[1] >= 0 && $num[1] <= 255 && $num[0] < $num[1]) { - $searchSeq[] = $num; //Leave them as numeric - } elseif (is_int($num) && $num >= 0 && $num <= 255) { - $searchSeq[] = chr($num); - } else { - perr("Found invalid search sequence at index $idx", true); - } - } - - //$pos denotes start position but it's also used to mark where start of a potential pattern match was found - fseek($fp, $pos); - do { //This loop is optimized for speed - $buf = fread($fp, $bufLen); - if (!isset($buf[$bufLen-1])) { - break; //Not enough data = no match - } - - $successfulLoops = 0; - foreach ($searchSeq as $byteIdx => $seekByte) { - if ($seekByte === null) { //any character - ++$successfulLoops; - continue; - } - - //element in the array can be a range [(int)from,(int)to] or a literal SINGLE byte - //if isset finds a second element it will mean for us that it's an array of 2 elements (as we don't expect - //a string longer than a single byte) - if (isset($seekByte[1])) { - $curByteNum = ord($buf[$byteIdx]); - if ($curByteNum < $seekByte[0] || $curByteNum > $seekByte[1]) { - break; - } - } elseif($buf[$byteIdx] !== $seekByte) { //If the byte doesn't match literally we know it's not a match - break; - } - - ++$successfulLoops; - } - if ($successfulLoops === $bufLen) { - return $pos; - } - - fseek($fp, ++$pos); - $maxToCheck--; - } while (!feof($fp) && $maxToCheck != 0); - - return -1; -} - -/** - * @return resource - */ -function getFileMemMapped(string $path) -{ - $fp = fopen('php://memory', 'r+'); - fwrite($fp, file_get_contents($path)); //poor man's mmap :D - - return $fp; -} - -function saveStreamToFile($fp, string $path) -{ - perr("Saving stream to $path ...\n"); - - $fp2 = fopen($path, 'w'); - fseek($fp, 0); - while (!feof($fp)) { - fwrite($fp2, fread($fp, 8192)); - } - fclose($fp2); - - perr("DONE!\n"); -} - -/** - * Do not call this in time-sensitive code... - */ -function readAt($fp, int $pos, int $len) -{ - fseek($fp, $pos); - return fread($fp, $len); -} diff --git a/files/board/arpl/overlayfs/opt/arpl/crc32.php b/files/board/arpl/overlayfs/opt/arpl/crc32.php deleted file mode 100755 index 1f2799c0..00000000 --- a/files/board/arpl/overlayfs/opt/arpl/crc32.php +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env php - 2) { - fwrite(STDERR, "Usage: " . $argv[0] . " \n"); - die(); -} -echo hash_file('crc32b', $argv[1]); -?> diff --git a/files/board/arpl/overlayfs/opt/arpl/kpatch b/files/board/arpl/overlayfs/opt/arpl/kpatch new file mode 100755 index 0000000000000000000000000000000000000000..cf716ad7a170869bae9776b01fdda291ca7d54dd GIT binary patch literal 141968 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ΝNQR}lklnCbuh7Z8H~m&83{ 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 E3m9C&EC2ui literal 0 HcmV?d00001 diff --git a/files/board/arpl/overlayfs/opt/arpl/patch-boot_params-check.php b/files/board/arpl/overlayfs/opt/arpl/patch-boot_params-check.php deleted file mode 100755 index 4e88d88e..00000000 --- a/files/board/arpl/overlayfs/opt/arpl/patch-boot_params-check.php +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env php - 3) { - perr("Usage: " . $argv[0] . " []\n", true); -} - -$file = getArgFilePath(1); -perr("\nGenerating patch for $file\n"); - -//The function will reside in init code part. We don't care we may potentially search beyond as we expect it to be found -$codeAddr = getELFSectionAddr($file, '.init.text', 3); - -//Finding a function boundary is non-trivial really as patters can vary, we can have multiple exit points, and in CISC -// there are many things which may match e.g. "PUSH EBP". Implementing even a rough disassembler is pointless. -//However, we can certainly cheat here as we know with CDECL a non-empty function will always contain one or more -// PUSH (0x41) R12-R15 (0x54-57) sequences. Then we can search like a 1K forward for these characteristic LOCK OR. -const PUSH_R12_R15_SEQ = [0x41, [0x54, 0x57]]; -const PUSH_R12_R15_SEQ_LEN = 2; -const LOCK_OR_LOOK_AHEAD = 1024; -const LOCK_OR_PTR_SEQs = [ - [0xF0, 0x80, null, null, null, null, null, 0x01], - [0xF0, 0x80, null, null, null, null, null, 0x02], - [0xF0, 0x80, null, null, null, null, null, 0x04], - [0xF0, 0x80, null, null, null, null, null, 0x08], -]; -const LOCK_OR_PTR_SEQs_NUM = 4; //how many sequences we are expecting -const LOCK_OR_PTR_SEQ_LEN = 8; //length of a single sequence - -$fp = getFileMemMapped($file); //Read the whole file to memory to make fseet/fread much faster -$pos = $codeAddr; //Start from where code starts -$orsPos = null; //When matched it will contain our resulting file offsets to LOCK(); OR BYTE PTR [rip+...],0x calls -perr("Looking for f() candidates...\n"); -do { - $find = findSequenceWithWildcard($fp, PUSH_R12_R15_SEQ, $pos, -1); - if ($find === -1) { - break; //no more "functions" left - } - - perr("\rAnalyzing f() candidate @ " . decTo32bUFhex($pos)); - - //we found something looking like PUSH R12-R15, now find the ORs - $orsPos = []; //file offsets where LOCK() calls should start - $orsPosNum = 0; //Number of LOCK(); OR ptr sequences found - $seqPos = $pos; - foreach (LOCK_OR_PTR_SEQs as $idx => $seq) { - $find = findSequenceWithWildcard($fp, $seq, $seqPos, LOCK_OR_LOOK_AHEAD); - if ($find === -1) { - break; //Seq not found - there's no point to look further - } - - $orsPos[] = $find; - ++$orsPosNum; - $seqPos = $find + LOCK_OR_PTR_SEQ_LEN; //Next search will start after the current sequence code - } - - //We can always move forward by the function token length (obvious) but if we couldn't find any LOCK-OR tokens - // we can skip the whole look ahead distance. We CANNOT do that if we found even a single token because the next one - // might have been just after the look ahead distance - if ($orsPosNum !== LOCK_OR_PTR_SEQs_NUM) { - $pos += PUSH_R12_R15_SEQ_LEN; - if ($orsPosNum === 0) { - $pos += LOCK_OR_LOOK_AHEAD; - } - continue; //Continue the main search loop to find next function candidate - } - - //We found LOCK(); OR ptr sequences so we can print some logs and collect ptrs (as this is quite expensive) - $seqPtrsDist = []; - perr("\n[?] Found possible f() @ " . decTo32bUFhex($pos) . "\n"); - $ptrOffset = null; - $equalJumps = 0; - foreach (LOCK_OR_PTR_SEQs as $idx => $seq) { - //data will have the following bytes: - // [0-LOCK()] [1-OR()] [2-BYTE-PTR] [3-OFFS-b3] [4-OFFS-b2] [5-OFFS-b1] [6-OFFS-b1] [7-NUMBER] - $seqData = readAt($fp, $orsPos[$idx], LOCK_OR_PTR_SEQ_LEN); - $newPtrOffset = //how far it "jumps" - $orsPos[$idx] + - (unpack('V', $seqData[3] . $seqData[4] . $seqData[5] . $seqData[6])[1]); //u32 bit LE - - if($ptrOffset === null) { - $ptrOffset = $newPtrOffset; //Save the first one to compare in the next loop - ++$equalJumps; - } elseif ($ptrOffset === $newPtrOffset) { - ++$equalJumps; - } - - perr( - "\t[+] Found LOCK-OR#$idx sequence @ " . decTo32bUFhex($orsPos[$idx]) . " => " . - rawToUFhex($seqData) . " [RIP+(dec)$newPtrOffset]\n" - ); - } - if ($equalJumps !== 4) { - perr("\t[-] LOCK-OR PTR offset mismatch - $equalJumps/" . LOCK_OR_PTR_SEQs_NUM . " matched\n"); - //If the pointer checking failed we can at least move beyond the last LOCK-OR found as we know there's no valid - // sequence of LOCK-ORs there - $pos = $orsPos[3]; - continue; - } - - perr("\t[+] All $equalJumps LOCK-OR PTR offsets equal - match found!\n"); - break; -} while(!feof($fp)); - -if ($orsPos === null) { //There's a small chance no candidates with LOCK ORs were found - perr("Failed to find matching sequences", true); -} - -//Patch offsets -foreach ($orsPos as $seqFileOffset) { - //The offset will point at LOCK(), we need to change the OR (0x80 0x0d) to AND (0x80 0x25) so the two bytes after - $seqFileOffset = $seqFileOffset+2; - - perr("Patching OR to AND @ file offset (dec)$seqFileOffset\n"); - fseek($fp, $seqFileOffset); - fwrite($fp, "\x25"); //0x0d is OR, 0x25 is AND -} - -if (!isset($argv[2])) { - perr("No output file specified - discarding data\n"); - exit; -} - -saveStreamToFile($fp, $argv[2]); -fclose($fp); diff --git a/files/board/arpl/overlayfs/opt/arpl/patch-ramdisk-check.php b/files/board/arpl/overlayfs/opt/arpl/patch-ramdisk-check.php deleted file mode 100755 index 0f464051..00000000 --- a/files/board/arpl/overlayfs/opt/arpl/patch-ramdisk-check.php +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env php - 3) { - perr("Usage: " . $argv[0] . " []\n", true); -} - -$file = getArgFilePath(1); -perr("\nGenerating patch for $file\n"); - -//Strings (e.g. error for printk()) reside in .rodata - start searching there to save time -$rodataAddr = getELFSectionAddr($file, '.rodata', 2); - -//Locate the precise location of "ramdisk error" string -$rdErrAddr = getELFStringLoc($file, '3ramdisk corrupt'); - - -//offsets will be 32 bit in ASM and in LE -$errPrintAddr = $rodataAddr + $rdErrAddr; -$errPrintCAddrLEH = decTo32bLEhex($errPrintAddr - 1); //Somehow rodata contains off-by-one sometimes... -$errPrintAddrLEH = decTo32bLEhex($errPrintAddr); -perr("LE arg addr: " . $errPrintCAddrLEH . "\n"); - -$fp = getFileMemMapped($file); //Read the whole file to memory to make fseet/fread much faster - -//Find the printk() call argument -$printkPos = findSequence($fp, hex2raw($errPrintCAddrLEH), 0, DIR_FWD, -1); -if ($printkPos === -1) { - perr("printk pos not found!\n", true); -} -perr("Found printk arg @ " . decTo32bUFhex($printkPos) . "\n"); - -//double check if it's a MOV reg,VAL (where reg is EAX/ECX/EDX/EBX/ESP/EBP/ESI/EDI) -fseek($fp, $printkPos - 3); -$instr = fread($fp, 3); -if (strncmp($instr, "\x48\xc7", 2) !== 0) { - perr("Expected MOV=>reg before printk error, got " . bin2hex($instr) . "\n", true); -} -$dstReg = ord($instr[2]); -if ($dstReg < 192 || $dstReg > 199) { - perr("Expected MOV w/reg operand [C0-C7], got " . bin2hex($instr[2]) . "\n", true); -} -$movPos = $printkPos - 3; -perr("Found printk MOV @ " . decTo32bUFhex($movPos) . "\n"); - -//now we should seek a reasonable amount (say, up to 32 bytes) for a sequence of CALL x => TEST EAX,EAX => JZ -$testPos = findSequence($fp, "\x85\xc0", $movPos, DIR_RWD, 32); -if ($testPos === -1) { - perr("Failed to find TEST eax,eax\n", true); -} - -$jzPos = $testPos + 2; -fseek($fp, $jzPos); -$jz = fread($fp, 2); -if ($jz[0] !== "\x74") { - perr("Failed to find JZ\n", true); -} - -$jzp = "\xEB" . $jz[1]; -perr('OK - patching ' . bin2hex($jz) . " (JZ) to " . bin2hex($jzp) . " (JMP) @ $jzPos\n"); -fseek($fp, $jzPos); //we should be here already -perr("Patched " . fwrite($fp, $jzp) . " bytes in memory\n"); - -if (!isset($argv[2])) { - perr("No output file specified - discarding data\n"); - exit; -} - -if (!isset($argv[2])) { - perr("No output file specified - discarding data\n"); - exit; -} - -saveStreamToFile($fp, $argv[2]); -fclose($fp); diff --git a/files/board/arpl/overlayfs/opt/arpl/patch/ramdisk-common-init-script.patch b/files/board/arpl/overlayfs/opt/arpl/patch/ramdisk-common-init-script.patch index b34f6a18..f550b024 100644 --- a/files/board/arpl/overlayfs/opt/arpl/patch/ramdisk-common-init-script.patch +++ b/files/board/arpl/overlayfs/opt/arpl/patch/ramdisk-common-init-script.patch @@ -13,7 +13,7 @@ echo "Insert basic USB modules..." SYNOLoadModules $USB_MODULES +SYNOLoadModules "usb-storage" -+/addons/addons.sh early; /addons/addons.sh patches ++/addons/addons.sh early # insert Etron USB3.0 drivers diff --git a/files/board/arpl/overlayfs/opt/arpl/vmlinux-to-bzImage.sh b/files/board/arpl/overlayfs/opt/arpl/vmlinux-to-bzImage.sh index 94978c45..d1b463b7 100755 --- a/files/board/arpl/overlayfs/opt/arpl/vmlinux-to-bzImage.sh +++ b/files/board/arpl/overlayfs/opt/arpl/vmlinux-to-bzImage.sh @@ -56,8 +56,7 @@ gzip -cd "${SCRIPT_DIR}/zImage_template.gz" > "${ZIMAGE_MOD}" dd if="${VMLINUX_MOD}" of="${ZIMAGE_MOD}" bs=16494 seek=1 conv=notrunc >"${LOG_FILE}" 2>&1 || dieLog file_size_le "${VMLINUX_MOD}" | dd of="${ZIMAGE_MOD}" bs=15745134 seek=1 conv=notrunc >"${LOG_FILE}" 2>&1 || dieLog -RUN_SIZE=$(objdump -h ${VMLINUX_MOD} | sh "${SCRIPT_DIR}/calc_run_size.sh") +RUN_SIZE=`objdump -h ${VMLINUX_MOD} | sh "${SCRIPT_DIR}/calc_run_size.sh"` size_le $RUN_SIZE | dd of=$ZIMAGE_MOD bs=15745210 seek=1 conv=notrunc >"${LOG_FILE}" 2>&1 || dieLog file_size_le "${VMLINUX_MOD}" | dd of="${ZIMAGE_MOD}" bs=15745244 seek=1 conv=notrunc >"${LOG_FILE}" 2>&1 || dieLog -# cksum $ZIMAGE_MOD # https://blog.box.com/crc32-checksums-the-good-the-bad-and-the-ugly -size_le $(($((16#$(php "${SCRIPT_DIR}/crc32.php" "${ZIMAGE_MOD}"))) ^ 0xFFFFFFFF)) | dd of="${ZIMAGE_MOD}" conv=notrunc oflag=append >"${LOG_FILE}" 2>&1 || dieLog +size_le $(($((16#`crc32 "${ZIMAGE_MOD}" | awk '{print$1}'`)) ^ 0xFFFFFFFF)) | dd of="${ZIMAGE_MOD}" conv=notrunc oflag=append >"${LOG_FILE}" 2>&1 || dieLog diff --git a/files/board/arpl/overlayfs/opt/arpl/zimage-patch.sh b/files/board/arpl/overlayfs/opt/arpl/zimage-patch.sh index 34e1c1b7..f3886277 100755 --- a/files/board/arpl/overlayfs/opt/arpl/zimage-patch.sh +++ b/files/board/arpl/overlayfs/opt/arpl/zimage-patch.sh @@ -12,14 +12,11 @@ echo -n "." # Extract vmlinux /opt/arpl/bzImage-to-vmlinux.sh "${ORI_ZIMAGE_FILE}" "${TMP_PATH}/vmlinux" >"${LOG_FILE}" 2>&1 || dieLog echo -n "." -# Patch boot params -/opt/arpl/patch-boot_params-check.php "${TMP_PATH}/vmlinux" "${TMP_PATH}/vmlinux-mod1" >"${LOG_FILE}" 2>&1 || dieLog -echo -n "." -# Patch ramdisk check -/opt/arpl/patch-ramdisk-check.php "${TMP_PATH}/vmlinux-mod1" "${TMP_PATH}/vmlinux-mod2" >"${LOG_FILE}" 2>&1 || dieLog +# Patch boot params and ramdisk check +/opt/arpl/kpatch "${TMP_PATH}/vmlinux" "${TMP_PATH}/vmlinux-mod" >"${LOG_FILE}" 2>&1 || dieLog echo -n "." # rebuild zImage -/opt/arpl/vmlinux-to-bzImage.sh "${TMP_PATH}/vmlinux-mod2" "${MOD_ZIMAGE_FILE}" >"${LOG_FILE}" 2>&1 || dieLog +/opt/arpl/vmlinux-to-bzImage.sh "${TMP_PATH}/vmlinux-mod" "${MOD_ZIMAGE_FILE}" >"${LOG_FILE}" 2>&1 || dieLog echo -n "." # Update HASH of new DSM zImage diff --git a/kpatch/Makefile b/kpatch/Makefile new file mode 100644 index 00000000..f16a524e --- /dev/null +++ b/kpatch/Makefile @@ -0,0 +1,12 @@ + +CFLAGS = -Wall -pedantic +LDFLAGS = +LIBS = /lib/x86_64-linux-gnu/libelf.a /lib/x86_64-linux-gnu/libz.a + +all: kpatch + +kpatch: main.o + cc $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + rm -f kpatch *.o diff --git a/kpatch/main.c b/kpatch/main.c new file mode 100644 index 00000000..f601ad53 --- /dev/null +++ b/kpatch/main.c @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2020 Fabio Belavenuto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +/** + * Converted from php code by Fabio Belavenuto + * + * A quick tool for patching the boot_params check in the DSM kernel image + * This lets you tinker with the initial ramdisk contents without disabling mount() features and modules loading + * + * The overall pattern we need to find is: + * - an CDECL function + * - does "LOCK OR [const-ptr],n" 4x + * - values of ORs are 1/2/4/8 respectively + * - [const-ptr] is always the same + * + */ +/** + * A quick tool for patching the ramdisk check in the DSM kernel image + * This lets you tinker with the initial ramdisk contents without disabling mount() features and modules loading + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const int DIR_FWD = 1; +const int DIR_RWD = -1; + +/* Variables */ +int fd; +int verbose = 1, read_only = 0; +Elf *elfHandle; +GElf_Ehdr elfExecHeader; +uint64_t orPos[4], fileSize, rodataAddr, rodataOffs, initTextOffs; +unsigned char *fileData; + +/*****************************************************************************/ +void errorMsg(char *message) { + fprintf(stderr, "%s\n", message); + exit(1); +} + +/*****************************************************************************/ +void errorNum() { + char str[100] = {0}; + perror(str); + exit(2); +} + +/*****************************************************************************/ +void elfErrno() { + int err; + + if ((err = elf_errno()) != 0) { + fprintf(stderr, "%s\n", elf_errmsg(err)); + exit(3); + } +} + +/*****************************************************************************/ +//Finding a function boundary is non-trivial really as patters can vary, we can have multiple exit points, and in CISC +// there are many things which may match e.g. "PUSH EBP". Implementing even a rough disassembler is pointless. +//However, we can certainly cheat here as we know with CDECL a non-empty function will always contain one or more +// PUSH (0x41) R12-R15 (0x54-57) sequences. Then we can search like a 1K forward for these characteristic LOCK OR. +uint64_t findPUSH_R12_R15_SEQ(uint64_t start) { + uint64_t i; + + for (i = start; i < fileSize; i++) { + if (fileData[i] == 0x41 && (fileData[i+1] >= 0x54 && fileData[i+1] <= 0x57)) { + return i; + } + } + return -1; +} + +/*****************************************************************************/ +//[0xF0, 0x80, null, null, null, null, null, 0xXX], +uint64_t findORs(uint64_t start, uint32_t maxCheck) { + uint64_t i; + int c = 0; + uint8_t lb = 0x01; + + for (i = start; i < fileSize; i++) { + if (fileData[i] == 0xF0 && fileData[i+1] == 0x80 && fileData[i+7] == lb) { + orPos[c++] = i; + i += 7; + lb <<= 1; + } + if (c == 4) { + break; + } + if (--maxCheck == 0) { + break; + } + } + return c; +} + +/*****************************************************************************/ +void patchBootParams() { + uint64_t addr, pos; + uint64_t newPtrOffset, ptrOffset; + int n; + + //The function will reside in init code part. We don't care we may potentially search beyond as we expect it to be found + printf("Found .init.text at %lX\n", initTextOffs); + while (initTextOffs < fileSize) { + addr = findPUSH_R12_R15_SEQ(initTextOffs); + if (addr == -1) + break; //no more "functions" left + printf("\rAnalyzing f() candidate @ %lX, PUSH @ %lX", initTextOffs, addr); + //we found something looking like PUSH R12-R15, now find the ORs + n = findORs(initTextOffs, 1024); + if (n != 4) { + //We can always move forward by the function token length (obvious) but if we couldn't find any LOCK-OR tokens + // we can skip the whole look ahead distance. We CANNOT do that if we found even a single token because the next one + // might have been just after the look ahead distance + initTextOffs += 2; + if (n == 0) { + initTextOffs += 1024; + } + continue; //Continue the main search loop to find next function candidate + } + //We found LOCK(); OR ptr sequences so we can print some logs and collect ptrs (as this is quite expensive) + printf("\n[?] Found possible f() @ %lX\n", initTextOffs); + ptrOffset=0; + int ec = 0; + for (n = 0; n < 4; n++) { + //data will have the following bytes: + // [0-LOCK()] [1-OR()] [2-BYTE-PTR] [3-OFFS-b3] [4-OFFS-b2] [5-OFFS-b1] [6-OFFS-b1] [7-NUMBER] + pos = orPos[n]; + //how far it "jumps" + newPtrOffset = pos + (fileData[pos+6] << 24 | fileData[pos+5] << 16 | fileData[pos+4] << 8 | fileData[pos+3]); + if (ptrOffset == 0) { + ptrOffset = newPtrOffset; + ++ec; + } else if (ptrOffset == newPtrOffset) { + ++ec; + } + printf("\t[+] Found LOCK-OR#$idx sequence @ %lX => %02X %02X %02X %02X %02X %02X %02X %02X [RIP+%lX]\n", + pos, fileData[pos], fileData[pos+1], fileData[pos+2], fileData[pos+3], fileData[pos+4], + fileData[pos+5], fileData[pos+6], fileData[pos+7], newPtrOffset); + } + if (ec != 4) { + printf("\t[-] LOCK-OR PTR offset mismatch - %d/4 matched\n", ec); + //If the pointer checking failed we can at least move beyond the last LOCK-OR found as we know there's no valid + // sequence of LOCK-ORs there + initTextOffs = orPos[3]; + continue; + } + printf("\t[+] All %d LOCK-OR PTR offsets equal - match found!\n", ec); + break; + } + if (addr == -1) { + errorMsg("\nFailed to find matching sequences"); + } else { + //Patch offsets + for (n = 0; n < 4; n++) { + //The offset will point at LOCK(), we need to change the OR (0x80 0x0d) to AND (0x80 0x25) so the two bytes after + pos = orPos[n] + 2; + printf("Patching OR to AND @ %lX\n", pos); + fileData[pos] = 0x25; + } + } +} + +/*****************************************************************************/ +uint32_t changeEndian(uint32_t num) { + return ((num>>24)&0xff) | // move byte 3 to byte 0 + ((num<<8)&0xff0000) | // move byte 1 to byte 2 + ((num>>8)&0xff00) | // move byte 2 to byte 1 + ((num<<24)&0xff000000); // move byte 0 to byte 3 +} + +/*****************************************************************************/ +uint64_t findSeq(const char* seq, int len, uint32_t pos, int dir, uint64_t max) { + uint64_t i; + + i = pos; + do { + if (strncmp((const char*)fileData+i, seq, len) == 0) { + return i; + } + i += dir; + --max; + } while(i > 0 && i < fileSize && max > 0); + return -1; +} + +/*****************************************************************************/ +void patchRamdiskCheck() { + uint64_t pos, errPrintAddr; + uint64_t printkPos, testPos, jzPos; + const char str[] = "3ramdisk corrupt"; + + printf("Patching ramdisk check\n"); + for (pos = rodataOffs; pos < fileSize; pos++) { + if (strncmp(str, (const char*)(fileData + pos), 16) == 0) { + pos -= rodataOffs; + break; + } + } + errPrintAddr = rodataAddr + pos - 1; + printf("LE arg addr: %08lX\n", errPrintAddr); + printkPos = findSeq((const char*)&errPrintAddr, 4, 0, DIR_FWD, -1); + if (printkPos == -1) { + errorMsg("printk pos not found!"); + } + //double check if it's a MOV reg,VAL (where reg is EAX/ECX/EDX/EBX/ESP/EBP/ESI/EDI) + printkPos -= 3; + if (strncmp((const char*)fileData+printkPos, "\x48\xc7", 2) != 0) { + printf("Expected MOV=>reg before printk error, got %02X %02X\n", fileData[printkPos], fileData[printkPos+1]); + errorMsg(""); + } + if (fileData[printkPos+2] < 0xC0 || fileData[printkPos+2] > 0xC7) { + printf("Expected MOV w/reg operand [C0-C7], got %02X\n", fileData[printkPos+2]); + errorMsg(""); + } + printf("Found printk MOV @ %08lX\n", printkPos); + + //now we should seek a reasonable amount (say, up to 32 bytes) for a sequence of CALL x => TEST EAX,EAX => JZ + testPos = findSeq("\x85\xc0", 2, printkPos, DIR_RWD, 32); + if (testPos == -1) { + errorMsg("Failed to find TEST eax,eax\n"); + } + printf("Found TEST eax,eax @ %08lX\n", testPos); + jzPos = testPos + 2; + if (fileData[jzPos] != 0x74) { + errorMsg("Failed to find JZ\n"); + } + printf("OK - patching %02X%02X (JZ) to %02X%02X (JMP) @ %08lX\n", + fileData[jzPos], fileData[jzPos+1], 0xEB, fileData[jzPos+1], jzPos); + fileData[jzPos] = 0xEB; +} + +/*****************************************************************************/ +int main(int argc, char *argv[]) { + struct stat fileInf; + Elf_Scn *section; + GElf_Shdr sectionHeader; + char *sectionName; + + if (argc != 3) { + errorMsg("Use: kpatch "); + } + + if (elf_version(EV_CURRENT) == EV_NONE) + elfErrno(); + + if ((fd = open(argv[1], O_RDONLY)) == -1) + errorNum(); + + if ((elfHandle = elf_begin(fd, ELF_C_READ, NULL)) == NULL) + elfErrno(); + if (gelf_getehdr(elfHandle, &elfExecHeader) == NULL) + elfErrno(); + + switch(elf_kind(elfHandle)) { + case ELF_K_NUM: + case ELF_K_NONE: + errorMsg("file type unknown"); + break; + case ELF_K_COFF: + errorMsg("COFF binaries not supported"); + break; + case ELF_K_AR: + errorMsg("AR archives not supported"); + break; + case ELF_K_ELF: + break; + } + + section = NULL; + while ((section = elf_nextscn(elfHandle, section)) != NULL) { + if (gelf_getshdr(section, §ionHeader) != §ionHeader) + elfErrno(); + if ((sectionName = elf_strptr(elfHandle, elfExecHeader.e_shstrndx, sectionHeader.sh_name)) == NULL) + elfErrno(); + if (strcmp(sectionName, ".init.text") == 0) { + initTextOffs = sectionHeader.sh_offset; + } else if (strcmp(sectionName, ".rodata") == 0) { + rodataAddr = sectionHeader.sh_addr & 0xFFFFFFFF; + rodataOffs = sectionHeader.sh_offset; + } + } + elfErrno(); /* If there isn't elf_errno set, nothing will happend. */ + elf_end(elfHandle); + + if (fstat(fd, &fileInf) == -1) + errorNum(); + + fileSize = fileInf.st_size; + fileData = malloc(fileSize); + if (fileSize != read(fd, fileData, fileSize)) { + errorNum(); + } + close(fd); + + patchBootParams(); + patchRamdiskCheck(); + if ((fd = open(argv[2], O_WRONLY | O_CREAT, 0644)) == -1) { + errorNum(); + } + if (fileSize != write(fd, fileData, fileSize)) { + errorNum(); + } + close(fd); + printf("\n"); + return 0; +}