/*! * \brief Find size of Linux kernel in boot image * * \pre The file position can be at any offset prior to calling this function. * * \post The file pointer position is undefined after this function returns. * Use mb_file_seek() to return to a known position. * * \param[in] bir MbBiReader to set error message * \param[in] file MbFile handle * \param[in] kernel_offset Offset of kernel in boot image * \param[out] kernel_size_out Pointer to store kernel size * * \return * * #MB_BI_OK if the kernel size is found * * #MB_BI_WARN if the kernel size cannot be found * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int find_linux_kernel_size(MbBiReader *bir, MbFile *file, uint32_t kernel_offset, uint32_t *kernel_size_out) { int ret; size_t n; uint32_t kernel_size; // If the boot image was patched with an early version of loki, the // original kernel size is not stored in the loki header properly (or in the // shellcode). The size is stored in the kernel image's header though, so // we'll use that. // http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html#d0e309 ret = mb_file_seek(file, kernel_offset + 0x2c, SEEK_SET, nullptr); if (ret != MB_FILE_OK) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to seek to kernel header: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } ret = mb_file_read_fully(file, &kernel_size, sizeof(kernel_size), &n); if (ret != MB_FILE_OK) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to read size from kernel header: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } else if (n != sizeof(kernel_size)) { mb_bi_reader_set_error(bir, mb_file_error(file), "Unexpected EOF when reading kernel header"); return MB_BI_WARN; } *kernel_size_out = mb_le32toh(kernel_size); return MB_BI_OK; }
MB_BEGIN_C_DECLS /*! * \brief Read MTK header * * \pre The file position can be at any offset prior to calling this function. * * \post The file pointer position is undefined after this function returns. * Use File::seek() to return to a known position. * * \param[in] bir MbBiReader for setting error messages * \param[in] file mb::File handle * \param[in] offset Offset to read MTK header * \param[out] mtkhdr_out Pointer to store MTK header (in host byte order) * * \return * * #MB_BI_OK if the header is found * * #MB_BI_WARN if the header is not found * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int read_mtk_header(MbBiReader *bir, mb::File *file, uint64_t offset, MtkHeader *mtkhdr_out) { MtkHeader mtkhdr; size_t n; mb::FileStatus file_ret; file_ret = file->seek(offset, SEEK_SET, nullptr); if (file_ret < mb::FileStatus::OK) { mb_bi_reader_set_error(bir, file->error(), "Failed to seek to MTK header at %" PRIu64 ": %s", offset, file->error_string().c_str()); return file_ret == mb::FileStatus::FATAL ? MB_BI_FATAL : MB_BI_FAILED; } file_ret = mb::file_read_fully(*file, &mtkhdr, sizeof(mtkhdr), &n); if (file_ret < mb::FileStatus::OK) { mb_bi_reader_set_error(bir, file->error(), "Failed to read MTK header: %s", file->error_string().c_str()); return file_ret == mb::FileStatus::FATAL ? MB_BI_FATAL : MB_BI_FAILED; } if (n != sizeof(MtkHeader) || memcmp(mtkhdr.magic, MTK_MAGIC, MTK_MAGIC_SIZE) != 0) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "MTK header not found at %" PRIu64, offset); return MB_BI_WARN; } *mtkhdr_out = mtkhdr; mtk_fix_header_byte_order(mtkhdr_out); return MB_BI_OK; }
/*! * \brief Find location of Samsung SEAndroid magic * * \pre The file position can be at any offset prior to calling this function. * * \post The file pointer position is undefined after this function returns. * Use mb_file_seek() to return to a known position. * * \param[in] bir MbBiReader for setting error messages * \param[in] file MbFile handle * \param[in] hdr Android boot image header (in host byte order) * \param[out] offset_out Pointer to store magic offset * * \return * * #MB_BI_OK if the magic is found * * #MB_BI_WARN if the magic is not found * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int find_samsung_seandroid_magic(MbBiReader *bir, MbFile *file, AndroidHeader *hdr, uint64_t *offset_out) { unsigned char buf[SAMSUNG_SEANDROID_MAGIC_SIZE]; size_t n; int ret; uint64_t pos = 0; // Skip header, whose size cannot exceed the page size pos += hdr->page_size; // Skip kernel pos += hdr->kernel_size; pos += align_page_size<uint64_t>(pos, hdr->page_size); // Skip ramdisk pos += hdr->ramdisk_size; pos += align_page_size<uint64_t>(pos, hdr->page_size); // Skip second bootloader pos += hdr->second_size; pos += align_page_size<uint64_t>(pos, hdr->page_size); // Skip device tree pos += hdr->dt_size; pos += align_page_size<uint64_t>(pos, hdr->page_size); ret = mb_file_seek(file, pos, SEEK_SET, nullptr); if (ret < 0) { mb_bi_reader_set_error(bir, mb_file_error(file), "SEAndroid magic not found: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } ret = mb_file_read_fully(file, buf, sizeof(buf), &n); if (ret < 0) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to read SEAndroid magic: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } if (n != SAMSUNG_SEANDROID_MAGIC_SIZE || memcmp(buf, SAMSUNG_SEANDROID_MAGIC, n) != 0) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "SEAndroid magic not found in last %d bytes", SAMSUNG_SEANDROID_MAGIC_SIZE); return MB_BI_WARN; } *offset_out = pos; return MB_BI_OK; }
/*! * \brief Enable support for Android boot image format * * \param bir MbBiReader * * \return * * #MB_BI_OK if the format is successfully enabled * * #MB_BI_WARN if the format is already enabled * * \<= #MB_BI_FAILED if an error occurs */ int mb_bi_reader_enable_format_android(MbBiReader *bir) { AndroidReaderCtx *const ctx = static_cast<AndroidReaderCtx *>( calloc(1, sizeof(AndroidReaderCtx))); if (!ctx) { mb_bi_reader_set_error(bir, -errno, "Failed to allocate AndroidReaderCtx: %s", strerror(errno)); return MB_BI_FAILED; } _segment_reader_init(&ctx->segctx); // Allow truncated dt image by default ctx->allow_truncated_dt = true; return _mb_bi_reader_register_format(bir, ctx, MB_BI_FORMAT_ANDROID, MB_BI_FORMAT_NAME_ANDROID, &android_reader_bid, &android_reader_set_option, &android_reader_read_header, &android_reader_read_entry, &android_reader_go_to_entry, &android_reader_read_data, &android_reader_free); }
/*! * \brief Enable support for Loki boot image format * * \param bir MbBiReader * * \return * * #MB_BI_OK if the format is successfully enabled * * #MB_BI_WARN if the format is already enabled * * \<= #MB_BI_FAILED if an error occurs */ int mb_bi_reader_enable_format_loki(MbBiReader *bir) { LokiReaderCtx *const ctx = static_cast<LokiReaderCtx *>( calloc(1, sizeof(LokiReaderCtx))); if (!ctx) { mb_bi_reader_set_error(bir, -errno, "Failed to allocate LokiReaderCtx: %s", strerror(errno)); return MB_BI_FAILED; } _segment_reader_init(&ctx->segctx); return _mb_bi_reader_register_format(bir, ctx, MB_BI_FORMAT_LOKI, MB_BI_FORMAT_NAME_LOKI, &loki_reader_bid, nullptr, &loki_reader_read_header, &loki_reader_read_entry, &loki_reader_go_to_entry, &loki_reader_read_data, &loki_reader_free); }
int android_reader_read_header(MbBiReader *bir, void *userdata, MbBiHeader *header) { AndroidReaderCtx *const ctx = static_cast<AndroidReaderCtx *>(userdata); int ret; if (!ctx->have_header_offset) { // A bid might not have been performed if the user forced a particular // format ret = find_android_header(bir, bir->file, ANDROID_MAX_HEADER_OFFSET, &ctx->hdr, &ctx->header_offset); if (ret < 0) { return ret; } ctx->have_header_offset = true; } ret = android_set_header(&ctx->hdr, header); if (ret != MB_BI_OK) { mb_bi_reader_set_error(bir, MB_BI_ERROR_INTERNAL_ERROR, "Failed to set header fields"); return ret; } // Calculate offsets for each section uint64_t pos = 0; uint32_t page_size = mb_bi_header_page_size(header); uint64_t kernel_offset; uint64_t ramdisk_offset; uint64_t second_offset; uint64_t dt_offset; // pos cannot overflow due to the nature of the operands (adding UINT32_MAX // a few times can't overflow a uint64_t). File length overflow is checked // during read. // Header pos += ctx->header_offset; pos += sizeof(AndroidHeader); pos += align_page_size<uint64_t>(pos, page_size); // Kernel kernel_offset = pos; pos += ctx->hdr.kernel_size; pos += align_page_size<uint64_t>(pos, page_size); // Ramdisk ramdisk_offset = pos; pos += ctx->hdr.ramdisk_size; pos += align_page_size<uint64_t>(pos, page_size); // Second bootloader second_offset = pos; pos += ctx->hdr.second_size; pos += align_page_size<uint64_t>(pos, page_size); // Device tree dt_offset = pos; pos += ctx->hdr.dt_size; pos += align_page_size<uint64_t>(pos, page_size); _segment_reader_entries_clear(&ctx->segctx); ret = _segment_reader_entries_add(&ctx->segctx, MB_BI_ENTRY_KERNEL, kernel_offset, ctx->hdr.kernel_size, false, bir); if (ret != MB_BI_OK) return ret; ret = _segment_reader_entries_add(&ctx->segctx, MB_BI_ENTRY_RAMDISK, ramdisk_offset, ctx->hdr.ramdisk_size, false, bir); if (ret != MB_BI_OK) return ret; if (ctx->hdr.second_size > 0) { ret = _segment_reader_entries_add(&ctx->segctx, MB_BI_ENTRY_SECONDBOOT, second_offset, ctx->hdr.second_size, false, bir); if (ret != MB_BI_OK) return ret; } if (ctx->hdr.dt_size > 0) { ret = _segment_reader_entries_add(&ctx->segctx, MB_BI_ENTRY_DEVICE_TREE, dt_offset, ctx->hdr.dt_size, ctx->allow_truncated_dt, bir); if (ret != MB_BI_OK) return ret; } return MB_BI_OK; }
MB_BEGIN_C_DECLS /*! * \brief Find and read Android boot image header * * \note The integral fields in the header will be converted to the host's byte * order. * * \pre The file position can be at any offset prior to calling this function. * * \post The file pointer position is undefined after this function returns. * Use mb_file_seek() to return to a known position. * * \param[in] bir MbBiReader for setting error messages * \param[in] file MbFile handle * \param[in] max_header_offset Maximum offset that a header can start (must be * less than #ANDROID_MAX_HEADER_OFFSET) * \param[out] header_out Pointer to store header * \param[out] offset_out Pointer to store header offset * * \return * * #MB_BI_OK if the header is found * * #MB_BI_WARN if the header is not found * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int find_android_header(MbBiReader *bir, MbFile *file, uint64_t max_header_offset, AndroidHeader *header_out, uint64_t *offset_out) { unsigned char buf[ANDROID_MAX_HEADER_OFFSET + sizeof(AndroidHeader)]; size_t n; int ret; void *ptr; size_t offset; if (max_header_offset > ANDROID_MAX_HEADER_OFFSET) { mb_bi_reader_set_error(bir, MB_BI_ERROR_INVALID_ARGUMENT, "Max header offset (%" PRIu64 ") must be less than %d", max_header_offset, ANDROID_MAX_HEADER_OFFSET); return MB_BI_WARN; } ret = mb_file_seek(file, 0, SEEK_SET, nullptr); if (ret != MB_FILE_OK) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to seek to beginning: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } ret = mb_file_read_fully( file, buf, max_header_offset + sizeof(AndroidHeader), &n); if (ret != MB_FILE_OK) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to read header: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } ptr = mb_memmem(buf, n, ANDROID_BOOT_MAGIC, ANDROID_BOOT_MAGIC_SIZE); if (!ptr) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Android magic not found in first %d bytes", ANDROID_MAX_HEADER_OFFSET); return MB_BI_WARN; } offset = static_cast<unsigned char *>(ptr) - buf; if (n - offset < sizeof(AndroidHeader)) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Android header at %" MB_PRIzu " exceeds file size", offset); return MB_BI_WARN; } // Copy header memcpy(header_out, ptr, sizeof(AndroidHeader)); android_fix_header_byte_order(header_out); *offset_out = offset; return MB_BI_OK; }
/*! * \brief Read header for new-style Loki image * * \param[in] bir MbBiReader to set error message * \param[in] file MbFile handle * \param[in] hdr Android header for image * \param[in] loki_hdr Loki header for image * \param[out] header MbBiHeader instance to store header values * \param[out] kernel_offset_out Pointer to store kernel offset * \param[out] kernel_size_out Pointer to store kernel size * \param[out] ramdisk_offset_out Pointer to store ramdisk offset * \param[out] ramdisk_size_out Pointer to store ramdisk size * \param[out] dt_offset_out Pointer to store device tree offset * * \return * * #MB_BI_OK if the header is successfully read * * #MB_BI_WARN if parts of the header are missing or invalid * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int loki_read_new_header(MbBiReader *bir, MbFile *file, AndroidHeader *hdr, LokiHeader *loki_hdr, MbBiHeader *header, uint64_t *kernel_offset_out, uint32_t *kernel_size_out, uint64_t *ramdisk_offset_out, uint32_t *ramdisk_size_out, uint64_t *dt_offset_out) { uint32_t fake_size; uint32_t ramdisk_addr; int ret; if (hdr->page_size == 0) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Page size cannot be 0"); return MB_BI_WARN; } if (LOKI_IS_LG_RAMDISK_ADDR(hdr->ramdisk_addr)) { fake_size = hdr->page_size; } else { fake_size = 0x200; } // Find original ramdisk address ret = loki_find_ramdisk_address(bir, file, hdr, loki_hdr, &ramdisk_addr); if (ret != MB_BI_OK) { return ret; } // Restore original values in boot image header *kernel_size_out = loki_hdr->orig_kernel_size; *ramdisk_size_out = loki_hdr->orig_ramdisk_size; char board_name[sizeof(hdr->name) + 1]; char cmdline[sizeof(hdr->cmdline) + 1]; strncpy(board_name, reinterpret_cast<char *>(hdr->name), sizeof(hdr->name)); strncpy(cmdline, reinterpret_cast<char *>(hdr->cmdline), sizeof(hdr->cmdline)); board_name[sizeof(hdr->name)] = '\0'; cmdline[sizeof(hdr->cmdline)] = '\0'; mb_bi_header_set_supported_fields(header, LOKI_NEW_SUPPORTED_FIELDS); ret = mb_bi_header_set_board_name(header, board_name); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_kernel_cmdline(header, cmdline); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_page_size(header, hdr->page_size); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_kernel_address(header, hdr->kernel_addr); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_ramdisk_address(header, ramdisk_addr); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_secondboot_address(header, hdr->second_addr); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_kernel_tags_address(header, hdr->tags_addr); if (ret != MB_BI_OK) return ret; uint64_t pos = 0; // pos cannot overflow due to the nature of the operands (adding UINT32_MAX // a few times can't overflow a uint64_t). File length overflow is checked // during read. // Header pos += hdr->page_size; // Kernel *kernel_offset_out = pos; pos += loki_hdr->orig_kernel_size; pos += align_page_size<uint64_t>(pos, hdr->page_size); // Ramdisk *ramdisk_offset_out = pos; pos += loki_hdr->orig_ramdisk_size; pos += align_page_size<uint64_t>(pos, hdr->page_size); // Device tree if (hdr->dt_size != 0) { pos += fake_size; } *dt_offset_out = pos; pos += hdr->dt_size; pos += align_page_size<uint64_t>(pos, hdr->page_size); return MB_BI_OK; }
/*! * \brief Read header for old-style Loki image * * \param[in] bir MbBiReader to set error message * \param[in] file MbFile handle * \param[in] hdr Android header for image * \param[in] loki_hdr Loki header for image * \param[out] header MbBiHeader instance to store header values * \param[out] kernel_offset_out Pointer to store kernel offset * \param[out] kernel_size_out Pointer to store kernel size * \param[out] ramdisk_offset_out Pointer to store ramdisk offset * \param[out] ramdisk_size_out Pointer to store ramdisk size * * \return * * #MB_BI_OK if the header is successfully read * * #MB_BI_WARN if parts of the header are missing or invalid * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int loki_read_old_header(MbBiReader *bir, MbFile *file, AndroidHeader *hdr, LokiHeader *loki_hdr, MbBiHeader *header, uint64_t *kernel_offset_out, uint32_t *kernel_size_out, uint64_t *ramdisk_offset_out, uint32_t *ramdisk_size_out) { uint32_t tags_addr; uint32_t kernel_size; uint32_t ramdisk_size; uint32_t ramdisk_addr; uint64_t gzip_offset; int ret; if (hdr->page_size == 0) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Page size cannot be 0"); return MB_BI_WARN; } // The kernel tags address is invalid in the old loki images, so use the // default for jflte tags_addr = hdr->kernel_addr - ANDROID_DEFAULT_KERNEL_OFFSET + ANDROID_DEFAULT_TAGS_OFFSET; // Try to guess kernel size ret = find_linux_kernel_size(bir, file, hdr->page_size, &kernel_size); if (ret != MB_BI_OK) { return ret; } // Look for gzip offset for the ramdisk ret = loki_old_find_gzip_offset( bir, file, hdr->page_size + kernel_size + align_page_size<uint64_t>(kernel_size, hdr->page_size), &gzip_offset); if (ret != MB_BI_OK) { return ret; } // Try to guess ramdisk size ret = loki_old_find_ramdisk_size(bir, file, hdr, gzip_offset, &ramdisk_size); if (ret != MB_BI_OK) { return ret; } // Guess original ramdisk address ret = loki_find_ramdisk_address(bir, file, hdr, loki_hdr, &ramdisk_addr); if (ret != MB_BI_OK) { return ret; } *kernel_size_out = kernel_size; *ramdisk_size_out = ramdisk_size; char board_name[sizeof(hdr->name) + 1]; char cmdline[sizeof(hdr->cmdline) + 1]; strncpy(board_name, reinterpret_cast<char *>(hdr->name), sizeof(hdr->name)); strncpy(cmdline, reinterpret_cast<char *>(hdr->cmdline), sizeof(hdr->cmdline)); board_name[sizeof(hdr->name)] = '\0'; cmdline[sizeof(hdr->cmdline)] = '\0'; mb_bi_header_set_supported_fields(header, LOKI_OLD_SUPPORTED_FIELDS); ret = mb_bi_header_set_board_name(header, board_name); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_kernel_cmdline(header, cmdline); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_page_size(header, hdr->page_size); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_kernel_address(header, hdr->kernel_addr); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_ramdisk_address(header, ramdisk_addr); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_secondboot_address(header, hdr->second_addr); if (ret != MB_BI_OK) return ret; ret = mb_bi_header_set_kernel_tags_address(header, tags_addr); if (ret != MB_BI_OK) return ret; uint64_t pos = 0; // pos cannot overflow due to the nature of the operands (adding UINT32_MAX // a few times can't overflow a uint64_t). File length overflow is checked // during read. // Header pos += hdr->page_size; // Kernel *kernel_offset_out = pos; pos += kernel_size; pos += align_page_size<uint64_t>(pos, hdr->page_size); // Ramdisk *ramdisk_offset_out = pos = gzip_offset; pos += ramdisk_size; pos += align_page_size<uint64_t>(pos, hdr->page_size); return MB_BI_OK; }
MB_BEGIN_C_DECLS /*! * \brief Find and read Loki boot image header * * \note The integral fields in the header will be converted to the host's byte * order. * * \pre The file position can be at any offset prior to calling this function. * * \post The file pointer position is undefined after this function returns. * Use mb_file_seek() to return to a known position. * * \param[in] bir MbBiReader * \param[in] file MbFile handle * \param[out] header_out Pointer to store header * \param[out] offset_out Pointer to store header offset * * \return * * #MB_BI_OK if the header is found * * #MB_BI_WARN if the header is not found * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int find_loki_header(MbBiReader *bir, MbFile *file, LokiHeader *header_out, uint64_t *offset_out) { LokiHeader header; size_t n; int ret; ret = mb_file_seek(file, LOKI_MAGIC_OFFSET, SEEK_SET, nullptr); if (ret < 0) { mb_bi_reader_set_error(bir, mb_file_error(file), "Loki magic not found: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_WARN; } ret = mb_file_read_fully(file, &header, sizeof(header), &n); if (ret < 0) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to read header: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } else if (n != sizeof(header)) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Too small to be Loki image"); return MB_BI_WARN; } if (memcmp(header.magic, LOKI_MAGIC, LOKI_MAGIC_SIZE) != 0) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Invalid loki magic"); return MB_BI_WARN; } loki_fix_header_byte_order(&header); *header_out = header; *offset_out = LOKI_MAGIC_OFFSET; return MB_BI_OK; }
/*! * \brief Find ramdisk size in old-style Loki image * * \pre The file position can be at any offset prior to calling this function. * * \post The file pointer position is undefined after this function returns. * Use mb_file_seek() to return to a known position. * * \param[in] bir MbBiReader to set error message * \param[in] file MbFile handle * \param[in] hdr Android header * \param[in] ramdisk_offset Offset of ramdisk in image * \param[out] ramdisk_size_out Pointer to store ramdisk size * * \return * * #MB_BI_OK if the ramdisk size is found * * #MB_BI_WARN if the ramdisk size is not found * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int loki_old_find_ramdisk_size(MbBiReader *bir, MbFile *file, const AndroidHeader *hdr, uint32_t ramdisk_offset, uint32_t *ramdisk_size_out) { int32_t aboot_size; uint64_t aboot_offset; #if 0 uint64_t cur_offset; char buf[1024]; size_t n; #endif int ret; // If the boot image was patched with an old version of loki, the ramdisk // size is not stored properly. We'll need to guess the size of the archive. // The ramdisk is supposed to be from the gzip header to EOF, but loki needs // to store a copy of aboot, so it is put in the last 0x200 bytes of the // file. if (LOKI_IS_LG_RAMDISK_ADDR(hdr->ramdisk_addr)) { aboot_size = hdr->page_size; } else { aboot_size = 0x200; } ret = mb_file_seek(file, -aboot_size, SEEK_END, &aboot_offset); if (ret != MB_FILE_OK) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to seek to end of file: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } if (ramdisk_offset > aboot_offset) { mb_bi_reader_set_error(bir, MB_BI_ERROR_INTERNAL_ERROR, "Ramdisk offset greater than aboot offset"); return MB_BI_FAILED; } // Ignore zero padding as we might strip away too much #if 1 *ramdisk_size_out = aboot_offset - ramdisk_offset; return MB_BI_OK; #else // Search backwards to find non-zero byte cur_offset = aboot_offset; while (cur_offset > ramdisk_offset) { size_t to_read = std::min<uint64_t>( sizeof(buf), cur_offset - ramdisk_offset); cur_offset -= to_read; ret = mb_file_seek(file, cur_offset, SEEK_SET, nullptr); if (ret != MB_FILE_OK) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to seek: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } ret = mb_file_read_fully(file, buf, to_read, &n); if (ret != MB_FILE_OK) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to read: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } else if (n != to_read) { mb_bi_reader_set_error(bir, MB_BI_ERROR_INTERNAL_ERROR, "Unexpected file truncation"); return MB_BI_FAILED; } for (size_t i = n; i-- > 0; ) { if (buf[i] != '\0') { *ramdisk_size_out = cur_offset - ramdisk_offset + i; return MB_BI_OK; } } } mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Could not determine ramdisk size"); return MB_BI_WARN; #endif }
/*! * \brief Find gzip ramdisk offset in old-style Loki image * * This function will search for gzip headers (`0x1f8b08`) with a flags byte of * `0x00` or `0x08`. It will find the first occurrence of either magic string. * If both are found, the one with the flags byte set to `0x08` takes precedence * as it indiciates that the original filename field is set. This is usually the * case for ramdisks packed via the `gzip` command line tool. * * \pre The file position can be at any offset prior to calling this function. * * \post The file pointer position is undefined after this function returns. * Use mb_file_seek() to return to a known position. * * \param[in] bir MbBiReader to set error message * \param[in] file MbFile handle * \param[in] start_offset Starting offset for search * \param[out] gzip_offset_out Pointer to store gzip ramdisk offset * * \return * * #MB_BI_OK if a gzip offset is found * * #MB_BI_WARN if no gzip offsets are found * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int loki_old_find_gzip_offset(MbBiReader *bir, MbFile *file, uint32_t start_offset, uint64_t *gzip_offset_out) { struct SearchResult { bool have_flag0 = false; bool have_flag8 = false; uint64_t flag0_offset; uint64_t flag8_offset; }; // gzip header: // byte 0-1 : magic bytes 0x1f, 0x8b // byte 2 : compression (0x08 = deflate) // byte 3 : flags // byte 4-7 : modification timestamp // byte 8 : compression flags // byte 9 : operating system static const unsigned char gzip_deflate_magic[] = { 0x1f, 0x8b, 0x08 }; SearchResult result = {}; int ret; // Find first result with flags == 0x00 and flags == 0x08 auto result_cb = [](MbFile *file, void *userdata, uint64_t offset) -> int { SearchResult *result = static_cast<SearchResult *>(userdata); uint64_t orig_offset; unsigned char flags; size_t n; int ret; // Stop early if possible if (result->have_flag0 && result->have_flag8) { return MB_FILE_WARN; } // Save original position ret = mb_file_seek(file, 0, SEEK_CUR, &orig_offset); if (ret != MB_FILE_OK) { return ret; } // Seek to flags byte ret = mb_file_seek(file, offset + 3, SEEK_SET, nullptr); if (ret != MB_FILE_OK) { return ret; } // Read next bytes for flags ret = mb_file_read_fully(file, &flags, sizeof(flags), &n); if (ret != MB_FILE_OK) { return ret; } else if (n != sizeof(flags)) { // EOF return MB_FILE_WARN; } if (!result->have_flag0 && flags == 0x00) { result->have_flag0 = true; result->flag0_offset = offset; } else if (!result->have_flag8 && flags == 0x08) { result->have_flag8 = true; result->flag8_offset = offset; } // Restore original position as per contract ret = mb_file_seek(file, orig_offset, SEEK_SET, nullptr); if (ret != MB_FILE_OK) { return ret; } return MB_FILE_OK; }; ret = mb_file_search(file, start_offset, -1, 0, gzip_deflate_magic, sizeof(gzip_deflate_magic), -1, result_cb, &result); if (ret < 0) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to search for gzip magic: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } // Prefer gzip header with original filename flag since most loki'd boot // images will have been compressed manually with the gzip tool if (result.have_flag8) { *gzip_offset_out = result.flag8_offset; } else if (result.have_flag0) { *gzip_offset_out = result.flag0_offset; } else { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "No gzip headers found"); return MB_BI_WARN; } return MB_BI_OK; }
/*! * \brief Find and read Loki ramdisk address * * \pre The file position can be at any offset prior to calling this function. * * \post The file pointer position is undefined after this function returns. * Use mb_file_seek() to return to a known position. * * \param[in] bir MbBiReader to set error message * \param[in] file MbFile handle * \param[in] hdr Android header * \param[in] loki_hdr Loki header * \param[out] ramdisk_addr_out Pointer to store ramdisk address * * \return * * #MB_BI_OK if the ramdisk address is found * * #MB_BI_WARN if the ramdisk address is not found * * #MB_BI_FAILED if any file operation fails non-fatally * * #MB_BI_FATAL if any file operation fails fatally */ int loki_find_ramdisk_address(MbBiReader *bir, MbFile *file, const AndroidHeader *hdr, const LokiHeader *loki_hdr, uint32_t *ramdisk_addr_out) { // If the boot image was patched with a newer version of loki, find the // ramdisk offset in the shell code uint32_t ramdisk_addr = 0; int ret; size_t n; if (loki_hdr->ramdisk_addr != 0) { uint64_t offset = 0; auto result_cb = [](MbFile *file, void *userdata, uint64_t offset) -> int { (void) file; uint64_t *offset_ptr = static_cast<uint64_t *>(userdata); *offset_ptr = offset; return MB_FILE_OK; }; ret = mb_file_search(file, -1, -1, 0, LOKI_SHELLCODE, LOKI_SHELLCODE_SIZE - 9, 1, result_cb, &offset); if (ret < 0) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to search for Loki shellcode: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } if (offset == 0) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Loki shellcode not found"); return MB_FILE_WARN; } offset += LOKI_SHELLCODE_SIZE - 5; ret = mb_file_seek(file, offset, SEEK_SET, nullptr); if (ret < 0) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to seek to ramdisk address offset: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } ret = mb_file_read_fully(file, &ramdisk_addr, sizeof(ramdisk_addr), &n); if (ret < 0) { mb_bi_reader_set_error(bir, mb_file_error(file), "Failed to read ramdisk address offset: %s", mb_file_error_string(file)); return ret == MB_FILE_FATAL ? MB_BI_FATAL : MB_BI_FAILED; } else if (n != sizeof(ramdisk_addr)) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Unexpected EOF when reading ramdisk address"); return MB_BI_WARN; } ramdisk_addr = mb_le32toh(ramdisk_addr); } else { // Otherwise, use the default for jflte (- 0x00008000 + 0x02000000) if (hdr->kernel_addr > UINT32_MAX - 0x01ff8000) { mb_bi_reader_set_error(bir, MB_BI_ERROR_FILE_FORMAT, "Invalid kernel address: %" PRIu32, hdr->kernel_addr); return MB_BI_WARN; } ramdisk_addr = hdr->kernel_addr + 0x01ff8000; } *ramdisk_addr_out = ramdisk_addr; return MB_BI_OK; }