Ejemplo 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 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;
}
Ejemplo n.º 2
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();
}
Ejemplo n.º 3
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 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;
}
Ejemplo n.º 4
0
/*!
 * \brief Patch Android boot image with Loki exploit in-place
 *
 * \param biw MbBiWriter instance for setting error message
 * \param file MbFile handle
 * \param aboot aboot image
 * \param aboot_size Size of aboot image
 *
 * \return
 *   * #MB_BI_OK if the boot image is successfully patched
 *   * #MB_BI_FAILED if a file operation fails non-fatally
 *   * #MB_BI_FATAL if a file operation fails fatally
 */
int _loki_patch_file(MbBiWriter *biw, MbFile *file,
                     const void *aboot, size_t aboot_size)
{
    const unsigned char *aboot_ptr =
            reinterpret_cast<const unsigned char *>(aboot);
    unsigned char patch[] = LOKI_SHELLCODE;
    uint32_t target = 0;
    uint32_t aboot_base;
    int offset;
    int fake_size;
    size_t aboot_func_offset;
    uint64_t aboot_offset;
    LokiTarget *tgt = nullptr;
    AndroidHeader ahdr;
    LokiHeader lhdr;
    int ret;

    if (aboot_size < MIN_ABOOT_SIZE) {
        mb_bi_writer_set_error(biw, MB_BI_ERROR_INVALID_ARGUMENT,
                               "aboot image size is too small");
        return MB_BI_FAILED;
    }

    aboot_base = mb_le32toh(*reinterpret_cast<const uint32_t *>(
            aboot_ptr + 12)) - 0x28;

    // Find the signature checking function via pattern matching
    for (const unsigned char *ptr = aboot_ptr;
            ptr < aboot_ptr + aboot_size - ABOOT_SEARCH_LIMIT; ++ptr) {
        if (memcmp(ptr, PATTERN1, ABOOT_PATTERN_SIZE) == 0
                || memcmp(ptr, PATTERN2, ABOOT_PATTERN_SIZE) == 0
                || memcmp(ptr, PATTERN3, ABOOT_PATTERN_SIZE) == 0
                || memcmp(ptr, PATTERN4, ABOOT_PATTERN_SIZE) == 0
                || memcmp(ptr, PATTERN5, ABOOT_PATTERN_SIZE) == 0) {
            target = static_cast<uint32_t>(ptr - aboot_ptr + aboot_base);
            break;
        }
    }

    // Do a second pass for the second LG pattern. This is necessary because
    // apparently some LG models have both LG patterns, which throws off the
    // fingerprinting.

    if (target == 0) {
        for (const unsigned char *ptr = aboot_ptr;
                ptr < aboot_ptr + aboot_size - ABOOT_SEARCH_LIMIT; ++ptr) {
            if (memcmp(ptr, PATTERN6, ABOOT_PATTERN_SIZE) == 0) {
                target = static_cast<uint32_t>(ptr - aboot_ptr + aboot_base);
                break;
            }
        }
    }

    if (target == 0) {
        mb_bi_writer_set_error(biw, MB_BI_ERROR_FILE_FORMAT,
                               "Failed to find aboot function to patch");
        return MB_BI_FAILED;
    }

    for (size_t i = 0; i < (sizeof(targets) / sizeof(targets[0])); ++i) {
        if (targets[i].check_sigs == target) {
            tgt = &targets[i];
            break;
        }
    }

    if (!tgt) {
        mb_bi_writer_set_error(biw, MB_BI_ERROR_FILE_FORMAT,
                               "Unsupported aboot image");
        return MB_BI_FAILED;
    }

    ret = _loki_read_android_header(biw, file, &ahdr);
    if (ret != MB_BI_OK) {
        return ret;
    }

    // Set up Loki header
    memset(&lhdr, 0, sizeof(lhdr));

    memcpy(lhdr.magic, LOKI_MAGIC, LOKI_MAGIC_SIZE);
    lhdr.recovery = 0;
    strncpy(lhdr.build, tgt->build, sizeof(lhdr.build) - 1);

    // Store the original values in unused fields of the header
    lhdr.orig_kernel_size = ahdr.kernel_size;
    lhdr.orig_ramdisk_size = ahdr.ramdisk_size;
    lhdr.ramdisk_addr = ahdr.kernel_addr + ahdr.kernel_size
            + align_page_size<uint32_t>(ahdr.kernel_size, ahdr.page_size);

    if (!_patch_shellcode(tgt->hdr, ahdr.ramdisk_addr, patch)) {
        mb_bi_writer_set_error(biw, MB_BI_ERROR_FILE_FORMAT,
                               "Failed to patch shellcode");
        return MB_BI_FAILED;
    }

    // Ramdisk must be aligned to a page boundary
    ahdr.kernel_size = ahdr.kernel_size
            + align_page_size<uint32_t>(ahdr.kernel_size, ahdr.page_size)
            + ahdr.ramdisk_size;

    // Guarantee 16-byte alignment
    offset = tgt->check_sigs & 0xf;
    ahdr.ramdisk_addr = tgt->check_sigs - offset;

    if (tgt->lg) {
        fake_size = ahdr.page_size;
        ahdr.ramdisk_size = ahdr.page_size;
    } else {
        fake_size = 0x200;
        ahdr.ramdisk_size = 0;
    }

    aboot_func_offset = tgt->check_sigs - aboot_base - offset;

    // Write Android header
    ret = _loki_write_android_header(biw, file, &ahdr);
    if (ret != MB_BI_OK) {
        return ret;
    }

    // Write Loki header
    ret = _loki_write_loki_header(biw, file, &lhdr);
    if (ret != MB_BI_OK) {
        return ret;
    }

    aboot_offset = static_cast<uint64_t>(ahdr.page_size)
            + lhdr.orig_kernel_size
            + align_page_size<uint32_t>(lhdr.orig_kernel_size, ahdr.page_size)
            + lhdr.orig_ramdisk_size
            + align_page_size<uint32_t>(lhdr.orig_ramdisk_size, ahdr.page_size);

    // The function calls below are no longer recoverable should an error occur

    // Move DT image
    ret = _loki_move_dt_image(biw, file, aboot_offset, fake_size, ahdr.dt_size);
    if (ret != MB_BI_OK) {
        return MB_BI_FATAL;
    }

    // Write aboot
    ret = _loki_write_aboot(biw, file, aboot_ptr, aboot_size, aboot_offset,
                            aboot_func_offset, fake_size);
    if (ret != MB_BI_OK) {
        return MB_BI_FATAL;
    }

    // Write shellcode
    ret = _loki_write_shellcode(biw, file, aboot_offset, offset, patch);
    if (ret != MB_BI_OK) {
        return MB_BI_FATAL;
    }

    return MB_BI_OK;
}
Ejemplo n.º 5
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();
}