From c9469069806c5a8b78192d037e93a88dd8efd84a Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Sat, 18 Apr 2020 17:40:39 +0300 Subject: [PATCH] change_os_version: scan os_patch_level on boot/recovery images Signed-off-by: Denis Efremov --- CMakeLists.txt | 73 ++++++++++ META-INF/com/google/android/update-binary | 91 +++++++++++++ change_os_version.c | 155 ++++++++++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 CMakeLists.txt create mode 100755 META-INF/com/google/android/update-binary create mode 100644 change_os_version.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..048bce6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,73 @@ +cmake_minimum_required(VERSION 3.12) + +if (NOT DEFINED NDK_ROOT) + if (DEFINED ENV{NDK_ROOT}) + set(NDK_ROOT "$ENV{NDK_ROOT}") + else () + message(FATAL_ERROR "Please define NDK_ROOT to point to your NDK path!") + endif () +endif () + +find_program(ZIP NAMES zip) + +# Set the tool chain file +set(CMAKE_TOOLCHAIN_FILE ${NDK_ROOT}/build/cmake/android.toolchain.cmake) +set(ANDROID_ABI arm64-v8a) +set(ANDROID_PLATFORM latest) +set(ANDROID_STL none) +set(ANDROID_LD lld) + +add_compile_options(-Wall -Wextra -pedantic -Werror) + +set(DEFAULT_FUTURE_DATE "2099-12" CACHE STRING "Select os_patch_level date.") +option(SELF_PACK_EXECUTABLES "Self-pack executables." ON) + +set(UPDATE_ZIP "backtothefuture-${DEFAULT_FUTURE_DATE}.zip") + +project(BackToTheFuture) + +set(CMAKE_C_FLAGS_RELEASE "-O2 -flto") +set(ANDROID_PIE FALSE) +link_libraries("-static") + +add_executable(change_os_version change_os_version.c) + +if (CMAKE_BUILD_TYPE STREQUAL Release) + add_custom_command( + TARGET change_os_version + POST_BUILD + COMMAND "${ANDROID_TOOLCHAIN_PREFIX}strip" --strip-all change_os_version + COMMENT "Stripping the executables" + VERBATIM + ) + if (SELF_PACK_EXECUTABLES) + include("${CMAKE_ROOT}/Modules/FindSelfPackers.cmake") + if (SELF_PACKER_FOR_EXECUTABLE) + add_custom_command( + TARGET change_os_version + POST_BUILD + COMMAND ${SELF_PACKER_FOR_EXECUTABLE} -9q ${SELF_PACKER_FOR_EXECUTABLE_FLAGS} change_os_version + COMMENT "Packing the executables" + VERBATIM + ) + endif () + endif () +endif () + +if (NOT CMAKE_CURRENT_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/META-INF/com/google/android/update-binary + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/META-INF/com/google/android/update-binary + ${CMAKE_CURRENT_BINARY_DIR}/META-INF/com/google/android/update-binary + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/META-INF/com/google/android/update-binary + ) +endif () + +add_custom_target(zip + DEPENDS change_os_version + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/META-INF/com/google/android/update-binary + COMMAND ${ZIP} ${UPDATE_ZIP} change_os_version META-INF/com/google/android/update-binary + COMMENT "Preparing ${UPDATE_ZIP}" + VERBATIM +) + diff --git a/META-INF/com/google/android/update-binary b/META-INF/com/google/android/update-binary new file mode 100755 index 0000000..90caecb --- /dev/null +++ b/META-INF/com/google/android/update-binary @@ -0,0 +1,91 @@ +#!/sbin/sh + +ZIPFILE=$3 +ZIPNAME=${ZIPFILE##*/} +OUTFD=$2 + +scr_wdth=50 + +# Detect real $OUTFD +# +if readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null; then + OUTFD=0 + for FD in $( ls /proc/$$/fd ); do + if readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null; then + if ps | grep " 3 $FD " | grep -v grep >/dev/null; then + OUTFD=$FD + break + fi + fi + done +fi + +ui_print() { + echo -ne "ui_print $1\n" >> /proc/self/fd/$OUTFD + echo -ne "ui_print\n" >> /proc/self/fd/$OUTFD +} + +print_full_bar() { + ui_print "$(printf '%*s\n' $scr_wdth | tr ' ' '=')" +} + +print_justified() { + local str="$1" + local str_len=${#str} + local padding_len=$(( ($scr_wdth - $str_len - 2) / 2)) + local ljust="$(printf '%*s' $padding_len)" + local rjust="$(printf '%*s' $(($padding_len + $str_len % 2)))" + + ui_print "=$ljust$str$rjust=" +} + + +ui_print " " +print_full_bar +print_justified "Back to the Future" +print_justified "OS Patch level changer v1.0" +print_justified "of boot and recovery partitions" +print_justified "for Samsung S10/Note10 devices." +print_justified "https://github.com/CruelKernel/" +print_full_bar +ui_print " " + +bl=$(getprop ro.boot.bootloader) + +# Device is first 5 characters of bootloader string. +# +device=${bl:0:$((${#bl} - 8))} + +if echo $device | grep -Ev 'G97([035][FN0]|7[BN])|N97([05][FN0]|6[BN0]|1N)' >/dev/null; then + ui_print " - Unsupported device detected. Installation aborted." + ui_print " " + exit 1 +fi + +date="$(echo $ZIPNAME | sed 's/^.*-\(2[0-9]\{3\}-[01][0-9]\).zip$/\1/')" + +if [ "$date" = "$ZIPNAME" ]; then + date="2127-12" + ui_print " - Can't determine os_patch_level date" + ui_print " - from filename '$ZIPNAME'." + ui_print " - Max possible date $date will be used." +else + ui_print " - Detected date from zip filename: $date." +fi + +unzip -d /tmp -o $ZIPFILE change_os_version +chmod +x /tmp/change_os_version + +ui_print " - Patching boot ..." +/tmp/change_os_version /dev/block/by-name/boot same $date 2>&1 | + sed 's/^\(.*\)$/ui_print - - \1\n/' >> /proc/self/fd/$OUTFD +ui_print " " +ui_print " - Patching recovery ..." +/tmp/change_os_version /dev/block/by-name/recovery same $date 2>&1 | + sed 's/^\(.*\)$/ui_print - - \1\n/' >> /proc/self/fd/$OUTFD + +rm -f /tmp/change_os_version + +ui_print " " +ui_print " - Finished." +ui_print " " diff --git a/change_os_version.c b/change_os_version.c new file mode 100644 index 0000000..256cf52 --- /dev/null +++ b/change_os_version.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BOOT_IMAGE_HEADER_V1_SIZE 1648 +#define BOOT_IMAGE_HEADER_V2_SIZE 1660 + +typedef union __attribute__((packed)) { + uint32_t version; + struct { + unsigned os_patch_level:11; + unsigned os_version:21; + }; + struct { + unsigned month:4; + unsigned year:7; + unsigned c:7; + unsigned b:7; + unsigned a:7; + }; +} os_version_t; + + +static inline void check(bool cond, const char *message, ...) +{ + if (cond) { + va_list args; + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char *argv[]) +{ + int fd; + uint32_t *addr; + const char *file, *os_version, *os_patch_level; + char *delim = NULL; + int year, month; + int a, b, c; + os_version_t curv, newv; + bool preserve_os_version = false; + bool preserve_os_patch_level = false; + + check(argc != 4, "Usage: %s \n", argv[0]); + file = argv[1]; + os_version = argv[2]; + os_patch_level = argv[3]; + + if (!strcmp(os_version, "same")) { + preserve_os_version = true; + } else { + // Format: a.b.c + for (const char *p = os_version; *p != '\0'; ++p) { + check(!(isdigit(*p) || *p == '.'), + "Incorrect os_version '%s'. Format: a.b.c\n", os_version); + } + a = strtol(os_version, &delim, 10); + check(*delim != '.', "Incorrect os_version '%s'. Format: a.b.c\n", os_version); + b = strtol(delim + 1, &delim, 10); + check(*delim != '.', "Incorrect os_version '%s'. Format: a.b.c\n", os_version); + c = strtol(delim + 1, NULL, 10); + check(!(0 <= a && a <= 127 && + 0 <= b && b <= 127 && + 0 <= c && c <= 127), + "Incorrect os_version '%s'. Format: a.b.c\n", os_version); + } + + + if (!strcmp(os_patch_level, "same")) { + preserve_os_patch_level = true; + } else { + // Format: YYYY-MM + check(strlen(os_patch_level) != 7 || + os_patch_level[4] != '-' || + !isdigit(os_patch_level[0]) || + !isdigit(os_patch_level[1]) || + !isdigit(os_patch_level[2]) || + !isdigit(os_patch_level[3]) || + !isdigit(os_patch_level[5]) || + !isdigit(os_patch_level[6]), + "Incorrent os_patch_level '%s'. Format: YYYY-MM\n", os_patch_level); + + year = atoi(os_patch_level); + month = atoi(os_patch_level + 5); + + check(!(2000 <= year && year <= 2127), + "Incorrect year: %ld (2000 <= year <= 2127)\n", year); + check(!(1 <= month && month <= 12), + "Incorrect month: %ld (01 <= month <= 12)\n", month); + } + + fd = open(file, O_RDWR); + check(fd < 0, "open %s failed: %s\n", file, strerror(errno)); + + addr = mmap(NULL, BOOT_IMAGE_HEADER_V1_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + check(addr == MAP_FAILED, "mmap %s failed: %s\n", file, strerror(errno)); + + check(strncmp((const char *)addr, "ANDROID!", 8), + "Incorrect magic number, not a boot file.\n"); + + curv.version = *(addr + 11); + printf("Current OS version:\t%u.%u.%u %u-%02u\n", + curv.a, curv.b, curv.c, + curv.year + 2000, curv.month); + + if (preserve_os_version) { + newv.os_version = curv.os_version; + } else { + newv.a = a; + newv.b = b; + newv.c = c; + } + + if (preserve_os_patch_level) { + newv.os_patch_level = curv.os_patch_level; + } else { + newv.year = (year - 2000); + newv.month = month; + } + + if (curv.version != newv.version) { + printf("New OS version:\t\t%u.%u.%u %u-%02u\n", + newv.a, newv.b, newv.c, + newv.year + 2000, newv.month); + + if (curv.os_version > newv.os_version) { + fprintf(stderr, "warn: new os_version is lower than current\n"); + } + + if (curv.os_patch_level > newv.os_patch_level) { + fprintf(stderr, "warn: new os_patch_level version is lower than current\n"); + } + + *(addr + 11) = newv.version; + check(msync(addr, BOOT_IMAGE_HEADER_V1_SIZE, MS_SYNC) < 0, "msync failed: %s\n", strerror(errno)); + } else { + printf("The dates are the same. Nothing to be done.\n"); + } + + check(munmap(addr, BOOT_IMAGE_HEADER_V1_SIZE) < 0, "munmap failed: %s\n", strerror(errno)); + check(close(fd) < 0, "close failed: %s\n", strerror(errno)); + + return 0; +}