Exemplo n.º 1
0
/*!
 * \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 File::seek() to return to a known position.
 *
 * \param[in] reader Reader to set error message
 * \param[in] file File handle
 * \param[in] kernel_offset Offset of kernel in boot image
 * \param[out] kernel_size_out Pointer to store kernel size
 *
 * \return
 *   * Nothing if the kernel size is found
 *   * FileError::UnexpectedEof if the kernel size cannot be found
 *   * A specific error if any file operation fails
 */
oc::result<void>
LokiFormatReader::find_linux_kernel_size(Reader &reader,File &file,
                                         uint32_t kernel_offset,
                                         uint32_t &kernel_size_out)
{
    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
    auto seek_ret = file.seek(kernel_offset + 0x2c, SEEK_SET);
    if (!seek_ret) {
        if (file.is_fatal()) { reader.set_fatal(); }
        return seek_ret.as_failure();
    }

    auto ret = file_read_exact(file, &kernel_size, sizeof(kernel_size));
    if (!ret) {
        if (file.is_fatal()) { reader.set_fatal(); }
        return ret.as_failure();
    }

    kernel_size_out = mb_le32toh(kernel_size);
    return oc::success();
}
Exemplo n.º 2
0
static oc::result<void>
_mtk_header_update_size(Writer &writer, File &file,
                        uint64_t offset, uint32_t size)
{
    uint32_t le32_size = mb_htole32(size);

    if (offset > SIZE_MAX - offsetof(MtkHeader, size)) {
        writer.set_fatal();
        return MtkError::MtkHeaderOffsetTooLarge;
    }

    auto seek_ret = file.seek(
            static_cast<int64_t>(offset + offsetof(MtkHeader, size)), SEEK_SET);
    if (!seek_ret) {
        if (file.is_fatal()) { writer.set_fatal(); }
        return seek_ret.as_failure();
    }

    auto ret = file_write_exact(file, &le32_size, sizeof(le32_size));
    if (!ret) {
        if (file.is_fatal()) { writer.set_fatal(); }
        return ret.as_failure();
    }

    return oc::success();
}
Exemplo n.º 3
0
/*!
 * \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 File::seek() to return to a known position.
 *
 * \param[in] reader Reader
 * \param[in] file File handle
 * \param[out] header_out Pointer to store header
 * \param[out] offset_out Pointer to store header offset
 *
 * \return
 *   * Nothing if the header is found
 *   * A LokiError if the header is not found
 *   * A specified error code if any file operation fails
 */
