Пример #1
0
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;
}
Пример #2
0
/*!
 * \brief Search file for binary sequence
 *
 * If \p buf_size is non-zero, a buffer of size \p buf_size will be allocated.
 * If it is less than or equal to \p pattern_size, then the function will fail
 * and set `errno` to `EINVAL`. If \p buf_size is zero, then the larger of 8 MiB
 * and 2 * \p pattern_size will be used. In the rare case that
 * 2 * \p pattern_size would exceed the maximum value of a `size_t`, `SIZE_MAX`
 * will be used.
 *
 * If \p file does not support seeking, then the file position must be set to
 * the beginning of the file before calling this function. Instead of seeking,
 * the function will read and discard any data before \p start.
 *
 * \note We do not do overlapping searches. For example, if a file's contents
 *       is "ababababab" and the search pattern is "abab", the resulting offsets
 *       will be (0 and 4), *not* (0, 2, 4, 6). In other words, the next search
 *       begins at the end of the curent search.
 *
 * \note The file position after this function returns is undefined. Be sure to
 *       seek to a known location before attempting further read or write
 *       operations.
 *
 * \param file MbFile handle
 * \param start Start offset or negative number for beginning of file
 * \param end End offset or negative number for end of file
 * \param bsize Buffer size or 0 to automatically choose a size
 * \param pattern Pattern to search
 * \param pattern_size Size of pattern
 * \param max_matches Maximum number of matches or -1 to find all matches
 * \param result_cb Callback to invoke upon finding a match
 * \param userdata User callback data
 *
 * \return
 *   * #MB_FILE_OK if the search completes successfully
 *   * \<= #MB_FILE_WARN if an error occurs
 */
int mb_file_search(struct MbFile *file, int64_t start, int64_t end,
                   size_t bsize, const void *pattern,
                   size_t pattern_size, int64_t max_matches,
                   MbFileSearchResultCallback result_cb,
                   void *userdata)
{
    int ret = MB_FILE_OK;
    char *buf = nullptr;
    size_t buf_size;
    char *ptr;
    size_t ptr_remain;
    char *match;
    size_t match_remain;
    uint64_t offset;
    size_t n;

    // Check boundaries
    if (start >= 0 && end >= 0 && end < start) {
        mb_file_set_error(file, MB_FILE_ERROR_INVALID_ARGUMENT,
                          "End offset < start offset");
        ret = MB_FILE_FAILED;
        goto done;
    }

    // Trivial case
    if (max_matches == 0 || pattern_size == 0) {
        goto done;
    }

    // Compute buffer size
    if (bsize != 0) {
        buf_size = bsize;
    } else {
        buf_size = DEFAULT_BUFFER_SIZE;

        if (pattern_size > SIZE_MAX / 2) {
            buf_size = SIZE_MAX;
        } else {
            buf_size = std::max(buf_size, pattern_size * 2);
        }
    }

    // Ensure buffer is large enough
    if (buf_size < pattern_size) {
        mb_file_set_error(file, MB_FILE_ERROR_INVALID_ARGUMENT,
                          "Buffer size cannot be less than pattern size");
        ret = MB_FILE_FAILED;
        goto done;
    }

    buf = static_cast<char *>(malloc(buf_size));
    if (!buf) {
        mb_file_set_error(file, -errno, "Failed to allocate buffer: %s",
                          strerror(errno));
        ret = MB_FILE_FAILED;
        goto done;
    }

    if (start >= 0) {
        offset = start;
    } else {
        offset = 0;
    }

    // Seek to starting point
    ret = mb_file_seek(file, offset, SEEK_SET, nullptr);
    if (ret == MB_FILE_UNSUPPORTED) {
        uint64_t discarded;
        ret = mb_file_read_discard(file, offset, &discarded);
        if (ret < 0) {
            goto done;
        } else if (discarded != offset) {
            mb_file_set_error(file, MB_FILE_ERROR_INVALID_ARGUMENT,
                              "Reached EOF before starting offset");
            ret = MB_FILE_FATAL;
            goto done;
        }
    } else if (ret < 0) {
        goto done;
    }

    // Initially read to beginning of buffer
    ptr = buf;
    ptr_remain = buf_size;

    while (true) {
        ret = mb_file_read_fully(file, ptr, ptr_remain, &n);
        if (ret < 0) {
            goto done;
        }

        // Number of available bytes in buf
        n += ptr - buf;

        if (n < pattern_size) {
            // Reached EOF
            goto done;
        } else if (end >= 0 && offset >= static_cast<uint64_t>(end)) {
            // Artificial EOF
            goto done;
        }

        // Ensure that offset + n (and consequently, offset + diff) cannot
        // overflow
        if (n > UINT64_MAX - offset) {
            mb_file_set_error(file, MB_FILE_ERROR_INTERNAL_ERROR,
                              "Read overflows offset value");
            ret = MB_FILE_FAILED;
            goto done;
        }

        // Search from beginning of buffer
        match = buf;
        match_remain = n;

        while ((match = static_cast<char *>(
                mb_memmem(match, match_remain, pattern, pattern_size)))) {
            // Stop if match falls outside of ending boundary
            if (end >= 0 && offset + match - buf + pattern_size
                    > static_cast<uint64_t>(end)) {
                ret = MB_FILE_OK;
                goto done;
            }

            // Invoke callback
            ret = result_cb(file, userdata, offset + match - buf);
            if (ret == MB_FILE_WARN) {
                // Stop searching early
                ret = MB_FILE_OK;
                goto done;
            } else if (ret < 0) {
                goto done;
            }

            if (max_matches > 0) {
                --max_matches;
                if (max_matches == 0) {
                    goto done;
                }
            }

            // We don't do overlapping searches
            if (match_remain >= pattern_size) {
                match += pattern_size;
                match_remain = n - (match - buf);
            } else {
                break;
            }
        }

        // Up to pattern_size - 1 bytes may still match, so move those to the
        // beginning. We will move fewer than pattern_size - 1 bytes if there
        // was a match close to the end.
        size_t to_move = std::min(match_remain, pattern_size - 1);
        memmove(buf, buf + n - to_move, to_move);
        ptr = buf + to_move;
        ptr_remain = buf_size - to_move;
        offset += n - to_move;
    }

done:
    free(buf);
    return ret;
}
Пример #3
0
/*!
 * \brief Replace byte sequence in byte sequence
 *
 * Replace (\p from, \p from_size) with (\p to, \p to_size) in (\p *mem,
 * \p *mem_size), up to \p n times if \p n \> 0. If the function succeeds,
 * \p *mem will be passed to `free()` and \p *mem and \p *mem_size will be
 * updated to point to the newly allocated block of memory and its size. If
 * \p n_replaced is not NULL, the number of replacements done will be stored at
 * the value pointed by \p n_replaced. If the function fails, \p *mem will be
 * left unchanged.
 *
 * \param[in,out] mem Pointer to byte sequence to modify
 * \param[in,out] mem_size Pointer to size of bytes sequence to modify
 * \param[in] from Byte sequence to replace
 * \param[in] from_size Size of byte sequence to replace
 * \param[in] to Replacement byte sequence
 * \param[in] to_size Size of replacement byte sequence
 * \param[in] n Number of replacements to attempt (0 to disable limit)
 * \param[out] n_replaced Pointer to store number of replacements made
 *
 * \return Nothing if successful. Otherwise, the error code.
 */
