diff options
author | 2025-09-26 18:42:11 +0200 | |
---|---|---|
committer | 2025-09-26 18:42:11 +0200 | |
commit | 4cf940b9680b94f8d2f1dbd573f1eb2d56574b0a (patch) | |
tree | 9771777090ef06e42ec61c0248c95dc51dbb9adc /src | |
parent | 1aea4df8c83053fcd4ef8826c19c0ed58aec323a (diff) | |
download | idevicerestore-4cf940b9680b94f8d2f1dbd573f1eb2d56574b0a.tar.gz idevicerestore-4cf940b9680b94f8d2f1dbd573f1eb2d56574b0a.tar.bz2 |
Add support for Mav25 baseband firmware (iPhone 17 family)
Co-authored-by: Nikias Bassen <nikias@gmx.li>
Diffstat (limited to 'src')
-rw-r--r-- | src/mbn.c | 490 | ||||
-rw-r--r-- | src/mbn.h | 85 | ||||
-rw-r--r-- | src/restore.c | 73 |
3 files changed, 484 insertions, 164 deletions
@@ -1,9 +1,10 @@ /* * mbn.c - * support for .mbn file format (found in .bbfw files) + * support for Qualcomm MBN (Modem Binary) formats * * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. + * Copyright (c) 2025 Visual Ehrmanntraut <visual@chefkiss.dev>. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,67 +26,456 @@ #include "mbn.h" #include "common.h" -mbn_file* mbn_parse(const void* data, size_t size) +#define MBN_V1_MAGIC "\x0A\x00\x00\x00" +#define MBN_V1_MAGIC_SIZE 4 + +#pragma pack(push, 1) +typedef struct { + uint32_t type; // the signed .mbn files have 0xA as value. + uint32_t unk_0x04; + uint32_t unk_0x08; + uint32_t unk_0x0c; + uint32_t data_size; // data_size = total_size - sizeof(mbn_header) + uint32_t sig_offset; // real offset = enc_sig_offset & 0xFFFFFF00 + uint32_t unk_0x18; + uint32_t unk_0x1c; + uint32_t unk_0x20; + uint32_t unk_0x24; +} mbn_header_v1; + +#define MBN_V2_MAGIC "\xD1\xDC\x4B\x84\x34\x10\xD7\x73" +#define MBN_V2_MAGIC_SIZE 8 + +typedef struct { + unsigned char magic1[8]; + uint32_t unk_0x08; + uint32_t unk_0x0c; // 0xFFFFFFFF + uint32_t unk_0x10; // 0xFFFFFFFF + uint32_t header_size; + uint32_t unk_0x18; + uint32_t data_size; // data_size = total_size - sizeof(mbn_header_v2) + uint32_t sig_offset; + uint32_t unk_0x24; + uint32_t unk_0x28; + uint32_t unk_0x2c; + uint32_t unk_0x30; + uint32_t unk_0x34; // 0x1 + uint32_t unk_0x38; // 0x1 + uint32_t unk_0x3c; // 0xFFFFFFFF + uint32_t unk_0x40; // 0xFFFFFFFF + uint32_t unk_0x44; // 0xFFFFFFFF + uint32_t unk_0x48; // 0xFFFFFFFF + uint32_t unk_0x4c; // 0xFFFFFFFF +} mbn_header_v2; + +#define MBN_BIN_MAGIC "\x04\x00\xEA\x6C\x69\x48\x55" +#define MBN_BIN_MAGIC_SIZE 7 +#define MBN_BIN_MAGIC_OFFSET 1 // we ignore the first byte + +typedef struct { + unsigned char magic[8]; + uint32_t unk_0x08; + uint32_t version; + uint32_t total_size; // size including header + uint32_t unk_0x14; // some offset +} mbn_bin_header; + +typedef struct +{ + uint32_t reserved; + uint32_t version; + uint32_t common_metadata_size; + uint32_t qti_metadata_size; + uint32_t oem_metadata_size; + uint32_t hash_table_size; + uint32_t qti_signature_size; + uint32_t qti_certificate_chain_size; + uint32_t oem_signature_size; + uint32_t oem_certificate_chain_size; +} mbn_v7_header; + +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_ABIVERSION 8 +#define EI_PAD 9 +#define EI_NIDENT 16 + +#define ELFMAG0 0x7F +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +typedef struct +{ + uint8_t e_ident[EI_NIDENT]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint32_t e_entry; + uint32_t e_phoff; + uint32_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +} elf32_header; + +typedef struct +{ + uint8_t e_ident[EI_NIDENT]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint64_t e_entry; + uint64_t e_phoff; + uint64_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +} elf64_header; + +typedef struct +{ + uint32_t p_type; + uint32_t p_offset; + uint32_t p_vaddr; + uint32_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +} elf32_pheader; + +typedef struct +{ + uint32_t p_type; + uint32_t p_flags; + uint64_t p_offset; + uint64_t p_vaddr; + uint64_t p_paddr; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_align; +} elf64_pheader; +#pragma pack(pop) + +static int mbn_is_valid_elf(const uint8_t* e_ident, size_t size) +{ + return size >= EI_NIDENT && e_ident[EI_MAG0] == ELFMAG0 && + e_ident[EI_MAG1] == ELFMAG1 && e_ident[EI_MAG2] == ELFMAG2 && + e_ident[EI_MAG3] == ELFMAG3 && e_ident[EI_CLASS] != ELFCLASSNONE; +} + +static int mbn_is_64bit_elf(const uint8_t* e_ident) { - mbn_file* mbn = (mbn_file*)malloc(sizeof(mbn_file)); - if (!mbn) { - return NULL; - } - memset(mbn, '\0', sizeof(mbn_file)); - mbn->data = malloc(size); - mbn->size = size; - memcpy(mbn->data, data, size); - /* FIXME: header parsing is not big endian safe */ - if (memcmp(data, MBN_V2_MAGIC, MBN_V2_MAGIC_SIZE) == 0) { - mbn->version = 2; - memcpy(&mbn->header.v2, data, sizeof(mbn_header_v2)); - mbn->parsed_size = mbn->header.v2.data_size + sizeof(mbn_header_v2); - } else if (memcmp(data, MBN_V1_MAGIC, MBN_V1_MAGIC_SIZE) == 0) { - mbn->version = 1; - memcpy(&mbn->header.v1, data, sizeof(mbn_header_v1)); - mbn->parsed_size = mbn->header.v1.data_size + sizeof(mbn_header_v1); - } else if (memcmp(data, BIN_MAGIC, BIN_MAGIC_SIZE) == 0) { - mbn->version = 3; - memcpy(&mbn->header.bin, data, sizeof(bin_header)); - mbn->parsed_size = mbn->header.bin.total_size; - } else if (memcmp(data, ELF_MAGIC, ELF_MAGIC_SIZE) == 0) { - mbn->version = 4; - memcpy(&mbn->header.elf, data, sizeof(elf_header)); - // we cheat here since we don't parse the actual ELF file - mbn->parsed_size = mbn->size; + return e_ident[EI_CLASS] == ELFCLASS64; +} + +void* mbn_stitch(const void* data, size_t data_size, const void* blob, size_t blob_size) +{ + if (!data) { + logger(LL_ERROR, "%s: data is NULL\n", __func__); + return NULL; + } + + if (!data_size) { + logger(LL_ERROR, "%s: data size is 0\n", __func__); + return NULL; + } + + if (!blob) { + logger(LL_ERROR, "%s: blob is NULL\n", __func__); + return NULL; + } + + if (!blob_size) { + logger(LL_ERROR, "%s: blob size is 0\n", __func__); + return NULL; + } + + size_t parsed_size = 0; + if (data_size > MBN_V2_MAGIC_SIZE && memcmp(data, MBN_V2_MAGIC, MBN_V2_MAGIC_SIZE) == 0) { + parsed_size = ((mbn_header_v2*)data)->data_size + sizeof(mbn_header_v2); + logger(LL_DEBUG, "%s: encountered MBN v2 image, parsed_size = 0x%zx\n", __func__, parsed_size); + } else if (data_size > MBN_V1_MAGIC_SIZE && memcmp(data, MBN_V1_MAGIC, MBN_V1_MAGIC_SIZE) == 0) { + parsed_size = ((mbn_header_v1*)data)->data_size + sizeof(mbn_header_v1); + logger(LL_DEBUG, "%s: encountered MBN v1 image, parsed_size = 0x%zx\n", __func__, parsed_size); + } else if (data_size > MBN_BIN_MAGIC_SIZE+MBN_BIN_MAGIC_OFFSET && memcmp((uint8_t*)data+MBN_BIN_MAGIC_OFFSET, (uint8_t*)MBN_BIN_MAGIC, MBN_BIN_MAGIC_SIZE) == 0) { + parsed_size = ((mbn_bin_header*)data)->total_size; + logger(LL_DEBUG, "%s: encountered MBN BIN image, parsed_size = 0x%zx\n", __func__, parsed_size); + } else if (mbn_is_valid_elf(data, data_size)) { + if (mbn_is_64bit_elf(data)) { + const elf64_header* ehdr = data; + const elf64_pheader* phdr = data + ehdr->e_phoff; + if (ehdr->e_phnum == 0) { + logger(LL_ERROR, "%s: ELF has no program sections\n", __func__); + return NULL; + } + uint64_t last_off = 0; + uint16_t last_index = 0; + for (uint16_t i = 0; i < ehdr->e_phnum; i++) { + if (phdr[i].p_offset > last_off) { + last_off = phdr[i].p_offset; + last_index = i; + } + } + parsed_size = last_off + phdr[last_index].p_filesz; + } else { + const elf32_header* ehdr = data; + const elf32_pheader* phdr = data + ehdr->e_phoff; + if (ehdr->e_phnum == 0) { + logger(LL_ERROR, "%s: ELF has no program sections\n", __func__); + return NULL; + } + uint32_t last_off = 0; + uint16_t last_index = 0; + for (uint16_t i = 0; i < ehdr->e_phnum; i++) { + if (phdr[i].p_offset > last_off) { + last_off = phdr[i].p_offset; + last_index = i; + } + } + parsed_size = last_off + phdr[last_index].p_filesz; + } + logger(LL_DEBUG, "%s: encountered ELF image, parsed_size = 0x%zx\n", __func__, parsed_size); } else { - logger(LL_DEBUG, "Unknown file format passed to %s\n", __func__); + logger(LL_WARNING, "Unknown file format passed to %s\n", __func__); } - if (mbn->parsed_size != mbn->size) { - logger(LL_WARNING, "Size mismatch when parsing MBN file. Continuing anyway.\n"); + if (parsed_size != data_size) { + logger(LL_WARNING, "%s: size mismatch for MBN data, expected 0x%zx, input size 0x%zx\n", __func__, parsed_size, data_size); } - return mbn; + + off_t stitch_offset = data_size - blob_size; + if (stitch_offset + blob_size > data_size) { + logger(LL_ERROR, "%s: stitch offset (0x%llx) + size (0x%zx) is larger than the destination (0x%zx)\n", __func__, stitch_offset, blob_size, data_size); + return NULL; + } + + void* buf = malloc(data_size); + if (buf == NULL) { + logger(LL_ERROR, "out of memory\n"); + return NULL; + } + + memcpy(buf, data, data_size); + logger(LL_DEBUG, "%s: stitching mbn at 0x%llx, size 0x%zx\n", __func__, stitch_offset, blob_size); + memcpy(buf + stitch_offset, blob, blob_size); + + return buf; } -void mbn_free(mbn_file* mbn) +// the sum of header size and all sizes inside it must fit within the size of +// the data +static int mbn_v7_header_sizes_valid(const mbn_v7_header* header, size_t size) { - if (mbn) { - if (mbn->data) { - free(mbn->data); - } - free(mbn); - } + return (sizeof(*header) + header->common_metadata_size + + header->qti_metadata_size + header->oem_metadata_size + + header->hash_table_size + header->qti_signature_size + + header->qti_certificate_chain_size + header->oem_signature_size + + header->oem_certificate_chain_size) <= size; +} + +// 0xE0 == sizeof(mav25_authority_meta_field_t), 0x68 == +// kExpectedOEMSignatureSize, 0xD20 == kExpectedOEMCertChainSize +static int mbn_v7_header_sizes_expected(const mbn_v7_header* header) +{ + return (header->qti_metadata_size == 0 || header->qti_metadata_size == 0xE0) && + (header->oem_metadata_size == 0 || header->oem_metadata_size == 0xE0) && + (header->oem_signature_size == 0 || header->oem_signature_size == 0x68) && + (header->oem_certificate_chain_size == 0 || header->oem_certificate_chain_size == 0xD20); +} + +static void mbn_v7_log_header(const mbn_v7_header* header, const char* func, const char* prefix) +{ + logger(LL_DEBUG, + "%s: %s header {version=0x%x, common_metadata_size=0x%x, " + "qti_metadata_size=0x%x, oem_metadata_size=0x%x, hash_table_size=0x%x, " + "qti_signature_size=0x%x, qti_certificate_chain_size=0x%x, " + "oem_signature_size=0x%x, oem_certificate_chain_size=0x%x}", + func, + prefix, + header->version, + header->common_metadata_size, + header->qti_metadata_size, + header->oem_metadata_size, + header->hash_table_size, + header->qti_signature_size, + header->qti_certificate_chain_size, + header->oem_signature_size, + header->oem_certificate_chain_size + ); } -int mbn_update_sig_blob(mbn_file* mbn, const void* sigdata, size_t siglen) +void* mbn_mav25_stitch(const void* data, size_t data_size, const void* blob, size_t blob_size) { - if (!mbn) { - logger(LL_ERROR, "%s: no data\n", __func__); - return -1; + if (!data) { + logger(LL_ERROR, "%s: data is NULL\n", __func__); + return NULL; } - mbn->parsed_sig_offset = mbn->size - siglen; - if ((mbn->parsed_sig_offset + siglen) > mbn->size) { - logger(LL_ERROR, "%s: signature is larger than mbn file size\n", __func__); - return -1; + + if (!data_size) { + logger(LL_ERROR, "%s: data size is 0\n", __func__); + return NULL; } - memcpy(mbn->data + mbn->parsed_sig_offset, sigdata, siglen); + if (!blob) { + logger(LL_ERROR, "%s: blob is NULL\n", __func__); + return NULL; + } - return 0; -} + if (!blob_size) { + logger(LL_ERROR, "%s: blob size is 0\n", __func__); + return NULL; + } + + if (!mbn_is_valid_elf(data, data_size)) { + logger(LL_ERROR, "%s: data is not a valid ELF\n", __func__); + return NULL; + } + + if (sizeof(mbn_v7_header) > blob_size) { + logger(LL_ERROR, "%s: header is bigger than blob\n", __func__); + return NULL; + } + + const mbn_v7_header* src_header = blob; + mbn_v7_log_header(src_header, __func__, "src"); + + if (src_header->version != 7) { + logger(LL_ERROR, "%s: src header version (0x%x) is incorrect\n", __func__, src_header->version); + return NULL; + } + + // NOTE: Apple does weird stuff, in this case blob is smaller than + // the sizes the header reports, so we can't check their validity. + if (!mbn_v7_header_sizes_expected(src_header)) { + logger(LL_WARNING, "%s: header sizes in header are unexpected (qti_metadata_size=0x%x, oem_metadata_size=0x%x, oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)\n", __func__, src_header->qti_metadata_size, src_header->oem_metadata_size, src_header->oem_signature_size, src_header->oem_certificate_chain_size); + } + + off_t sect_off; + size_t sect_size; + if (mbn_is_64bit_elf(data)) { + const elf64_header* ehdr = data; + const elf64_pheader* phdr = data + ehdr->e_phoff; + if (ehdr->e_phnum == 0) { + logger(LL_ERROR, "%s: ELF has no program sections\n", __func__); + return NULL; + } + if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(elf32_pheader)) > data_size) { + logger(LL_ERROR, "%s: Last ELF program section is out of bounds\n", __func__); + return NULL; + } + sect_off = phdr[ehdr->e_phnum-1].p_offset; + sect_size = phdr[ehdr->e_phnum-1].p_filesz; + } else { + const elf32_header* ehdr = data; + const elf32_pheader* phdr = data + ehdr->e_phoff; + if (ehdr->e_phnum == 0) { + logger(LL_ERROR, "%s: ELF has no program sections\n", __func__); + return NULL; + } + if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(elf64_pheader)) > data_size) { + logger(LL_ERROR, "%s: Last ELF program section is out of bounds\n", __func__); + return NULL; + } + sect_off = phdr[ehdr->e_phnum-1].p_offset; + sect_size = phdr[ehdr->e_phnum-1].p_filesz; + } + + if (sect_off == 0) { + logger(LL_ERROR, "%s: section has 0 offset\n", __func__); + return NULL; + } + + if (sect_size == 0) { + logger(LL_ERROR, "%s: section has 0 size\n", __func__); + return NULL; + } + + if (sect_off + sect_size > data_size) { + logger(LL_ERROR, "%s: section (0x%llx+0x%zx) is bigger than the data\n", __func__, sect_off, sect_size); + return NULL; + } + if (sizeof(mbn_v7_header) > sect_size) { + logger(LL_ERROR, "%s: dest header is bigger than the section (0x%zx)\n", __func__, sect_size); + return NULL; + } + + const mbn_v7_header* header = data + sect_off; + mbn_v7_log_header(header, __func__, "dest"); + if (header->version != 7) { + logger(LL_ERROR, "%s: dest header version (0x%x) is incorrect\n", __func__, header->version); + return NULL; + } + + if (!mbn_v7_header_sizes_valid(header, sect_size)) { + logger(LL_ERROR, "%s: sizes in dest header are invalid (common_metadata_size=0x%x, qti_metadata_size=0x%x, oem_metadata_size=0x%x, hash_table_size=0x%x, qti_signature_size=0x%x, qti_certificate_chain_size=0x%x, oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)\n", __func__, header->common_metadata_size, header->qti_metadata_size, header->oem_metadata_size, header->hash_table_size, header->qti_signature_size, header->qti_certificate_chain_size, header->oem_signature_size, header->oem_certificate_chain_size); + return NULL; + } + + if (!mbn_v7_header_sizes_expected(header)) { + logger(LL_WARNING, "%s: header sizes in dest header are unexpected (qti_metadata_size=0x%x, oem_metadata_size=0x%x, oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)\n", __func__, header->qti_metadata_size, header->oem_metadata_size, header->oem_signature_size, header->oem_certificate_chain_size); + } + + size_t new_metadata_size = + sizeof(*src_header) + src_header->common_metadata_size + + src_header->qti_metadata_size + src_header->oem_metadata_size; + size_t new_metadata_and_hash_table_size = + new_metadata_size + src_header->hash_table_size; + size_t new_oem_sig_and_cert_chain_size = + src_header->oem_signature_size + src_header->oem_certificate_chain_size; + off_t new_oem_sig_and_cert_chain_off = new_metadata_and_hash_table_size + + header->qti_signature_size + + header->qti_certificate_chain_size; + + if (new_metadata_and_hash_table_size > blob_size) { + logger(LL_ERROR, "%s: new metadata (0x%zx) and hash table (0x%x) are bigger than the source (0x%zx)\n", __func__, new_metadata_size, src_header->hash_table_size, blob_size); + return NULL; + } + + if (new_metadata_and_hash_table_size > sect_size) { + logger(LL_ERROR, "%s: new metadata (0x%zx) and hash table (0x%x) are bigger than the destination (0x%zx)\n", __func__, new_metadata_size, src_header->hash_table_size, sect_size); + return NULL; + } + + if (new_metadata_and_hash_table_size + new_oem_sig_and_cert_chain_size > blob_size) { + logger(LL_ERROR, "%s: new OEM signature and certificate chain are bigger than the source\n", __func__); + return NULL; + } + + if (new_oem_sig_and_cert_chain_off + new_oem_sig_and_cert_chain_size > sect_size) { + logger(LL_ERROR, "%s: new OEM signature and certificate chain are outside the bounds of the destination\n", __func__); + return NULL; + } + + void* buf = malloc(data_size); + if (buf == NULL) { + logger(LL_ERROR, "out of memory\n"); + return NULL; + } + + memcpy(buf, data, data_size); + logger(LL_DEBUG, "%s: stitching mbn at 0x%llx (0x%zx bytes)\n", __func__, sect_off, new_metadata_and_hash_table_size); + memcpy(buf + sect_off, blob, new_metadata_and_hash_table_size); + logger(LL_DEBUG, "%s: stitching mbn at 0x%llx (0x%zx bytes)\n", __func__, sect_off + new_oem_sig_and_cert_chain_off, new_oem_sig_and_cert_chain_size); + memcpy(buf + sect_off + new_oem_sig_and_cert_chain_off, blob + new_metadata_and_hash_table_size, new_oem_sig_and_cert_chain_size); + + return buf; +} @@ -1,8 +1,9 @@ /* * mbn.h - * support for .mbn file format (found in .bbfw files) + * support for Qualcomm MBN (Modem Binary) formats * * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. + * Copyright (c) 2025 Visual Ehrmanntraut <visual@chefkiss.dev>. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,85 +24,7 @@ #include <stdint.h> -#define MBN_V1_MAGIC "\x0A\x00\x00\x00" -#define MBN_V1_MAGIC_SIZE 4 - -struct _mbn_header_v1 { - uint32_t type; // the signed .mbn files have 0xA as value. - uint32_t unk_0x04; - uint32_t unk_0x08; - uint32_t unk_0x0c; - uint32_t data_size; // data_size = total_size - sizeof(mbn_header) - uint32_t sig_offset; // real offset = enc_sig_offset & 0xFFFFFF00 - uint32_t unk_0x18; - uint32_t unk_0x1c; - uint32_t unk_0x20; - uint32_t unk_0x24; -} __attribute__((packed)); -typedef struct _mbn_header_v1 mbn_header_v1; - -#define MBN_V2_MAGIC "\xD1\xDC\x4B\x84\x34\x10\xD7\x73" -#define MBN_V2_MAGIC_SIZE 8 - -struct _mbn_header_v2 { - unsigned char magic1[8]; - uint32_t unk_0x08; - uint32_t unk_0x0c; // 0xFFFFFFFF - uint32_t unk_0x10; // 0xFFFFFFFF - uint32_t header_size; - uint32_t unk_0x18; - uint32_t data_size; // data_size = total_size - sizeof(mbn_header_v2) - uint32_t sig_offset; - uint32_t unk_0x24; - uint32_t unk_0x28; - uint32_t unk_0x2c; - uint32_t unk_0x30; - uint32_t unk_0x34; // 0x1 - uint32_t unk_0x38; // 0x1 - uint32_t unk_0x3c; // 0xFFFFFFFF - uint32_t unk_0x40; // 0xFFFFFFFF - uint32_t unk_0x44; // 0xFFFFFFFF - uint32_t unk_0x48; // 0xFFFFFFFF - uint32_t unk_0x4c; // 0xFFFFFFFF -} __attribute__((packed)); -typedef struct _mbn_header_v2 mbn_header_v2; - -#define BIN_MAGIC "\x7D\x04\x00\xEA\x6C\x69\x48\x55" -#define BIN_MAGIC_SIZE 8 - -struct _bin_header { - unsigned char magic[8]; - uint32_t unk_0x08; - uint32_t version; - uint32_t total_size; // size including header - uint32_t unk_0x14; // some offset -} __attribute__((packed)); -typedef struct _bin_header bin_header; - -#define ELF_MAGIC "\x7F\x45\x4C\x46\x01\x01\x01\x00" // ELF magic, 32bit, little endian, SYSV -#define ELF_MAGIC_SIZE 8 - -struct _elf_header { - unsigned char magic[8]; -} __attribute__((packed)); -typedef struct _elf_header elf_header; - -typedef struct { - uint32_t version; - union { - mbn_header_v1 v1; - mbn_header_v2 v2; - bin_header bin; - elf_header elf; - } header; - uint32_t parsed_size; - uint32_t parsed_sig_offset; - void* data; - uint32_t size; -} mbn_file; - -mbn_file* mbn_parse(const void* data, size_t size); -void mbn_free(mbn_file* mbn); -int mbn_update_sig_blob(mbn_file* mbn, const void* data, size_t size); +void* mbn_stitch(const void* data, size_t data_size, const void* blob, size_t blob_size); +void* mbn_mav25_stitch(const void* data, size_t data_size, const void* blob, size_t blob_size); #endif diff --git a/src/restore.c b/src/restore.c index 09cbe16..f2fd9be 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1857,7 +1857,7 @@ int restore_send_nor(struct idevicerestore_client_t* client, plist_t message) return 0; } -static const char* restore_get_bbfw_fn_for_element(const char* elem) +static const char* restore_get_bbfw_fn_for_element(const char* elem, uint32_t bb_chip_id) { struct bbfw_fn_elem_t { const char* element; @@ -1888,16 +1888,30 @@ static const char* restore_get_bbfw_fn_for_element(const char* elem) { NULL, NULL } }; + struct bbfw_fn_elem_t bbfw_fn_elem_mav25[] = { + // Mav25 Firmware files + { "Misc", "multi_image.mbn" }, + { "RestoreSBL1", "restorexbl_sc.elf" }, + { "SBL1", "xbl_sc.elf" }, + { "TME", "signed_firmware_soc_view.elf" }, + { NULL, NULL } + }; + + struct bbfw_fn_elem_t* bbfw_fn_elems = (struct bbfw_fn_elem_t*)bbfw_fn_elem; + if (bb_chip_id == 0x1F30E1) { + bbfw_fn_elems = (struct bbfw_fn_elem_t*)bbfw_fn_elem_mav25; + } + int i; - for (i = 0; bbfw_fn_elem[i].element != NULL; i++) { - if (strcmp(bbfw_fn_elem[i].element, elem) == 0) { - return bbfw_fn_elem[i].fn; + for (i = 0; bbfw_fn_elems[i].element != NULL; i++) { + if (strcmp(bbfw_fn_elems[i].element, elem) == 0) { + return bbfw_fn_elems[i].fn; } } return NULL; } -static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned char* bb_nonce) +static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned char* bb_nonce, uint32_t bb_chip_id) { int res = -1; @@ -1925,7 +1939,6 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned struct zip_file* zfile = NULL; struct zip* za = NULL; struct zip_source* zs = NULL; - mbn_file* mbn = NULL; fls_file* fls = NULL; za = zip_open(bbfwtmp, 0, &zerr); @@ -1953,7 +1966,7 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned if (node && (strcmp(key + (strlen(key) - 5), "-Blob") == 0) && (plist_get_node_type(node) == PLIST_DATA)) { char *ptr = strchr(key, '-'); *ptr = '\0'; - const char* signfn = restore_get_bbfw_fn_for_element(key); + const char* signfn = restore_get_bbfw_fn_for_element(key, bb_chip_id); if (!signfn) { logger(LL_ERROR, "can't match element name '%s' to baseband firmware file name.\n", key); goto leave; @@ -2002,12 +2015,6 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned logger(LL_ERROR, "could not parse fls file\n"); goto leave; } - } else { - mbn = mbn_parse(buffer, zstat.size); - if (!mbn) { - logger(LL_ERROR, "could not parse mbn file\n"); - goto leave; - } } free(buffer); buffer = NULL; @@ -2019,32 +2026,33 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned goto leave; } + logger(LL_VERBOSE, "Stitching %s\n", signfn); if (is_fls) { - if (fls_update_sig_blob(fls, blob, (unsigned int)blob_size) != 0) { - logger(LL_ERROR, "could not sign %s\n", signfn); + if (fls_update_sig_blob(fls, blob, (size_t)blob_size) != 0) { + logger(LL_ERROR, "Could not stitch %s\n", signfn); goto leave; } - } else { - if (mbn_update_sig_blob(mbn, blob, (unsigned int)blob_size) != 0) { - logger(LL_ERROR, "could not sign %s\n", signfn); + fsize = fls->size; + fdata = (unsigned char*)malloc(fsize); + if (fdata == NULL) { + logger(LL_ERROR, "out of memory\n"); goto leave; } - } - - fsize = (is_fls ? fls->size : mbn->size); - fdata = (unsigned char*)malloc(fsize); - if (fdata == NULL) { - logger(LL_ERROR, "out of memory\n"); - goto leave; - } - if (is_fls) { memcpy(fdata, fls->data, fsize); fls_free(fls); fls = NULL; + } else if (bb_chip_id == 0x1F30E1) { // Mav25 - Qualcomm Snapdragon X80 5G Modem + fdata = mbn_mav25_stitch(buffer, zstat.size, blob, (size_t)blob_size); + if (!fdata) { + logger(LL_ERROR, "Could not stitch %s\n", signfn); + goto leave; + } } else { - memcpy(fdata, mbn->data, fsize); - mbn_free(mbn); - mbn = NULL; + fdata = mbn_stitch(buffer, zstat.size, blob, (size_t)blob_size); + if (!fdata) { + logger(LL_ERROR, "Could not stitch %s\n", signfn); + goto leave; + } } zs = zip_source_buffer(za, fdata, fsize, 1); @@ -2055,7 +2063,7 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned } if (zip_file_replace(za, zindex, zs, 0) == -1) { - logger(LL_ERROR, "could not update signed '%s' in archive\n", signfn); + logger(LL_ERROR, "Could not update signed '%s' in archive\n", signfn); goto leave; } @@ -2218,7 +2226,6 @@ leave: zip_unchange_all(za); zip_close(za); } - mbn_free(mbn); fls_free(fls); free(buffer); @@ -2352,7 +2359,7 @@ static int restore_send_baseband_data(struct idevicerestore_client_t* client, pl response = NULL; } - res = restore_sign_bbfw(bbfwtmp, (client->restore->bbtss) ? client->restore->bbtss : response, bb_nonce); + res = restore_sign_bbfw(bbfwtmp, (client->restore->bbtss) ? client->restore->bbtss : response, bb_nonce, bb_chip_id); if (res != 0) { goto leave; } |