oc::result<void> LokiFormatReader::find_loki_header(Reader &reader, File &file,
                                                    LokiHeader &header_out,
                                                    uint64_t &offset_out)
{
    LokiHeader header;

    auto seek_ret = file.seek(LOKI_MAGIC_OFFSET, SEEK_SET);
    if (!seek_ret) {
        if (file.is_fatal()) { reader.set_fatal(); }
        return seek_ret.as_failure();
    }

    auto ret = file_read_exact(file, &header, sizeof(header));
    if (!ret) {
        if (ret.error() == FileError::UnexpectedEof) {
            return LokiError::LokiHeaderTooSmall;
        } else {
            if (file.is_fatal()) { reader.set_fatal(); }
            return ret.as_failure();
        }
    }

    if (memcmp(header.magic, LOKI_MAGIC, LOKI_MAGIC_SIZE) != 0) {
        return LokiError::InvalidLokiMagic;
    }

    loki_fix_header_byte_order(header);
    header_out = header;
    offset_out = LOKI_MAGIC_OFFSET;

    return oc::success();
}
oc::result<void> SegmentReader::move_to_entry(File &file, Entry &entry,
                                              std::vector<SegmentReaderEntry>::iterator srentry,
                                              Reader &reader)
{
    if (srentry->offset > UINT64_MAX - srentry->size) {
        return SegmentError::EntryWouldOverflowOffset;
    }

    uint64_t read_start_offset = srentry->offset;
    uint64_t read_end_offset = read_start_offset + srentry->size;
    uint64_t read_cur_offset = read_start_offset;

    if (m_read_cur_offset != srentry->offset) {
        auto ret = file.seek(static_cast<int64_t>(read_start_offset), SEEK_SET);
        if (!ret) {
            if (file.is_fatal()) { reader.set_fatal(); }
            return ret.as_failure();
        }
    }

    entry.set_type(srentry->type);
    entry.set_size(srentry->size);

    m_state = SegmentReaderState::Entries;
    m_entry = srentry;
    m_read_start_offset = read_start_offset;
    m_read_end_offset = read_end_offset;
    m_read_cur_offset = read_cur_offset;

    return oc::success();
}
oc::result<size_t> SegmentReader::read_data(File &file, void *buf,
                                            size_t buf_size, Reader &reader)
{
    auto to_copy = static_cast<size_t>(std::min<uint64_t>(
            buf_size, m_read_end_offset - m_read_cur_offset));

    if (m_read_cur_offset > SIZE_MAX - to_copy) {
        //DEBUG("Current offset %" PRIu64 " with read size %" MB_PRIzu
        //      " would overflow integer", m_read_cur_offset, to_copy);
        return SegmentError::ReadWouldOverflowInteger;
    }

    // We allow truncation for certain things, so we can't use file_read_exact()
    auto n = file_read_retry(file, buf, to_copy);
    if (!n) {
        if (file.is_fatal()) { reader.set_fatal(); }
        return n.as_failure();
    }

    m_read_cur_offset += n.value();

    // Fail if we reach EOF early
    if (n.value() < to_copy && m_read_cur_offset != m_read_end_offset
            && !m_entry->can_truncate) {
        //DEBUG("Entry is truncated (expected %" PRIu64 " more bytes)",
        //      m_read_end_offset - m_read_cur_offset);
        reader.set_fatal();
        return FileError::UnexpectedEof;
    }

    return n.value();
}
Exemplo n.º 6
0
oc::result<void> selinux_lset_context_recursive(const std::string &path,
                                                const std::string &context)
{
    RecursiveSetContext rsc(path, context, false);
    if (!rsc.run()) {
        auto ret = rsc.result();
        if (ret) {
            return ec_from_errno();
        } else {
            return ret.as_failure();
        }
    }

    return rsc.result();
}
Exemplo n.º 7
0
oc::result<void> FileUtils::open_file(StandardFile &file,
                                      const std::string &path,
                                      FileOpenMode mode)
{
#ifdef _WIN32
    auto w_filename = utf8_to_wcs(path);

    if (!w_filename) {
        LOGE("%s: Failed to convert from UTF8 to WCS: %s",
             path.c_str(), w_filename.error().message().c_str());
        return w_filename.as_failure();
    }

    return file.open(w_filename.value(), mode);
#else
    return file.open(path, mode);
#endif
}
Exemplo n.º 8
0
static oc::result<void>
_mtk_compute_sha1(Writer &writer, SegmentWriter &seg,
                  File &file, unsigned char digest[SHA_DIGEST_LENGTH])
{
    SHA_CTX sha_ctx;
    char buf[10240];

    uint32_t kernel_mtkhdr_size = 0;
    uint32_t ramdisk_mtkhdr_size = 0;

    if (!SHA1_Init(&sha_ctx)) {
        return android::AndroidError::Sha1InitError;
    }

    for (auto const &entry : seg.entries()) {
        uint64_t remain = *entry.size;

        auto seek_ret = file.seek(static_cast<int64_t>(entry.offset),
                                  SEEK_SET);
        if (!seek_ret) {
            if (file.is_fatal()) { writer.set_fatal(); }
            return seek_ret.as_failure();
        }

        // Update checksum with data
        while (remain > 0) {
            auto to_read = std::min<uint64_t>(remain, sizeof(buf));

            auto ret = file_read_exact(file, buf, static_cast<size_t>(to_read));
            if (!ret) {
                if (writer.is_fatal()) { writer.set_fatal(); }
                return ret.as_failure();
            }

            if (!SHA1_Update(&sha_ctx, buf, static_cast<size_t>(to_read))) {
                return android::AndroidError::Sha1UpdateError;
            }

            remain -= to_read;
        }

        uint32_t le32_size;

        // Update checksum with size
        switch (entry.type) {
        case ENTRY_TYPE_MTK_KERNEL_HEADER:
            kernel_mtkhdr_size = *entry.size;
            continue;
        case ENTRY_TYPE_MTK_RAMDISK_HEADER:
            ramdisk_mtkhdr_size = *entry.size;
            continue;
        case ENTRY_TYPE_KERNEL:
            le32_size = mb_htole32(*entry.size + kernel_mtkhdr_size);
            break;
        case ENTRY_TYPE_RAMDISK:
            le32_size = mb_htole32(*entry.size + ramdisk_mtkhdr_size);
            break;
        case ENTRY_TYPE_SECONDBOOT:
            le32_size = mb_htole32(*entry.size);
            break;
        case ENTRY_TYPE_DEVICE_TREE:
            if (*entry.size == 0) {
                continue;
            }
            le32_size = mb_htole32(*entry.size);
            break;
        default:
            continue;
        }

        if (!SHA1_Update(&sha_ctx, &le32_size, sizeof(le32_size))) {
            return android::AndroidError::Sha1UpdateError;
        }
    }

    if (!SHA1_Final(digest, &sha_ctx)) {
        return android::AndroidError::Sha1UpdateError;
    }

    return oc::success();
}
Exemplo n.º 9
0
oc::result<void> MtkFormatWriter::write_header(File &file, const Header &header)
{
    // Construct header
    m_hdr = {};
    memcpy(m_hdr.magic, android::BOOT_MAGIC, android::BOOT_MAGIC_SIZE);

    if (auto address = header.kernel_address()) {
        m_hdr.kernel_addr = *address;
    }
    if (auto address = header.ramdisk_address()) {
        m_hdr.ramdisk_addr = *address;
    }
    if (auto address = header.secondboot_address()) {
        m_hdr.second_addr = *address;
    }
    if (auto address = header.kernel_tags_address()) {
        m_hdr.tags_addr = *address;
    }
    if (auto page_size = header.page_size()) {
        switch (*page_size) {
        case 2048:
        case 4096:
        case 8192:
        case 16384:
        case 32768:
        case 65536:
        case 131072:
            m_hdr.page_size = *page_size;
            break;
        default:
            //DEBUG("Invalid page size: %" PRIu32, *page_size);
            return android::AndroidError::InvalidPageSize;
        }
    } else {
        return android::AndroidError::MissingPageSize;
    }

    if (auto board_name = header.board_name()) {
        if (board_name->size() >= sizeof(m_hdr.name)) {
            return android::AndroidError::BoardNameTooLong;
        }

        strncpy(reinterpret_cast<char *>(m_hdr.name), board_name->c_str(),
                sizeof(m_hdr.name) - 1);
        m_hdr.name[sizeof(m_hdr.name) - 1] = '\0';
    }
    if (auto cmdline = header.kernel_cmdline()) {
        if (cmdline->size() >= sizeof(m_hdr.cmdline)) {
            return android::AndroidError::KernelCmdlineTooLong;
        }

        strncpy(reinterpret_cast<char *>(m_hdr.cmdline), cmdline->c_str(),
                sizeof(m_hdr.cmdline) - 1);
        m_hdr.cmdline[sizeof(m_hdr.cmdline) - 1] = '\0';
    }

    // TODO: UNUSED
    // TODO: ID

    std::vector<SegmentWriterEntry> entries;

    entries.push_back({ ENTRY_TYPE_MTK_KERNEL_HEADER, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_KERNEL, 0, {}, m_hdr.page_size });
    entries.push_back({ ENTRY_TYPE_MTK_RAMDISK_HEADER, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_RAMDISK, 0, {}, m_hdr.page_size });
    entries.push_back({ ENTRY_TYPE_SECONDBOOT, 0, {}, m_hdr.page_size });
    entries.push_back({ ENTRY_TYPE_DEVICE_TREE, 0, {}, m_hdr.page_size });

    OUTCOME_TRYV(m_seg->set_entries(std::move(entries)));

    // Start writing after first page
    auto seek_ret = file.seek(m_hdr.page_size, SEEK_SET);
    if (!seek_ret) {
        if (file.is_fatal()) { m_writer.set_fatal(); }
        return seek_ret.as_failure();
    }

    return oc::success();
}
Exemplo n.º 10
0
oc::result<void> MtkFormatWriter::close(File &file)
{
    auto reset_state = finally([&] {
        m_hdr = {};
        m_seg = {};
    });

    if (m_writer.is_open()) {
        auto swentry = m_seg->entry();

        // If successful, finish up the boot image
        if (swentry == m_seg->entries().end()) {
            auto file_size = file.seek(0, SEEK_CUR);
            if (!file_size) {
                if (file.is_fatal()) { m_writer.set_fatal(); }
                return file_size.as_failure();
            }

            // Truncate to set size
            auto truncate_ret = file.truncate(file_size.value());
            if (!truncate_ret) {
                if (file.is_fatal()) { m_writer.set_fatal(); }
                return truncate_ret.as_failure();
            }

            // Update MTK header sizes
            for (auto const &entry : m_seg->entries()) {
                if (entry.type == ENTRY_TYPE_MTK_KERNEL_HEADER) {
                    OUTCOME_TRYV(_mtk_header_update_size(
                            m_writer, file, entry.offset,
                            static_cast<uint32_t>(
                                    m_hdr.kernel_size - sizeof(MtkHeader))));
                } else if (entry.type == ENTRY_TYPE_MTK_RAMDISK_HEADER) {
                    OUTCOME_TRYV(_mtk_header_update_size(
                            m_writer, file, entry.offset,
                            static_cast<uint32_t>(
                                    m_hdr.ramdisk_size - sizeof(MtkHeader))));
                }
            }

            // We need to take the performance hit and compute the SHA1 here.
            // We can't fill in the sizes in the MTK headers when we're writing
            // them. Thus, if we calculated the SHA1sum during write, it would
            // be incorrect.
            OUTCOME_TRYV(_mtk_compute_sha1(
                    m_writer, *m_seg, file,
                    reinterpret_cast<unsigned char *>(m_hdr.id)));

            // Convert fields back to little-endian
            android_fix_header_byte_order(m_hdr);

            // Seek back to beginning to write header
            auto seek_ret = file.seek(0, SEEK_SET);
            if (!seek_ret) {
                if (file.is_fatal()) { m_writer.set_fatal(); }
                return seek_ret.as_failure();
            }

            // Write header
            auto ret = file_write_exact(file, &m_hdr, sizeof(m_hdr));
            if (!ret) {
                if (file.is_fatal()) { m_writer.set_fatal(); }
                return ret.as_failure();
            }
        }
    }

    return oc::success();
}
Exemplo n.º 11
0
oc::result<void> SonyElfFormatWriter::write_header(File &file,
                                                   const Header &header)
{
    m_cmdline.clear();

    m_hdr = {};
    m_hdr_kernel = {};
    m_hdr_ramdisk = {};
    m_hdr_cmdline = {};
    m_hdr_ipl = {};
    m_hdr_rpm = {};
    m_hdr_appsbl = {};

    // Construct ELF header
    memcpy(&m_hdr.e_ident, SONY_E_IDENT, SONY_EI_NIDENT);
    m_hdr.e_type = 2;
    m_hdr.e_machine = 40;
    m_hdr.e_version = 1;
    m_hdr.e_phoff = 52;
    m_hdr.e_shoff = 0;
    m_hdr.e_flags = 0;
    m_hdr.e_ehsize = sizeof(Sony_Elf32_Ehdr);
    m_hdr.e_phentsize = sizeof(Sony_Elf32_Phdr);
    m_hdr.e_shentsize = 0;
    m_hdr.e_shnum = 0;
    m_hdr.e_shstrndx = 0;

    if (auto entrypoint_address = header.entrypoint_address()) {
        m_hdr.e_entry = *entrypoint_address;
    } else if (auto kernel_address = header.kernel_address()) {
        m_hdr.e_entry = *kernel_address;
    }

    // Construct kernel program header
    m_hdr_kernel.p_type = SONY_E_TYPE_KERNEL;
    m_hdr_kernel.p_flags = SONY_E_FLAGS_KERNEL;
    m_hdr_kernel.p_align = 0;

    if (auto address = header.kernel_address()) {
        m_hdr_kernel.p_vaddr = *address;
        m_hdr_kernel.p_paddr = *address;
    }

    // Construct ramdisk program header
    m_hdr_ramdisk.p_type = SONY_E_TYPE_RAMDISK;
    m_hdr_ramdisk.p_flags = SONY_E_FLAGS_RAMDISK;
    m_hdr_ramdisk.p_align = 0;

    if (auto address = header.ramdisk_address()) {
        m_hdr_ramdisk.p_vaddr = *address;
        m_hdr_ramdisk.p_paddr = *address;
    }

    // Construct cmdline program header
    m_hdr_cmdline.p_type = SONY_E_TYPE_CMDLINE;
    m_hdr_cmdline.p_vaddr = 0;
    m_hdr_cmdline.p_paddr = 0;
    m_hdr_cmdline.p_flags = SONY_E_FLAGS_CMDLINE;
    m_hdr_cmdline.p_align = 0;

    if (auto cmdline = header.kernel_cmdline()) {
        m_cmdline = *cmdline;
    }

    // Construct IPL program header
    m_hdr_ipl.p_type = SONY_E_TYPE_IPL;
    m_hdr_ipl.p_flags = SONY_E_FLAGS_IPL;
    m_hdr_ipl.p_align = 0;

    if (auto address = header.sony_ipl_address()) {
        m_hdr_ipl.p_vaddr = *address;
        m_hdr_ipl.p_paddr = *address;
    }

    // Construct RPM program header
    m_hdr_rpm.p_type = SONY_E_TYPE_RPM;
    m_hdr_rpm.p_flags = SONY_E_FLAGS_RPM;
    m_hdr_rpm.p_align = 0;

    if (auto address = header.sony_rpm_address()) {
        m_hdr_rpm.p_vaddr = *address;
        m_hdr_rpm.p_paddr = *address;
    }

    // Construct APPSBL program header
    m_hdr_appsbl.p_type = SONY_E_TYPE_APPSBL;
    m_hdr_appsbl.p_flags = SONY_E_FLAGS_APPSBL;
    m_hdr_appsbl.p_align = 0;

    if (auto address = header.sony_appsbl_address()) {
        m_hdr_appsbl.p_vaddr = *address;
        m_hdr_appsbl.p_paddr = *address;
    }

    std::vector<SegmentWriterEntry> entries;

    entries.push_back({ ENTRY_TYPE_KERNEL, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_RAMDISK, 0, {}, 0 });
    entries.push_back({ SONY_ELF_ENTRY_CMDLINE, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_SONY_IPL, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_SONY_RPM, 0, {}, 0 });
    entries.push_back({ ENTRY_TYPE_SONY_APPSBL, 0, {}, 0 });

    OUTCOME_TRYV(m_seg->set_entries(std::move(entries)));

    // Start writing at offset 4096
    auto seek_ret = file.seek(4096, SEEK_SET);
    if (!seek_ret) {
        if (file.is_fatal()) { m_writer.set_fatal(); }
        return seek_ret.as_failure();
    }

    return oc::success();
}
Exemplo n.º 12
0
/*!
 * \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 File::seek() to return to a known position.
 *
 * \param[in] reader Reader to set error message
 * \param[in] file File 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
 *   * Nothing if the ramdisk size is found
 *   * A LokiError if the ramdisk size is not found
 *   * A specific error if any file operation fails
 */
