From 43f4e1a9d5f1e96dda2b87416ca4e1c291d157df Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Mon, 3 Aug 2026 20:47:29 +0800 Subject: [PATCH 01/12] Update dualboot to Multi-Boot Co-authored-by: GPT 5.6 Sol --- .github/workflows/cmake-multi-platform.yml | 167 +++++--- .gitignore | 4 +- CMakeLists.txt | 38 +- Config/DualBoot.Sm7125.cfg | 21 +- Config/DualBoot.Sm8150.cfg | 21 +- Config/DualBoot.Sm8250.cfg | 21 +- Config/DualBoot.Sm8250DT.cfg | 22 +- Config/DualBoot.Sm8550.cfg | 21 +- Config/Shim.Sample.cfg | 30 ++ README.md | 45 +- ShellCode/CommonTail.S | 38 -- ShellCode/ShellCode.KernelWrapper.S | 73 ---- {ShellCode => ShimLoader}/CMakeLists.txt | 17 +- {ShellCode => ShimLoader}/DummyHead.S | 22 +- ShimLoader/ShimLoader.S | 12 + Shims/CMakeLists.txt | 48 +++ Shims/CommonTail.S | 76 ++++ Shims/DummyHead.S | 24 ++ .../ShellCode.AYAPS.S => Shims/Shim.AYAPS.S | 9 +- .../Shim.Andromeda.S | 9 +- .../Shim.Cepheus.S | 9 +- .../ShellCode.Hotdog.S => Shims/Shim.Hotdog.S | 9 +- Shims/Shim.KernelWrapper.S | 65 +++ .../Shim.Miatoll.S | 9 +- .../ShellCode.Nabu.S => Shims/Shim.Nabu.S | 9 +- .../ShellCode.Pipa.S => Shims/Shim.Pipa.S | 9 +- patcher.c | 298 +------------ remover.c | 129 ------ shim.c | 402 ++++++++++++++++++ shim.h | 95 +++++ shim_config.c | 290 +++++++++++++ utils.c | 31 -- utils.h | 21 +- vcpkg.json | 7 + 34 files changed, 1397 insertions(+), 704 deletions(-) create mode 100644 Config/Shim.Sample.cfg delete mode 100644 ShellCode/CommonTail.S delete mode 100644 ShellCode/ShellCode.KernelWrapper.S rename {ShellCode => ShimLoader}/CMakeLists.txt (68%) rename {ShellCode => ShimLoader}/DummyHead.S (60%) create mode 100644 ShimLoader/ShimLoader.S create mode 100644 Shims/CMakeLists.txt create mode 100644 Shims/CommonTail.S create mode 100644 Shims/DummyHead.S rename ShellCode/ShellCode.AYAPS.S => Shims/Shim.AYAPS.S (79%) rename ShellCode/ShellCode.Andromeda.S => Shims/Shim.Andromeda.S (85%) rename ShellCode/ShellCode.Cepheus.S => Shims/Shim.Cepheus.S (85%) rename ShellCode/ShellCode.Hotdog.S => Shims/Shim.Hotdog.S (78%) create mode 100644 Shims/Shim.KernelWrapper.S rename ShellCode/ShellCode.Miatoll.S => Shims/Shim.Miatoll.S (88%) rename ShellCode/ShellCode.Nabu.S => Shims/Shim.Nabu.S (83%) rename ShellCode/ShellCode.Pipa.S => Shims/Shim.Pipa.S (76%) delete mode 100644 remover.c create mode 100644 shim.c create mode 100644 shim.h create mode 100644 shim_config.c create mode 100644 vcpkg.json diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 1330f13..1c0de14 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -1,101 +1,142 @@ -name: Build patcher for multi platforms. +name: Build patcher for multiple platforms on: push: - branches: [ "main" ] + branches: [main, feature/multi-boot] pull_request: - branches: [ "main" ] + branches: [main] workflow_dispatch: jobs: build: + name: ${{ matrix.name }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - # Windows x86_64 - - os: windows-latest - arch: x86_64 - c_compiler: cl - cpp_compiler: cl - - # Windows ARM64 - - os: windows-11-arm - arch: aarch64 - c_compiler: cl - cpp_compiler: cl - - # Linux x86_64 - - os: ubuntu-latest - arch: x86_64 + - name: Windows x64 + os: windows-latest + arch: x64 + cmake_arch: x64 + vcpkg_triplet: x64-windows-static + build_shim_loader: 'OFF' + executable_suffix: .exe + executable_directory: Release + - name: Windows ARM64 + os: windows-11-arm + arch: arm64 + cmake_arch: ARM64 + vcpkg_triplet: arm64-windows-static + build_shim_loader: 'OFF' + executable_suffix: .exe + executable_directory: Release + - name: Linux x64 + os: ubuntu-latest + arch: x64 c_compiler: clang - cpp_compiler: clang - - # Linux ARM64 - - os: ubuntu-24.04-arm - arch: aarch64 + build_shim_loader: 'ON' + executable_suffix: '' + executable_directory: . + - name: Linux ARM64 + os: ubuntu-24.04-arm + arch: arm64 c_compiler: clang - cpp_compiler: clang - - # macOS x86_64 - - os: macos-latest - arch: x86_64 + build_shim_loader: 'OFF' + executable_suffix: '' + executable_directory: . + - name: macOS x64 + os: macos-15-intel + arch: x64 c_compiler: clang - cpp_compiler: clang + build_shim_loader: 'OFF' + executable_suffix: '' + executable_directory: . steps: - uses: actions/checkout@v4 - - name: Set reusable strings - id: strings - shell: bash + - name: Install Linux dependencies + if: runner.os == 'Linux' run: | - echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + sudo apt-get update + sudo apt-get install --yes libinih-dev pkg-config - - name: Configure Compiler - if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm' }} - run: | - sudo apt install gcc-aarch64-linux-gnu - echo -e \\nset\(CMAKE_C_FLAGS "--target=${{ matrix.arch }}-linux-gnu"\) \\n >> ${{ github.workspace }}/CMakeLists.txt + - name: Install ShimLoader toolchain + if: matrix.build_shim_loader == 'ON' + run: sudo apt-get install --yes binutils-aarch64-linux-gnu - - name: Configure CMake - run: > - cmake -B ${{ steps.strings.outputs.build-output-dir }} - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + - name: Install macOS dependencies + if: runner.os == 'macOS' + run: brew install inih pkg-config + + - name: Configure CMake on Windows + if: runner.os == 'Windows' + run: >- + cmake -S "${{ github.workspace }}" + -B "${{ github.workspace }}/build" + -A ${{ matrix.cmake_arch }} + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" + -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg_triplet }} + -DBUILD_SHIM_LOADER=${{ matrix.build_shim_loader }} + + - name: Configure CMake on Unix + if: runner.os != 'Windows' + run: >- + cmake -S "${{ github.workspace }}" + -B "${{ github.workspace }}/build" -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} -DCMAKE_BUILD_TYPE=Release - -S ${{ github.workspace }} + -DBUILD_SHIM_LOADER=${{ matrix.build_shim_loader }} + + - name: Build host tools + run: >- + cmake --build "${{ github.workspace }}/build" + --config Release + --target MultiBootKernelPatcher HDRTool - - name: Build Patcher and other tools - if: ${{ matrix.os != 'ubuntu-latest' || matrix.os != 'ubuntu-24.04-arm' }} - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release --target DualBootKernelPatcher DualBootPatchRemover HDRTool + - name: Build ShimLoader + if: matrix.build_shim_loader == 'ON' + run: >- + cmake --build "${{ github.workspace }}/build" + --config Release + --target ShimLoader_BIN Shims - - name: Build ASM Codes - if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm' }} - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release + - name: Stage host tools + shell: bash + run: | + mkdir -p artifacts/tools + cp "build/${{ matrix.executable_directory }}/MultiBootKernelPatcher${{ matrix.executable_suffix }}" artifacts/tools/ + cp "build/${{ matrix.executable_directory }}/HDRTool${{ matrix.executable_suffix }}" artifacts/tools/ - - name: Upload Tools + - name: Upload host tools uses: actions/upload-artifact@v4 with: name: Tools-${{ matrix.os }}-${{ matrix.arch }} - path: | - ${{ steps.strings.outputs.build-output-dir }}/**/DualBootKernelPatcher* - ${{ steps.strings.outputs.build-output-dir }}/**/DualBootPatchRemover* - ${{ steps.strings.outputs.build-output-dir }}/**/HDRTool* + path: artifacts/tools + if-no-files-found: error + + - name: Upload ShimLoader + if: matrix.build_shim_loader == 'ON' + uses: actions/upload-artifact@v4 + with: + name: ShimLoader + path: build/ShimLoader/ShimLoader.bin + if-no-files-found: error - - name: Upload ASM Bins + - name: Upload simplified Shims + if: matrix.build_shim_loader == 'ON' uses: actions/upload-artifact@v4 - if: ${{ matrix.os == 'ubuntu-latest' }} with: - name: Shellcodes - path: | - ${{ steps.strings.outputs.build-output-dir }}/ShellCode/* + name: Shims + path: build/Shims/Shim.*.bin + if-no-files-found: error - - name: Upload Configs + - name: Upload configs + if: matrix.build_shim_loader == 'ON' uses: actions/upload-artifact@v4 - if: ${{ matrix.os == 'ubuntu-latest' }} with: name: Configs - path: | - Config/* + path: Config + if-no-files-found: error diff --git a/.gitignore b/.gitignore index 3526100..a81404b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .idea -ShellCode.Epsilon.S -cmake-build-debug \ No newline at end of file +cmake-build-debug +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 05a82f1..eff603d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,43 @@ cmake_minimum_required(VERSION 3.18) -project(DualBootKernelPatcher C) +project(MultiBootKernelPatcher C) + +option(BUILD_SHIM_LOADER "Build the ARM64 ShimLoader binary" ON) +option(BUILD_SHIMS "Build the simplified ARM64 Shims" ${BUILD_SHIM_LOADER}) + +find_package(unofficial-inih CONFIG QUIET) +if (TARGET unofficial::inih::libinih) + set(INIH_TARGET unofficial::inih::libinih) +else () + find_package(PkgConfig QUIET) + if (PkgConfig_FOUND) + pkg_check_modules(INIH QUIET IMPORTED_TARGET inih) + endif () + if (TARGET PkgConfig::INIH) + set(INIH_TARGET PkgConfig::INIH) + else () + message(FATAL_ERROR + "inih was not found. Install libinih-dev on Debian/Ubuntu, " + "inih-devel on Fedora, or configure CMake with the vcpkg " + "toolchain; vcpkg.json declares the inih dependency.") + endif () +endif () set(CMAKE_C_STANDARD 11) if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_EXE_LINKER_FLAGS "-static") endif () +if (MSVC) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif () # Compile the patcher. -add_executable(DualBootKernelPatcher patcher.c utils.c) -add_executable(DualBootPatchRemover remover.c utils.c) +add_executable(MultiBootKernelPatcher patcher.c shim.c shim_config.c utils.c) +target_link_libraries(MultiBootKernelPatcher PRIVATE ${INIH_TARGET}) add_executable(HDRTool HDRTool.c utils.c) -# Compile Shell Codes with aarch64 gcc. -add_subdirectory(ShellCode) \ No newline at end of file +if (BUILD_SHIM_LOADER) + add_subdirectory(ShimLoader) +endif () +if (BUILD_SHIMS) + add_subdirectory(Shims) +endif () \ No newline at end of file diff --git a/Config/DualBoot.Sm7125.cfg b/Config/DualBoot.Sm7125.cfg index e5aaf19..8ca08bf 100644 --- a/Config/DualBoot.Sm7125.cfg +++ b/Config/DualBoot.Sm7125.cfg @@ -1,2 +1,19 @@ -StackBase=0x9FC00000 -StackSize=0x00300000 \ No newline at end of file +[Pack] +Shim=../build/Shims/Shim.Miatoll.bin +Output=../build/PatchedKernel.Sm7125 +Loader=../build/ShimLoader/ShimLoader.bin +Default=Image-A +Timeout=0 + +[Image-A] +Name=Redmi Note 9S Linux +Path=/path/to/Image +BaseImage=true +Align=0x200000 + +[Image-UEFI] +Name=UEFI +Path=../../firmware/SM7125_EFI.fd +BaseImage=false +CopyTo=0x9FC00000 +CopySizeMax=0x00300000 \ No newline at end of file diff --git a/Config/DualBoot.Sm8150.cfg b/Config/DualBoot.Sm8150.cfg index e5aaf19..5e00d5b 100644 --- a/Config/DualBoot.Sm8150.cfg +++ b/Config/DualBoot.Sm8150.cfg @@ -1,2 +1,19 @@ -StackBase=0x9FC00000 -StackSize=0x00300000 \ No newline at end of file +[Pack] +Shim=../build/Shims/Shim.Hotdog.bin +Output=../build/PatchedKernel.Sm8150 +Loader=../build/ShimLoader/ShimLoader.bin +Default=Image-A +Timeout=0 + +[Image-A] +Name=OnePlus 7T Pro Linux +Path=/path/to/Image +BaseImage=true +Align=0x200000 + +[Image-UEFI] +Name=UEFI +Path=../../firmware/SM8150_EFI.fd +BaseImage=false +CopyTo=0x9FC00000 +CopySizeMax=0x00300000 \ No newline at end of file diff --git a/Config/DualBoot.Sm8250.cfg b/Config/DualBoot.Sm8250.cfg index d8f37a9..3d53a64 100644 --- a/Config/DualBoot.Sm8250.cfg +++ b/Config/DualBoot.Sm8250.cfg @@ -1,2 +1,19 @@ -StackBase=0x9FC00000 -StackSize=0x00300000 +[Pack] +Shim=../build/Shims/Shim.Pipa.bin +Output=../build/PatchedKernel.Sm8250 +Loader=../build/ShimLoader/ShimLoader.bin +Default=Image-A +Timeout=0 + +[Image-A] +Name=Xiaomi Pad 6 Linux +Path=/path/to/Image +BaseImage=true +Align=0x200000 + +[Image-UEFI] +Name=UEFI +Path=../../firmware/SM8250_EFI.fd +BaseImage=false +CopyTo=0x9FC00000 +CopySizeMax=0x00300000 diff --git a/Config/DualBoot.Sm8250DT.cfg b/Config/DualBoot.Sm8250DT.cfg index e5aaf19..2a84186 100644 --- a/Config/DualBoot.Sm8250DT.cfg +++ b/Config/DualBoot.Sm8250DT.cfg @@ -1,2 +1,20 @@ -StackBase=0x9FC00000 -StackSize=0x00300000 \ No newline at end of file +[Pack] +Shim=../build/Shims/Shim.KernelWrapper.bin +Output=../build/PatchedKernel.Sm8250DT +Loader=../build/ShimLoader/ShimLoader.bin +Default=Image-A +Timeout=0 + +[Image-A] +Name=SM8250 Mainline Linux +Path=/path/to/Image +BaseImage=true +Align=0x200000 + +[Image-DTB] +Name=Mainline Device Tree +Path=../../firmware/mainline.dtb +BaseImage=false +CopyTo=0x9FC00000 +CopySizeMax=0x00300000 +Align=8 \ No newline at end of file diff --git a/Config/DualBoot.Sm8550.cfg b/Config/DualBoot.Sm8550.cfg index eeb7b95..795065d 100644 --- a/Config/DualBoot.Sm8550.cfg +++ b/Config/DualBoot.Sm8550.cfg @@ -1,2 +1,19 @@ -StackBase=0xC7CC0000 -StackSize=0x00400000 \ No newline at end of file +[Pack] +Shim=../build/Shims/Shim.AYAPS.bin +Output=../build/PatchedKernel.Sm8550 +Loader=../build/ShimLoader/ShimLoader.bin +Default=Image-A +Timeout=0 + +[Image-A] +Name=AYANEO Pocket S Linux +Path=/path/to/Image +BaseImage=true +Align=0x200000 + +[Image-UEFI] +Name=UEFI +Path=../../firmware/SM8550_EFI.fd +BaseImage=false +CopyTo=0xC7CC0000 +CopySizeMax=0x00400000 \ No newline at end of file diff --git a/Config/Shim.Sample.cfg b/Config/Shim.Sample.cfg new file mode 100644 index 0000000..5dd1504 --- /dev/null +++ b/Config/Shim.Sample.cfg @@ -0,0 +1,30 @@ +[Pack] +# Paths are resolved relative to this configuration file. +Shim=/path/to/shim.bin +Output=../build/ShimImage +Loader=../build/ShimLoader/ShimLoader.bin + +# Default references an Image section. Timeout=0 waits indefinitely. +Default=Image-A +Timeout=5000 + +[Image-A] +Name=Android Linux +Path=/path/to/base-Image +BaseImage=true +Align=0x200000 + +[Image-B] +Name=Recovery Linux +Path=/path/to/recovery-Image +BaseImage=false +Align=0x200000 + +# Images with CopyTo are copied to that address and booted from it. +# [Image-UEFI] +# Name=UEFI +# Path=../../firmware/UEFI.fd +# BaseImage=false +# CopyTo=0x9FC00000 +# CopySizeMax=0x00300000 +# Align defaults to 4 for UEFI-style executable payloads. \ No newline at end of file diff --git a/README.md b/README.md index 0d7cc71..0199ff0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ -# Dual Boot Kernel Patcher -> Based On [SurfaceDuoDualBootKernelImagePatcher +# Multi-Boot Kernel Patcher +> Inspired by [SurfaceDuoDualBootKernelImagePatcher ](https://github.com/WOA-Project/SurfaceDuoDualBootKernelImagePatcher) +Multi-Boot Kernel Patcher packages a base ARM64 Linux Image, a compatible +Shim runtime, and multiple bootable images from one INI configuration. + ## Build - Preparation + A computer with Windows or Linux @@ -9,13 +12,27 @@ + aarch64 GNU Assembler + Git + CMake + + - Install dependencies + + Linux (Debian / Ubuntu) + ```sh + sudo apt install build-essential cmake gcc-aarch64-linux-gnu git \ + libinih-dev pkg-config + ``` + + Windows + Install and integrate [vcpkg](https://github.com/microsoft/vcpkg). The + included `vcpkg.json` declares `inih` as one of the project dependencies. + ```powershell + cmake -B output -S . ` + -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" + ``` - Clone this repo ``` git clone https://github.com/Project-Aloha/DualBootKernelPatcher ``` - Setup CMake. ``` - cd DualBootKernelPacther + cd MultiBootKernelPatcher cmake -B output -S . ``` - Build! @@ -23,19 +40,17 @@ cmake --build output -j 12 ``` ## Usage - - Common usage. - ``` - DualBootKernelPatcher - ``` - - Example - ``` - DualBootKernelPatcher kernel SM8150_EFI.fd PacthedKernel DualBoot.Sm8150.cfg ShellCode.Hotdog.bin - ``` - - Notice - + Shell Code binaries can be find under `output/ShellCode/` - + - Copy and edit configuration file, fill paths in it. + - Pack an image from the configuration file. + ```sh + MultiBootKernelPatcher Config/Shim.Sample.cfg + ``` + - Notice + + ShimLoader binaries are generated under `output/ShimLoader/`. + + Simplified assembly Shim binaries are generated under `output/Shims/`. + ## See More - - You can go to our [document website](https://aloha.firmware.icu/) to get more infomation about the Dual Boot Patcher. + - You can go to our [document website](https://aloha.firmware.icu/) to get more infomation about the Multi-Boot Kernel Patcher. ## License MIT License. diff --git a/ShellCode/CommonTail.S b/ShellCode/CommonTail.S deleted file mode 100644 index 48120b7..0000000 --- a/ShellCode/CommonTail.S +++ /dev/null @@ -1,38 +0,0 @@ -// DO NOT EDIT THIS FILE -// -// Instructions handles common behaviour after compare. -// 1. Provide _Linux label, if w5 != 0 jmp to linux kernel. -// 2. Provide _UEFI label, if x5 == 0 jmp to UEFI. -// 3. Provide _CopyLoop function, to copy memory from end of kernel to UEFI Stack Region. -// 4. Dead Loop if something wrong. - -_Linux: - b _LinuxStart // if w5 != 0, continue booting linux kernel. - -_UEFI: - adr x4, _KernelHead // Store kernel head address in x4. - ldr x5, _KernelSize // Store kernel size in x5. - add x4, x4, x5 // Add kernel base + kernel size, store value in x4. - ldr x5, _StackBase // Store FD Base in x5. - ldr x6, _StackSize // Store FD Size in x6. - bl _CopyLoop // Copy UEFI FD to stack region. - adr x0, _ShellCodeEnd // Store address of ShellCode end to x0. - adr x1, _ShellCodeEnd // Store address of ShellCode end to x1. - ldr x5, _StackBase // Store stack base address in x5. - br x5 // Jump to UEFI. - -_CopyLoop: - ldp x2, x3, [x4], #0x10 // Save value at [x4](pointer) to x2 and x3, then x4 add 16. - stp x2, x3, [x5], #0x10 // Save value in x2 and x3 to [x5](pointer), then x5 add 16 - subs x6, x6, #0x10 // x6 - 16, if , set CPSR register to 0. - b.ne _CopyLoop // Check CPSR, if CPSR != 0, jump back to _CopyLoop. - ret // Return when finish. - -_Dead: - b _Dead // We should never get here. - -.text -.align 4 - -_ShellCodeEnd: -/* Do not remove the last line */ diff --git a/ShellCode/ShellCode.KernelWrapper.S b/ShellCode/ShellCode.KernelWrapper.S deleted file mode 100644 index 61e78ad..0000000 --- a/ShellCode/ShellCode.KernelWrapper.S +++ /dev/null @@ -1,73 +0,0 @@ -/** -* A Wrapper for Linux Kernel to by pass android dtb check -* -* Qualcomm application bootloader (ABL) will check msm-id/board-id -* or apply overlay before booting kernel. By replacing android dtb with linux dtb, -* abl will failed to check or apply dtbo and then refuse to boot. -* This wrapper is used to bypass the check. -* -* Scheme: -* Only replace kernel instead of replacing dtb. But inject the dtb in kernel -* -* Step: -* Inject this wrapper into kernel header and make a payload with linux kernel + linux dtb. -* Then, repack the android boot image with the payload, but not replace dtb. -* Linux will boot successfully then. -* -* Note: -* You need to add memory region for linux dtb in device tree manually -* otherwise linux will NOT boot. check the sample below, you can find -* it in android fdt at /sys/firmware/fdt. -* memory { -* ddr_device_type = <0x08>; -* device_type = "memory"; -* #address-cells = <2>; -* #size-cells = <2>; -* reg = <0x0 0x80000000 0x0 0x3b800000>, -* <0x1 0x80000000 0x1 0x00000000>, -* <0x0 0xc0000000 0x0 0xc0000000>; -* }; -* -* Usage: -* ./DualBootKernelPatcher OriginalKernel mainline_dtb output DualBoot.Sm8250DT.cfg ShellCode.KernelWrapper.bin -* Then repack android image with the output file. -* -* Inspired by @bigfootACA -* -*/ - -/* Dummy Header for shellcode */ -.include "DummyHead.S" - -_ShellCodeStart: - // Calculate UEFI FD(dtb addr here) start address and store in X4 - adr x4, _KernelHead // Store kernel head address in x4. - ldr x5, _KernelSize // Store kernel size in x5. - add x4, x4, x5 // Add kernel base + kernel size, store value in x4. - - // Copy dtb to safe place (StackRegion, you can configure it freely in DualBoot config file) - ldr x5, _StackBase // Store FD Base in x5. - ldr x6, _StackSize // Store FD Size in x6. - bl _CopyLoop // Copy DTB to stack region. - - // Set X0 to StackBase, which is the new DTB address - ldr x0, _StackBase // Store stack base address in x5. - b _LinuxStart // Boot linux kernel. - b _Dead // We should never get here. - -// Copy Sub program, X4 is src, X5 is dst, X6 is size -_CopyLoop: - ldp x7, x8, [x4], #0x10 // Save value at [x4](pointer) to x7 and x8, then x4 add 16. - stp x7, x8, [x5], #0x10 // Save value in x7 and x8 to [x5](pointer), then x5 add 16 - subs x6, x6, #0x10 // x6 - 16, if , set CPSR register to 0. - b.ne _CopyLoop // Check CPSR, if CPSR != 0, jump back to _CopyLoop. - ret // Return when finish. - -_Dead: - b _Dead // We should never get here. - -.text -.align 4 - -_ShellCodeEnd: -/* Do not remove the last line */ diff --git a/ShellCode/CMakeLists.txt b/ShimLoader/CMakeLists.txt similarity index 68% rename from ShellCode/CMakeLists.txt rename to ShimLoader/CMakeLists.txt index 325a847..6dd4bdd 100644 --- a/ShellCode/CMakeLists.txt +++ b/ShimLoader/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.27) -project(ShellCode ASM) +project(ShimLoader ASM) # Set up compilers if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") @@ -17,26 +17,27 @@ endif () set(CMAKE_AS ${CROSS_COMPILE}as) set(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy) -set(SHELLCODE_ELF_DIR "${CMAKE_CURRENT_BINARY_DIR}/elf") -file(MAKE_DIRECTORY ${SHELLCODE_ELF_DIR}) +set(SHIM_LOADER_ELF_DIR "${CMAKE_CURRENT_BINARY_DIR}/elf") +file(MAKE_DIRECTORY ${SHIM_LOADER_ELF_DIR}) -# Compile ShellCode one by one. -file(GLOB ASM_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "ShellCode.*.S") +# Compile the generic ShimLoader. +set(ASM_SOURCES ShimLoader.S) foreach (CURRENT_ASM_SOURCE_SUFFIX ${ASM_SOURCES}) - string(REGEX REPLACE "(ShellCode.*)\\.S" "\\1" CURRENT_ASM_SOURCE ${CURRENT_ASM_SOURCE_SUFFIX}) + string(REGEX REPLACE "\\.S$" "" CURRENT_ASM_SOURCE + "${CURRENT_ASM_SOURCE_SUFFIX}") add_custom_target( ${CURRENT_ASM_SOURCE}_ELF ALL ${CMAKE_AS} "${CMAKE_CURRENT_SOURCE_DIR}/${CURRENT_ASM_SOURCE}.S" -I ${CMAKE_CURRENT_SOURCE_DIR} - -o "${SHELLCODE_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" + -o "${SHIM_LOADER_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" ) add_custom_target( ${CURRENT_ASM_SOURCE}_BIN ALL ${CMAKE_OBJCOPY} -O binary - "${SHELLCODE_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" + "${SHIM_LOADER_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" "${CURRENT_ASM_SOURCE}.bin" DEPENDS ${CURRENT_ASM_SOURCE}_ELF ) diff --git a/ShellCode/DummyHead.S b/ShimLoader/DummyHead.S similarity index 60% rename from ShellCode/DummyHead.S rename to ShimLoader/DummyHead.S index 4beebe6..5b1c2dd 100644 --- a/ShellCode/DummyHead.S +++ b/ShimLoader/DummyHead.S @@ -6,26 +6,28 @@ // Size of this file must be 0x40 after compiling to binary. _KernelHead: - b 0x40 // Code 1, Goto ShellCode + b 0x40 // Code 1, go to ShimLoader _LinuxStart: b 0x0 // Code2, Goto Linux Kernel - .ascii "SHLL" // Image load offset, use it as shell code magic here - .ascii "COD\0" // Image load offset, use it as shell code magic here + .ascii "SHIM" // ShimLoader magic + .ascii "LDR\0" .long 0 // Effective Image size L .long 0 // Effective Image size H .long 0 // Flags L .long 0 // Flags H -_StackBase: - .long 0 // reserved2, Stack Base LWORD - .long 0 // reserved2, Stack Base HWORD +_CopyAddress: +_ManifestOffset: + .long 0 // reserved2 LWORD + .long 0 // reserved2 HWORD -_StackSize: - .long 0 // reserved3, Stack Size L - .long 0 // reserved3, Stack Size H +_CopySize: +_ManifestSize: + .long 0 // reserved3 LWORD + .long 0 // reserved3 HWORD -_KernelSize: +_BaseImageSize: .long 0 // reserved4, Kernel Size L .long 0 // reserved4, Kernel Size H diff --git a/ShimLoader/ShimLoader.S b/ShimLoader/ShimLoader.S new file mode 100644 index 0000000..9faf87f --- /dev/null +++ b/ShimLoader/ShimLoader.S @@ -0,0 +1,12 @@ +.include "DummyHead.S" + +_ShimLoaderStart: + adr x4, _KernelHead + ldr x5, _ManifestOffset + add x5, x4, x5 + ldr x6, _ManifestSize + ldr x7, _BaseImageSize + add x7, x4, x7 + br x7 + +_ShimLoaderEnd: diff --git a/Shims/CMakeLists.txt b/Shims/CMakeLists.txt new file mode 100644 index 0000000..a001820 --- /dev/null +++ b/Shims/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.27) +project(Shims ASM) + +# Set up compilers +if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + # Windows + # MinGW compiler download link: + # https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads + set(CROSS_COMPILE "E:/aarch64-gcc/aarch64-none-elf/bin/") +elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + # Linux + set(CROSS_COMPILE "aarch64-linux-gnu-") +else () + message("build on ${CMAKE_HOST_SYSTEM_NAME} is not supported") +endif () + +set(CMAKE_AS ${CROSS_COMPILE}as) +set(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy) + +set(SHIM_ELF_DIR "${CMAKE_CURRENT_BINARY_DIR}/elf") +file(MAKE_DIRECTORY ${SHIM_ELF_DIR}) + +# Compile each simplified Shim. +file(GLOB ASM_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "Shim.*.S") +set(SHIM_BIN_TARGETS) +foreach (CURRENT_ASM_SOURCE_SUFFIX ${ASM_SOURCES}) + string(REGEX REPLACE "\\.S$" "" CURRENT_ASM_SOURCE + "${CURRENT_ASM_SOURCE_SUFFIX}") + + add_custom_target( + ${CURRENT_ASM_SOURCE}_ELF ALL + ${CMAKE_AS} + "${CMAKE_CURRENT_SOURCE_DIR}/${CURRENT_ASM_SOURCE}.S" + -I ${CMAKE_CURRENT_SOURCE_DIR} + -o "${SHIM_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" + ) + + add_custom_target( + ${CURRENT_ASM_SOURCE}_BIN ALL + ${CMAKE_OBJCOPY} -O binary + "${SHIM_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" + "${CURRENT_ASM_SOURCE}.bin" + DEPENDS ${CURRENT_ASM_SOURCE}_ELF + ) + list(APPEND SHIM_BIN_TARGETS ${CURRENT_ASM_SOURCE}_BIN) +endforeach () + +add_custom_target(Shims DEPENDS ${SHIM_BIN_TARGETS}) diff --git a/Shims/CommonTail.S b/Shims/CommonTail.S new file mode 100644 index 0000000..fdaafdd --- /dev/null +++ b/Shims/CommonTail.S @@ -0,0 +1,76 @@ +// Shared implementation for simplified assembly Shims. The ShimLoader ABI +// supplies x4 = base Linux and x5/x6 = manifest address/size; simplified +// Shims intentionally ignore x5/x6 and use direct header parameters instead. + +_Linux: + mov x0, x19 + mov x1, x20 + mov x2, x21 + mov x3, x22 + mov x8, x23 + add x8, x8, #4 + br x8 + +_UEFI: + ldr x8, _PayloadOffset + add x8, x23, x8 + ldr x9, _CopyAddress + ldr x10, _CopySize + mov x11, x9 + mov x12, x10 + bl _CopyLoop + mov x0, x11 + mov x1, x12 + bl _SyncImageCache + adr x0, _ShimImageEnd + adr x1, _ShimImageEnd + br x11 + +_CopyLoop: + cbz x10, 2f +1: + ldrb w13, [x8], #1 + strb w13, [x9], #1 + subs x10, x10, #1 + b.ne 1b +2: + ret + +// x0 = destination, x1 = size +_SyncImageCache: + mrs x2, ctr_el0 + ubfx x3, x2, #16, #4 + mov x5, #4 + lsl x3, x5, x3 + sub x6, x3, #1 + bic x5, x0, x6 + add x6, x0, x1 +3: + dc cvau, x5 + add x5, x5, x3 + cmp x5, x6 + b.lo 3b + dsb ish + + and x3, x2, #0xf + mov x5, #4 + lsl x3, x5, x3 + sub x7, x3, #1 + bic x5, x0, x7 +4: + ic ivau, x5 + add x5, x5, x3 + cmp x5, x6 + b.lo 4b + dsb ish + isb + ret + +_Dead: + b _Dead + +.text +.align 4 + +_ShimImageEnd: +/* Do not remove the last line */ diff --git a/Shims/DummyHead.S b/Shims/DummyHead.S new file mode 100644 index 0000000..44aea95 --- /dev/null +++ b/Shims/DummyHead.S @@ -0,0 +1,24 @@ +// Simplified Shim Image header. The packer writes direct payload parameters +// here; these Shims do not parse the manifest blob. + +_ShimHead: + b _ShimStart + + nop + .ascii "SHIMASM\0" + .quad _ShimImageEnd - _ShimHead + .quad 0 + +_PayloadOffset: + .quad 0 + +_CopyAddress: + .quad 0 + +_CopySize: + .quad 0 + + .ascii "ARM\x64" + .long 0 + +/* Do not remove the last line */ diff --git a/ShellCode/ShellCode.AYAPS.S b/Shims/Shim.AYAPS.S similarity index 79% rename from ShellCode/ShellCode.AYAPS.S rename to Shims/Shim.AYAPS.S index 8bce62d..be8d405 100644 --- a/ShellCode/ShellCode.AYAPS.S +++ b/Shims/Shim.AYAPS.S @@ -1,7 +1,12 @@ .include "DummyHead.S" -/* Shell Code for Ayaneo Pocket S */ -_ShellCodeStart: +/* Simplified Shim for Ayaneo Pocket S */ +_ShimStart: + mov x19, x0 + mov x20, x1 + mov x21, x2 + mov x22, x3 + mov x23, x4 movz x4, #0xd000 movk x4, #0x0f16, lsl #16 // Store 0xf16d000 to x4, which is io control of gpio109 on sm8550. str wzr, [x4], #4 // Set 0 in control reg, then x4 add 4 diff --git a/ShellCode/ShellCode.Andromeda.S b/Shims/Shim.Andromeda.S similarity index 85% rename from ShellCode/ShellCode.Andromeda.S rename to Shims/Shim.Andromeda.S index 0ec2a86..0cb264e 100644 --- a/ShellCode/ShellCode.Andromeda.S +++ b/Shims/Shim.Andromeda.S @@ -1,7 +1,12 @@ .include "DummyHead.S" -/* Shell Code for Xiaomi Mix3 5G */ -_ShellCodeStart: +/* Simplified Shim for Xiaomi Mix3 5G */ +_ShimStart: + mov x19, x0 + mov x20, x1 + mov x21, x2 + mov x22, x3 + mov x23, x4 movz x4, #0x9000 movk x4, #0x392, lsl #16 // Store 0x3929000 to x4, which is io control of gpio 41. ldr w5, [x4] diff --git a/ShellCode/ShellCode.Cepheus.S b/Shims/Shim.Cepheus.S similarity index 85% rename from ShellCode/ShellCode.Cepheus.S rename to Shims/Shim.Cepheus.S index ef966ad..6582b2c 100644 --- a/ShellCode/ShellCode.Cepheus.S +++ b/Shims/Shim.Cepheus.S @@ -1,7 +1,12 @@ .include "DummyHead.S" -/* Shell Code for Xiaomi 9 */ -_ShellCodeStart: +/* Simplified Shim for Xiaomi 9 */ +_ShimStart: + mov x19, x0 + mov x20, x1 + mov x21, x2 + mov x22, x3 + mov x23, x4 movz x4, #0x1000 movk x4, #0x396, lsl #16 // Store 0x3961004 to x4, which is io status reg of gpio 97. ldr w5, [x4] diff --git a/ShellCode/ShellCode.Hotdog.S b/Shims/Shim.Hotdog.S similarity index 78% rename from ShellCode/ShellCode.Hotdog.S rename to Shims/Shim.Hotdog.S index df880d1..4b8ece4 100644 --- a/ShellCode/ShellCode.Hotdog.S +++ b/Shims/Shim.Hotdog.S @@ -1,7 +1,12 @@ .include "DummyHead.S" -/* Shell Code for Oneplus 7T Pro */ -_ShellCodeStart: +/* Simplified Shim for Oneplus 7T Pro */ +_ShimStart: + mov x19, x0 + mov x20, x1 + mov x21, x2 + mov x22, x3 + mov x23, x4 movz x4, #0xd000 movk x4, #0x317, lsl #16 // Store 0x317d004 to x4, which is control reg of gpio 125. str wzr, [x4], #4 // Set 0 in control reg, then x4 add 4 diff --git a/Shims/Shim.KernelWrapper.S b/Shims/Shim.KernelWrapper.S new file mode 100644 index 0000000..953e9d7 --- /dev/null +++ b/Shims/Shim.KernelWrapper.S @@ -0,0 +1,65 @@ +/* + * Simplified assembly Shim for bypassing Android DTB compatibility checks. + * The packer writes payload offset, CopyTo and actual payload size directly + * into this Shim Image header. No manifest parsing is performed here. + */ +.include "DummyHead.S" + +_ShimStart: + mov x19, x0 + mov x20, x1 + mov x21, x2 + mov x22, x3 + mov x23, x4 + + ldr x8, _PayloadOffset + add x8, x23, x8 + ldr x9, _CopyAddress + ldr x10, _CopySize + mov x11, x9 + mov x12, x10 + bl _CopyLoop + + mov x0, x11 + mov x1, x12 + bl _SyncDataCache + + mov x0, x11 + mov x1, x20 + mov x2, x21 + mov x3, x22 + add x8, x23, #4 + br x8 + +_CopyLoop: + cbz x10, 2f +1: + ldrb w13, [x8], #1 + strb w13, [x9], #1 + subs x10, x10, #1 + b.ne 1b +2: + ret + +// x0 = destination, x1 = size +_SyncDataCache: + mrs x2, ctr_el0 + ubfx x3, x2, #16, #4 + mov x4, #4 + lsl x3, x4, x3 + sub x5, x3, #1 + bic x4, x0, x5 + add x5, x0, x1 +3: + dc cvac, x4 + add x4, x4, x3 + cmp x4, x5 + b.lo 3b + dsb sy + ret + +.text +.align 4 + +_ShimImageEnd: +/* Do not remove the last line */ diff --git a/ShellCode/ShellCode.Miatoll.S b/Shims/Shim.Miatoll.S similarity index 88% rename from ShellCode/ShellCode.Miatoll.S rename to Shims/Shim.Miatoll.S index a72c640..ba3302a 100644 --- a/ShellCode/ShellCode.Miatoll.S +++ b/Shims/Shim.Miatoll.S @@ -1,7 +1,12 @@ .include "DummyHead.S" -/* Shell Code for Redmi Note 9S */ -_ShellCodeStart: +/* Simplified Shim for Redmi Note 9S */ +_ShimStart: + mov x19, x0 + mov x20, x1 + mov x21, x2 + mov x22, x3 + mov x23, x4 movz x4, #0x6000 movk x4, #0xE60, lsl #16 // Store 0xE606000 to x4, which is address of 0xC0 spmi channel movz x5, #0x0081 diff --git a/ShellCode/ShellCode.Nabu.S b/Shims/Shim.Nabu.S similarity index 83% rename from ShellCode/ShellCode.Nabu.S rename to Shims/Shim.Nabu.S index 34032b7..a15d289 100644 --- a/ShellCode/ShellCode.Nabu.S +++ b/Shims/Shim.Nabu.S @@ -1,7 +1,12 @@ .include "DummyHead.S" -/* Shell Code for Xiaomi Pad 5 Pro */ -_ShellCodeStart: +/* Simplified Shim for Xiaomi Pad 5 Pro */ +_ShimStart: + mov x19, x0 + mov x20, x1 + mov x21, x2 + mov x22, x3 + mov x23, x4 movz x4, #0x9004 movk x4, #0x390, lsl #16 // Store 0x3909004 to x4, which is io status reg of gpio 9. movz x5, #0x3004 diff --git a/ShellCode/ShellCode.Pipa.S b/Shims/Shim.Pipa.S similarity index 76% rename from ShellCode/ShellCode.Pipa.S rename to Shims/Shim.Pipa.S index 9960131..8b5b05c 100644 --- a/ShellCode/ShellCode.Pipa.S +++ b/Shims/Shim.Pipa.S @@ -1,7 +1,12 @@ .include "DummyHead.S" -/* Shell Code for Xiaomi Pad 6 (Pipa) */ -_ShellCodeStart: +/* Simplified Shim for Xiaomi Pad 6 (Pipa) */ +_ShimStart: + mov x19, x0 + mov x20, x1 + mov x21, x2 + mov x22, x3 + mov x23, x4 movz x4, #0xe004 movk x4, #0xf96, lsl #16 // Store 0xf96E004 to x4, which is address of GPIO 110. ldr w6, [x4] // Get status value from address stored in x4 and store it to w6. diff --git a/patcher.c b/patcher.c index d8fa704..defdf9b 100644 --- a/patcher.c +++ b/patcher.c @@ -1,297 +1,21 @@ /** @file - * DualBoot Kernel Patcher Source File. - * - * This Program will help you inject shell code into header of linux kernel. - * - * It only supports several formats of kernel: - * 1. Image file compile from source - * 2. Qualcomm patched kernel. - * 3. Image file kernel with efi stub. - * - * Copyright (c) 2021-2025 The DuoWoa authors. All rights reserved. - * MIT License + * Multi-Boot Kernel Patcher entry point. * + * Copyright (c) 2021-2025 The DuoWoa authors. All rights reserved. + * MIT License */ -#include "utils.h" +#include "shim.h" + +#include +#include -/** - * The main function will check and read given files, - * pass it to patch function, and write patched kernel to file. - * - * @param argc argc is numbers of argues given in cmdline. - * @param argv argv is a array that contains all given values. - * - * @retval -EINVAL Given files are not found or format not match. - * - */ int main(int argc, char *argv[]) { - // Print hello message. - printf("Project Aloha DualBoot Kernel Image Patcher v1.2.0.0\n"); + printf("Project Aloha Multi-Boot Kernel Patcher v1.2.0.0\n"); printf("Copyright (c) 2021-2025 The DuoWoA authors\n\n"); - if (argc != 6) { - // Print usage if arg numbers not meet. - printf("Usage: " - " \n"); + if (argc != 2) { + printf("Usage: MultiBootKernelPatcher \n"); return -EINVAL; } - - // Get file paths. - FileContent originImage = {.filePath = argv[1]}; - FileContent uefiImage = {.filePath = argv[2]}; - FileContent outputImage = {.filePath = argv[3]}; - FileContent config = {.filePath = argv[4]}; - FileContent shellCode = {.filePath = argv[5]}; - - // Print processing msg. - printf("Patching %s with %s and saving to %s...\n\n", originImage.filePath, - uefiImage.filePath, - outputImage.filePath); - - // Read buffer from old kernel. - if (!get_file_size(&originImage)) - return -EINVAL; - originImage.fileBuffer = malloc(originImage.fileSize); - read_file_content(&originImage); - - // Read buffer from uefi image. - if (!get_file_size(&uefiImage)) - return -EINVAL; - uefiImage.fileBuffer = malloc(uefiImage.fileSize); - read_file_content(&uefiImage); - - // Parse config file. - Config stack = {0}; - if (parse_config(&config, &stack)) { - printf("Error: Parse config failed\n"); - return -EINVAL; - } - - // Get ShellCode buffer. - if (!get_file_size(&shellCode)) - return -EINVAL; - shellCode.fileBuffer = malloc(shellCode.fileSize); - read_file_content(&shellCode); - - // Patch Kernel. - void *buf = PatchKernel(&originImage, &uefiImage, &shellCode, &outputImage, &stack); - - // Output buffer to new kernel. - if (outputImage.fileBuffer != NULL && buf != NULL) { - write_file_content(&outputImage); - } else { - printf("Error Patching Kernel.\n"); - return -EINVAL; - } - - // Free buffers we allocated. - free(shellCode.fileBuffer); - free(originImage.fileBuffer); - free(uefiImage.fileBuffer); - free(outputImage.fileBuffer); - - // Print end message. - printf("Image successfully patched.\n"); - printf("Please check the patched kernel image at %s.\n", outputImage.filePath); - return 0; -} - - -/** - * Patch kernel based on given fileContents, - * - * @param[in] kernel origin kernel fileContent - * @param[in] uefi uefi fd fileContent - * @param[in] shellCode shell code binary - * @param[in,out] patchedKernel patched kernel fileContent - * @param[in] config config info read from config - * - * @return patched kernel buffer - * - */ -uint8_t *PatchKernel(pFileContent kernel, pFileContent uefi, pFileContent shellCode, - pFileContent patchedKernel, pConfig config) { - // Allocate output buffer - patchedKernel->fileSize = kernel->fileSize + uefi->fileSize; - patchedKernel->fileBuffer = malloc(patchedKernel->fileSize); - - // Copy two buffers into patchedBuffer. - memcpy(patchedKernel->fileBuffer, kernel->fileBuffer, kernel->fileSize); - memcpy(patchedKernel->fileBuffer + kernel->fileSize, uefi->fileBuffer, uefi->fileSize); - - // Check ShellCode binary magic. - if (shellCode->fileSize > 0x40 && strcmp((char *) (shellCode->fileBuffer + 8), "SHLLCOD") != 0) { - printf("Error: shell code binary format not recognize.\n"); - return NULL; - } - - // Check if kernel has UNCOMPRESSED_IMG Header. - char kernelHeader[0x11] = {0}; - uint8_t hasHeader = 0; - memcpy(kernelHeader, kernel->fileBuffer, 0x10); - if (strcmp(kernelHeader, "UNCOMPRESSED_IMG") == 0) { - printf("Kernel has UNCOMPRESSED_IMG header.\n"); - kernel->fileSize -= 0x14; // The magic is not part of kernel. - hasHeader |= 0b1; - } - - // Check if kernel size % 0x10 == 0, which will cause copy loop(in shellcode) issue. - if (kernel->fileSize % 0x10) { - printf("Align kernel size to 0x10.\n"); - - // Calculate align padding - uint8_t padding = 0x10 - (kernel->fileSize % 0x10); - - // New kernel size and output size - kernel->fileSize += padding; - patchedKernel->fileSize += padding; - - // Reallocate patched kernel. - void *ptr = realloc(patchedKernel->fileBuffer, patchedKernel->fileSize); - if (ptr == NULL) { - printf("Failed to reallocate patched kernel buffer."); - return NULL; - } else patchedKernel->fileBuffer = ptr; - - // Copy uefi image to 0x10 align place and fill padding part to 0. - if (hasHeader & 0b1) // Kernel has header, need to add 0x14 offset while copying. - { - memmove(patchedKernel->fileBuffer + kernel->fileSize + 0x14, - patchedKernel->fileBuffer + kernel->fileSize - padding + 0x14, uefi->fileSize); - memset(patchedKernel->fileBuffer + kernel->fileSize - padding + 0x14, 0, padding); - } else { - memmove(patchedKernel->fileBuffer + kernel->fileSize, - patchedKernel->fileBuffer + kernel->fileSize - padding, uefi->fileSize); - memset(patchedKernel->fileBuffer + kernel->fileSize - padding, 0, padding); - } - } - - // Kernel has uncompressed_img header, - if (hasHeader & 1) - // Move our pointer after UNCOMPRESSED_IMG header before further processing, - patchedKernel->fileBuffer += 0x14; - - /* Reserved for feature use */ - // Check if kernel was already patched. - // if config in kernel was same with the config provided here, - // set kernel size to previous kernel size. (which skipped copying previously uefi fd) - // if (*(uint64_t *) (kernel->fileBuffer + 0x20) == config->StackBase && - // *(uint64_t *) (kernel->fileBuffer + 0x28) == config->StackSize) { - // printf("Kernel was already patched previously!\n" - // "Re-patching with new UEFI...\n"); - // kernel->fileSize = *(uint64_t *) (kernel->fileBuffer + 0x30); - // patchedKernel->fileSize = kernel->fileSize + uefi->fileSize; // Update output kernel size - // printf("Old kernel size %llx\n", *(uint64_t *) (kernel->fileBuffer + 0x30)); - // hasHeader |= 0b10; - // } - - // Determine the loading offset of the kernel first, - // we are either going to find a b instruction on the - // first instruction or the second one. First is problematic, - // second is fine. - - // 0x14 is AArch64 b opcode - // If Code1 is jump instruction, the kernel is an original kernel or a patched kernel. - // However, if Code2 is also jump instruction, the kernel should be a patched kernel. - if (patchedKernel->fileBuffer[3] == 0x14 && patchedKernel->fileBuffer[7] != 0x14) { - // For kernel without EFI stub, we have to move the jump kernel instruction forward to Code2. - // We have a branch instruction first, we need to fix things a bit. - - // First start by getting the actual value of the branch addr offset. - uint8_t offsetInstructionBuffer[] = {patchedKernel->fileBuffer[0], patchedKernel->fileBuffer[1], - patchedKernel->fileBuffer[2], 0}; - uint32_t *offsetInstruction = (uint32_t *) offsetInstructionBuffer; - - // Now subtract 1 instruction because we'll move this to the second instruction of the kernel header. - (*offsetInstruction)--; - - // Convert back into an actual value that is usable as a b instruction - offsetInstructionBuffer[3] = 0x14; // Useless but just for our sanity :) - - // Now write the instruction back into the kernel (instr 2) - patchedKernel->fileBuffer[4] = offsetInstructionBuffer[0]; - patchedKernel->fileBuffer[5] = offsetInstructionBuffer[1]; - patchedKernel->fileBuffer[6] = offsetInstructionBuffer[2]; - patchedKernel->fileBuffer[7] = 0x14; - } else // Patched Kernel - if (patchedKernel->fileBuffer[3] == 0x14 && patchedKernel->fileBuffer[7] == 0x14) { - printf("Patched kernel detected, redo patch with new fd.\n"); - kernel->fileSize = *(uint64_t *) (kernel->fileBuffer + 0x30); - patchedKernel->fileSize = kernel->fileSize + uefi->fileSize; // Update output kernel size - } else // Kernel with EFI Stub - if (*(uint16_t *) (patchedKernel->fileBuffer) == 0x5A4D && patchedKernel->fileBuffer[7] == 0x14) { - // Check if kernel has EFI Stub, 4D5A, "MZ" - // Note: - // The magic is also an arm64 instruction, and the second instruction jump to kernel. - // We will only rewrite EFI header here. (This will break the kernel) - // For kernel with EFI stub, we only need to patch the efi header. - printf("Kernel has EFI header.\n"); - } else // Unknown stuff - if (patchedKernel->fileBuffer[7] != 0x14) { - // There is no branch instruction! - printf("Error: Invalid Kernel Image. Branch instruction not found within first two instruction slots.\n" - "Please Check the kernel you provided.\n"); - return NULL; - } - - // Alright, our kernel image has a compatible branch instruction, let's start. - - // First, add the jump to our code on instr 1 - // This directly jump right after the kernel header (there's enough headroom here) - patchedKernel->fileBuffer[0] = 0x10; - patchedKernel->fileBuffer[1] = 0; - patchedKernel->fileBuffer[2] = 0; - patchedKernel->fileBuffer[3] = 0x14; - - // Now we need to fill in the config base of our firmware - // Config Base: 0x00000000 9FC00000 (64 bit!) - patchedKernel->fileBuffer[0x20] = config->StackBase >> 0 & 0xFF; - patchedKernel->fileBuffer[0x21] = config->StackBase >> 8 & 0xFF; - patchedKernel->fileBuffer[0x22] = config->StackBase >> 16 & 0xFF; - patchedKernel->fileBuffer[0x23] = config->StackBase >> 24 & 0xFF; - patchedKernel->fileBuffer[0x24] = config->StackBase >> 32 & 0xFF; - patchedKernel->fileBuffer[0x25] = config->StackBase >> 40 & 0xFF; - patchedKernel->fileBuffer[0x26] = config->StackBase >> 48 & 0xFF; - patchedKernel->fileBuffer[0x27] = config->StackBase >> 56 & 0xFF; - - // Then we need to fill in the config size of our firmware - // Config Base: 0x00000000 00300000 (64 bit!) - patchedKernel->fileBuffer[0x28] = config->StackSize >> 0 & 0xFF; - patchedKernel->fileBuffer[0x29] = config->StackSize >> 8 & 0xFF; - patchedKernel->fileBuffer[0x2A] = config->StackSize >> 16 & 0xFF; - patchedKernel->fileBuffer[0x2B] = config->StackSize >> 24 & 0xFF; - patchedKernel->fileBuffer[0x2C] = config->StackSize >> 32 & 0xFF; - patchedKernel->fileBuffer[0x2D] = config->StackSize >> 40 & 0xFF; - patchedKernel->fileBuffer[0x2E] = config->StackSize >> 48 & 0xFF; - patchedKernel->fileBuffer[0x2F] = config->StackSize >> 56 & 0xFF; - - // Finally, we add in the total kernel image size because we need to jump over! - patchedKernel->fileBuffer[0x30] = kernel->fileSize >> 0 & 0xFF; - patchedKernel->fileBuffer[0x31] = kernel->fileSize >> 8 & 0xFF; - patchedKernel->fileBuffer[0x32] = kernel->fileSize >> 16 & 0xFF; - patchedKernel->fileBuffer[0x33] = kernel->fileSize >> 24 & 0xFF; - patchedKernel->fileBuffer[0x34] = kernel->fileSize >> 32 & 0xFF; - patchedKernel->fileBuffer[0x35] = kernel->fileSize >> 40 & 0xFF; - patchedKernel->fileBuffer[0x36] = kernel->fileSize >> 48 & 0xFF; - patchedKernel->fileBuffer[0x37] = kernel->fileSize >> 56 & 0xFF; - - // Now our header is fully patched, let's add a tiny bit - // of code as well to decide what to do. - // Ignore 0x40 Dummy Header in shellCode->fileBuffer, so we add 0x40 offset after it. - memcpy(patchedKernel->fileBuffer + 0x40, shellCode->fileBuffer + 0x40, shellCode->fileSize - 0x40); - - // Move back our pointer and recalculate kernel size in kernel - // header if the patched kernel buffer has UNCOMPRESSED_IMG header. - if (hasHeader & 0b1) { - patchedKernel->fileBuffer -= 0x14; - size_t newKernelSize = kernel->fileSize + uefi->fileSize; - patchedKernel->fileBuffer[0x10] = newKernelSize >> 0 & 0xFF; - patchedKernel->fileBuffer[0x11] = newKernelSize >> 8 & 0xFF; - patchedKernel->fileBuffer[0x12] = newKernelSize >> 16 & 0xFF; - patchedKernel->fileBuffer[0x13] = newKernelSize >> 24 & 0xFF; - } - - // And that's it, the user now can append executable code right after the kernel, - // and upon closing up the device said code will run at boot. Have fun! - return patchedKernel->fileBuffer; + return PackConfig(argv[1]); } diff --git a/remover.c b/remover.c deleted file mode 100644 index 8e69e3a..0000000 --- a/remover.c +++ /dev/null @@ -1,129 +0,0 @@ -/** @file - * Patch Remover Source File. - * - * This Program will help you revert patched instructions in a patched kernel. - * - * It only supports several formats of kernel: - * 1. Image file compile from source - * 2. Qualcomm patched kernel. - * 3. Image file kernel with efi stub. - * - * Copyright (c) 2021-2025 The DuoWoa authors. All rights reserved. - * MIT License - * - */ - -#include "utils.h" - -/** - * Receive args and handle status. - * - * @param argc argc is numbers of argues given in cmdline. - * @param argv argv is a array that contains all given values. - * @return program status - */ -int main(int argc, char *argv[]) { - printf("Project Aloha DualBoot Patch Remover v1.2.0.0\n"); - printf("Copyright (c) 2021-2024 The DuoWoA authors\n\n"); - if (argc != 3) { - // Print usage if arg numbers not meet. - printf("args: %d\n", argc); - printf("Usage: \n"); - return -EINVAL; - } - - // Program status. - int status = 0; - - // Get file paths. - FileContent patchedImage = {.filePath = argv[1]}; - FileContent outputImage = {.filePath = argv[2]}; - - // Read buffer from patched kernel. - if (!get_file_size(&patchedImage)) { - status = -EINVAL; - goto free_and_exit; - } - patchedImage.fileBuffer = malloc(patchedImage.fileSize); - read_file_content(&patchedImage); - - // Remove! - Remove(&patchedImage, &outputImage); - - // Output buffer to new kernel. - if (outputImage.fileBuffer != NULL) { - write_file_content(&outputImage); - } else { - printf("Error Removing Patch.\n"); - status = -EINVAL; - goto free_and_exit; - } - - // Free buffers we allocated. - free_and_exit: - free(patchedImage.fileBuffer); - free(outputImage.fileBuffer); - - // Everything goes well - printf("Patch successfully removed.\n"); - printf("Please check the unpatched kernel image at %s.\n", outputImage.filePath); - return status; -} - -/** - * Revert patch in kernel and return output file buffer. - * - * @param patchedKernel Input Patched kernel. - * @param outputKernel Output Reverted kernel. - * @return Reverted kernel buffer, NULL if error processing. - */ -uint8_t *Remove(pFileContent patchedKernel, pFileContent outputKernel) { - // Get previous config from patched kernel. - size_t originKernelSize = *(uint64_t *) (patchedKernel->fileBuffer + 0x30); - - // Check if kernel has UNCOMPRESSED_IMG Header. - char kernelHeader[0x11] = {0}; - uint8_t hasHeader = 0; - memcpy(kernelHeader, patchedKernel->fileBuffer, 0x10); - if (strcmp(kernelHeader, "UNCOMPRESSED_IMG") == 0) { - printf("Kernel has UNCOMPRESSED_IMG header.\n"); - originKernelSize = *(uint64_t *) (patchedKernel->fileBuffer + 0x30 + 0x14) + 0x14; - hasHeader |= 0b1; - } - - // Allocate output buffer - outputKernel->fileSize = originKernelSize; - outputKernel->fileBuffer = malloc(outputKernel->fileSize); - - // Copy new buffer into outputBuffer. - memcpy(outputKernel->fileBuffer, patchedKernel->fileBuffer, outputKernel->fileSize); - - // After copying, jump over header - if (hasHeader & 0b1) - outputKernel->fileBuffer += 0x14; - - // Now check if it is a patched kernel - if (outputKernel->fileBuffer[3] == 0x14 && outputKernel->fileBuffer[7] == 0x14) { - printf("Patched kernel detected."); - // Recover Code1 jump instruction, jump to linux kernel directly. - *(uint32_t *) outputKernel->fileBuffer = - ((*(uint32_t *) (outputKernel->fileBuffer + 4) & ~(0xFF << 24)) + 1) | (0x14 << 24); - // Clean Code2 - *(uint32_t *) (outputKernel->fileBuffer + 4) = 0; - } else { - printf("Not an valid kernel."); - return NULL; - } - - // Move back our pointer and recalculate kernel size in kernel - // header if the patched kernel buffer has UNCOMPRESSED_IMG header. - if (hasHeader & 0b1) { - outputKernel->fileBuffer -= 0x14; - size_t newKernelSize = outputKernel->fileSize - 0x14; - outputKernel->fileBuffer[0x10] = newKernelSize >> 0 & 0xFF; - outputKernel->fileBuffer[0x11] = newKernelSize >> 8 & 0xFF; - outputKernel->fileBuffer[0x12] = newKernelSize >> 16 & 0xFF; - outputKernel->fileBuffer[0x13] = newKernelSize >> 24 & 0xFF; - } - return outputKernel->fileBuffer; -} \ No newline at end of file diff --git a/shim.c b/shim.c new file mode 100644 index 0000000..e84fd46 --- /dev/null +++ b/shim.c @@ -0,0 +1,402 @@ +#include "shim.h" +#include "utils.h" + +#include + +typedef struct { + size_t headerOffset; + size_t payloadSize; + uint64_t textOffset; + uint64_t imageSize; +} Arm64ImageInfo; + +static uint32_t read_le32(const uint8_t *buffer) { + uint32_t value = 0; + for (int index = 3; index >= 0; index--) + value = (value << 8) | buffer[index]; + return value; +} + +static uint64_t read_le64(const uint8_t *buffer) { + uint64_t value = 0; + for (int index = 7; index >= 0; index--) + value = (value << 8) | buffer[index]; + return value; +} + +static void write_le32(uint8_t *buffer, uint32_t value) { + for (int index = 0; index < 4; index++) + buffer[index] = value >> (index * 8) & 0xff; +} + +static void write_le64(uint8_t *buffer, uint64_t value) { + for (int index = 0; index < 8; index++) + buffer[index] = value >> (index * 8) & 0xff; +} + +static bool align_up_size(size_t value, size_t alignment, size_t *result) { + if (alignment == 0 || value > SIZE_MAX - (alignment - 1)) + return false; + *result = (value + alignment - 1) & ~(alignment - 1); + return true; +} + +static bool inspect_arm64_image(const FileContent *file, Arm64ImageInfo *info) { + static const uint8_t arm64Magic[] = {'A', 'R', 'M', 0x64}; + + info->headerOffset = 0; + if (file->fileSize >= 0x14 && + memcmp(file->fileBuffer, "UNCOMPRESSED_IMG", 0x10) == 0) + info->headerOffset = 0x14; + + if (file->fileSize < info->headerOffset + 0x40 || + memcmp(file->fileBuffer + info->headerOffset + 0x38, + arm64Magic, sizeof(arm64Magic)) != 0) + return false; + + info->payloadSize = file->fileSize - info->headerOffset; + info->textOffset = read_le64(file->fileBuffer + info->headerOffset + 0x08); + info->imageSize = read_le64(file->fileBuffer + info->headerOffset + 0x10); + return true; +} + +static bool align_image_offset(size_t minimum, + uint64_t primaryTextOffset, + uint64_t imageTextOffset, + size_t alignment, + size_t *result) { + const size_t mask = SHIM_KERNEL_ALIGNMENT - 1; + size_t requiredResidue = + ((size_t)imageTextOffset - (size_t)primaryTextOffset) & mask; + size_t adjustment = (requiredResidue - (minimum & mask)) & mask; + if (minimum > SIZE_MAX - adjustment) + return false; + *result = minimum + adjustment; + return (*result & (alignment - 1)) == 0; +} + +static bool load_file(FileContent *file) { + if (!get_file_size(file)) + return false; + file->fileBuffer = malloc(file->fileSize); + return file->fileBuffer != NULL && read_file_content(file) != NULL; +} + +static bool resize_output(FileContent *output, size_t newSize) { + uint8_t *resized = realloc(output->fileBuffer, newSize); + if (resized == NULL) + return false; + if (newSize > output->fileSize) + memset(resized + output->fileSize, 0, newSize - output->fileSize); + output->fileBuffer = resized; + output->fileSize = newSize; + return true; +} + +static bool initialize_output(const FileContent *primary, + const Arm64ImageInfo *primaryInfo, + size_t primarySlot, + const FileContent *shim, + const FileContent *loader, + FileContent *output) { + if (primaryInfo->headerOffset > SIZE_MAX - primarySlot || + primaryInfo->headerOffset + primarySlot > SIZE_MAX - shim->fileSize || + loader->fileSize > primarySlot) + return false; + + output->fileSize = primaryInfo->headerOffset + primarySlot + shim->fileSize; + output->fileBuffer = calloc(1, output->fileSize); + if (output->fileBuffer == NULL) + return false; + + memcpy(output->fileBuffer, primary->fileBuffer, primary->fileSize); + memcpy(output->fileBuffer + primaryInfo->headerOffset + primarySlot, + shim->fileBuffer, shim->fileSize); + + uint8_t *kernelHeader = output->fileBuffer + primaryInfo->headerOffset; + uint32_t firstInstruction = read_le32(kernelHeader); + uint32_t secondInstruction = read_le32(kernelHeader + 4); + bool firstIsBranch = (firstInstruction & 0xfc000000U) == 0x14000000U; + bool secondIsBranch = (secondInstruction & 0xfc000000U) == 0x14000000U; + + if (firstIsBranch && !secondIsBranch) { + uint32_t movedBranch = 0x14000000U | + ((firstInstruction - 1U) & 0x03ffffffU); + write_le32(kernelHeader + 4, movedBranch); + } else if (!secondIsBranch) { + printf("Error: Base image has no supported entry branch.\n"); + return false; + } + + write_le32(kernelHeader, 0x14000010U); + memcpy(kernelHeader + 0x40, loader->fileBuffer + 0x40, + loader->fileSize - 0x40); + write_le64(kernelHeader + 0x30, primarySlot); + return true; +} + +int PackShim(const ShimPackConfig *config) { + size_t extraImageCount = config->imageCount - 1; + const ShimImageConfig *baseImage = + &config->images[config->baseImageIndex]; + int status = -EINVAL; + + FileContent primary = {0}; + FileContent shim = {0}; + FileContent loader = {0}; + FileContent output = {0}; + + primary.filePath = baseImage->path; + shim.filePath = config->shim; + output.filePath = config->output; + loader.filePath = config->loader; + + if (!load_file(&primary) || !load_file(&shim) || !load_file(&loader)) + goto cleanup; + + Arm64ImageInfo primaryInfo = {0}; + Arm64ImageInfo shimInfo = {0}; + if (!inspect_arm64_image(&primary, &primaryInfo)) { + printf("Error: Base image is not an ARM64 Linux Image.\n"); + goto cleanup; + } + if (!inspect_arm64_image(&shim, &shimInfo) || shimInfo.headerOffset != 0) { + printf("Error: Shim is not a raw ARM64 Image.\n"); + goto cleanup; + } + if (shimInfo.imageSize == 0 || shim.fileSize != shimInfo.imageSize) { + printf("Error: Shim file size must match its non-zero Image size.\n"); + goto cleanup; + } + bool isSimpleShim = + memcmp(shim.fileBuffer + 8, SIMPLE_SHIM_MAGIC, + sizeof(SIMPLE_SHIM_MAGIC)) == 0; + if (isSimpleShim) { + if (config->defaultEntry != 0 || config->timeoutMs != 0) { + printf("Error: Simplified assembly Shim requires the base image " + "as Default and Timeout=0.\n"); + goto cleanup; + } + if (config->imageCount != 2) { + printf("Error: Simplified assembly Shim requires exactly one " + "base image and one CopyTo image.\n"); + goto cleanup; + } + for (size_t index = 0; index < config->imageCount; index++) { + if (index != config->baseImageIndex && + !config->images[index].hasCopyAddress) { + printf("Error: Simplified assembly Shim requires its second " + "image to set CopyTo.\n"); + goto cleanup; + } + } + } + if (loader.fileSize <= 0x40 || + memcmp(loader.fileBuffer + 8, SHIM_LOADER_MAGIC, + sizeof(SHIM_LOADER_MAGIC)) != 0) { + printf("Error: Invalid ShimLoader image.\n"); + goto cleanup; + } + + size_t primarySlot = primaryInfo.payloadSize; + if (primaryInfo.imageSize > primarySlot) + primarySlot = (size_t)primaryInfo.imageSize; + if (!align_up_size(primarySlot, 0x1000, &primarySlot) || + primarySlot > SIZE_MAX - primaryInfo.headerOffset) { + printf("Error: Base image is too large.\n"); + goto cleanup; + } + + if (!initialize_output(&primary, &primaryInfo, primarySlot, &shim, + &loader, &output)) + goto cleanup; + + ShimManifest manifest = {0}; + memcpy(manifest.header.magic, SHIM_MANIFEST_MAGIC, 8); + manifest.header.version = SHIM_MANIFEST_VERSION; + manifest.header.headerSize = sizeof(ShimManifestHeader); + manifest.header.entrySize = sizeof(ShimManifestEntry); + manifest.header.entryCount = (uint32_t)extraImageCount + 1; + manifest.header.defaultEntry = config->defaultEntry; + manifest.header.timeoutMs = config->timeoutMs; + + memcpy(manifest.entries[0].name, baseImage->name, strlen(baseImage->name) + 1); + manifest.entries[0].offset = 0; + manifest.entries[0].size = primaryInfo.payloadSize; + manifest.entries[0].type = SHIM_ENTRY_TYPE_LINUX; + manifest.entries[0].flags = SHIM_ENTRY_FLAG_BASE_IMAGE; + + size_t manifestIndex = 1; + for (size_t index = 0; index < config->imageCount; index++) { + if (index == config->baseImageIndex) + continue; + + const ShimImageConfig *image = &config->images[index]; + FileContent extra = {.filePath = image->path}; + if (!load_file(&extra)) + goto cleanup; + if (image->hasCopyAddress && extra.fileSize > image->copySizeMax) { + printf("Error: [%s] image size 0x%zx exceeds CopySizeMax " + "0x%llx.\n", image->section, extra.fileSize, + (unsigned long long)image->copySizeMax); + free(extra.fileBuffer); + goto cleanup; + } + + size_t runtimeSize = output.fileSize - primaryInfo.headerOffset; + size_t imageOffset = 0; + size_t imageSlot = 0; + size_t payloadOffset = 0; + size_t payloadSize = extra.fileSize; + Arm64ImageInfo extraInfo = {0}; + + if (image->hasCopyAddress) { + if (image->copyAddress > UINT64_MAX - extra.fileSize) { + printf("Error: Copy address range overflows: %s\n", + image->section); + free(extra.fileBuffer); + goto cleanup; + } + if (!align_up_size(runtimeSize, (size_t)image->alignment, + &imageOffset)) { + free(extra.fileBuffer); + goto cleanup; + } + imageSlot = extra.fileSize; + } else { + if (!inspect_arm64_image(&extra, &extraInfo)) { + printf("Error: Image without CopyTo is not an ARM64 Linux " + "Image: %s\n", extra.filePath); + free(extra.fileBuffer); + goto cleanup; + } + payloadOffset = extraInfo.headerOffset; + payloadSize = extraInfo.payloadSize; + imageSlot = extraInfo.payloadSize; + if (extraInfo.imageSize > imageSlot) + imageSlot = (size_t)extraInfo.imageSize; + if (!align_image_offset(runtimeSize, primaryInfo.textOffset, + extraInfo.textOffset, + (size_t)image->alignment, &imageOffset) || + !align_up_size(imageSlot, 0x1000, &imageSlot)) { + printf("Error: [%s] cannot satisfy Align=0x%llx and ARM64 " + "Image text_offset constraints.\n", image->section, + (unsigned long long)image->alignment); + free(extra.fileBuffer); + goto cleanup; + } + } + + if (imageOffset > SIZE_MAX - imageSlot || + primaryInfo.headerOffset > SIZE_MAX - imageOffset - imageSlot || + !resize_output(&output, + primaryInfo.headerOffset + imageOffset + imageSlot)) { + free(extra.fileBuffer); + goto cleanup; + } + + memcpy(output.fileBuffer + primaryInfo.headerOffset + imageOffset, + extra.fileBuffer + payloadOffset, payloadSize); + + ShimManifestEntry *entry = &manifest.entries[manifestIndex++]; + memcpy(entry->name, image->name, strlen(image->name) + 1); + entry->offset = imageOffset; + entry->size = payloadSize; + if (image->hasCopyAddress) { + entry->type = SHIM_ENTRY_TYPE_EXECUTABLE; + entry->flags = SHIM_ENTRY_FLAG_COPY; + entry->loadAddress = image->copyAddress; + } else { + entry->type = SHIM_ENTRY_TYPE_LINUX; + } + free(extra.fileBuffer); + } + + if (isSimpleShim) { + ShimManifestEntry *payload = &manifest.entries[1]; + uint8_t *shimHeader = output.fileBuffer + primaryInfo.headerOffset + + primarySlot; + write_le64(shimHeader + SIMPLE_SHIM_PAYLOAD_OFFSET, payload->offset); + write_le64(shimHeader + SIMPLE_SHIM_COPY_ADDRESS_OFFSET, + payload->loadAddress); + write_le64(shimHeader + SIMPLE_SHIM_COPY_SIZE_OFFSET, payload->size); + + uint8_t *kernelHeader = output.fileBuffer + primaryInfo.headerOffset; + write_le64(kernelHeader + 0x20, 0); + write_le64(kernelHeader + 0x28, 0); + + if (primaryInfo.headerOffset != 0) { + size_t imageSize = output.fileSize - primaryInfo.headerOffset; + if (imageSize > UINT32_MAX) { + printf("Error: UNCOMPRESSED_IMG payload exceeds 32-bit size.\n"); + goto cleanup; + } + write_le32(output.fileBuffer + 0x10, (uint32_t)imageSize); + } + if (write_file_content(&output) != 0) { + printf("Error: Failed to write simplified Shim image.\n"); + goto cleanup; + } + + printf("Simplified Shim image packed successfully.\n"); + printf(" Payload: offset 0x%llx, CopyTo 0x%llx, " + "size 0x%llx\n", + (unsigned long long)payload->offset, + (unsigned long long)payload->loadAddress, + (unsigned long long)payload->size); + printf(" Output size: 0x%zx\n", output.fileSize); + status = 0; + goto cleanup; + } + + size_t manifestSize = sizeof(ShimManifestHeader) + + manifest.header.entryCount * sizeof(ShimManifestEntry); + size_t manifestOffset = output.fileSize - primaryInfo.headerOffset; + size_t oldOutputSize = output.fileSize; + if (manifestSize > SHIM_MANIFEST_MAX_SIZE || + manifestSize > SIZE_MAX - oldOutputSize || + !resize_output(&output, oldOutputSize + manifestSize)) { + printf("Error: Failed to append Shim manifest.\n"); + goto cleanup; + } + manifest.header.imageSize = manifestOffset + manifestSize; + memcpy(output.fileBuffer + oldOutputSize, &manifest, manifestSize); + + uint8_t *kernelHeader = output.fileBuffer + primaryInfo.headerOffset; + write_le64(kernelHeader + 0x20, manifestOffset); + write_le64(kernelHeader + 0x28, manifestSize); + + if (primaryInfo.headerOffset != 0) { + if (manifest.header.imageSize > UINT32_MAX) { + printf("Error: UNCOMPRESSED_IMG payload exceeds 32-bit size.\n"); + goto cleanup; + } + write_le32(output.fileBuffer + 0x10, (uint32_t)manifest.header.imageSize); + } + + if (write_file_content(&output) != 0) { + printf("Error: Failed to write Shim image.\n"); + goto cleanup; + } + + printf("Shim image packed successfully.\n"); + printf(" Base image slot: 0x%zx\n", primarySlot); + printf(" Shim slot: 0x%zx\n", shim.fileSize); + printf(" Manifest blob: offset 0x%zx, size 0x%zx\n", + manifestOffset, manifestSize); + for (uint32_t index = 0; index < manifest.header.entryCount; index++) + printf(" Image %u: %s @ 0x%llx (0x%llx bytes)\n", + index + 1, manifest.entries[index].name, + (unsigned long long)manifest.entries[index].offset, + (unsigned long long)manifest.entries[index].size); + printf(" Output size: 0x%llx\n", + (unsigned long long)manifest.header.imageSize); + status = 0; + +cleanup: + free(primary.fileBuffer); + free(shim.fileBuffer); + free(loader.fileBuffer); + free(output.fileBuffer); + return status; +} \ No newline at end of file diff --git a/shim.h b/shim.h new file mode 100644 index 0000000..8f563e7 --- /dev/null +++ b/shim.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#define SHIM_MANIFEST_MAGIC "SHIMMF\0" +#define SHIM_MANIFEST_VERSION 3U +#define SHIM_MANIFEST_MAX_SIZE 0x1000U +#define SHIM_MAX_ENTRIES 63U +#define SHIM_KERNEL_ALIGNMENT 0x200000U +#define SHIM_LOADER_MAGIC "SHIMLDR" +#define SIMPLE_SHIM_MAGIC "SHIMASM" +#define SIMPLE_SHIM_PAYLOAD_OFFSET 0x20U +#define SIMPLE_SHIM_COPY_ADDRESS_OFFSET 0x28U +#define SIMPLE_SHIM_COPY_SIZE_OFFSET 0x30U + +#define SHIM_ENTRY_TYPE_LINUX 1U +#define SHIM_ENTRY_TYPE_EXECUTABLE 2U +#define SHIM_ENTRY_FLAG_BASE_IMAGE 1U +#define SHIM_ENTRY_FLAG_COPY 2U + +#define SHIM_PATH_SIZE 4096U + +typedef struct { + char section[32]; + char name[32]; + char path[SHIM_PATH_SIZE]; + int baseImage; + uint64_t copyAddress; + int hasCopyAddress; + uint64_t copySizeMax; + int hasCopySizeMax; + uint64_t alignment; + int hasAlignment; +} ShimImageConfig; + +typedef struct { + char shim[SHIM_PATH_SIZE]; + char output[SHIM_PATH_SIZE]; + char loader[SHIM_PATH_SIZE]; + char defaultSection[32]; + ShimImageConfig images[SHIM_MAX_ENTRIES]; + size_t imageCount; + size_t baseImageIndex; + uint32_t defaultEntry; + uint32_t timeoutMs; +} ShimPackConfig; + +#ifdef _MSC_VER +#pragma pack(push, 1) +#define SHIM_PACKED +#else +#define SHIM_PACKED __attribute__((packed)) +#endif + +typedef struct SHIM_PACKED { + char magic[8]; + uint32_t version; + uint32_t headerSize; + uint32_t entrySize; + uint32_t entryCount; + uint32_t defaultEntry; + uint32_t timeoutMs; + uint64_t imageSize; + uint64_t reserved[3]; +} ShimManifestHeader; + +typedef struct SHIM_PACKED { + char name[32]; + uint64_t offset; + uint64_t size; + uint32_t type; + uint32_t flags; + uint64_t loadAddress; +} ShimManifestEntry; + +typedef struct SHIM_PACKED { + ShimManifestHeader header; + ShimManifestEntry entries[SHIM_MAX_ENTRIES]; +} ShimManifest; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif +#undef SHIM_PACKED + +_Static_assert(sizeof(ShimManifestHeader) == 64, + "Invalid Shim manifest header size"); +_Static_assert(sizeof(ShimManifestEntry) == 64, + "Invalid Shim manifest entry size"); +_Static_assert(sizeof(ShimManifest) == SHIM_MANIFEST_MAX_SIZE, + "Invalid Shim manifest size"); + +int PackConfig(const char *configPath); +int PackShim(const ShimPackConfig *config); \ No newline at end of file diff --git a/shim_config.c b/shim_config.c new file mode 100644 index 0000000..7901fe3 --- /dev/null +++ b/shim_config.c @@ -0,0 +1,290 @@ +#include "shim.h" + +#include + +#include +#include +#include +#include +#include + +typedef struct { + ShimPackConfig config; + char configDirectory[SHIM_PATH_SIZE]; + char error[256]; +} ConfigParser; + +static bool copy_text(char *destination, size_t size, const char *value) { + size_t length = strlen(value); + if (length >= size) + return false; + memcpy(destination, value, length + 1); + return true; +} + +static bool resolve_path(char *destination, + size_t size, + const char *configDirectory, + const char *value) { + if (value[0] == '/') + return copy_text(destination, size, value); + + int written = snprintf(destination, size, "%s/%s", configDirectory, value); + return written >= 0 && (size_t)written < size; +} + +static bool parse_u32(const char *value, uint32_t *result) { + char *end = NULL; + errno = 0; + unsigned long parsed = strtoul(value, &end, 0); + if (errno != 0 || value[0] == '\0' || *end != '\0' || parsed > UINT32_MAX) + return false; + *result = (uint32_t)parsed; + return true; +} + +static bool parse_u64(const char *value, uint64_t *result) { + char *end = NULL; + errno = 0; + if (value[0] == '-') + return false; + unsigned long long parsed = strtoull(value, &end, 0); + if (errno != 0 || value[0] == '\0' || *end != '\0') + return false; + *result = (uint64_t)parsed; + return true; +} + +static bool parse_bool(const char *value, int *result) { + if (strcmp(value, "true") == 0 || strcmp(value, "yes") == 0 || + strcmp(value, "1") == 0) { + *result = 1; + return true; + } + if (strcmp(value, "false") == 0 || strcmp(value, "no") == 0 || + strcmp(value, "0") == 0) { + *result = 0; + return true; + } + return false; +} + +static bool set_once(char *destination, size_t size, const char *value) { + return destination[0] == '\0' && copy_text(destination, size, value); +} + +static bool set_path_once(char *destination, + size_t size, + const ConfigParser *parser, + const char *value) { + return destination[0] == '\0' && + resolve_path(destination, size, parser->configDirectory, value); +} + +static ShimImageConfig *find_or_add_image(ConfigParser *parser, + const char *section) { + for (size_t index = 0; index < parser->config.imageCount; index++) { + if (strcmp(parser->config.images[index].section, section) == 0) + return &parser->config.images[index]; + } + + if (parser->config.imageCount >= SHIM_MAX_ENTRIES || + strlen(section) >= sizeof(parser->config.images[0].section)) + return NULL; + + ShimImageConfig *image = + &parser->config.images[parser->config.imageCount++]; + memcpy(image->section, section, strlen(section) + 1); + image->baseImage = -1; + return image; +} + +static int config_handler(void *user, + const char *section, + const char *name, + const char *value) { + ConfigParser *parser = user; + bool valid = false; + + if (strcmp(section, "Pack") == 0) { + if (strcmp(name, "Shim") == 0) + valid = set_path_once(parser->config.shim, + sizeof(parser->config.shim), parser, value); + else if (strcmp(name, "Output") == 0) + valid = set_path_once(parser->config.output, + sizeof(parser->config.output), parser, value); + else if (strcmp(name, "Loader") == 0) + valid = set_path_once(parser->config.loader, + sizeof(parser->config.loader), parser, value); + else if (strcmp(name, "Default") == 0) + valid = set_once(parser->config.defaultSection, + sizeof(parser->config.defaultSection), value); + else if (strcmp(name, "Timeout") == 0) + valid = parse_u32(value, &parser->config.timeoutMs); + } else if (strncmp(section, "Image-", 6) == 0 && section[6] != '\0') { + ShimImageConfig *image = find_or_add_image(parser, section); + if (image != NULL) { + if (strcmp(name, "Name") == 0) + valid = set_once(image->name, sizeof(image->name), value); + else if (strcmp(name, "Path") == 0) + valid = set_path_once(image->path, sizeof(image->path), + parser, value); + else if (strcmp(name, "BaseImage") == 0 && image->baseImage == -1) + valid = parse_bool(value, &image->baseImage); + else if (strcmp(name, "CopyTo") == 0 && + !image->hasCopyAddress) { + valid = parse_u64(value, &image->copyAddress); + image->hasCopyAddress = valid; + } else if (strcmp(name, "CopySizeMax") == 0 && + !image->hasCopySizeMax) { + valid = parse_u64(value, &image->copySizeMax); + image->hasCopySizeMax = valid; + } else if (strcmp(name, "Align") == 0 && + !image->hasAlignment) { + valid = parse_u64(value, &image->alignment); + image->hasAlignment = valid; + } + } + } + + if (!valid) { + snprintf(parser->error, sizeof(parser->error), + "invalid or duplicate key [%s] %s", section, name); + return 0; + } + return 1; +} + +static bool get_config_directory(const char *configPath, + char *directory, + size_t size) { + const char *separator = strrchr(configPath, '/'); + if (separator == NULL) + return copy_text(directory, size, "."); + + size_t length = (size_t)(separator - configPath); + if (length == 0) + length = 1; + if (length >= size) + return false; + memcpy(directory, configPath, length); + directory[length] = '\0'; + return true; +} + +static bool validate_config(ConfigParser *parser, + size_t *baseImageIndex, + size_t *defaultIndex) { + ShimPackConfig *config = &parser->config; + if (config->shim[0] == '\0' || config->output[0] == '\0' || + config->loader[0] == '\0' || config->defaultSection[0] == '\0') { + printf("Error: [Pack] requires Shim, Output, Loader and Default.\n"); + return false; + } + if (config->imageCount == 0) { + printf("Error: At least one [Image-*] section is required.\n"); + return false; + } + + size_t baseImageCount = 0; + bool defaultFound = false; + for (size_t index = 0; index < config->imageCount; index++) { + ShimImageConfig *image = &config->images[index]; + if (image->name[0] == '\0' || image->path[0] == '\0' || + image->baseImage == -1) { + printf("Error: [%s] requires Name, Path and BaseImage.\n", + image->section); + return false; + } + if (!image->hasAlignment) + image->alignment = 4; + if (image->alignment < 4 || + image->alignment > SHIM_KERNEL_ALIGNMENT || + (image->alignment & (image->alignment - 1)) != 0) { + printf("Error: [%s] Align must be a power of two from 4 to " + "0x%x.\n", image->section, SHIM_KERNEL_ALIGNMENT); + return false; + } + if (image->baseImage) { + if (image->hasCopyAddress) { + printf("Error: Base image [%s] cannot set CopyTo.\n", + image->section); + return false; + } + *baseImageIndex = index; + baseImageCount++; + } + if (image->hasCopyAddress) { + if (image->copyAddress == 0) { + printf("Error: [%s] CopyTo address must be non-zero.\n", + image->section); + return false; + } + if ((image->copyAddress & 3) != 0) { + printf("Error: [%s] CopyTo address must be aligned to 4 " + "bytes.\n", image->section); + return false; + } + if (!image->hasCopySizeMax || image->copySizeMax == 0) { + printf("Error: [%s] with CopyTo requires non-zero " + "CopySizeMax.\n", image->section); + return false; + } + if (image->copyAddress > UINT64_MAX - image->copySizeMax) { + printf("Error: [%s] CopyTo region overflows.\n", + image->section); + return false; + } + } else if (image->hasCopySizeMax) { + printf("Error: [%s] CopySizeMax requires CopyTo.\n", + image->section); + return false; + } + if (strcmp(image->section, config->defaultSection) == 0) { + *defaultIndex = index; + defaultFound = true; + } + } + + if (baseImageCount != 1) { + printf("Error: Exactly one [Image-*] must set BaseImage=true.\n"); + return false; + } + config->baseImageIndex = *baseImageIndex; + if (!defaultFound) { + printf("Error: Default must name an [Image-*] section.\n"); + return false; + } + config->defaultEntry = (uint32_t)( + *defaultIndex == *baseImageIndex + ? 0 + : 1 + *defaultIndex - (*defaultIndex > *baseImageIndex)); + return true; +} + +int PackConfig(const char *configPath) { + ConfigParser parser = {0}; + parser.config.timeoutMs = 5000; + if (!get_config_directory(configPath, parser.configDirectory, + sizeof(parser.configDirectory))) { + printf("Error: Config path is too long.\n"); + return -EINVAL; + } + + int parseStatus = ini_parse(configPath, config_handler, &parser); + if (parseStatus != 0) { + if (parseStatus == -1) + printf("Error: Cannot open config: %s\n", configPath); + else + printf("Error: Config line %d: %s\n", parseStatus, + parser.error[0] != '\0' ? parser.error : "parse failure"); + return -EINVAL; + } + + size_t baseImageIndex = 0; + size_t defaultIndex = SIZE_MAX; + if (!validate_config(&parser, &baseImageIndex, &defaultIndex)) + return -EINVAL; + + return PackShim(&parser.config); +} \ No newline at end of file diff --git a/utils.c b/utils.c index 00b89a9..2edc0c8 100644 --- a/utils.c +++ b/utils.c @@ -53,34 +53,3 @@ int write_file_content(pFileContent fileContent) { fclose(pFile); return 0; } - -/** - * Parse given config. - * - * @param fileContent - * @param config Config info read from config file - * @retval -EINVAL Give File not found. - * - */ -int parse_config(FileContent *fileContent, pConfig config) { - // Check file size - if (!get_file_size(fileContent)) - return -EINVAL; - - // Open config file - FILE *pConfigFile = fopen(fileContent->filePath, "r"); - char key[256]; - uint32_t value = 0; - - // Parse - while (fscanf(pConfigFile, "%[^=]=%x\n", key, &value) != EOF) { - if (strcmp(key, "StackBase") == 0) { - config->StackBase = value; - } else if (strcmp(key, "StackSize") == 0) { - config->StackSize = value; - } - } - - fclose(pConfigFile); - return 0; -} diff --git a/utils.h b/utils.h index 0748513..820988c 100644 --- a/utils.h +++ b/utils.h @@ -1,13 +1,12 @@ /** @file - * DualBoot Kernel Patcher Header File. + * Multi-Boot Kernel Patcher Header File. * * Copyright (c) 2021-2025 The DuoWoa authors. All rights reserved. * MIT License * */ -#ifndef LINUX_KERNEL_PATCHER_PATCHER_H -#define LINUX_KERNEL_PATCHER_PATCHER_H +#pragma once #include #include @@ -15,14 +14,6 @@ #include #include -// -// Config info read from config file and set to kernel. -// -typedef struct { - uint64_t StackBase; - uint64_t StackSize; -} Config, *pConfig; - // // Store some file information and file buffer. // @@ -37,11 +28,3 @@ size_t get_file_size(FileContent *fileContent); uint8_t *read_file_content(FileContent *fileContent); int write_file_content(pFileContent fileContent); - -int parse_config(FileContent *fileContent, pConfig config); - -uint8_t *PatchKernel(pFileContent kernel, pFileContent uefi, pFileContent shellCode, - pFileContent patchedKernel, pConfig config); - -uint8_t *Remove(pFileContent patchedKernel, pFileContent outputKernel); -#endif //LINUX_KERNEL_PATCHER_PATCHER_H diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..1a3e260 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,7 @@ +{ + "name": "multi-boot-kernel-patcher", + "version-string": "1.2.0.0", + "dependencies": [ + "inih" + ] +} \ No newline at end of file From 51ac32409ec1510c0adc734a6549e342c48c906f Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Tue, 4 Aug 2026 15:33:39 +0800 Subject: [PATCH 02/12] fix linux static link --- CMakeLists.txt | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eff603d..4703f20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,16 +4,36 @@ project(MultiBootKernelPatcher C) option(BUILD_SHIM_LOADER "Build the ARM64 ShimLoader binary" ON) option(BUILD_SHIMS "Build the simplified ARM64 Shims" ${BUILD_SHIM_LOADER}) +set(CMAKE_C_STANDARD 11) +if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") +endif () + find_package(unofficial-inih CONFIG QUIET) if (TARGET unofficial::inih::libinih) set(INIH_TARGET unofficial::inih::libinih) else () find_package(PkgConfig QUIET) if (PkgConfig_FOUND) - pkg_check_modules(INIH QUIET IMPORTED_TARGET inih) + pkg_check_modules(INIH QUIET inih) endif () - if (TARGET PkgConfig::INIH) - set(INIH_TARGET PkgConfig::INIH) + if (INIH_FOUND) + find_file(INIH_STATIC_LIBRARY + NAMES libinih.a inih.a + PATHS ${INIH_LIBRARY_DIRS} + NO_DEFAULT_PATH + ) + if (NOT INIH_STATIC_LIBRARY) + message(FATAL_ERROR + "inih was found, but its static library libinih.a was not found. " + "Install the inih development package with static libraries enabled.") + endif () + add_library(inih_static STATIC IMPORTED) + set_target_properties(inih_static PROPERTIES + IMPORTED_LOCATION "${INIH_STATIC_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${INIH_INCLUDE_DIRS}" + ) + set(INIH_TARGET inih_static) else () message(FATAL_ERROR "inih was not found. Install libinih-dev on Debian/Ubuntu, " @@ -22,10 +42,6 @@ else () endif () endif () -set(CMAKE_C_STANDARD 11) -if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") - set(CMAKE_EXE_LINKER_FLAGS "-static") -endif () if (MSVC) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif () From fe3871443092c9108dec63fac3021783af0c85bf Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Tue, 4 Aug 2026 16:40:02 +0800 Subject: [PATCH 03/12] use inih submodule instead of prebuilt libs. --- .github/workflows/cmake-multi-platform.yml | 12 ++----- .gitmodules | 3 ++ CMakeLists.txt | 42 ++++++---------------- README.md | 16 ++++----- lib/inih | 1 + vcpkg.json | 7 ---- 6 files changed, 26 insertions(+), 55 deletions(-) create mode 100644 .gitmodules create mode 160000 lib/inih delete mode 100644 vcpkg.json diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 1c0de14..1ef8496 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -19,7 +19,6 @@ jobs: os: windows-latest arch: x64 cmake_arch: x64 - vcpkg_triplet: x64-windows-static build_shim_loader: 'OFF' executable_suffix: .exe executable_directory: Release @@ -27,7 +26,6 @@ jobs: os: windows-11-arm arch: arm64 cmake_arch: ARM64 - vcpkg_triplet: arm64-windows-static build_shim_loader: 'OFF' executable_suffix: .exe executable_directory: Release @@ -55,21 +53,19 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive - name: Install Linux dependencies if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install --yes libinih-dev pkg-config + sudo apt-get install --yes build-essential cmake git - name: Install ShimLoader toolchain if: matrix.build_shim_loader == 'ON' run: sudo apt-get install --yes binutils-aarch64-linux-gnu - - name: Install macOS dependencies - if: runner.os == 'macOS' - run: brew install inih pkg-config - - name: Configure CMake on Windows if: runner.os == 'Windows' run: >- @@ -77,8 +73,6 @@ jobs: -B "${{ github.workspace }}/build" -A ${{ matrix.cmake_arch }} -DCMAKE_BUILD_TYPE=Release - -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" - -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg_triplet }} -DBUILD_SHIM_LOADER=${{ matrix.build_shim_loader }} - name: Configure CMake on Unix diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2a27619 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/inih"] + path = lib/inih + url = https://github.com/benhoyt/inih.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 4703f20..bac3448 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,39 +9,19 @@ if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") endif () -find_package(unofficial-inih CONFIG QUIET) -if (TARGET unofficial::inih::libinih) - set(INIH_TARGET unofficial::inih::libinih) -else () - find_package(PkgConfig QUIET) - if (PkgConfig_FOUND) - pkg_check_modules(INIH QUIET inih) - endif () - if (INIH_FOUND) - find_file(INIH_STATIC_LIBRARY - NAMES libinih.a inih.a - PATHS ${INIH_LIBRARY_DIRS} - NO_DEFAULT_PATH - ) - if (NOT INIH_STATIC_LIBRARY) - message(FATAL_ERROR - "inih was found, but its static library libinih.a was not found. " - "Install the inih development package with static libraries enabled.") - endif () - add_library(inih_static STATIC IMPORTED) - set_target_properties(inih_static PROPERTIES - IMPORTED_LOCATION "${INIH_STATIC_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${INIH_INCLUDE_DIRS}" - ) - set(INIH_TARGET inih_static) - else () - message(FATAL_ERROR - "inih was not found. Install libinih-dev on Debian/Ubuntu, " - "inih-devel on Fedora, or configure CMake with the vcpkg " - "toolchain; vcpkg.json declares the inih dependency.") - endif () +if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/lib/inih/ini.c) + message(FATAL_ERROR + "The inih submodule is missing. Run: git submodule update --init --recursive") endif () +add_library(inih_static STATIC + ${CMAKE_CURRENT_SOURCE_DIR}/lib/inih/ini.c +) +target_include_directories(inih_static PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/lib/inih +) +set(INIH_TARGET inih_static) + if (MSVC) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif () diff --git a/README.md b/README.md index 0199ff0..6d2f0e2 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,19 @@ Shim runtime, and multiple bootable images from one INI configuration. - Install dependencies + Linux (Debian / Ubuntu) ```sh - sudo apt install build-essential cmake gcc-aarch64-linux-gnu git \ - libinih-dev pkg-config + sudo apt install build-essential cmake gcc-aarch64-linux-gnu git ``` + Windows - Install and integrate [vcpkg](https://github.com/microsoft/vcpkg). The - included `vcpkg.json` declares `inih` as one of the project dependencies. - ```powershell - cmake -B output -S . ` - -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" - ``` + Install CMake and a Visual Studio C compiler. The inih dependency is + built from the checked-out `lib/inih` submodule. + + macOS + Install CMake and Xcode Command Line Tools. The inih dependency is built + from the checked-out `lib/inih` submodule. - Clone this repo ``` git clone https://github.com/Project-Aloha/DualBootKernelPatcher + cd DualBootKernelPatcher + git submodule update --init --recursive ``` - Setup CMake. ``` diff --git a/lib/inih b/lib/inih new file mode 160000 index 0000000..26254ee --- /dev/null +++ b/lib/inih @@ -0,0 +1 @@ +Subproject commit 26254ee9de7681f8825433415443e7116ff24b98 diff --git a/vcpkg.json b/vcpkg.json deleted file mode 100644 index 1a3e260..0000000 --- a/vcpkg.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "multi-boot-kernel-patcher", - "version-string": "1.2.0.0", - "dependencies": [ - "inih" - ] -} \ No newline at end of file From 768f2d248a9b6145834648bd9055577ff72ce6f2 Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Tue, 4 Aug 2026 16:53:10 +0800 Subject: [PATCH 04/12] try fix windows inih build issue --- CMakeLists.txt | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bac3448..f4c63ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,10 @@ option(BUILD_SHIM_LOADER "Build the ARM64 ShimLoader binary" ON) option(BUILD_SHIMS "Build the simplified ARM64 Shims" ${BUILD_SHIM_LOADER}) set(CMAKE_C_STANDARD 11) +if (MSVC) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif () + if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") endif () @@ -17,19 +21,27 @@ endif () add_library(inih_static STATIC ${CMAKE_CURRENT_SOURCE_DIR}/lib/inih/ini.c ) +if (MSVC) + set_property(TARGET inih_static PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif () target_include_directories(inih_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/lib/inih ) set(INIH_TARGET inih_static) -if (MSVC) - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -endif () - # Compile the patcher. add_executable(MultiBootKernelPatcher patcher.c shim.c shim_config.c utils.c) +if (MSVC) + set_property(TARGET MultiBootKernelPatcher PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif () target_link_libraries(MultiBootKernelPatcher PRIVATE ${INIH_TARGET}) add_executable(HDRTool HDRTool.c utils.c) +if (MSVC) + set_property(TARGET HDRTool PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif () if (BUILD_SHIM_LOADER) add_subdirectory(ShimLoader) From bd851f08649e8828de109e3cb86643f5190f7cf7 Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Tue, 4 Aug 2026 17:02:55 +0800 Subject: [PATCH 05/12] remove library installation step --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index 6d2f0e2..ec78c66 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,6 @@ Shim runtime, and multiple bootable images from one INI configuration. + Git + CMake - - Install dependencies - + Linux (Debian / Ubuntu) - ```sh - sudo apt install build-essential cmake gcc-aarch64-linux-gnu git - ``` - + Windows - Install CMake and a Visual Studio C compiler. The inih dependency is - built from the checked-out `lib/inih` submodule. - + macOS - Install CMake and Xcode Command Line Tools. The inih dependency is built - from the checked-out `lib/inih` submodule. - Clone this repo ``` git clone https://github.com/Project-Aloha/DualBootKernelPatcher From d1c6362658c5c1192a65b334c329fa069114107f Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Tue, 4 Aug 2026 20:42:17 +0800 Subject: [PATCH 06/12] optimize default configs --- Config/{DualBoot.Sm8550.cfg => DualBoot.AYAPS.cfg} | 10 +++++----- Config/{DualBoot.Sm8150.cfg => DualBoot.Hotdog.cfg} | 10 +++++----- ...ualBoot.Sm8250DT.cfg => DualBoot.KernelWarpper.cfg} | 10 +++++----- Config/{DualBoot.Sm7125.cfg => DualBoot.Miatoll.cfg} | 10 +++++----- Config/{DualBoot.Sm8250.cfg => DualBoot.Pipa.cfg} | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) rename Config/{DualBoot.Sm8550.cfg => DualBoot.AYAPS.cfg} (64%) rename Config/{DualBoot.Sm8150.cfg => DualBoot.Hotdog.cfg} (64%) rename Config/{DualBoot.Sm8250DT.cfg => DualBoot.KernelWarpper.cfg} (67%) rename Config/{DualBoot.Sm7125.cfg => DualBoot.Miatoll.cfg} (65%) rename Config/{DualBoot.Sm8250.cfg => DualBoot.Pipa.cfg} (65%) diff --git a/Config/DualBoot.Sm8550.cfg b/Config/DualBoot.AYAPS.cfg similarity index 64% rename from Config/DualBoot.Sm8550.cfg rename to Config/DualBoot.AYAPS.cfg index 795065d..1d4aff0 100644 --- a/Config/DualBoot.Sm8550.cfg +++ b/Config/DualBoot.AYAPS.cfg @@ -1,19 +1,19 @@ [Pack] Shim=../build/Shims/Shim.AYAPS.bin -Output=../build/PatchedKernel.Sm8550 +Output=../build/PatchedKernel Loader=../build/ShimLoader/ShimLoader.bin -Default=Image-A +Default=Image Timeout=0 -[Image-A] -Name=AYANEO Pocket S Linux +[Image] +Name=Android/Linux Path=/path/to/Image BaseImage=true Align=0x200000 [Image-UEFI] Name=UEFI -Path=../../firmware/SM8550_EFI.fd +Path=/path/to/SM8550_EFI.fd BaseImage=false CopyTo=0xC7CC0000 CopySizeMax=0x00400000 \ No newline at end of file diff --git a/Config/DualBoot.Sm8150.cfg b/Config/DualBoot.Hotdog.cfg similarity index 64% rename from Config/DualBoot.Sm8150.cfg rename to Config/DualBoot.Hotdog.cfg index 5e00d5b..13b076c 100644 --- a/Config/DualBoot.Sm8150.cfg +++ b/Config/DualBoot.Hotdog.cfg @@ -1,19 +1,19 @@ [Pack] Shim=../build/Shims/Shim.Hotdog.bin -Output=../build/PatchedKernel.Sm8150 +Output=../build/PatchedKernel Loader=../build/ShimLoader/ShimLoader.bin -Default=Image-A +Default=Image Timeout=0 -[Image-A] -Name=OnePlus 7T Pro Linux +[Image] +Name=Android/Linux Path=/path/to/Image BaseImage=true Align=0x200000 [Image-UEFI] Name=UEFI -Path=../../firmware/SM8150_EFI.fd +Path=/path/to/SM8150_EFI.fd BaseImage=false CopyTo=0x9FC00000 CopySizeMax=0x00300000 \ No newline at end of file diff --git a/Config/DualBoot.Sm8250DT.cfg b/Config/DualBoot.KernelWarpper.cfg similarity index 67% rename from Config/DualBoot.Sm8250DT.cfg rename to Config/DualBoot.KernelWarpper.cfg index 2a84186..7393e57 100644 --- a/Config/DualBoot.Sm8250DT.cfg +++ b/Config/DualBoot.KernelWarpper.cfg @@ -1,19 +1,19 @@ [Pack] Shim=../build/Shims/Shim.KernelWrapper.bin -Output=../build/PatchedKernel.Sm8250DT +Output=../build/PatchedKernel Loader=../build/ShimLoader/ShimLoader.bin -Default=Image-A +Default=Image Timeout=0 -[Image-A] -Name=SM8250 Mainline Linux +[Image] +Name=Linux Path=/path/to/Image BaseImage=true Align=0x200000 [Image-DTB] Name=Mainline Device Tree -Path=../../firmware/mainline.dtb +Path=/path/to/mainline.dtb BaseImage=false CopyTo=0x9FC00000 CopySizeMax=0x00300000 diff --git a/Config/DualBoot.Sm7125.cfg b/Config/DualBoot.Miatoll.cfg similarity index 65% rename from Config/DualBoot.Sm7125.cfg rename to Config/DualBoot.Miatoll.cfg index 8ca08bf..9318c0b 100644 --- a/Config/DualBoot.Sm7125.cfg +++ b/Config/DualBoot.Miatoll.cfg @@ -1,19 +1,19 @@ [Pack] Shim=../build/Shims/Shim.Miatoll.bin -Output=../build/PatchedKernel.Sm7125 +Output=../build/PatchedKernel Loader=../build/ShimLoader/ShimLoader.bin -Default=Image-A +Default=Image Timeout=0 -[Image-A] -Name=Redmi Note 9S Linux +[Image] +Name=Android/Linux Path=/path/to/Image BaseImage=true Align=0x200000 [Image-UEFI] Name=UEFI -Path=../../firmware/SM7125_EFI.fd +Path=/path/to/SM7125_EFI.fd BaseImage=false CopyTo=0x9FC00000 CopySizeMax=0x00300000 \ No newline at end of file diff --git a/Config/DualBoot.Sm8250.cfg b/Config/DualBoot.Pipa.cfg similarity index 65% rename from Config/DualBoot.Sm8250.cfg rename to Config/DualBoot.Pipa.cfg index 3d53a64..544cb79 100644 --- a/Config/DualBoot.Sm8250.cfg +++ b/Config/DualBoot.Pipa.cfg @@ -1,19 +1,19 @@ [Pack] Shim=../build/Shims/Shim.Pipa.bin -Output=../build/PatchedKernel.Sm8250 +Output=../build/PatchedKernel Loader=../build/ShimLoader/ShimLoader.bin -Default=Image-A +Default=Image Timeout=0 -[Image-A] -Name=Xiaomi Pad 6 Linux +[Image] +Name=Android/Linux Path=/path/to/Image BaseImage=true Align=0x200000 [Image-UEFI] Name=UEFI -Path=../../firmware/SM8250_EFI.fd +Path=/path/to/SM8250_EFI.fd BaseImage=false CopyTo=0x9FC00000 CopySizeMax=0x00300000 From 3500828f500cc0ef2ddec788108463dc8079019b Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Tue, 4 Aug 2026 21:25:23 +0800 Subject: [PATCH 07/12] merge shim loader code files. --- ShimLoader/ShimLoader.S | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/ShimLoader/ShimLoader.S b/ShimLoader/ShimLoader.S index 9faf87f..8dca1a7 100644 --- a/ShimLoader/ShimLoader.S +++ b/ShimLoader/ShimLoader.S @@ -1,4 +1,40 @@ -.include "DummyHead.S" +/* DO NOT EDIT START */ +// 16 instructions in total. +// +// Codes here will not be injected into the kernel, so they won't be run. +// This header is used to make things clearer. +// Size of this header must be 0x40 after compiling to binary. + +_KernelHead: + b 0x40 // Code 1, go to ShimLoader + +_LinuxStart: + b 0x0 // Code 2, go to Linux Kernel + .ascii "SHIM" // ShimLoader magic + .ascii "LDR\0" + .long 0 // Effective Image size L + .long 0 // Effective Image size H + .long 0 // Flags L + .long 0 // Flags H + +_CopyAddress: +_ManifestOffset: + .long 0 // reserved2 LWORD, Manifest offset L + .long 0 // reserved2 HWORD, Manifest offset H + +_CopySize: +_ManifestSize: + .long 0 // reserved3 LWORD, Manifest size L + .long 0 // reserved3 HWORD, Manifest size H + +_BaseImageSize: + .long 0 // reserved4, Kernel Size L + .long 0 // reserved4, Kernel Size H + + .ascii "ARM\x64" // magic_64 + .long 0 // reserve5 + +/* DO NOT EDIT END */ _ShimLoaderStart: adr x4, _KernelHead From 1641dd47b4dc24018e8838b09abff9dcbdf6e238 Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Tue, 4 Aug 2026 21:41:23 +0800 Subject: [PATCH 08/12] Merge CMakeLists. --- CMakeLists.txt | 21 +++++++++++++++++++++ ShimLoader/CMakeLists.txt | 23 ++--------------------- ShimLoader/DummyHead.S | 37 ------------------------------------- Shims/CMakeLists.txt | 23 ++--------------------- 4 files changed, 25 insertions(+), 79 deletions(-) delete mode 100644 ShimLoader/DummyHead.S diff --git a/CMakeLists.txt b/CMakeLists.txt index f4c63ca..93f85a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,27 @@ project(MultiBootKernelPatcher C) option(BUILD_SHIM_LOADER "Build the ARM64 ShimLoader binary" ON) option(BUILD_SHIMS "Build the simplified ARM64 Shims" ${BUILD_SHIM_LOADER}) +if (BUILD_SHIM_LOADER OR BUILD_SHIMS) + if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + # MinGW compiler download link: + # https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads + set(ARM64_TOOLCHAIN_PREFIX + "E:/aarch64-gcc/aarch64-none-elf/bin/" + CACHE STRING "ARM64 binutils path and executable prefix") + elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + set(ARM64_TOOLCHAIN_PREFIX "aarch64-linux-gnu-" + CACHE STRING "ARM64 binutils path and executable prefix") + else () + message(FATAL_ERROR + "Building ARM64 Shim binaries on ${CMAKE_HOST_SYSTEM_NAME} is not supported") + endif () + + set(ARM64_AS "${ARM64_TOOLCHAIN_PREFIX}as" + CACHE FILEPATH "ARM64 assembler") + set(ARM64_OBJCOPY "${ARM64_TOOLCHAIN_PREFIX}objcopy" + CACHE FILEPATH "ARM64 objcopy") +endif () + set(CMAKE_C_STANDARD 11) if (MSVC) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") diff --git a/ShimLoader/CMakeLists.txt b/ShimLoader/CMakeLists.txt index 6dd4bdd..f968a3e 100644 --- a/ShimLoader/CMakeLists.txt +++ b/ShimLoader/CMakeLists.txt @@ -1,22 +1,3 @@ -cmake_minimum_required(VERSION 3.27) -project(ShimLoader ASM) - -# Set up compilers -if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - # Windows - # MinGW compiler download link: - # https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads - set(CROSS_COMPILE "E:/aarch64-gcc/aarch64-none-elf/bin/") -elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") - # Linux - set(CROSS_COMPILE "aarch64-linux-gnu-") -else () - message("build on ${CMAKE_HOST_SYSTEM_NAME} is not supported") -endif () - -set(CMAKE_AS ${CROSS_COMPILE}as) -set(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy) - set(SHIM_LOADER_ELF_DIR "${CMAKE_CURRENT_BINARY_DIR}/elf") file(MAKE_DIRECTORY ${SHIM_LOADER_ELF_DIR}) @@ -28,7 +9,7 @@ foreach (CURRENT_ASM_SOURCE_SUFFIX ${ASM_SOURCES}) add_custom_target( ${CURRENT_ASM_SOURCE}_ELF ALL - ${CMAKE_AS} + ${ARM64_AS} "${CMAKE_CURRENT_SOURCE_DIR}/${CURRENT_ASM_SOURCE}.S" -I ${CMAKE_CURRENT_SOURCE_DIR} -o "${SHIM_LOADER_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" @@ -36,7 +17,7 @@ foreach (CURRENT_ASM_SOURCE_SUFFIX ${ASM_SOURCES}) add_custom_target( ${CURRENT_ASM_SOURCE}_BIN ALL - ${CMAKE_OBJCOPY} -O binary + ${ARM64_OBJCOPY} -O binary "${SHIM_LOADER_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" "${CURRENT_ASM_SOURCE}.bin" DEPENDS ${CURRENT_ASM_SOURCE}_ELF diff --git a/ShimLoader/DummyHead.S b/ShimLoader/DummyHead.S deleted file mode 100644 index 5b1c2dd..0000000 --- a/ShimLoader/DummyHead.S +++ /dev/null @@ -1,37 +0,0 @@ -// DO NOT EDIT THIS FILE -// 16 instruction in total. -// -// Codes here will not be inject into kernel actually, so it won't be run. -// This file is used to make things more clearly. -// Size of this file must be 0x40 after compiling to binary. - -_KernelHead: - b 0x40 // Code 1, go to ShimLoader - -_LinuxStart: - b 0x0 // Code2, Goto Linux Kernel - .ascii "SHIM" // ShimLoader magic - .ascii "LDR\0" - .long 0 // Effective Image size L - .long 0 // Effective Image size H - .long 0 // Flags L - .long 0 // Flags H - -_CopyAddress: -_ManifestOffset: - .long 0 // reserved2 LWORD - .long 0 // reserved2 HWORD - -_CopySize: -_ManifestSize: - .long 0 // reserved3 LWORD - .long 0 // reserved3 HWORD - -_BaseImageSize: - .long 0 // reserved4, Kernel Size L - .long 0 // reserved4, Kernel Size H - - .ascii "ARM\x64" // magic_64 - .long 0 // reserve5 - -/* Do not remove the last line */ diff --git a/Shims/CMakeLists.txt b/Shims/CMakeLists.txt index a001820..fc8dbde 100644 --- a/Shims/CMakeLists.txt +++ b/Shims/CMakeLists.txt @@ -1,22 +1,3 @@ -cmake_minimum_required(VERSION 3.27) -project(Shims ASM) - -# Set up compilers -if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - # Windows - # MinGW compiler download link: - # https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads - set(CROSS_COMPILE "E:/aarch64-gcc/aarch64-none-elf/bin/") -elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") - # Linux - set(CROSS_COMPILE "aarch64-linux-gnu-") -else () - message("build on ${CMAKE_HOST_SYSTEM_NAME} is not supported") -endif () - -set(CMAKE_AS ${CROSS_COMPILE}as) -set(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy) - set(SHIM_ELF_DIR "${CMAKE_CURRENT_BINARY_DIR}/elf") file(MAKE_DIRECTORY ${SHIM_ELF_DIR}) @@ -29,7 +10,7 @@ foreach (CURRENT_ASM_SOURCE_SUFFIX ${ASM_SOURCES}) add_custom_target( ${CURRENT_ASM_SOURCE}_ELF ALL - ${CMAKE_AS} + ${ARM64_AS} "${CMAKE_CURRENT_SOURCE_DIR}/${CURRENT_ASM_SOURCE}.S" -I ${CMAKE_CURRENT_SOURCE_DIR} -o "${SHIM_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" @@ -37,7 +18,7 @@ foreach (CURRENT_ASM_SOURCE_SUFFIX ${ASM_SOURCES}) add_custom_target( ${CURRENT_ASM_SOURCE}_BIN ALL - ${CMAKE_OBJCOPY} -O binary + ${ARM64_OBJCOPY} -O binary "${SHIM_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" "${CURRENT_ASM_SOURCE}.bin" DEPENDS ${CURRENT_ASM_SOURCE}_ELF From 9deb91863f49018d63860b0f9bc7b825d2403f6f Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Tue, 4 Aug 2026 22:56:56 +0800 Subject: [PATCH 09/12] sort codes. --- CMakeLists.txt | 15 +++++++++++++-- shim.h => inc/shim.h | 0 utils.h => inc/utils.h | 0 HDRTool.c => src/HDRTool.c | 0 patcher.c => src/patcher.c | 0 shim.c => src/shim.c | 0 shim_config.c => src/shim_config.c | 0 utils.c => src/utils.c | 0 8 files changed, 13 insertions(+), 2 deletions(-) rename shim.h => inc/shim.h (100%) rename utils.h => inc/utils.h (100%) rename HDRTool.c => src/HDRTool.c (100%) rename patcher.c => src/patcher.c (100%) rename shim.c => src/shim.c (100%) rename shim_config.c => src/shim_config.c (100%) rename utils.c => src/utils.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 93f85a8..85de2ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,13 +52,24 @@ target_include_directories(inih_static PUBLIC set(INIH_TARGET inih_static) # Compile the patcher. -add_executable(MultiBootKernelPatcher patcher.c shim.c shim_config.c utils.c) +add_executable(MultiBootKernelPatcher + src/patcher.c + src/shim.c + src/shim_config.c + src/utils.c +) +target_include_directories(MultiBootKernelPatcher PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/inc +) if (MSVC) set_property(TARGET MultiBootKernelPatcher PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif () target_link_libraries(MultiBootKernelPatcher PRIVATE ${INIH_TARGET}) -add_executable(HDRTool HDRTool.c utils.c) +add_executable(HDRTool src/HDRTool.c src/utils.c) +target_include_directories(HDRTool PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/inc +) if (MSVC) set_property(TARGET HDRTool PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") diff --git a/shim.h b/inc/shim.h similarity index 100% rename from shim.h rename to inc/shim.h diff --git a/utils.h b/inc/utils.h similarity index 100% rename from utils.h rename to inc/utils.h diff --git a/HDRTool.c b/src/HDRTool.c similarity index 100% rename from HDRTool.c rename to src/HDRTool.c diff --git a/patcher.c b/src/patcher.c similarity index 100% rename from patcher.c rename to src/patcher.c diff --git a/shim.c b/src/shim.c similarity index 100% rename from shim.c rename to src/shim.c diff --git a/shim_config.c b/src/shim_config.c similarity index 100% rename from shim_config.c rename to src/shim_config.c diff --git a/utils.c b/src/utils.c similarity index 100% rename from utils.c rename to src/utils.c From 72e4f59bdca45c1b195df6ab0f64d08966c0fec7 Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Wed, 5 Aug 2026 15:55:22 +0800 Subject: [PATCH 10/12] update packer and configs. --- .github/workflows/cmake-multi-platform.yml | 36 +-- CMakeLists.txt | 8 +- Config/DualBoot.AYAPS.cfg | 9 +- Config/DualBoot.Hotdog.cfg | 9 +- Config/DualBoot.KernelWarpper.cfg | 8 +- Config/DualBoot.Miatoll.cfg | 9 +- Config/DualBoot.Pipa.cfg | 7 +- Config/Shim.Sample.cfg | 10 +- README.md | 3 +- ShimLoader/CMakeLists.txt | 25 -- ShimLoader/ShimLoader.S | 48 --- Shims/CMakeLists.txt | 9 +- Shims/CommonTail.S | 75 ++++- Shims/DummyHead.S | 24 +- Shims/Shim.AYAPS.S | 2 +- Shims/Shim.Andromeda.S | 2 +- Shims/Shim.Cepheus.S | 2 +- Shims/Shim.Hotdog.S | 2 +- Shims/Shim.KernelWrapper.S | 66 ++++- Shims/Shim.Miatoll.S | 2 +- Shims/Shim.Nabu.S | 2 +- Shims/Shim.Pipa.S | 2 +- inc/shim.h | 40 ++- src/shim.c | 324 ++++++++++----------- src/shim_config.c | 101 ++++++- 25 files changed, 454 insertions(+), 371 deletions(-) delete mode 100644 ShimLoader/CMakeLists.txt delete mode 100644 ShimLoader/ShimLoader.S diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 1ef8496..6094d75 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -19,35 +19,35 @@ jobs: os: windows-latest arch: x64 cmake_arch: x64 - build_shim_loader: 'OFF' + build_shims: 'OFF' executable_suffix: .exe executable_directory: Release - name: Windows ARM64 os: windows-11-arm arch: arm64 cmake_arch: ARM64 - build_shim_loader: 'OFF' + build_shims: 'OFF' executable_suffix: .exe executable_directory: Release - name: Linux x64 os: ubuntu-latest arch: x64 c_compiler: clang - build_shim_loader: 'ON' + build_shims: 'ON' executable_suffix: '' executable_directory: . - name: Linux ARM64 os: ubuntu-24.04-arm arch: arm64 c_compiler: clang - build_shim_loader: 'OFF' + build_shims: 'OFF' executable_suffix: '' executable_directory: . - name: macOS x64 os: macos-15-intel arch: x64 c_compiler: clang - build_shim_loader: 'OFF' + build_shims: 'OFF' executable_suffix: '' executable_directory: . @@ -62,8 +62,8 @@ jobs: sudo apt-get update sudo apt-get install --yes build-essential cmake git - - name: Install ShimLoader toolchain - if: matrix.build_shim_loader == 'ON' + - name: Install ARM64 Shim toolchain + if: matrix.build_shims == 'ON' run: sudo apt-get install --yes binutils-aarch64-linux-gnu - name: Configure CMake on Windows @@ -73,7 +73,7 @@ jobs: -B "${{ github.workspace }}/build" -A ${{ matrix.cmake_arch }} -DCMAKE_BUILD_TYPE=Release - -DBUILD_SHIM_LOADER=${{ matrix.build_shim_loader }} + -DBUILD_SHIMS=${{ matrix.build_shims }} - name: Configure CMake on Unix if: runner.os != 'Windows' @@ -82,7 +82,7 @@ jobs: -B "${{ github.workspace }}/build" -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} -DCMAKE_BUILD_TYPE=Release - -DBUILD_SHIM_LOADER=${{ matrix.build_shim_loader }} + -DBUILD_SHIMS=${{ matrix.build_shims }} - name: Build host tools run: >- @@ -90,12 +90,12 @@ jobs: --config Release --target MultiBootKernelPatcher HDRTool - - name: Build ShimLoader - if: matrix.build_shim_loader == 'ON' + - name: Build Shims + if: matrix.build_shims == 'ON' run: >- cmake --build "${{ github.workspace }}/build" --config Release - --target ShimLoader_BIN Shims + --target Shims - name: Stage host tools shell: bash @@ -111,16 +111,8 @@ jobs: path: artifacts/tools if-no-files-found: error - - name: Upload ShimLoader - if: matrix.build_shim_loader == 'ON' - uses: actions/upload-artifact@v4 - with: - name: ShimLoader - path: build/ShimLoader/ShimLoader.bin - if-no-files-found: error - - name: Upload simplified Shims - if: matrix.build_shim_loader == 'ON' + if: matrix.build_shims == 'ON' uses: actions/upload-artifact@v4 with: name: Shims @@ -128,7 +120,7 @@ jobs: if-no-files-found: error - name: Upload configs - if: matrix.build_shim_loader == 'ON' + if: matrix.build_shims == 'ON' uses: actions/upload-artifact@v4 with: name: Configs diff --git a/CMakeLists.txt b/CMakeLists.txt index 85de2ce..cafc089 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,9 @@ cmake_minimum_required(VERSION 3.18) project(MultiBootKernelPatcher C) -option(BUILD_SHIM_LOADER "Build the ARM64 ShimLoader binary" ON) -option(BUILD_SHIMS "Build the simplified ARM64 Shims" ${BUILD_SHIM_LOADER}) +option(BUILD_SHIMS "Build the ARM64 Shim binaries" ON) -if (BUILD_SHIM_LOADER OR BUILD_SHIMS) +if (BUILD_SHIMS) if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") # MinGW compiler download link: # https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads @@ -75,9 +74,6 @@ if (MSVC) MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif () -if (BUILD_SHIM_LOADER) - add_subdirectory(ShimLoader) -endif () if (BUILD_SHIMS) add_subdirectory(Shims) endif () \ No newline at end of file diff --git a/Config/DualBoot.AYAPS.cfg b/Config/DualBoot.AYAPS.cfg index 1d4aff0..f7dd219 100644 --- a/Config/DualBoot.AYAPS.cfg +++ b/Config/DualBoot.AYAPS.cfg @@ -1,19 +1,24 @@ [Pack] Shim=../build/Shims/Shim.AYAPS.bin Output=../build/PatchedKernel -Loader=../build/ShimLoader/ShimLoader.bin Default=Image Timeout=0 [Image] Name=Android/Linux Path=/path/to/Image +Type=Linux BaseImage=true Align=0x200000 [Image-UEFI] Name=UEFI Path=/path/to/SM8550_EFI.fd +Type=FreeExec BaseImage=false CopyTo=0xC7CC0000 -CopySizeMax=0x00400000 \ No newline at end of file +CopySizeMax=0x00400000 +EntryOffset=0 + +[Manifest] +Type=Manifest \ No newline at end of file diff --git a/Config/DualBoot.Hotdog.cfg b/Config/DualBoot.Hotdog.cfg index 13b076c..9b201d0 100644 --- a/Config/DualBoot.Hotdog.cfg +++ b/Config/DualBoot.Hotdog.cfg @@ -1,19 +1,24 @@ [Pack] Shim=../build/Shims/Shim.Hotdog.bin Output=../build/PatchedKernel -Loader=../build/ShimLoader/ShimLoader.bin Default=Image Timeout=0 [Image] Name=Android/Linux Path=/path/to/Image +Type=Linux BaseImage=true Align=0x200000 [Image-UEFI] Name=UEFI Path=/path/to/SM8150_EFI.fd +Type=FreeExec BaseImage=false CopyTo=0x9FC00000 -CopySizeMax=0x00300000 \ No newline at end of file +CopySizeMax=0x00300000 +EntryOffset=0 + +[Manifest] +Type=Manifest \ No newline at end of file diff --git a/Config/DualBoot.KernelWarpper.cfg b/Config/DualBoot.KernelWarpper.cfg index 7393e57..ca282a4 100644 --- a/Config/DualBoot.KernelWarpper.cfg +++ b/Config/DualBoot.KernelWarpper.cfg @@ -1,20 +1,24 @@ [Pack] Shim=../build/Shims/Shim.KernelWrapper.bin Output=../build/PatchedKernel -Loader=../build/ShimLoader/ShimLoader.bin Default=Image Timeout=0 [Image] Name=Linux Path=/path/to/Image +Type=Linux BaseImage=true Align=0x200000 [Image-DTB] Name=Mainline Device Tree Path=/path/to/mainline.dtb +Type=DTB BaseImage=false CopyTo=0x9FC00000 CopySizeMax=0x00300000 -Align=8 \ No newline at end of file +Align=8 + +[Manifest] +Type=Manifest \ No newline at end of file diff --git a/Config/DualBoot.Miatoll.cfg b/Config/DualBoot.Miatoll.cfg index 9318c0b..4d844ee 100644 --- a/Config/DualBoot.Miatoll.cfg +++ b/Config/DualBoot.Miatoll.cfg @@ -1,19 +1,24 @@ [Pack] Shim=../build/Shims/Shim.Miatoll.bin Output=../build/PatchedKernel -Loader=../build/ShimLoader/ShimLoader.bin Default=Image Timeout=0 [Image] Name=Android/Linux Path=/path/to/Image +Type=Linux BaseImage=true Align=0x200000 [Image-UEFI] Name=UEFI Path=/path/to/SM7125_EFI.fd +Type=FreeExec BaseImage=false CopyTo=0x9FC00000 -CopySizeMax=0x00300000 \ No newline at end of file +CopySizeMax=0x00300000 +EntryOffset=0 + +[Manifest] +Type=Manifest \ No newline at end of file diff --git a/Config/DualBoot.Pipa.cfg b/Config/DualBoot.Pipa.cfg index 544cb79..5a3fcbd 100644 --- a/Config/DualBoot.Pipa.cfg +++ b/Config/DualBoot.Pipa.cfg @@ -1,19 +1,24 @@ [Pack] Shim=../build/Shims/Shim.Pipa.bin Output=../build/PatchedKernel -Loader=../build/ShimLoader/ShimLoader.bin Default=Image Timeout=0 [Image] Name=Android/Linux Path=/path/to/Image +Type=Linux BaseImage=true Align=0x200000 [Image-UEFI] Name=UEFI Path=/path/to/SM8250_EFI.fd +Type=FreeExec BaseImage=false CopyTo=0x9FC00000 CopySizeMax=0x00300000 +EntryOffset=0 + +[Manifest] +Type=Manifest diff --git a/Config/Shim.Sample.cfg b/Config/Shim.Sample.cfg index 5dd1504..8b87f39 100644 --- a/Config/Shim.Sample.cfg +++ b/Config/Shim.Sample.cfg @@ -2,7 +2,6 @@ # Paths are resolved relative to this configuration file. Shim=/path/to/shim.bin Output=../build/ShimImage -Loader=../build/ShimLoader/ShimLoader.bin # Default references an Image section. Timeout=0 waits indefinitely. Default=Image-A @@ -11,12 +10,14 @@ Timeout=5000 [Image-A] Name=Android Linux Path=/path/to/base-Image +Type=Linux BaseImage=true Align=0x200000 [Image-B] Name=Recovery Linux Path=/path/to/recovery-Image +Type=Linux BaseImage=false Align=0x200000 @@ -24,7 +25,12 @@ Align=0x200000 # [Image-UEFI] # Name=UEFI # Path=../../firmware/UEFI.fd +# Type=FreeExec # BaseImage=false # CopyTo=0x9FC00000 # CopySizeMax=0x00300000 -# Align defaults to 4 for UEFI-style executable payloads. \ No newline at end of file +# EntryOffset=0 +# Align defaults to 4 for UEFI-style executable payloads. + +[Manifest] +Type=Manifest \ No newline at end of file diff --git a/README.md b/README.md index ec78c66..ca79edd 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,7 @@ Shim runtime, and multiple bootable images from one INI configuration. MultiBootKernelPatcher Config/Shim.Sample.cfg ``` - Notice - + ShimLoader binaries are generated under `output/ShimLoader/`. - + Simplified assembly Shim binaries are generated under `output/Shims/`. + + Device Shim blobs are generated under `output/Shims/`. ## See More - You can go to our [document website](https://aloha.firmware.icu/) to get more infomation about the Multi-Boot Kernel Patcher. diff --git a/ShimLoader/CMakeLists.txt b/ShimLoader/CMakeLists.txt deleted file mode 100644 index f968a3e..0000000 --- a/ShimLoader/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -set(SHIM_LOADER_ELF_DIR "${CMAKE_CURRENT_BINARY_DIR}/elf") -file(MAKE_DIRECTORY ${SHIM_LOADER_ELF_DIR}) - -# Compile the generic ShimLoader. -set(ASM_SOURCES ShimLoader.S) -foreach (CURRENT_ASM_SOURCE_SUFFIX ${ASM_SOURCES}) - string(REGEX REPLACE "\\.S$" "" CURRENT_ASM_SOURCE - "${CURRENT_ASM_SOURCE_SUFFIX}") - - add_custom_target( - ${CURRENT_ASM_SOURCE}_ELF ALL - ${ARM64_AS} - "${CMAKE_CURRENT_SOURCE_DIR}/${CURRENT_ASM_SOURCE}.S" - -I ${CMAKE_CURRENT_SOURCE_DIR} - -o "${SHIM_LOADER_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" - ) - - add_custom_target( - ${CURRENT_ASM_SOURCE}_BIN ALL - ${ARM64_OBJCOPY} -O binary - "${SHIM_LOADER_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" - "${CURRENT_ASM_SOURCE}.bin" - DEPENDS ${CURRENT_ASM_SOURCE}_ELF - ) -endforeach () diff --git a/ShimLoader/ShimLoader.S b/ShimLoader/ShimLoader.S deleted file mode 100644 index 8dca1a7..0000000 --- a/ShimLoader/ShimLoader.S +++ /dev/null @@ -1,48 +0,0 @@ -/* DO NOT EDIT START */ -// 16 instructions in total. -// -// Codes here will not be injected into the kernel, so they won't be run. -// This header is used to make things clearer. -// Size of this header must be 0x40 after compiling to binary. - -_KernelHead: - b 0x40 // Code 1, go to ShimLoader - -_LinuxStart: - b 0x0 // Code 2, go to Linux Kernel - .ascii "SHIM" // ShimLoader magic - .ascii "LDR\0" - .long 0 // Effective Image size L - .long 0 // Effective Image size H - .long 0 // Flags L - .long 0 // Flags H - -_CopyAddress: -_ManifestOffset: - .long 0 // reserved2 LWORD, Manifest offset L - .long 0 // reserved2 HWORD, Manifest offset H - -_CopySize: -_ManifestSize: - .long 0 // reserved3 LWORD, Manifest size L - .long 0 // reserved3 HWORD, Manifest size H - -_BaseImageSize: - .long 0 // reserved4, Kernel Size L - .long 0 // reserved4, Kernel Size H - - .ascii "ARM\x64" // magic_64 - .long 0 // reserve5 - -/* DO NOT EDIT END */ - -_ShimLoaderStart: - adr x4, _KernelHead - ldr x5, _ManifestOffset - add x5, x4, x5 - ldr x6, _ManifestSize - ldr x7, _BaseImageSize - add x7, x4, x7 - br x7 - -_ShimLoaderEnd: diff --git a/Shims/CMakeLists.txt b/Shims/CMakeLists.txt index fc8dbde..bdc41cf 100644 --- a/Shims/CMakeLists.txt +++ b/Shims/CMakeLists.txt @@ -1,8 +1,9 @@ set(SHIM_ELF_DIR "${CMAKE_CURRENT_BINARY_DIR}/elf") file(MAKE_DIRECTORY ${SHIM_ELF_DIR}) -# Compile each simplified Shim. -file(GLOB ASM_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "Shim.*.S") +# Compile each Shim blob. +file(GLOB ASM_SOURCES CONFIGURE_DEPENDS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + "Shim.*.S") set(SHIM_BIN_TARGETS) foreach (CURRENT_ASM_SOURCE_SUFFIX ${ASM_SOURCES}) string(REGEX REPLACE "\\.S$" "" CURRENT_ASM_SOURCE @@ -14,6 +15,10 @@ foreach (CURRENT_ASM_SOURCE_SUFFIX ${ASM_SOURCES}) "${CMAKE_CURRENT_SOURCE_DIR}/${CURRENT_ASM_SOURCE}.S" -I ${CMAKE_CURRENT_SOURCE_DIR} -o "${SHIM_ELF_DIR}/${CURRENT_ASM_SOURCE}.elf" + DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/${CURRENT_ASM_SOURCE}.S" + "${CMAKE_CURRENT_SOURCE_DIR}/DummyHead.S" + "${CMAKE_CURRENT_SOURCE_DIR}/CommonTail.S" ) add_custom_target( diff --git a/Shims/CommonTail.S b/Shims/CommonTail.S index fdaafdd..99d42fb 100644 --- a/Shims/CommonTail.S +++ b/Shims/CommonTail.S @@ -1,30 +1,75 @@ -// Shared implementation for simplified assembly Shims. The ShimLoader ABI -// supplies x4 = base Linux and x5/x6 = manifest address/size; simplified -// Shims intentionally ignore x5/x6 and use direct header parameters instead. +// Shared implementation for two-entry assembly Shims. The entry BL leaves +// x30 at BaseImage + 4; x23 preserves it while the Shim selects an image. _Linux: mov x0, x19 mov x1, x20 mov x2, x21 mov x3, x22 - mov x8, x23 - add x8, x8, #4 - br x8 + mov x30, x23 + ret _UEFI: - ldr x8, _PayloadOffset - add x8, x23, x8 - ldr x9, _CopyAddress - ldr x10, _CopySize - mov x11, x9 + sub x24, x23, #4 + ldr x25, [x24, #0x20] + cbz x25, _Linux + ldr x8, [x24, #0x28] + cmp x8, #224 + b.lo _Linux + add x25, x24, x25 + + ldr x8, [x25] + ldr x9, _ManifestMagic + cmp x8, x9 + b.ne _Linux + ldr w8, [x25, #8] + cmp w8, #4 + b.ne _Linux + ldr w8, [x25, #12] + cmp w8, #64 + b.ne _Linux + ldr w8, [x25, #16] + cmp w8, #80 + b.ne _Linux + ldr w8, [x25, #20] + cmp w8, #2 + b.lo _Linux + + add x25, x25, #64 + add x25, x25, #80 + ldr w8, [x25, #48] + cbz w8, _Linux + cmp w8, #3 + b.hi _Linux + ldr x10, [x25, #40] + cmp x10, #4 + b.lo _Linux + ldr x11, [x25, #64] + sub x12, x10, #4 + cmp x11, x12 + b.hi _Linux + + ldr x8, [x25, #32] + add x8, x24, x8 + ldr w9, [x25, #52] + tbz w9, #1, _ExecuteEntry + ldr x9, [x25, #56] + cbz x9, _Linux + mov x24, x9 mov x12, x10 bl _CopyLoop - mov x0, x11 + mov x0, x24 mov x1, x12 bl _SyncImageCache + mov x8, x24 + +_ExecuteEntry: + add x24, x8, x11 adr x0, _ShimImageEnd adr x1, _ShimImageEnd - br x11 + mov x2, x21 + mov x3, x22 + br x24 _CopyLoop: cbz x10, 2f @@ -69,6 +114,10 @@ _SyncImageCache: _Dead: b _Dead +.align 3 +_ManifestMagic: + .ascii "SHIMMF\0\0" + .text .align 4 diff --git a/Shims/DummyHead.S b/Shims/DummyHead.S index 44aea95..7e71e7a 100644 --- a/Shims/DummyHead.S +++ b/Shims/DummyHead.S @@ -1,24 +1,4 @@ -// Simplified Shim Image header. The packer writes direct payload parameters -// here; these Shims do not parse the manifest blob. - -_ShimHead: - b _ShimStart - - nop - .ascii "SHIMASM\0" - .quad _ShimImageEnd - _ShimHead - .quad 0 - -_PayloadOffset: - .quad 0 - -_CopyAddress: - .quad 0 - -_CopySize: - .quad 0 - - .ascii "ARM\x64" - .long 0 +.text +.align 2 /* Do not remove the last line */ diff --git a/Shims/Shim.AYAPS.S b/Shims/Shim.AYAPS.S index be8d405..530d2ae 100644 --- a/Shims/Shim.AYAPS.S +++ b/Shims/Shim.AYAPS.S @@ -6,7 +6,7 @@ _ShimStart: mov x20, x1 mov x21, x2 mov x22, x3 - mov x23, x4 + mov x23, x30 movz x4, #0xd000 movk x4, #0x0f16, lsl #16 // Store 0xf16d000 to x4, which is io control of gpio109 on sm8550. str wzr, [x4], #4 // Set 0 in control reg, then x4 add 4 diff --git a/Shims/Shim.Andromeda.S b/Shims/Shim.Andromeda.S index 0cb264e..1431386 100644 --- a/Shims/Shim.Andromeda.S +++ b/Shims/Shim.Andromeda.S @@ -6,7 +6,7 @@ _ShimStart: mov x20, x1 mov x21, x2 mov x22, x3 - mov x23, x4 + mov x23, x30 movz x4, #0x9000 movk x4, #0x392, lsl #16 // Store 0x3929000 to x4, which is io control of gpio 41. ldr w5, [x4] diff --git a/Shims/Shim.Cepheus.S b/Shims/Shim.Cepheus.S index 6582b2c..2bd8df5 100644 --- a/Shims/Shim.Cepheus.S +++ b/Shims/Shim.Cepheus.S @@ -6,7 +6,7 @@ _ShimStart: mov x20, x1 mov x21, x2 mov x22, x3 - mov x23, x4 + mov x23, x30 movz x4, #0x1000 movk x4, #0x396, lsl #16 // Store 0x3961004 to x4, which is io status reg of gpio 97. ldr w5, [x4] diff --git a/Shims/Shim.Hotdog.S b/Shims/Shim.Hotdog.S index 4b8ece4..e466e81 100644 --- a/Shims/Shim.Hotdog.S +++ b/Shims/Shim.Hotdog.S @@ -6,7 +6,7 @@ _ShimStart: mov x20, x1 mov x21, x2 mov x22, x3 - mov x23, x4 + mov x23, x30 movz x4, #0xd000 movk x4, #0x317, lsl #16 // Store 0x317d004 to x4, which is control reg of gpio 125. str wzr, [x4], #4 // Set 0 in control reg, then x4 add 4 diff --git a/Shims/Shim.KernelWrapper.S b/Shims/Shim.KernelWrapper.S index 953e9d7..6591ab3 100644 --- a/Shims/Shim.KernelWrapper.S +++ b/Shims/Shim.KernelWrapper.S @@ -1,8 +1,4 @@ -/* - * Simplified assembly Shim for bypassing Android DTB compatibility checks. - * The packer writes payload offset, CopyTo and actual payload size directly - * into this Shim Image header. No manifest parsing is performed here. - */ +/* Assembly Shim for replacing the Linux DTB before entering BaseImage. */ .include "DummyHead.S" _ShimStart: @@ -10,12 +6,46 @@ _ShimStart: mov x20, x1 mov x21, x2 mov x22, x3 - mov x23, x4 + mov x23, x30 + sub x24, x23, #4 - ldr x8, _PayloadOffset - add x8, x23, x8 - ldr x9, _CopyAddress - ldr x10, _CopySize + ldr x25, [x24, #0x20] + cbz x25, _Linux + ldr x8, [x24, #0x28] + cmp x8, #224 + b.lo _Linux + add x25, x24, x25 + ldr x8, [x25] + ldr x9, _ManifestMagic + cmp x8, x9 + b.ne _Linux + ldr w8, [x25, #8] + cmp w8, #4 + b.ne _Linux + ldr w8, [x25, #12] + cmp w8, #64 + b.ne _Linux + ldr w8, [x25, #16] + cmp w8, #80 + b.ne _Linux + ldr w8, [x25, #20] + cmp w8, #2 + b.lo _Linux + + add x25, x25, #64 + add x25, x25, #80 + ldr w8, [x25, #48] + cmp w8, #5 + b.ne _Linux + ldr w8, [x25, #52] + tbz w8, #1, _Linux + + ldr x8, [x25, #32] + add x8, x24, x8 + ldr x9, [x25, #56] + cbz x9, _Linux + ldr x10, [x25, #40] + cbz x10, _Linux mov x11, x9 mov x12, x10 bl _CopyLoop @@ -28,8 +58,16 @@ _ShimStart: mov x1, x20 mov x2, x21 mov x3, x22 - add x8, x23, #4 - br x8 + mov x30, x23 + ret + +_Linux: + mov x0, x19 + mov x1, x20 + mov x2, x21 + mov x3, x22 + mov x30, x23 + ret _CopyLoop: cbz x10, 2f @@ -58,6 +96,10 @@ _SyncDataCache: dsb sy ret +.align 3 +_ManifestMagic: + .ascii "SHIMMF\0\0" + .text .align 4 diff --git a/Shims/Shim.Miatoll.S b/Shims/Shim.Miatoll.S index ba3302a..501341d 100644 --- a/Shims/Shim.Miatoll.S +++ b/Shims/Shim.Miatoll.S @@ -6,7 +6,7 @@ _ShimStart: mov x20, x1 mov x21, x2 mov x22, x3 - mov x23, x4 + mov x23, x30 movz x4, #0x6000 movk x4, #0xE60, lsl #16 // Store 0xE606000 to x4, which is address of 0xC0 spmi channel movz x5, #0x0081 diff --git a/Shims/Shim.Nabu.S b/Shims/Shim.Nabu.S index a15d289..d286958 100644 --- a/Shims/Shim.Nabu.S +++ b/Shims/Shim.Nabu.S @@ -6,7 +6,7 @@ _ShimStart: mov x20, x1 mov x21, x2 mov x22, x3 - mov x23, x4 + mov x23, x30 movz x4, #0x9004 movk x4, #0x390, lsl #16 // Store 0x3909004 to x4, which is io status reg of gpio 9. movz x5, #0x3004 diff --git a/Shims/Shim.Pipa.S b/Shims/Shim.Pipa.S index 8b5b05c..630ea58 100644 --- a/Shims/Shim.Pipa.S +++ b/Shims/Shim.Pipa.S @@ -6,7 +6,7 @@ _ShimStart: mov x20, x1 mov x21, x2 mov x22, x3 - mov x23, x4 + mov x23, x30 movz x4, #0xe004 movk x4, #0xf96, lsl #16 // Store 0xf96E004 to x4, which is address of GPIO 110. ldr w6, [x4] // Get status value from address stored in x4 and store it to w6. diff --git a/inc/shim.h b/inc/shim.h index 8f563e7..416f4df 100644 --- a/inc/shim.h +++ b/inc/shim.h @@ -4,18 +4,20 @@ #include #define SHIM_MANIFEST_MAGIC "SHIMMF\0" -#define SHIM_MANIFEST_VERSION 3U +#define SHIM_MANIFEST_VERSION 4U #define SHIM_MANIFEST_MAX_SIZE 0x1000U -#define SHIM_MAX_ENTRIES 63U -#define SHIM_KERNEL_ALIGNMENT 0x200000U -#define SHIM_LOADER_MAGIC "SHIMLDR" -#define SIMPLE_SHIM_MAGIC "SHIMASM" -#define SIMPLE_SHIM_PAYLOAD_OFFSET 0x20U -#define SIMPLE_SHIM_COPY_ADDRESS_OFFSET 0x28U -#define SIMPLE_SHIM_COPY_SIZE_OFFSET 0x30U +#define SHIM_MAX_ENTRIES 50U +#define SHIM_LINUX_ALIGNMENT 0x200000U +#define SHIM_BRANCH_ALIGNMENT 4U +#define SHIM_BRANCH_MIN_OFFSET (-0x08000000LL) +#define SHIM_BRANCH_MAX_OFFSET 0x08000000LL #define SHIM_ENTRY_TYPE_LINUX 1U -#define SHIM_ENTRY_TYPE_EXECUTABLE 2U +#define SHIM_ENTRY_TYPE_FREE_EXEC 2U +#define SHIM_ENTRY_TYPE_SHIM 3U +#define SHIM_ENTRY_TYPE_BLOB 4U +#define SHIM_ENTRY_TYPE_DTB 5U +#define SHIM_ENTRY_TYPE_MANIFEST 6U #define SHIM_ENTRY_FLAG_BASE_IMAGE 1U #define SHIM_ENTRY_FLAG_COPY 2U @@ -25,6 +27,8 @@ typedef struct { char section[32]; char name[32]; char path[SHIM_PATH_SIZE]; + uint32_t type; + int hasType; int baseImage; uint64_t copyAddress; int hasCopyAddress; @@ -32,18 +36,26 @@ typedef struct { int hasCopySizeMax; uint64_t alignment; int hasAlignment; + uint64_t entryOffset; + int hasEntryOffset; } ShimImageConfig; +typedef struct { + int present; + uint32_t type; + int hasType; +} ShimManifestConfig; + typedef struct { char shim[SHIM_PATH_SIZE]; char output[SHIM_PATH_SIZE]; - char loader[SHIM_PATH_SIZE]; char defaultSection[32]; ShimImageConfig images[SHIM_MAX_ENTRIES]; size_t imageCount; size_t baseImageIndex; uint32_t defaultEntry; uint32_t timeoutMs; + ShimManifestConfig manifest; } ShimPackConfig; #ifdef _MSC_VER @@ -72,6 +84,8 @@ typedef struct SHIM_PACKED { uint32_t type; uint32_t flags; uint64_t loadAddress; + uint64_t entryOffset; + uint64_t reserved; } ShimManifestEntry; typedef struct SHIM_PACKED { @@ -86,10 +100,10 @@ typedef struct SHIM_PACKED { _Static_assert(sizeof(ShimManifestHeader) == 64, "Invalid Shim manifest header size"); -_Static_assert(sizeof(ShimManifestEntry) == 64, +_Static_assert(sizeof(ShimManifestEntry) == 80, "Invalid Shim manifest entry size"); -_Static_assert(sizeof(ShimManifest) == SHIM_MANIFEST_MAX_SIZE, - "Invalid Shim manifest size"); +_Static_assert(sizeof(ShimManifest) <= SHIM_MANIFEST_MAX_SIZE, + "Shim manifest exceeds its maximum size"); int PackConfig(const char *configPath); int PackShim(const ShimPackConfig *config); \ No newline at end of file diff --git a/src/shim.c b/src/shim.c index e84fd46..f931fc6 100644 --- a/src/shim.c +++ b/src/shim.c @@ -35,7 +35,8 @@ static void write_le64(uint8_t *buffer, uint64_t value) { } static bool align_up_size(size_t value, size_t alignment, size_t *result) { - if (alignment == 0 || value > SIZE_MAX - (alignment - 1)) + if (alignment == 0 || (alignment & (alignment - 1)) != 0 || + value > SIZE_MAX - (alignment - 1)) return false; *result = (value + alignment - 1) & ~(alignment - 1); return true; @@ -60,12 +61,12 @@ static bool inspect_arm64_image(const FileContent *file, Arm64ImageInfo *info) { return true; } -static bool align_image_offset(size_t minimum, +static bool align_linux_offset(size_t minimum, uint64_t primaryTextOffset, uint64_t imageTextOffset, size_t alignment, size_t *result) { - const size_t mask = SHIM_KERNEL_ALIGNMENT - 1; + const size_t mask = SHIM_LINUX_ALIGNMENT - 1; size_t requiredResidue = ((size_t)imageTextOffset - (size_t)primaryTextOffset) & mask; size_t adjustment = (requiredResidue - (minimum & mask)) & mask; @@ -93,27 +94,41 @@ static bool resize_output(FileContent *output, size_t newSize) { return true; } -static bool initialize_output(const FileContent *primary, - const Arm64ImageInfo *primaryInfo, - size_t primarySlot, - const FileContent *shim, - const FileContent *loader, - FileContent *output) { - if (primaryInfo->headerOffset > SIZE_MAX - primarySlot || - primaryInfo->headerOffset + primarySlot > SIZE_MAX - shim->fileSize || - loader->fileSize > primarySlot) - return false; +static bool is_executable_type(uint32_t type) { + return type == SHIM_ENTRY_TYPE_LINUX || + type == SHIM_ENTRY_TYPE_FREE_EXEC || + type == SHIM_ENTRY_TYPE_SHIM; +} - output->fileSize = primaryInfo->headerOffset + primarySlot + shim->fileSize; - output->fileBuffer = calloc(1, output->fileSize); - if (output->fileBuffer == NULL) - return false; +static bool inspect_typed_blob(const FileContent *file, + const ShimImageConfig *image) { + if (image->type == SHIM_ENTRY_TYPE_DTB) { + static const uint8_t fdtMagic[] = {0xd0, 0x0d, 0xfe, 0xed}; + if (file->fileSize < sizeof(fdtMagic) || + memcmp(file->fileBuffer, fdtMagic, sizeof(fdtMagic)) != 0) { + printf("Error: [%s] is not a flattened device tree blob.\n", + image->section); + return false; + } + } else if (image->type == SHIM_ENTRY_TYPE_MANIFEST) { + if (file->fileSize < sizeof(ShimManifestHeader) || + memcmp(file->fileBuffer, SHIM_MANIFEST_MAGIC, 8) != 0 || + read_le32(file->fileBuffer + 8) != SHIM_MANIFEST_VERSION) { + printf("Error: [%s] is not a v%u Shim manifest.\n", + image->section, SHIM_MANIFEST_VERSION); + return false; + } + } + return true; +} - memcpy(output->fileBuffer, primary->fileBuffer, primary->fileSize); - memcpy(output->fileBuffer + primaryInfo->headerOffset + primarySlot, - shim->fileBuffer, shim->fileSize); +static bool patch_kernel_entry(uint8_t *kernelHeader, size_t shimOffset) { + if ((shimOffset & (SHIM_BRANCH_ALIGNMENT - 1)) != 0 || + shimOffset >= (size_t)SHIM_BRANCH_MAX_OFFSET) { + printf("Error: Shim is outside the ARM64 BL range.\n"); + return false; + } - uint8_t *kernelHeader = output->fileBuffer + primaryInfo->headerOffset; uint32_t firstInstruction = read_le32(kernelHeader); uint32_t secondInstruction = read_le32(kernelHeader + 4); bool firstIsBranch = (firstInstruction & 0xfc000000U) == 0x14000000U; @@ -128,87 +143,60 @@ static bool initialize_output(const FileContent *primary, return false; } - write_le32(kernelHeader, 0x14000010U); - memcpy(kernelHeader + 0x40, loader->fileBuffer + 0x40, - loader->fileSize - 0x40); - write_le64(kernelHeader + 0x30, primarySlot); + uint32_t branchImmediate = (uint32_t)(shimOffset >> 2); + write_le32(kernelHeader, 0x94000000U | branchImmediate); return true; } int PackShim(const ShimPackConfig *config) { - size_t extraImageCount = config->imageCount - 1; const ShimImageConfig *baseImage = &config->images[config->baseImageIndex]; + bool writeManifest = config->manifest.present; int status = -EINVAL; - FileContent primary = {0}; - FileContent shim = {0}; - FileContent loader = {0}; - FileContent output = {0}; + FileContent primary = {.filePath = baseImage->path}; + FileContent shim = {.filePath = config->shim}; + FileContent output = {.filePath = config->output}; - primary.filePath = baseImage->path; - shim.filePath = config->shim; - output.filePath = config->output; - loader.filePath = config->loader; - - if (!load_file(&primary) || !load_file(&shim) || !load_file(&loader)) + if (!load_file(&primary) || !load_file(&shim)) + goto cleanup; + if (shim.fileSize == 0) { + printf("Error: Shim must be a non-empty blob.\n"); goto cleanup; + } Arm64ImageInfo primaryInfo = {0}; - Arm64ImageInfo shimInfo = {0}; if (!inspect_arm64_image(&primary, &primaryInfo)) { printf("Error: Base image is not an ARM64 Linux Image.\n"); goto cleanup; } - if (!inspect_arm64_image(&shim, &shimInfo) || shimInfo.headerOffset != 0) { - printf("Error: Shim is not a raw ARM64 Image.\n"); - goto cleanup; - } - if (shimInfo.imageSize == 0 || shim.fileSize != shimInfo.imageSize) { - printf("Error: Shim file size must match its non-zero Image size.\n"); - goto cleanup; - } - bool isSimpleShim = - memcmp(shim.fileBuffer + 8, SIMPLE_SHIM_MAGIC, - sizeof(SIMPLE_SHIM_MAGIC)) == 0; - if (isSimpleShim) { - if (config->defaultEntry != 0 || config->timeoutMs != 0) { - printf("Error: Simplified assembly Shim requires the base image " - "as Default and Timeout=0.\n"); - goto cleanup; - } - if (config->imageCount != 2) { - printf("Error: Simplified assembly Shim requires exactly one " - "base image and one CopyTo image.\n"); + + size_t primarySlot = primaryInfo.payloadSize; + if (primaryInfo.imageSize > primarySlot) { + if (primaryInfo.imageSize > SIZE_MAX) { + printf("Error: Base image is too large.\n"); goto cleanup; } - for (size_t index = 0; index < config->imageCount; index++) { - if (index != config->baseImageIndex && - !config->images[index].hasCopyAddress) { - printf("Error: Simplified assembly Shim requires its second " - "image to set CopyTo.\n"); - goto cleanup; - } - } + primarySlot = (size_t)primaryInfo.imageSize; } - if (loader.fileSize <= 0x40 || - memcmp(loader.fileBuffer + 8, SHIM_LOADER_MAGIC, - sizeof(SHIM_LOADER_MAGIC)) != 0) { - printf("Error: Invalid ShimLoader image.\n"); + if (!align_up_size(primarySlot, SHIM_BRANCH_ALIGNMENT, &primarySlot) || + primarySlot >= (size_t)SHIM_BRANCH_MAX_OFFSET || + primaryInfo.headerOffset > SIZE_MAX - primarySlot || + primaryInfo.headerOffset + primarySlot > SIZE_MAX - shim.fileSize) { + printf("Error: Base image cannot reach the attached Shim with BL.\n"); goto cleanup; } - size_t primarySlot = primaryInfo.payloadSize; - if (primaryInfo.imageSize > primarySlot) - primarySlot = (size_t)primaryInfo.imageSize; - if (!align_up_size(primarySlot, 0x1000, &primarySlot) || - primarySlot > SIZE_MAX - primaryInfo.headerOffset) { - printf("Error: Base image is too large.\n"); + output.fileSize = primaryInfo.headerOffset + primarySlot + shim.fileSize; + output.fileBuffer = calloc(1, output.fileSize); + if (output.fileBuffer == NULL) goto cleanup; - } + memcpy(output.fileBuffer, primary.fileBuffer, primary.fileSize); + memcpy(output.fileBuffer + primaryInfo.headerOffset + primarySlot, + shim.fileBuffer, shim.fileSize); - if (!initialize_output(&primary, &primaryInfo, primarySlot, &shim, - &loader, &output)) + uint8_t *kernelHeader = output.fileBuffer + primaryInfo.headerOffset; + if (!patch_kernel_entry(kernelHeader, primarySlot)) goto cleanup; ShimManifest manifest = {0}; @@ -216,15 +204,17 @@ int PackShim(const ShimPackConfig *config) { manifest.header.version = SHIM_MANIFEST_VERSION; manifest.header.headerSize = sizeof(ShimManifestHeader); manifest.header.entrySize = sizeof(ShimManifestEntry); - manifest.header.entryCount = (uint32_t)extraImageCount + 1; + manifest.header.entryCount = (uint32_t)config->imageCount; manifest.header.defaultEntry = config->defaultEntry; manifest.header.timeoutMs = config->timeoutMs; - memcpy(manifest.entries[0].name, baseImage->name, strlen(baseImage->name) + 1); - manifest.entries[0].offset = 0; - manifest.entries[0].size = primaryInfo.payloadSize; - manifest.entries[0].type = SHIM_ENTRY_TYPE_LINUX; - manifest.entries[0].flags = SHIM_ENTRY_FLAG_BASE_IMAGE; + ShimManifestEntry *baseEntry = &manifest.entries[0]; + memcpy(baseEntry->name, baseImage->name, strlen(baseImage->name) + 1); + baseEntry->type = SHIM_ENTRY_TYPE_LINUX; + baseEntry->entryOffset = baseImage->entryOffset; + baseEntry->offset = 0; + baseEntry->size = primaryInfo.payloadSize; + baseEntry->flags = SHIM_ENTRY_FLAG_BASE_IMAGE; size_t manifestIndex = 1; for (size_t index = 0; index < config->imageCount; index++) { @@ -235,6 +225,10 @@ int PackShim(const ShimPackConfig *config) { FileContent extra = {.filePath = image->path}; if (!load_file(&extra)) goto cleanup; + if (!inspect_typed_blob(&extra, image)) { + free(extra.fileBuffer); + goto cleanup; + } if (image->hasCopyAddress && extra.fileSize > image->copySizeMax) { printf("Error: [%s] image size 0x%zx exceeds CopySizeMax " "0x%llx.\n", image->section, extra.fileSize, @@ -242,51 +236,63 @@ int PackShim(const ShimPackConfig *config) { free(extra.fileBuffer); goto cleanup; } + if (image->hasCopyAddress && + image->copyAddress > UINT64_MAX - extra.fileSize) { + printf("Error: Copy address range overflows: %s\n", + image->section); + free(extra.fileBuffer); + goto cleanup; + } size_t runtimeSize = output.fileSize - primaryInfo.headerOffset; size_t imageOffset = 0; - size_t imageSlot = 0; size_t payloadOffset = 0; size_t payloadSize = extra.fileSize; + size_t imageSlot = extra.fileSize; Arm64ImageInfo extraInfo = {0}; - if (image->hasCopyAddress) { - if (image->copyAddress > UINT64_MAX - extra.fileSize) { - printf("Error: Copy address range overflows: %s\n", - image->section); - free(extra.fileBuffer); - goto cleanup; - } - if (!align_up_size(runtimeSize, (size_t)image->alignment, - &imageOffset)) { - free(extra.fileBuffer); - goto cleanup; - } - imageSlot = extra.fileSize; - } else { + if (image->type == SHIM_ENTRY_TYPE_LINUX) { if (!inspect_arm64_image(&extra, &extraInfo)) { - printf("Error: Image without CopyTo is not an ARM64 Linux " - "Image: %s\n", extra.filePath); + printf("Error: [%s] is not an ARM64 Linux Image.\n", + image->section); free(extra.fileBuffer); goto cleanup; } payloadOffset = extraInfo.headerOffset; payloadSize = extraInfo.payloadSize; imageSlot = extraInfo.payloadSize; - if (extraInfo.imageSize > imageSlot) + if (extraInfo.imageSize > imageSlot) { + if (extraInfo.imageSize > SIZE_MAX) { + free(extra.fileBuffer); + goto cleanup; + } imageSlot = (size_t)extraInfo.imageSize; - if (!align_image_offset(runtimeSize, primaryInfo.textOffset, - extraInfo.textOffset, - (size_t)image->alignment, &imageOffset) || + } + if (!align_linux_offset(runtimeSize, primaryInfo.textOffset, + extraInfo.textOffset, + (size_t)image->alignment, &imageOffset) || !align_up_size(imageSlot, 0x1000, &imageSlot)) { - printf("Error: [%s] cannot satisfy Align=0x%llx and ARM64 " - "Image text_offset constraints.\n", image->section, - (unsigned long long)image->alignment); + printf("Error: [%s] cannot satisfy Linux Image alignment.\n", + image->section); free(extra.fileBuffer); goto cleanup; } + } else if (!align_up_size(runtimeSize, (size_t)image->alignment, + &imageOffset)) { + printf("Error: [%s] image alignment overflows.\n", image->section); + free(extra.fileBuffer); + goto cleanup; } + if (is_executable_type(image->type) && + (image->entryOffset > payloadSize || + payloadSize - (size_t)image->entryOffset < 4 || + (image->entryOffset & (SHIM_BRANCH_ALIGNMENT - 1)) != 0)) { + printf("Error: [%s] EntryOffset is outside the executable image.\n", + image->section); + free(extra.fileBuffer); + goto cleanup; + } if (imageOffset > SIZE_MAX - imageSlot || primaryInfo.headerOffset > SIZE_MAX - imageOffset - imageSlot || !resize_output(&output, @@ -294,109 +300,77 @@ int PackShim(const ShimPackConfig *config) { free(extra.fileBuffer); goto cleanup; } - memcpy(output.fileBuffer + primaryInfo.headerOffset + imageOffset, extra.fileBuffer + payloadOffset, payloadSize); ShimManifestEntry *entry = &manifest.entries[manifestIndex++]; memcpy(entry->name, image->name, strlen(image->name) + 1); + entry->type = image->type; + entry->entryOffset = image->entryOffset; entry->offset = imageOffset; entry->size = payloadSize; if (image->hasCopyAddress) { - entry->type = SHIM_ENTRY_TYPE_EXECUTABLE; - entry->flags = SHIM_ENTRY_FLAG_COPY; + entry->flags |= SHIM_ENTRY_FLAG_COPY; entry->loadAddress = image->copyAddress; - } else { - entry->type = SHIM_ENTRY_TYPE_LINUX; } free(extra.fileBuffer); } - if (isSimpleShim) { - ShimManifestEntry *payload = &manifest.entries[1]; - uint8_t *shimHeader = output.fileBuffer + primaryInfo.headerOffset + - primarySlot; - write_le64(shimHeader + SIMPLE_SHIM_PAYLOAD_OFFSET, payload->offset); - write_le64(shimHeader + SIMPLE_SHIM_COPY_ADDRESS_OFFSET, - payload->loadAddress); - write_le64(shimHeader + SIMPLE_SHIM_COPY_SIZE_OFFSET, payload->size); - - uint8_t *kernelHeader = output.fileBuffer + primaryInfo.headerOffset; - write_le64(kernelHeader + 0x20, 0); - write_le64(kernelHeader + 0x28, 0); - - if (primaryInfo.headerOffset != 0) { - size_t imageSize = output.fileSize - primaryInfo.headerOffset; - if (imageSize > UINT32_MAX) { - printf("Error: UNCOMPRESSED_IMG payload exceeds 32-bit size.\n"); - goto cleanup; - } - write_le32(output.fileBuffer + 0x10, (uint32_t)imageSize); + size_t manifestOffset = 0; + size_t manifestSize = 0; + if (writeManifest) { + manifestSize = sizeof(ShimManifestHeader) + + manifest.header.entryCount * sizeof(ShimManifestEntry); + size_t oldRuntimeSize = output.fileSize - primaryInfo.headerOffset; + if (!align_up_size(oldRuntimeSize, sizeof(uint64_t), &manifestOffset)) { + printf("Error: Failed to align Shim manifest.\n"); + goto cleanup; } - if (write_file_content(&output) != 0) { - printf("Error: Failed to write simplified Shim image.\n"); + size_t manifestFileOffset = primaryInfo.headerOffset + manifestOffset; + if (manifestSize > SHIM_MANIFEST_MAX_SIZE || + manifestSize > SIZE_MAX - manifestFileOffset || + !resize_output(&output, manifestFileOffset + manifestSize)) { + printf("Error: Failed to append Shim manifest.\n"); goto cleanup; } - - printf("Simplified Shim image packed successfully.\n"); - printf(" Payload: offset 0x%llx, CopyTo 0x%llx, " - "size 0x%llx\n", - (unsigned long long)payload->offset, - (unsigned long long)payload->loadAddress, - (unsigned long long)payload->size); - printf(" Output size: 0x%zx\n", output.fileSize); - status = 0; - goto cleanup; - } - - size_t manifestSize = sizeof(ShimManifestHeader) + - manifest.header.entryCount * sizeof(ShimManifestEntry); - size_t manifestOffset = output.fileSize - primaryInfo.headerOffset; - size_t oldOutputSize = output.fileSize; - if (manifestSize > SHIM_MANIFEST_MAX_SIZE || - manifestSize > SIZE_MAX - oldOutputSize || - !resize_output(&output, oldOutputSize + manifestSize)) { - printf("Error: Failed to append Shim manifest.\n"); - goto cleanup; + manifest.header.imageSize = manifestOffset + manifestSize; + memcpy(output.fileBuffer + manifestFileOffset, &manifest, manifestSize); } - manifest.header.imageSize = manifestOffset + manifestSize; - memcpy(output.fileBuffer + oldOutputSize, &manifest, manifestSize); - uint8_t *kernelHeader = output.fileBuffer + primaryInfo.headerOffset; + size_t runtimeImageSize = output.fileSize - primaryInfo.headerOffset; + kernelHeader = output.fileBuffer + primaryInfo.headerOffset; + write_le64(kernelHeader + 0x10, runtimeImageSize); write_le64(kernelHeader + 0x20, manifestOffset); write_le64(kernelHeader + 0x28, manifestSize); + write_le64(kernelHeader + 0x30, primarySlot); if (primaryInfo.headerOffset != 0) { - if (manifest.header.imageSize > UINT32_MAX) { + if (runtimeImageSize > UINT32_MAX) { printf("Error: UNCOMPRESSED_IMG payload exceeds 32-bit size.\n"); goto cleanup; } - write_le32(output.fileBuffer + 0x10, (uint32_t)manifest.header.imageSize); + write_le32(output.fileBuffer + 0x10, (uint32_t)runtimeImageSize); } - if (write_file_content(&output) != 0) { - printf("Error: Failed to write Shim image.\n"); + printf("Error: Failed to write packed image.\n"); goto cleanup; } printf("Shim image packed successfully.\n"); printf(" Base image slot: 0x%zx\n", primarySlot); - printf(" Shim slot: 0x%zx\n", shim.fileSize); - printf(" Manifest blob: offset 0x%zx, size 0x%zx\n", - manifestOffset, manifestSize); - for (uint32_t index = 0; index < manifest.header.entryCount; index++) - printf(" Image %u: %s @ 0x%llx (0x%llx bytes)\n", - index + 1, manifest.entries[index].name, - (unsigned long long)manifest.entries[index].offset, - (unsigned long long)manifest.entries[index].size); - printf(" Output size: 0x%llx\n", - (unsigned long long)manifest.header.imageSize); + printf(" Shim blob: offset 0x%zx, size 0x%zx\n", + primarySlot, shim.fileSize); + if (writeManifest) + printf(" Manifest v%u: offset 0x%zx, size 0x%zx\n", + SHIM_MANIFEST_VERSION, manifestOffset, manifestSize); + else + printf(" Manifest: omitted\n"); + printf(" Output size: 0x%zx\n", runtimeImageSize); status = 0; cleanup: free(primary.fileBuffer); free(shim.fileBuffer); - free(loader.fileBuffer); free(output.fileBuffer); return status; -} \ No newline at end of file +} diff --git a/src/shim_config.c b/src/shim_config.c index 7901fe3..4a4125c 100644 --- a/src/shim_config.c +++ b/src/shim_config.c @@ -69,6 +69,28 @@ static bool parse_bool(const char *value, int *result) { return false; } +static bool parse_image_type(const char *value, uint32_t *result) { + static const struct { + const char *name; + uint32_t type; + } types[] = { + {"Linux", SHIM_ENTRY_TYPE_LINUX}, + {"FreeExec", SHIM_ENTRY_TYPE_FREE_EXEC}, + {"Shim", SHIM_ENTRY_TYPE_SHIM}, + {"Blob", SHIM_ENTRY_TYPE_BLOB}, + {"DTB", SHIM_ENTRY_TYPE_DTB}, + {"Manifest", SHIM_ENTRY_TYPE_MANIFEST}, + }; + + for (size_t index = 0; index < sizeof(types) / sizeof(types[0]); index++) { + if (strcmp(value, types[index].name) == 0) { + *result = types[index].type; + return true; + } + } + return false; +} + static bool set_once(char *destination, size_t size, const char *value) { return destination[0] == '\0' && copy_text(destination, size, value); } @@ -106,6 +128,13 @@ static int config_handler(void *user, ConfigParser *parser = user; bool valid = false; + if (parser->config.manifest.present && + strcmp(section, "Manifest") != 0) { + snprintf(parser->error, sizeof(parser->error), + "[Manifest] must be the final section"); + return 0; + } + if (strcmp(section, "Pack") == 0) { if (strcmp(name, "Shim") == 0) valid = set_path_once(parser->config.shim, @@ -113,15 +142,19 @@ static int config_handler(void *user, else if (strcmp(name, "Output") == 0) valid = set_path_once(parser->config.output, sizeof(parser->config.output), parser, value); - else if (strcmp(name, "Loader") == 0) - valid = set_path_once(parser->config.loader, - sizeof(parser->config.loader), parser, value); else if (strcmp(name, "Default") == 0) valid = set_once(parser->config.defaultSection, sizeof(parser->config.defaultSection), value); else if (strcmp(name, "Timeout") == 0) valid = parse_u32(value, &parser->config.timeoutMs); - } else if (strncmp(section, "Image-", 6) == 0 && section[6] != '\0') { + } else if (strcmp(section, "Manifest") == 0) { + parser->config.manifest.present = 1; + if (strcmp(name, "Type") == 0 && !parser->config.manifest.hasType) { + valid = parse_image_type(value, &parser->config.manifest.type); + parser->config.manifest.hasType = valid; + } + } else if (strcmp(section, "Image") == 0 || + (strncmp(section, "Image-", 6) == 0 && section[6] != '\0')) { ShimImageConfig *image = find_or_add_image(parser, section); if (image != NULL) { if (strcmp(name, "Name") == 0) @@ -129,6 +162,10 @@ static int config_handler(void *user, else if (strcmp(name, "Path") == 0) valid = set_path_once(image->path, sizeof(image->path), parser, value); + else if (strcmp(name, "Type") == 0 && !image->hasType) { + valid = parse_image_type(value, &image->type); + image->hasType = valid; + } else if (strcmp(name, "BaseImage") == 0 && image->baseImage == -1) valid = parse_bool(value, &image->baseImage); else if (strcmp(name, "CopyTo") == 0 && @@ -143,6 +180,10 @@ static int config_handler(void *user, !image->hasAlignment) { valid = parse_u64(value, &image->alignment); image->hasAlignment = valid; + } else if (strcmp(name, "EntryOffset") == 0 && + !image->hasEntryOffset) { + valid = parse_u64(value, &image->entryOffset); + image->hasEntryOffset = valid; } } } @@ -176,9 +217,8 @@ static bool validate_config(ConfigParser *parser, size_t *baseImageIndex, size_t *defaultIndex) { ShimPackConfig *config = &parser->config; - if (config->shim[0] == '\0' || config->output[0] == '\0' || - config->loader[0] == '\0' || config->defaultSection[0] == '\0') { - printf("Error: [Pack] requires Shim, Output, Loader and Default.\n"); + if (config->shim[0] == '\0' || config->output[0] == '\0') { + printf("Error: [Pack] requires Shim and Output.\n"); return false; } if (config->imageCount == 0) { @@ -191,26 +231,36 @@ static bool validate_config(ConfigParser *parser, for (size_t index = 0; index < config->imageCount; index++) { ShimImageConfig *image = &config->images[index]; if (image->name[0] == '\0' || image->path[0] == '\0' || - image->baseImage == -1) { - printf("Error: [%s] requires Name, Path and BaseImage.\n", + !image->hasType || image->baseImage == -1) { + printf("Error: [%s] requires Name, Path, Type and BaseImage.\n", image->section); return false; } if (!image->hasAlignment) image->alignment = 4; - if (image->alignment < 4 || - image->alignment > SHIM_KERNEL_ALIGNMENT || + if (image->alignment < SHIM_BRANCH_ALIGNMENT || + image->alignment > UINT32_MAX || (image->alignment & (image->alignment - 1)) != 0) { printf("Error: [%s] Align must be a power of two from 4 to " - "0x%x.\n", image->section, SHIM_KERNEL_ALIGNMENT); + "0xffffffff.\n", image->section); return false; } if (image->baseImage) { + if (image->type != SHIM_ENTRY_TYPE_LINUX) { + printf("Error: Base image [%s] must set Type=Linux.\n", + image->section); + return false; + } if (image->hasCopyAddress) { printf("Error: Base image [%s] cannot set CopyTo.\n", image->section); return false; } + if (image->entryOffset != 0) { + printf("Error: Base image [%s] cannot set EntryOffset.\n", + image->section); + return false; + } *baseImageIndex = index; baseImageCount++; } @@ -240,17 +290,42 @@ static bool validate_config(ConfigParser *parser, image->section); return false; } - if (strcmp(image->section, config->defaultSection) == 0) { + bool executable = image->type == SHIM_ENTRY_TYPE_LINUX || + image->type == SHIM_ENTRY_TYPE_FREE_EXEC || + image->type == SHIM_ENTRY_TYPE_SHIM; + if (image->type == SHIM_ENTRY_TYPE_MANIFEST) { + printf("Error: [%s] must be configured as the [Manifest] " + "section.\n", image->section); + return false; + } + if (!executable && image->hasEntryOffset) { + printf("Error: [%s] Type does not allow EntryOffset.\n", + image->section); + return false; + } + if (config->defaultSection[0] != '\0' && + strcmp(image->section, config->defaultSection) == 0) { *defaultIndex = index; defaultFound = true; } } + if (config->manifest.present && + (!config->manifest.hasType || + config->manifest.type != SHIM_ENTRY_TYPE_MANIFEST)) { + printf("Error: [Manifest] must set Type=Manifest.\n"); + return false; + } + if (baseImageCount != 1) { printf("Error: Exactly one [Image-*] must set BaseImage=true.\n"); return false; } config->baseImageIndex = *baseImageIndex; + if (config->defaultSection[0] == '\0') { + *defaultIndex = *baseImageIndex; + defaultFound = true; + } if (!defaultFound) { printf("Error: Default must name an [Image-*] section.\n"); return false; From 3cc757839805ffebbd99f656cab92aabbe82286a Mon Sep 17 00:00:00 2001 From: Kancy Joe Date: Wed, 5 Aug 2026 18:53:59 +0800 Subject: [PATCH 11/12] rewrite HDRTool to support multi-boot image format. --- .github/workflows/cmake-multi-platform.yml | 4 +- CMakeLists.txt | 11 +- README.md | 9 + Shims/CommonTail.S | 4 +- Shims/Shim.KernelWrapper.S | 4 +- inc/shim.h | 32 +- inc/shim_format.h | 22 ++ src/HDRTool.c | 80 ----- src/shim.c | 152 ++++----- src/shim_format.c | 79 +++++ src/unpack.c | 357 +++++++++++++++++++++ src/utils.c | 27 +- 12 files changed, 599 insertions(+), 182 deletions(-) create mode 100644 inc/shim_format.h delete mode 100644 src/HDRTool.c create mode 100644 src/shim_format.c create mode 100644 src/unpack.c diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 6094d75..a35200a 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -88,7 +88,7 @@ jobs: run: >- cmake --build "${{ github.workspace }}/build" --config Release - --target MultiBootKernelPatcher HDRTool + --target MultiBootKernelPatcher UnpackTool - name: Build Shims if: matrix.build_shims == 'ON' @@ -102,7 +102,7 @@ jobs: run: | mkdir -p artifacts/tools cp "build/${{ matrix.executable_directory }}/MultiBootKernelPatcher${{ matrix.executable_suffix }}" artifacts/tools/ - cp "build/${{ matrix.executable_directory }}/HDRTool${{ matrix.executable_suffix }}" artifacts/tools/ + cp "build/${{ matrix.executable_directory }}/UnpackTool${{ matrix.executable_suffix }}" artifacts/tools/ - name: Upload host tools uses: actions/upload-artifact@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt index cafc089..0f77808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ set(INIH_TARGET inih_static) add_executable(MultiBootKernelPatcher src/patcher.c src/shim.c + src/shim_format.c src/shim_config.c src/utils.c ) @@ -65,12 +66,16 @@ if (MSVC) MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif () target_link_libraries(MultiBootKernelPatcher PRIVATE ${INIH_TARGET}) -add_executable(HDRTool src/HDRTool.c src/utils.c) -target_include_directories(HDRTool PRIVATE +add_executable(UnpackTool + src/unpack.c + src/shim_format.c + src/utils.c +) +target_include_directories(UnpackTool PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc ) if (MSVC) - set_property(TARGET HDRTool PROPERTY + set_property(TARGET UnpackTool PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif () diff --git a/README.md b/README.md index ca79edd..075a8ea 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,17 @@ Shim runtime, and multiple bootable images from one INI configuration. ```sh MultiBootKernelPatcher Config/Shim.Sample.cfg ``` + - Unpack a patched image that contains a reversible Manifest. + ```sh + UnpackTool PatchedKernel unpacked + ``` + The output directory contains BaseImage, Shim, image payloads, and + `config.cfg`. Edit those files as needed, then repack `config.cfg` to + create `RepackedKernel` in the same directory. - Notice + Device Shim blobs are generated under `output/Shims/`. + + Images without the current reversible Manifest are not supported by + `UnpackTool`. ## See More - You can go to our [document website](https://aloha.firmware.icu/) to get more infomation about the Multi-Boot Kernel Patcher. diff --git a/Shims/CommonTail.S b/Shims/CommonTail.S index 99d42fb..57eade3 100644 --- a/Shims/CommonTail.S +++ b/Shims/CommonTail.S @@ -14,7 +14,7 @@ _UEFI: ldr x25, [x24, #0x20] cbz x25, _Linux ldr x8, [x24, #0x28] - cmp x8, #224 + cmp x8, #448 b.lo _Linux add x25, x24, x25 @@ -23,7 +23,7 @@ _UEFI: cmp x8, x9 b.ne _Linux ldr w8, [x25, #8] - cmp w8, #4 + cmp w8, #5 b.ne _Linux ldr w8, [x25, #12] cmp w8, #64 diff --git a/Shims/Shim.KernelWrapper.S b/Shims/Shim.KernelWrapper.S index 6591ab3..006378e 100644 --- a/Shims/Shim.KernelWrapper.S +++ b/Shims/Shim.KernelWrapper.S @@ -12,7 +12,7 @@ _ShimStart: ldr x25, [x24, #0x20] cbz x25, _Linux ldr x8, [x24, #0x28] - cmp x8, #224 + cmp x8, #448 b.lo _Linux add x25, x24, x25 ldr x8, [x25] @@ -20,7 +20,7 @@ _ShimStart: cmp x8, x9 b.ne _Linux ldr w8, [x25, #8] - cmp w8, #4 + cmp w8, #5 b.ne _Linux ldr w8, [x25, #12] cmp w8, #64 diff --git a/inc/shim.h b/inc/shim.h index 416f4df..6ac0e92 100644 --- a/inc/shim.h +++ b/inc/shim.h @@ -4,9 +4,13 @@ #include #define SHIM_MANIFEST_MAGIC "SHIMMF\0" -#define SHIM_MANIFEST_VERSION 4U -#define SHIM_MANIFEST_MAX_SIZE 0x1000U +#define SHIM_MANIFEST_VERSION 5U +#define SHIM_MANIFEST_MAX_SIZE 0x2000U #define SHIM_MAX_ENTRIES 50U +#define SHIM_MANIFEST_RECOVERY_MAGIC "SHIMREC\0" +#define SHIM_MANIFEST_RECOVERY_VERSION 1U +#define SHIM_LINUX_HEADER_SIZE 0x40U +#define SHIM_WRAPPER_SIZE 0x14U #define SHIM_LINUX_ALIGNMENT 0x200000U #define SHIM_BRANCH_ALIGNMENT 4U #define SHIM_BRANCH_MIN_OFFSET (-0x08000000LL) @@ -88,6 +92,26 @@ typedef struct SHIM_PACKED { uint64_t reserved; } ShimManifestEntry; +typedef struct SHIM_PACKED { + char magic[8]; + uint32_t version; + uint32_t headerSize; + uint32_t entrySize; + uint32_t entryCount; + uint64_t shimSize; + uint8_t baseHeader[SHIM_LINUX_HEADER_SIZE]; + uint8_t reserved[32]; +} ShimManifestRecoveryHeader; + +typedef struct SHIM_PACKED { + uint64_t alignment; + uint64_t copySizeMax; + uint32_t sourcePrefixSize; + uint32_t reserved; + uint8_t sourcePrefix[SHIM_WRAPPER_SIZE]; + uint8_t padding[4]; +} ShimManifestRecoveryEntry; + typedef struct SHIM_PACKED { ShimManifestHeader header; ShimManifestEntry entries[SHIM_MAX_ENTRIES]; @@ -104,6 +128,10 @@ _Static_assert(sizeof(ShimManifestEntry) == 80, "Invalid Shim manifest entry size"); _Static_assert(sizeof(ShimManifest) <= SHIM_MANIFEST_MAX_SIZE, "Shim manifest exceeds its maximum size"); +_Static_assert(sizeof(ShimManifestRecoveryHeader) == 128, + "Invalid Shim manifest recovery header size"); +_Static_assert(sizeof(ShimManifestRecoveryEntry) == 48, + "Invalid Shim manifest recovery entry size"); int PackConfig(const char *configPath); int PackShim(const ShimPackConfig *config); \ No newline at end of file diff --git a/inc/shim_format.h b/inc/shim_format.h new file mode 100644 index 0000000..324ff8b --- /dev/null +++ b/inc/shim_format.h @@ -0,0 +1,22 @@ +#pragma once + +#include "shim.h" +#include "utils.h" + +#include + +typedef struct { + size_t headerOffset; + size_t payloadSize; + uint64_t textOffset; + uint64_t imageSize; +} Arm64ImageInfo; + +uint32_t shim_read_le32(const uint8_t *buffer); +uint64_t shim_read_le64(const uint8_t *buffer); +void shim_write_le32(uint8_t *buffer, uint32_t value); +void shim_write_le64(uint8_t *buffer, uint64_t value); +bool shim_align_up(size_t value, size_t alignment, size_t *result); +bool shim_inspect_arm64_image(const FileContent *file, Arm64ImageInfo *info); +bool shim_is_executable_type(uint32_t type); +const char *shim_type_name(uint32_t type); diff --git a/src/HDRTool.c b/src/HDRTool.c deleted file mode 100644 index 2ea4728..0000000 --- a/src/HDRTool.c +++ /dev/null @@ -1,80 +0,0 @@ -/** @file -* Create a header or remove a header of a -* kernel for android boot v1. -* -* Copyright (c) 2021-2025 The DuoWoa authors. All rights reserved. -* MIT License -* -*/ - -#include "utils.h" - -int main( - int argc, - char *argv[] -) { - // Print hello world message - printf("Project Aloha Kernel Image HDR Patcher v1.2.0.0\n"); - printf("Copyright (c) 2021-2025 The DuoWoA authors\n\n"); - - // Check parameters and print help message - if (argc != 3) { - printf("Usage: \n"); - return -EINVAL; - } - - // Check if file exist - // Init Input file content - FileContent kernelInput = {.filePath = argv[1]}; - if (!get_file_size(&kernelInput)) { - printf("Error: Input kernel file not found or invalid.\n"); - return -EINVAL; - } - kernelInput.fileBuffer = malloc(kernelInput.fileSize + 0x14); - read_file_content(&kernelInput); - // add a 0x14 offset of the buffer - memmove(kernelInput.fileBuffer + 0x14, kernelInput.fileBuffer, kernelInput.fileSize); - - // Init Output file content - FileContent kernelOutput = {.filePath = argv[2]}; - kernelOutput.fileSize = kernelInput.fileSize; - kernelOutput.fileBuffer = kernelInput.fileBuffer + 0x14; - - // OK now check if kernel has a header - // if it is, then remove it - // if it is not, then create a header - // Header format: - // 0x00-0x0F: "UNCOMPRESSED_IMG" (16 bytes) - // 0x10-0x17: Kernel size (8 bytes, little-endian) - if (strncmp((char *) kernelInput.fileBuffer, "UNCOMPRESSED_IMG", 0x10) == 0) { - // Kernel has a header, remove it - printf("Kernel has UNCOMPRESSED_IMG header, removing...\n"); - kernelOutput.fileBuffer += 0x14; // Move past the header - kernelOutput.fileSize -= 0x14; // Reduce size by header size - } else { - // Kernel does not have a header, create one - printf("Kernel does not have UNCOMPRESSED_IMG header, creating...\n"); - // Reallocate buffer to add header - kernelOutput.fileBuffer -= 0x14; - // Set header value - memcpy(kernelOutput.fileBuffer, "UNCOMPRESSED_IMG", 0x10); - kernelOutput.fileBuffer[0x10] = kernelOutput.fileSize >> 0 & 0xFF; - kernelOutput.fileBuffer[0x11] = kernelOutput.fileSize >> 8 & 0xFF; - kernelOutput.fileBuffer[0x12] = kernelOutput.fileSize >> 16 & 0xFF; - kernelOutput.fileBuffer[0x13] = kernelOutput.fileSize >> 24 & 0xFF; - kernelOutput.fileSize += 0x14; - } - - // Save the output kernel - if (write_file_content(&kernelOutput)) { - printf("Error: Failed to write output kernel file.\n"); - free(kernelInput.fileBuffer); - return -EINVAL; - } - - // Free allocated memory - free(kernelInput.fileBuffer); - - // Print success message - printf("Kernel image processed successfully.\n"); -} diff --git a/src/shim.c b/src/shim.c index f931fc6..486919d 100644 --- a/src/shim.c +++ b/src/shim.c @@ -1,66 +1,9 @@ #include "shim.h" +#include "shim_format.h" #include "utils.h" #include -typedef struct { - size_t headerOffset; - size_t payloadSize; - uint64_t textOffset; - uint64_t imageSize; -} Arm64ImageInfo; - -static uint32_t read_le32(const uint8_t *buffer) { - uint32_t value = 0; - for (int index = 3; index >= 0; index--) - value = (value << 8) | buffer[index]; - return value; -} - -static uint64_t read_le64(const uint8_t *buffer) { - uint64_t value = 0; - for (int index = 7; index >= 0; index--) - value = (value << 8) | buffer[index]; - return value; -} - -static void write_le32(uint8_t *buffer, uint32_t value) { - for (int index = 0; index < 4; index++) - buffer[index] = value >> (index * 8) & 0xff; -} - -static void write_le64(uint8_t *buffer, uint64_t value) { - for (int index = 0; index < 8; index++) - buffer[index] = value >> (index * 8) & 0xff; -} - -static bool align_up_size(size_t value, size_t alignment, size_t *result) { - if (alignment == 0 || (alignment & (alignment - 1)) != 0 || - value > SIZE_MAX - (alignment - 1)) - return false; - *result = (value + alignment - 1) & ~(alignment - 1); - return true; -} - -static bool inspect_arm64_image(const FileContent *file, Arm64ImageInfo *info) { - static const uint8_t arm64Magic[] = {'A', 'R', 'M', 0x64}; - - info->headerOffset = 0; - if (file->fileSize >= 0x14 && - memcmp(file->fileBuffer, "UNCOMPRESSED_IMG", 0x10) == 0) - info->headerOffset = 0x14; - - if (file->fileSize < info->headerOffset + 0x40 || - memcmp(file->fileBuffer + info->headerOffset + 0x38, - arm64Magic, sizeof(arm64Magic)) != 0) - return false; - - info->payloadSize = file->fileSize - info->headerOffset; - info->textOffset = read_le64(file->fileBuffer + info->headerOffset + 0x08); - info->imageSize = read_le64(file->fileBuffer + info->headerOffset + 0x10); - return true; -} - static bool align_linux_offset(size_t minimum, uint64_t primaryTextOffset, uint64_t imageTextOffset, @@ -94,12 +37,6 @@ static bool resize_output(FileContent *output, size_t newSize) { return true; } -static bool is_executable_type(uint32_t type) { - return type == SHIM_ENTRY_TYPE_LINUX || - type == SHIM_ENTRY_TYPE_FREE_EXEC || - type == SHIM_ENTRY_TYPE_SHIM; -} - static bool inspect_typed_blob(const FileContent *file, const ShimImageConfig *image) { if (image->type == SHIM_ENTRY_TYPE_DTB) { @@ -113,7 +50,7 @@ static bool inspect_typed_blob(const FileContent *file, } else if (image->type == SHIM_ENTRY_TYPE_MANIFEST) { if (file->fileSize < sizeof(ShimManifestHeader) || memcmp(file->fileBuffer, SHIM_MANIFEST_MAGIC, 8) != 0 || - read_le32(file->fileBuffer + 8) != SHIM_MANIFEST_VERSION) { + shim_read_le32(file->fileBuffer + 8) != SHIM_MANIFEST_VERSION) { printf("Error: [%s] is not a v%u Shim manifest.\n", image->section, SHIM_MANIFEST_VERSION); return false; @@ -129,22 +66,22 @@ static bool patch_kernel_entry(uint8_t *kernelHeader, size_t shimOffset) { return false; } - uint32_t firstInstruction = read_le32(kernelHeader); - uint32_t secondInstruction = read_le32(kernelHeader + 4); + uint32_t firstInstruction = shim_read_le32(kernelHeader); + uint32_t secondInstruction = shim_read_le32(kernelHeader + 4); bool firstIsBranch = (firstInstruction & 0xfc000000U) == 0x14000000U; bool secondIsBranch = (secondInstruction & 0xfc000000U) == 0x14000000U; if (firstIsBranch && !secondIsBranch) { uint32_t movedBranch = 0x14000000U | ((firstInstruction - 1U) & 0x03ffffffU); - write_le32(kernelHeader + 4, movedBranch); + shim_write_le32(kernelHeader + 4, movedBranch); } else if (!secondIsBranch) { printf("Error: Base image has no supported entry branch.\n"); return false; } uint32_t branchImmediate = (uint32_t)(shimOffset >> 2); - write_le32(kernelHeader, 0x94000000U | branchImmediate); + shim_write_le32(kernelHeader, 0x94000000U | branchImmediate); return true; } @@ -157,6 +94,7 @@ int PackShim(const ShimPackConfig *config) { FileContent primary = {.filePath = baseImage->path}; FileContent shim = {.filePath = config->shim}; FileContent output = {.filePath = config->output}; + ShimManifestRecoveryEntry recoveryEntries[SHIM_MAX_ENTRIES] = {0}; if (!load_file(&primary) || !load_file(&shim)) goto cleanup; @@ -166,7 +104,7 @@ int PackShim(const ShimPackConfig *config) { } Arm64ImageInfo primaryInfo = {0}; - if (!inspect_arm64_image(&primary, &primaryInfo)) { + if (!shim_inspect_arm64_image(&primary, &primaryInfo)) { printf("Error: Base image is not an ARM64 Linux Image.\n"); goto cleanup; } @@ -179,7 +117,7 @@ int PackShim(const ShimPackConfig *config) { } primarySlot = (size_t)primaryInfo.imageSize; } - if (!align_up_size(primarySlot, SHIM_BRANCH_ALIGNMENT, &primarySlot) || + if (!shim_align_up(primarySlot, SHIM_BRANCH_ALIGNMENT, &primarySlot) || primarySlot >= (size_t)SHIM_BRANCH_MAX_OFFSET || primaryInfo.headerOffset > SIZE_MAX - primarySlot || primaryInfo.headerOffset + primarySlot > SIZE_MAX - shim.fileSize) { @@ -195,6 +133,25 @@ int PackShim(const ShimPackConfig *config) { memcpy(output.fileBuffer + primaryInfo.headerOffset + primarySlot, shim.fileBuffer, shim.fileSize); + ShimManifestRecoveryHeader recovery = {0}; + if (writeManifest) { + memcpy(recovery.magic, SHIM_MANIFEST_RECOVERY_MAGIC, 8); + recovery.version = SHIM_MANIFEST_RECOVERY_VERSION; + recovery.headerSize = sizeof(ShimManifestRecoveryHeader); + recovery.entrySize = sizeof(ShimManifestRecoveryEntry); + recovery.entryCount = (uint32_t)config->imageCount; + recovery.shimSize = shim.fileSize; + memcpy(recovery.baseHeader, + primary.fileBuffer + primaryInfo.headerOffset, + sizeof(recovery.baseHeader)); + recoveryEntries[0].alignment = baseImage->alignment; + recoveryEntries[0].sourcePrefixSize = + (uint32_t)primaryInfo.headerOffset; + if (primaryInfo.headerOffset != 0) + memcpy(recoveryEntries[0].sourcePrefix, + primary.fileBuffer, primaryInfo.headerOffset); + } + uint8_t *kernelHeader = output.fileBuffer + primaryInfo.headerOffset; if (!patch_kernel_entry(kernelHeader, primarySlot)) goto cleanup; @@ -252,7 +209,7 @@ int PackShim(const ShimPackConfig *config) { Arm64ImageInfo extraInfo = {0}; if (image->type == SHIM_ENTRY_TYPE_LINUX) { - if (!inspect_arm64_image(&extra, &extraInfo)) { + if (!shim_inspect_arm64_image(&extra, &extraInfo)) { printf("Error: [%s] is not an ARM64 Linux Image.\n", image->section); free(extra.fileBuffer); @@ -271,20 +228,31 @@ int PackShim(const ShimPackConfig *config) { if (!align_linux_offset(runtimeSize, primaryInfo.textOffset, extraInfo.textOffset, (size_t)image->alignment, &imageOffset) || - !align_up_size(imageSlot, 0x1000, &imageSlot)) { + !shim_align_up(imageSlot, 0x1000, &imageSlot)) { printf("Error: [%s] cannot satisfy Linux Image alignment.\n", image->section); free(extra.fileBuffer); goto cleanup; } - } else if (!align_up_size(runtimeSize, (size_t)image->alignment, + } else if (!shim_align_up(runtimeSize, (size_t)image->alignment, &imageOffset)) { printf("Error: [%s] image alignment overflows.\n", image->section); free(extra.fileBuffer); goto cleanup; } - if (is_executable_type(image->type) && + if (writeManifest) { + recoveryEntries[manifestIndex].alignment = image->alignment; + recoveryEntries[manifestIndex].copySizeMax = + image->hasCopySizeMax ? image->copySizeMax : 0; + recoveryEntries[manifestIndex].sourcePrefixSize = + (uint32_t)payloadOffset; + if (payloadOffset != 0) + memcpy(recoveryEntries[manifestIndex].sourcePrefix, + extra.fileBuffer, payloadOffset); + } + + if (shim_is_executable_type(image->type) && (image->entryOffset > payloadSize || payloadSize - (size_t)image->entryOffset < 4 || (image->entryOffset & (SHIM_BRANCH_ALIGNMENT - 1)) != 0)) { @@ -316,13 +284,23 @@ int PackShim(const ShimPackConfig *config) { free(extra.fileBuffer); } + size_t recoveryOffset = 0; + size_t recoverySize = 0; size_t manifestOffset = 0; size_t manifestSize = 0; if (writeManifest) { - manifestSize = sizeof(ShimManifestHeader) + + size_t runtimeManifestSize = sizeof(ShimManifestHeader) + manifest.header.entryCount * sizeof(ShimManifestEntry); + recoveryOffset = runtimeManifestSize; + recoverySize = sizeof(ShimManifestRecoveryHeader) + + config->imageCount * sizeof(ShimManifestRecoveryEntry); + if (runtimeManifestSize > SIZE_MAX - recoverySize) { + printf("Error: Shim manifest size overflows.\n"); + goto cleanup; + } + manifestSize = runtimeManifestSize + recoverySize; size_t oldRuntimeSize = output.fileSize - primaryInfo.headerOffset; - if (!align_up_size(oldRuntimeSize, sizeof(uint64_t), &manifestOffset)) { + if (!shim_align_up(oldRuntimeSize, sizeof(uint64_t), &manifestOffset)) { printf("Error: Failed to align Shim manifest.\n"); goto cleanup; } @@ -334,22 +312,30 @@ int PackShim(const ShimPackConfig *config) { goto cleanup; } manifest.header.imageSize = manifestOffset + manifestSize; - memcpy(output.fileBuffer + manifestFileOffset, &manifest, manifestSize); + manifest.header.reserved[0] = recoveryOffset; + manifest.header.reserved[1] = recoverySize; + memcpy(output.fileBuffer + manifestFileOffset, &manifest, + runtimeManifestSize); + memcpy(output.fileBuffer + manifestFileOffset + recoveryOffset, + &recovery, sizeof(recovery)); + memcpy(output.fileBuffer + manifestFileOffset + recoveryOffset + + sizeof(recovery), recoveryEntries, + config->imageCount * sizeof(ShimManifestRecoveryEntry)); } size_t runtimeImageSize = output.fileSize - primaryInfo.headerOffset; kernelHeader = output.fileBuffer + primaryInfo.headerOffset; - write_le64(kernelHeader + 0x10, runtimeImageSize); - write_le64(kernelHeader + 0x20, manifestOffset); - write_le64(kernelHeader + 0x28, manifestSize); - write_le64(kernelHeader + 0x30, primarySlot); + shim_write_le64(kernelHeader + 0x10, runtimeImageSize); + shim_write_le64(kernelHeader + 0x20, manifestOffset); + shim_write_le64(kernelHeader + 0x28, manifestSize); + shim_write_le64(kernelHeader + 0x30, primarySlot); if (primaryInfo.headerOffset != 0) { if (runtimeImageSize > UINT32_MAX) { printf("Error: UNCOMPRESSED_IMG payload exceeds 32-bit size.\n"); goto cleanup; } - write_le32(output.fileBuffer + 0x10, (uint32_t)runtimeImageSize); + shim_write_le32(output.fileBuffer + 0x10, (uint32_t)runtimeImageSize); } if (write_file_content(&output) != 0) { printf("Error: Failed to write packed image.\n"); diff --git a/src/shim_format.c b/src/shim_format.c new file mode 100644 index 0000000..f6ea56e --- /dev/null +++ b/src/shim_format.c @@ -0,0 +1,79 @@ +#include "shim_format.h" + +uint32_t shim_read_le32(const uint8_t *buffer) { + uint32_t value = 0; + for (int index = 3; index >= 0; index--) + value = (value << 8) | buffer[index]; + return value; +} + +uint64_t shim_read_le64(const uint8_t *buffer) { + uint64_t value = 0; + for (int index = 7; index >= 0; index--) + value = (value << 8) | buffer[index]; + return value; +} + +void shim_write_le32(uint8_t *buffer, uint32_t value) { + for (int index = 0; index < 4; index++) + buffer[index] = (uint8_t)(value >> (index * 8) & 0xff); +} + +void shim_write_le64(uint8_t *buffer, uint64_t value) { + for (int index = 0; index < 8; index++) + buffer[index] = (uint8_t)(value >> (index * 8) & 0xff); +} + +bool shim_align_up(size_t value, size_t alignment, size_t *result) { + if (alignment == 0 || (alignment & (alignment - 1)) != 0 || + value > SIZE_MAX - (alignment - 1)) + return false; + *result = (value + alignment - 1) & ~(alignment - 1); + return true; +} + +bool shim_inspect_arm64_image(const FileContent *file, Arm64ImageInfo *info) { + static const uint8_t arm64Magic[] = {'A', 'R', 'M', 0x64}; + + info->headerOffset = 0; + if (file->fileSize >= SHIM_WRAPPER_SIZE && + memcmp(file->fileBuffer, "UNCOMPRESSED_IMG", 0x10) == 0) + info->headerOffset = SHIM_WRAPPER_SIZE; + + if (file->fileSize < info->headerOffset + SHIM_LINUX_HEADER_SIZE || + memcmp(file->fileBuffer + info->headerOffset + 0x38, + arm64Magic, sizeof(arm64Magic)) != 0) + return false; + + info->payloadSize = file->fileSize - info->headerOffset; + info->textOffset = shim_read_le64( + file->fileBuffer + info->headerOffset + 0x08); + info->imageSize = shim_read_le64( + file->fileBuffer + info->headerOffset + 0x10); + return true; +} + +bool shim_is_executable_type(uint32_t type) { + return type == SHIM_ENTRY_TYPE_LINUX || + type == SHIM_ENTRY_TYPE_FREE_EXEC || + type == SHIM_ENTRY_TYPE_SHIM; +} + +const char *shim_type_name(uint32_t type) { + switch (type) { + case SHIM_ENTRY_TYPE_LINUX: + return "Linux"; + case SHIM_ENTRY_TYPE_FREE_EXEC: + return "FreeExec"; + case SHIM_ENTRY_TYPE_SHIM: + return "Shim"; + case SHIM_ENTRY_TYPE_BLOB: + return "Blob"; + case SHIM_ENTRY_TYPE_DTB: + return "DTB"; + case SHIM_ENTRY_TYPE_MANIFEST: + return "Manifest"; + default: + return NULL; + } +} \ No newline at end of file diff --git a/src/unpack.c b/src/unpack.c new file mode 100644 index 0000000..abf36af --- /dev/null +++ b/src/unpack.c @@ -0,0 +1,357 @@ +#include "shim_format.h" + +#include +#include + +#ifdef _WIN32 +#include +#define mkdir(path, mode) _mkdir(path) +#endif + +typedef struct { + uint8_t *buffer; + size_t size; + size_t headerOffset; + const ShimManifest *manifest; + const ShimManifestRecoveryHeader *recovery; + const ShimManifestRecoveryEntry *recoveryEntries; +} PackedImage; + +static bool join_path(char *result, size_t size, const char *directory, + const char *name) { + int written = snprintf(result, size, "%s/%s", directory, name); + return written >= 0 && (size_t)written < size; +} + +static bool make_directory(const char *path) { + if (mkdir(path, 0777) == 0) + return true; + return errno == EEXIST; +} + +static bool write_buffer(const char *path, const uint8_t *buffer, size_t size) { + FileContent file = {.filePath = path, .fileBuffer = (uint8_t *)buffer, + .fileSize = size}; + return write_file_content(&file) == 0; +} + +static bool range_inside(size_t offset, size_t size, size_t limit) { + return offset <= limit && size <= limit - offset; +} + +static bool ranges_overlap(size_t firstOffset, size_t firstSize, + size_t secondOffset, size_t secondSize) { + return firstOffset < secondOffset + secondSize && + secondOffset < firstOffset + firstSize; +} + +static bool valid_name(const char name[32]) { + const char *end = memchr(name, '\0', 32); + if (end == NULL || end == name) + return false; + for (const char *current = name; current < end; current++) { + unsigned char character = (unsigned char)*current; + if (character < 0x20 || character == 0x7f) + return false; + } + return true; +} + +static bool load_packed(const char *path, PackedImage *packed) { + FileContent file = {.filePath = path}; + if (!get_file_size(&file)) + return false; + packed->buffer = malloc(file.fileSize); + if (packed->buffer == NULL) + return false; + file.fileBuffer = packed->buffer; + if (read_file_content(&file) == NULL) { + free(packed->buffer); + packed->buffer = NULL; + return false; + } + packed->size = file.fileSize; + + FileContent view = {.fileBuffer = packed->buffer, .fileSize = packed->size}; + Arm64ImageInfo image = {0}; + if (!shim_inspect_arm64_image(&view, &image)) { + printf("Error: Input is not an ARM64 Linux Image.\n"); + return false; + } + packed->headerOffset = image.headerOffset; + if (packed->size - packed->headerOffset < SHIM_LINUX_HEADER_SIZE) { + printf("Error: Input image is truncated.\n"); + return false; + } + + const uint8_t *header = packed->buffer + packed->headerOffset; + uint64_t manifestOffset = shim_read_le64(header + 0x20); + uint64_t manifestSize = shim_read_le64(header + 0x28); + uint64_t runtimeSize = shim_read_le64(header + 0x10); + if (manifestOffset > SIZE_MAX || manifestSize > SIZE_MAX || + runtimeSize != packed->size - packed->headerOffset || + !range_inside((size_t)manifestOffset, (size_t)manifestSize, + (size_t)runtimeSize) || + manifestSize < sizeof(ShimManifestHeader)) { + printf("Error: Input image has invalid Manifest bounds.\n"); + return false; + } + + const uint8_t *manifestBuffer = header + (size_t)manifestOffset; + if (memcmp(manifestBuffer, SHIM_MANIFEST_MAGIC, 8) != 0 || + shim_read_le32(manifestBuffer + 8) != SHIM_MANIFEST_VERSION) { + printf("Error: Input image is not a supported reversible image.\n"); + return false; + } + const ShimManifest *manifest = (const ShimManifest *)manifestBuffer; + if (manifest->header.headerSize != sizeof(ShimManifestHeader) || + manifest->header.entrySize != sizeof(ShimManifestEntry) || + manifest->header.entryCount == 0 || + manifest->header.entryCount > SHIM_MAX_ENTRIES || + manifest->header.imageSize != runtimeSize || + manifestSize > SHIM_MANIFEST_MAX_SIZE || + manifest->header.defaultEntry >= manifest->header.entryCount || + manifest->header.reserved[0] > SIZE_MAX || + manifest->header.reserved[1] > SIZE_MAX) { + printf("Error: Input image has an invalid Manifest.\n"); + return false; + } + + size_t recoveryOffset = (size_t)manifest->header.reserved[0]; + size_t recoverySize = (size_t)manifest->header.reserved[1]; + size_t entryCount = manifest->header.entryCount; + size_t expectedRecoverySize = sizeof(ShimManifestRecoveryHeader) + + entryCount * sizeof(ShimManifestRecoveryEntry); + if (recoveryOffset == 0 || recoverySize != expectedRecoverySize || + !range_inside(recoveryOffset, recoverySize, (size_t)manifestSize) || + recoveryOffset != sizeof(ShimManifestHeader) + + entryCount * sizeof(ShimManifestEntry)) { + printf("Error: Input image has no supported Recovery data.\n"); + return false; + } + + const uint8_t *recoveryBuffer = manifestBuffer + recoveryOffset; + const ShimManifestRecoveryHeader *recovery = + (const ShimManifestRecoveryHeader *)recoveryBuffer; + if (memcmp(recovery->magic, SHIM_MANIFEST_RECOVERY_MAGIC, 8) != 0 || + recovery->version != SHIM_MANIFEST_RECOVERY_VERSION || + recovery->headerSize != sizeof(ShimManifestRecoveryHeader) || + recovery->entrySize != sizeof(ShimManifestRecoveryEntry) || + recovery->entryCount != entryCount || recovery->shimSize == 0 || + recovery->shimSize > SIZE_MAX) { + printf("Error: Input image has an invalid Recovery header.\n"); + return false; + } + + size_t primarySlot = (size_t)shim_read_le64(header + 0x30); + if (primarySlot == 0 || recovery->shimSize > SIZE_MAX - primarySlot || + !range_inside(primarySlot, (size_t)recovery->shimSize, + (size_t)manifestOffset)) { + printf("Error: Input image has invalid Shim bounds.\n"); + return false; + } + for (size_t index = 0; index < entryCount; index++) { + const ShimManifestEntry *entry = &manifest->entries[index]; + if (shim_type_name(entry->type) == NULL || !valid_name(entry->name) || + entry->offset > SIZE_MAX || entry->size > SIZE_MAX || + !range_inside((size_t)entry->offset, (size_t)entry->size, + (size_t)manifestOffset)) { + printf("Error: Input image has an invalid Manifest entry.\n"); + return false; + } + } + + packed->manifest = manifest; + packed->recovery = recovery; + packed->recoveryEntries = + (const ShimManifestRecoveryEntry *)(recoveryBuffer + + sizeof(ShimManifestRecoveryHeader)); + + if (manifest->entries[0].offset != 0 || + manifest->entries[0].type != SHIM_ENTRY_TYPE_LINUX || + manifest->entries[0].flags != SHIM_ENTRY_FLAG_BASE_IMAGE || + manifest->entries[0].size < SHIM_LINUX_HEADER_SIZE) + return false; + size_t shimEnd = primarySlot + (size_t)recovery->shimSize; + for (size_t index = 0; index < entryCount; index++) { + const ShimManifestEntry *entry = &manifest->entries[index]; + const ShimManifestRecoveryEntry *metadata = + &packed->recoveryEntries[index]; + uint32_t allowedFlags = index == 0 + ? SHIM_ENTRY_FLAG_BASE_IMAGE + : SHIM_ENTRY_FLAG_COPY; + if ((entry->flags & ~allowedFlags) != 0 || + metadata->alignment < SHIM_BRANCH_ALIGNMENT || + metadata->alignment > UINT32_MAX || + (metadata->alignment & (metadata->alignment - 1)) != 0 || + metadata->sourcePrefixSize > SHIM_WRAPPER_SIZE || + (metadata->sourcePrefixSize != 0 && + metadata->sourcePrefixSize != SHIM_WRAPPER_SIZE) || + (entry->flags & SHIM_ENTRY_FLAG_COPY) != 0 && + (entry->loadAddress == 0 || metadata->copySizeMax == 0 || + entry->size > metadata->copySizeMax)) { + printf("Error: Input image has invalid recovery metadata.\n"); + return false; + } + if (index == 0) { + if (entry->size > primarySlot) + return false; + continue; + } + if (ranges_overlap((size_t)entry->offset, (size_t)entry->size, + primarySlot, (size_t)recovery->shimSize)) { + printf("Error: Manifest entry overlaps the attached Shim.\n"); + return false; + } + for (size_t previous = 1; previous < index; previous++) { + const ShimManifestEntry *other = &manifest->entries[previous]; + if (ranges_overlap((size_t)entry->offset, (size_t)entry->size, + (size_t)other->offset, + (size_t)other->size)) { + printf("Error: Manifest entries overlap.\n"); + return false; + } + } + if ((size_t)entry->offset < shimEnd) + return false; + } + return true; +} + +static bool restore_files(const PackedImage *packed, const char *directory) { + char path[SHIM_PATH_SIZE * 2]; + const ShimManifest *manifest = packed->manifest; + const ShimManifestRecoveryHeader *recovery = packed->recovery; + size_t entryCount = manifest->header.entryCount; + size_t primarySlot = (size_t)shim_read_le64( + packed->buffer + packed->headerOffset + 0x30); + + FileContent base = {0}; + base.fileSize = (size_t)manifest->entries[0].size; + base.fileBuffer = malloc(base.fileSize); + if (base.fileBuffer == NULL) + return false; + memcpy(base.fileBuffer, packed->buffer + packed->headerOffset, + base.fileSize); + memcpy(base.fileBuffer, recovery->baseHeader, + sizeof(recovery->baseHeader)); + { + const ShimManifestRecoveryEntry *baseMetadata = + &packed->recoveryEntries[0]; + size_t prefixSize = baseMetadata->sourcePrefixSize; + if (prefixSize > SHIM_WRAPPER_SIZE || + !join_path(path, sizeof(path), directory, "BaseImage") || + prefixSize + base.fileSize > SIZE_MAX) { + free(base.fileBuffer); + return false; + } + size_t restoredSize = prefixSize + base.fileSize; + uint8_t *restored = malloc(restoredSize); + if (restored == NULL) { + free(base.fileBuffer); + return false; + } + memcpy(restored, baseMetadata->sourcePrefix, prefixSize); + memcpy(restored + prefixSize, base.fileBuffer, base.fileSize); + bool result = write_buffer(path, restored, restoredSize); + free(restored); + free(base.fileBuffer); + if (!result) + return false; + } + + if (!join_path(path, sizeof(path), directory, "Shim.bin") || + !write_buffer(path, packed->buffer + packed->headerOffset + primarySlot, + (size_t)recovery->shimSize)) + return false; + + for (size_t index = 1; index < entryCount; index++) { + const ShimManifestEntry *entry = &manifest->entries[index]; + const ShimManifestRecoveryEntry *metadata = + &packed->recoveryEntries[index]; + const char *typeName = shim_type_name(entry->type); + char name[64]; + snprintf(name, sizeof(name), "Image-%02zu-%s", index, typeName); + if (!join_path(path, sizeof(path), directory, name)) + return false; + size_t prefixSize = metadata->sourcePrefixSize; + if (prefixSize > SHIM_WRAPPER_SIZE || + prefixSize + (size_t)entry->size < prefixSize) + return false; + size_t outputSize = prefixSize + (size_t)entry->size; + uint8_t *output = malloc(outputSize); + if (output == NULL) + return false; + memcpy(output, metadata->sourcePrefix, prefixSize); + memcpy(output + prefixSize, + packed->buffer + packed->headerOffset + entry->offset, + (size_t)entry->size); + bool result = write_buffer(path, output, outputSize); + free(output); + if (!result) + return false; + } + return true; +} + +static bool write_config(const PackedImage *packed, const char *directory) { + char path[SHIM_PATH_SIZE * 2]; + if (!join_path(path, sizeof(path), directory, "config.cfg")) + return false; + FILE *file = fopen(path, "w"); + if (file == NULL) + return false; + + const ShimManifest *manifest = packed->manifest; + fprintf(file, "[Pack]\nShim=Shim.bin\nOutput=RepackedKernel\n"); + fprintf(file, "Default=Image-%02u\nTimeout=%u\n\n", + manifest->header.defaultEntry, manifest->header.timeoutMs); + for (size_t index = 0; index < manifest->header.entryCount; index++) { + const ShimManifestEntry *entry = &manifest->entries[index]; + const ShimManifestRecoveryEntry *metadata = + &packed->recoveryEntries[index]; + const char *typeName = shim_type_name(entry->type); + fprintf(file, "[Image-%02zu]\nName=%s\n", index, entry->name); + if (index == 0) + fprintf(file, "Path=BaseImage\n"); + else { + char name[64]; + snprintf(name, sizeof(name), "Image-%02zu-%s", index, typeName); + fprintf(file, "Path=%s\n", name); + } + fprintf(file, "Type=%s\nBaseImage=%s\nAlign=0x%llx\n", + typeName, index == 0 ? "true" : "false", + (unsigned long long)metadata->alignment); + if (entry->flags & SHIM_ENTRY_FLAG_COPY) + fprintf(file, "CopyTo=0x%llx\nCopySizeMax=0x%llx\n", + (unsigned long long)entry->loadAddress, + (unsigned long long)metadata->copySizeMax); + if (shim_is_executable_type(entry->type)) + fprintf(file, "EntryOffset=0x%llx\n", + (unsigned long long)entry->entryOffset); + fprintf(file, "\n"); + } + fprintf(file, "[Manifest]\nType=Manifest\n"); + return fclose(file) == 0; +} + +int main(int argc, char **argv) { + if (argc != 3) { + printf("Usage: UnpackTool \n"); + return EINVAL; + } + PackedImage packed = {0}; + if (!load_packed(argv[1], &packed)) { + free(packed.buffer); + return EINVAL; + } + if (!make_directory(argv[2]) || !restore_files(&packed, argv[2]) || + !write_config(&packed, argv[2])) { + printf("Error: Failed to write unpacked files.\n"); + free(packed.buffer); + return EINVAL; + } + printf("Kernel image unpacked successfully.\n"); + free(packed.buffer); + return 0; +} \ No newline at end of file diff --git a/src/utils.c b/src/utils.c index 2edc0c8..130fd0c 100644 --- a/src/utils.c +++ b/src/utils.c @@ -9,14 +9,20 @@ * */ size_t get_file_size(FileContent *fileContent) { - FILE *pFile = fopen(fileContent->filePath, "r"); + FILE *pFile = fopen(fileContent->filePath, "rb"); if (pFile == NULL) { printf("Error: %s not found\n", fileContent->filePath); return 0; } - fseek(pFile, 0, SEEK_END); - size_t len = ftell(pFile); + if (fseek(pFile, 0, SEEK_END) != 0) { + fclose(pFile); + return 0; + } + long position = ftell(pFile); fclose(pFile); + if (position <= 0) + return 0; + size_t len = (size_t)position; fileContent->fileSize = len; return len; } @@ -33,8 +39,11 @@ uint8_t *read_file_content(FileContent *fileContent) { FILE *pFile = fopen(fileContent->filePath, "rb"); if (pFile == NULL) return NULL; - fread(fileContent->fileBuffer, fileContent->fileSize, 1, pFile); - fclose(pFile); + size_t readSize = fread(fileContent->fileBuffer, 1, + fileContent->fileSize, pFile); + int closeStatus = fclose(pFile); + if (readSize != fileContent->fileSize || closeStatus != 0) + return NULL; return fileContent->fileBuffer; } @@ -49,7 +58,9 @@ int write_file_content(pFileContent fileContent) { FILE *pFile = fopen(fileContent->filePath, "wb"); if (pFile == NULL) return -EBADF; - fwrite(fileContent->fileBuffer, fileContent->fileSize, 1, pFile); - fclose(pFile); - return 0; + size_t written = fwrite(fileContent->fileBuffer, 1, + fileContent->fileSize, pFile); + int closeStatus = fclose(pFile); + return written == fileContent->fileSize && closeStatus == 0 + ? 0 : -EBADF; } From c79449f18e1830de78c5d8be654865b72ec3f10f Mon Sep 17 00:00:00 2001 From: XiaoYeZi Date: Thu, 6 Aug 2026 01:26:06 +0800 Subject: [PATCH 12/12] CI: Update GitHub Actions to use latest versions --- .github/workflows/cmake-multi-platform.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index a35200a..401c3c8 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -52,7 +52,7 @@ jobs: executable_directory: . steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: submodules: recursive @@ -105,7 +105,7 @@ jobs: cp "build/${{ matrix.executable_directory }}/UnpackTool${{ matrix.executable_suffix }}" artifacts/tools/ - name: Upload host tools - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: Tools-${{ matrix.os }}-${{ matrix.arch }} path: artifacts/tools