oc::result<void> mem_replace(void **mem, size_t *mem_size,
                             const void *from, size_t from_size,
                             const void *to, size_t to_size,
                             size_t n, size_t *n_replaced)
{
    char *buf = nullptr;
    size_t buf_size = 0;
    void *target_ptr;
    auto base_ptr = static_cast<char *>(*mem);
    auto ptr = static_cast<char *>(*mem);
    size_t ptr_remain = *mem_size;
    size_t matches = 0;

    // Special case for replacing nothing
    if (from_size == 0) {
        if (n_replaced) {
            *n_replaced = 0;
        }
        return oc::success();
    }

    while ((n == 0 || matches < n) && (ptr = static_cast<char *>(
            mb_memmem(ptr, ptr_remain, from, from_size)))) {
        // Resize buffer to accomodate data
        if (buf_size >= SIZE_MAX - static_cast<size_t>(ptr - base_ptr)
                || buf_size + static_cast<size_t>(ptr - base_ptr)
                        >= SIZE_MAX - to_size) {
            free(buf);
            return std::make_error_code(std::errc::value_too_large);
        }

        size_t new_buf_size =
                buf_size + static_cast<size_t>(ptr - base_ptr) + to_size;
        auto new_buf = static_cast<char *>(realloc(buf, new_buf_size));
        if (!new_buf) {
            free(buf);
            return ec_from_errno();
        }

        target_ptr = new_buf + buf_size;

        // Copy data left of the match
        target_ptr = _mb_mempcpy(target_ptr, base_ptr,
                                 static_cast<size_t>(ptr - base_ptr));

        // Copy replacement
        target_ptr = _mb_mempcpy(target_ptr, to, to_size);

        buf = new_buf;
        buf_size = new_buf_size;

        ptr += from_size;
        ptr_remain -= static_cast<size_t>(ptr - base_ptr);
        base_ptr = ptr;

        ++matches;
    }

    // Copy remainder of string
    if (ptr_remain > 0) {
        if (buf_size >= SIZE_MAX - ptr_remain) {
            free(buf);
            return std::make_error_code(std::errc::value_too_large);
        }

        size_t new_buf_size = buf_size + ptr_remain;
        auto new_buf = static_cast<char *>(realloc(buf, new_buf_size));
        if (!new_buf) {
            free(buf);
            return ec_from_errno();
        }

        target_ptr = new_buf + buf_size;
        target_ptr = _mb_mempcpy(target_ptr, base_ptr, ptr_remain);

        buf = new_buf;
        buf_size = new_buf_size;
    }

    free(*mem);
    *mem = buf;
    *mem_size = buf_size;

    if (n_replaced) {
        *n_replaced = matches;
    }

    return oc::success();
}