oc::result<void>
LokiFormatReader::find_ramdisk_size_old(Reader &reader, File &file,
                                        const android::AndroidHeader &hdr,
                                        uint32_t ramdisk_offset,
                                        uint32_t &ramdisk_size_out)
{
    int32_t aboot_size;

    // 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 (is_lg_ramdisk_address(hdr.ramdisk_addr)) {
        aboot_size = static_cast<int32_t>(hdr.page_size);
    } else {
        aboot_size = 0x200;
    }

    auto aboot_offset = file.seek(-aboot_size, SEEK_END);
    if (!aboot_offset) {
        if (file.is_fatal()) { reader.set_fatal(); }
        return aboot_offset.as_failure();
    }

    if (ramdisk_offset > aboot_offset.value()) {
        return LokiError::RamdiskOffsetGreaterThanAbootOffset;
    }

    // Ignore zero padding as we might strip away too much
#if 1
    ramdisk_size_out = static_cast<uint32_t>(
            aboot_offset.value() - ramdisk_offset);
    return oc::success();
#else
    char buf[1024];

    // Search backwards to find non-zero byte
    uint64_t cur_offset = aboot_offset.value();

    while (cur_offset > ramdisk_offset) {
        size_t to_read = std::min<uint64_t>(
                sizeof(buf), cur_offset - ramdisk_offset);
        cur_offset -= to_read;

        auto seek_ret = file.seek(cur_offset, SEEK_SET);
        if (!seek_ret) {
            if (file.is_fatal()) { reader.set_fatal(); }
            return seek_ret.as_failure();
        }

        auto ret = file_read_exact(file, buf, to_read);
        if (!ret) {
            if (file.is_fatal()) { reader.set_fatal(); }
            return ret.as_failure();
        }

        for (size_t i = to_read; i-- > 0; ) {
            if (buf[i] != '\0') {
                ramdisk_size_out = cur_offset - ramdisk_offset + i;
                return oc::success();
            }
        }
    }

    return LokiError::FailedToDetermineRamdiskSize;
#endif
}
Exemplo n.º 13
0
/*!
 * \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 File::seek() to return to a known position.
 *
 * \param[in] reader Reader to set error message
 * \param[in] file File handle
 * \param[in] start_offset Starting offset for search
 * \param[out] gzip_offset_out Pointer to store gzip ramdisk offset
 *
 * \return
 *   * Nothing if a gzip offset is found
 *   * A LokiError if no gzip offsets are found
 *   * A specific error if any file operation fails
 */
oc::result<void>
LokiFormatReader::find_gzip_offset_old(Reader &reader, File &file,
                                       uint32_t start_offset,
                                       uint64_t &gzip_offset_out)
{
    struct SearchResult
    {
        std::optional<uint64_t> flag0_offset;
        std::optional<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 = {};

    // Find first result with flags == 0x00 and flags == 0x08
    auto result_cb = [&](File &file_, uint64_t offset)
            -> oc::result<FileSearchAction> {
        unsigned char flags;

        // Stop early if possible
        if (result.flag0_offset && result.flag8_offset) {
            return FileSearchAction::Stop;
        }

        // Save original position
        OUTCOME_TRY(orig_offset, file_.seek(0, SEEK_CUR));

        // Seek to flags byte
        OUTCOME_TRYV(file_.seek(static_cast<int64_t>(offset + 3), SEEK_SET));

        // Read next bytes for flags
        auto ret = file_read_exact(file_, &flags, sizeof(flags));
        if (!ret) {
            if (ret.error() == FileError::UnexpectedEof) {
                return FileSearchAction::Stop;
            } else {
                return ret.as_failure();
            }
        }

        if (!result.flag0_offset && flags == 0x00) {
            result.flag0_offset = offset;
        } else if (!result.flag8_offset && flags == 0x08) {
            result.flag8_offset = offset;
        }

        // Restore original position as per contract
        OUTCOME_TRYV(file_.seek(static_cast<int64_t>(orig_offset), SEEK_SET));

        return FileSearchAction::Continue;
    };

    auto ret = file_search(file, start_offset, {}, 0, gzip_deflate_magic,
                           sizeof(gzip_deflate_magic), {}, result_cb);
    if (!ret) {
        if (file.is_fatal()) { reader.set_fatal(); }
        return ret.as_failure();
    }

    // Prefer gzip header with original filename flag since most loki'd boot
    // images will have been compressed manually with the gzip tool
    if (result.flag8_offset) {
        gzip_offset_out = *result.flag8_offset;
    } else if (result.flag0_offset) {
        gzip_offset_out = *result.flag0_offset;
    } else {
        return LokiError::NoRamdiskGzipHeaderFound;
    }

    return oc::success();
}
Exemplo n.º 14
0
/*!
 * \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 File::seek() to return to a known position.
 *
 * \param[in] reader Reader to set error message
 * \param[in] file File handle
 * \param[in] hdr Android header
 * \param[in] loki_hdr Loki header
 * \param[out] ramdisk_addr_out Pointer to store ramdisk address
 *
 * \return
 *   * Nothing if the ramdisk address is found
 *   * A LokiError if the ramdisk address is not found
 *   * A specific error code if any file operation fails
 */
oc::result<void>
LokiFormatReader::find_ramdisk_address(Reader &reader, File &file,
                                       const android::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;

    if (loki_hdr.ramdisk_addr != 0) {
        uint64_t offset = 0;

        auto result_cb = [&](File &file_, uint64_t offset_)
                -> oc::result<FileSearchAction> {
            (void) file_;
            offset = offset_;
            return FileSearchAction::Continue;
        };

        auto ret = file_search(file, {}, {}, 0, LOKI_SHELLCODE,
                               LOKI_SHELLCODE_SIZE - 9, 1, result_cb);
        if (!ret) {
            if (file.is_fatal()) { reader.set_fatal(); }
            return ret.as_failure();
        }

        if (offset == 0) {
            return LokiError::ShellcodeNotFound;
        }

        offset += LOKI_SHELLCODE_SIZE - 5;

        auto seek_ret = file.seek(static_cast<int64_t>(offset), SEEK_SET);
        if (!seek_ret) {
            if (file.is_fatal()) { reader.set_fatal(); }
            return seek_ret.as_failure();
        }

        auto read_ret = file_read_exact(file, &ramdisk_addr,
                                        sizeof(ramdisk_addr));
        if (!read_ret) {
            if (file.is_fatal()) { reader.set_fatal(); }
            return read_ret.as_failure();
        }

        ramdisk_addr = mb_le32toh(ramdisk_addr);
    } else {
        // Otherwise, use the default for jflte (- 0x00008000 + 0x02000000)

        if (hdr.kernel_addr > UINT32_MAX - 0x01ff8000) {
            //DEBUG("Invalid kernel address: %" PRIu32, hdr.kernel_addr);
            return LokiError::InvalidKernelAddress;
        }

        ramdisk_addr = hdr.kernel_addr + 0x01ff8000;
    }

    ramdisk_addr_out = ramdisk_addr;
    return oc::success();
}