示例#1
0
static inline void rehook32(struct func_hook *hook, bool force, intptr_t offset)
{
	fix_permissions((void*)(hook->func_addr - JMP_32_SIZE),
			JMP_32_SIZE * 2);

	if (force || !hook->started) {
		uint8_t *p        = (uint8_t*)hook->func_addr - JMP_32_SIZE;
		size_t  nop_count = 0;

		/* check for reverse chain hook availability */
		for (size_t i = 0; i < JMP_32_SIZE; i++) {
			if (p[i] == X86_NOP)
				nop_count++;
		}

		if (nop_count == JMP_32_SIZE && p[5] == 0x8B && p[6] == 0xFF) {
			hook_reverse_new(hook, p);

		} else if (p[0] == 0xE9 && *(uint16_t*)&p[5] == X86_JMP_NEG_5) {
			hook_reverse_chain(hook, p);

		} else if (p[5] == 0xE9) {
			hook_forward_chain(hook, p, offset);

		} else if (hook->type != HOOKTYPE_FORWARD_OVERWRITE) {
			hook->type = HOOKTYPE_FORWARD_OVERWRITE;
		}

		hook->started = true;
	}

	if (hook->type == HOOKTYPE_FORWARD_OVERWRITE) {
		hook_forward_overwrite(hook, offset);
	}
}
示例#2
0
void hook_init(struct func_hook *hook,
		void *func_addr, void *hook_addr, const char *name)
{
	memset(hook, 0, sizeof(*hook));

	hook->func_addr = (uintptr_t)func_addr;
	hook->hook_addr = (uintptr_t)hook_addr;
	hook->name = name;

	fix_permissions((void*)(hook->func_addr - JMP_32_SIZE),
			JMP_64_SIZE + JMP_32_SIZE);

	memcpy(hook->unhook_data, func_addr, JMP_64_SIZE);
}
示例#3
0
void unhook(struct func_hook *hook)
{
	size_t size;

	/* chain hooks do not need to unhook */
	if (!hook->hooked || hook->type != HOOKTYPE_FORWARD_OVERWRITE)
		return;

	size = patch_size(hook);
	fix_permissions((void*)hook->func_addr, size);
	memcpy(hook->rehook_data, (void*)hook->func_addr, size);
	memcpy((void*)hook->func_addr, hook->unhook_data, size);

	hook->hooked = false;
}
示例#4
0
static inline void rehook64(struct func_hook *hook)
{
	uint8_t data[JMP_64_SIZE];
	uintptr_t *ptr_loc = (uintptr_t*)((uint8_t*)data + sizeof(longjmp64));

	fix_permissions((void*)hook->func_addr, JMP_64_SIZE);

	memcpy(data, (void*)hook->func_addr, JMP_64_SIZE);
	memcpy(data, longjmp64, sizeof(longjmp64));
	*ptr_loc = hook->hook_addr;

	hook->call_addr = (void*)hook->func_addr;
	hook->type = HOOKTYPE_FORWARD_OVERWRITE;
	hook->hooked = true;

	memcpy((void*)hook->func_addr, data, JMP_64_SIZE);
}
示例#5
0
/*!
 * \brief Set the kernel for a ROM
 *
 * \note This will update the checksum for the image in
 *       \a /data/multiboot/checksums.prop.
 *
 * \param id ROM ID to set the kernel for
 * \param boot_blockdev Block device path of the boot partition
 *
 * \return True if the kernel was successfully set. Otherwise, false.
 */
bool set_kernel(const std::string &id, const std::string &boot_blockdev)
{
    LOGD("Attempting to set the kernel for %s", id.c_str());

    // Path for all of the images
    std::string multiboot_path(MULTIBOOT_DIR);
    multiboot_path += "/";
    multiboot_path += id;

    std::string bootimg_path(multiboot_path);
    bootimg_path += "/boot.img";

    // Verify ROM ID
    Roms roms;
    roms.add_installed();

    auto r = roms.find_by_id(id);
    if (!r) {
        LOGE("Invalid ROM ID: %s", id.c_str());
        return false;
    }

    if (!util::mkdir_recursive(multiboot_path, 0775)) {
        LOGE("%s: Failed to create directory: %s",
             multiboot_path.c_str(), strerror(errno));
        return false;
    }

    unsigned char *data;
    std::size_t size;

    if (!util::file_read_all(boot_blockdev, &data, &size)) {
        LOGE("%s: Failed to read block device: %s",
             boot_blockdev.c_str(), strerror(errno));
        return false;
    }

    auto free_data = util::finally([&]{
        free(data);
    });

    // Get actual sha512sum
    unsigned char digest[SHA512_DIGEST_LENGTH];
    SHA512(data, size, digest);
    std::string hash = util::hex_string(digest, SHA512_DIGEST_LENGTH);

    // Add to checksums.prop
    std::unordered_map<std::string, std::string> props;
    checksums_read(&props);
    checksums_update(&props, id, "boot.img", hash);

    // NOTE: This function isn't responsible for updating the checksums for
    //       any extra images. We don't want to mask any malicious changes.

    // Cast is okay. The data is just passed to fwrite (ie. no signed
    // extension issues)
    if (!util::file_write_data(bootimg_path, (char *) data, size)) {
        LOGE("%s: Failed to write image: %s",
             bootimg_path.c_str(), strerror(errno));
        return false;
    }

    LOGD("Updating checksums file");
    checksums_write(props);

    if (!fix_permissions()) {
        return false;
    }

    return true;
}
示例#6
0
/*!
 * \brief Switch to another ROM
 *
 * \note If the checksum is missing for some images to be flashed and invalid
 *       for some other images to be flashed, this function will always return
 *       SwitchRomResult::CHECKSUM_INVALID.
 *
 * \param id ROM ID to switch to
 * \param boot_blockdev Block device path of the boot partition
 * \param blockdev_base_dirs Search paths (non-recursive) for block devices
 *                           corresponding to extra flashable images in
 *                           /sdcard/MultiBoot/[ROM ID]/ *.img
 *
 * \return SwitchRomResult::SUCCEEDED if the switching succeeded,
 *         SwitchRomResult::FAILED if the switching failed,
 *         SwitchRomResult::CHECKSUM_NOT_FOUND if the checksum for some image is missing,
 *         SwitchRomResult::CHECKSUM_INVALID if the checksum for some image is invalid
 *
 */
SwitchRomResult switch_rom(const std::string &id, const std::string &boot_blockdev,
                           const std::vector<std::string> &blockdev_base_dirs,
                           bool force_update_checksums)
{
    LOGD("Attempting to switch to %s", id.c_str());
    LOGD("Force update checksums: %d", force_update_checksums);

    // Path for all of the images
    std::string multiboot_path(MULTIBOOT_DIR);
    multiboot_path += "/";
    multiboot_path += id;

    std::string bootimg_path(multiboot_path);
    bootimg_path += "/boot.img";

    // Verify ROM ID
    Roms roms;
    roms.add_installed();

    auto r = roms.find_by_id(id);
    if (!r) {
        LOGE("Invalid ROM ID: %s", id.c_str());
        return SwitchRomResult::FAILED;
    }

    if (!util::mkdir_recursive(multiboot_path, 0775)) {
        LOGE("%s: Failed to create directory: %s",
             multiboot_path.c_str(), strerror(errno));
        return SwitchRomResult::FAILED;
    }

    // We'll read the files we want to flash into memory so a malicious app
    // can't change the file between the hash verification step and flashing
    // step.

    std::vector<Flashable> flashables;
    auto free_flashables = util::finally([&]{
        for (Flashable &f : flashables) {
            free(f.data);
        }
    });

    flashables.emplace_back();
    flashables.back().image = bootimg_path;
    flashables.back().block_dev = boot_blockdev;

    if (!add_extra_images(multiboot_path, blockdev_base_dirs, &flashables)) {
        LOGW("Failed to find extra images");
    }

    std::unordered_map<std::string, std::string> props;
    checksums_read(&props);

    for (Flashable &f : flashables) {
        // If memory becomes an issue, an alternative method is to create a
        // temporary directory in /data/multiboot/ that's only writable by root
        // and copy the images there.
        if (!util::file_read_all(f.image, &f.data, &f.size)) {
            LOGE("%s: Failed to read image: %s",
                 f.image.c_str(), strerror(errno));
            return SwitchRomResult::FAILED;
        }

        // Get actual sha512sum
        unsigned char digest[SHA512_DIGEST_LENGTH];
        SHA512(f.data, f.size, digest);
        f.hash = util::hex_string(digest, SHA512_DIGEST_LENGTH);

        if (force_update_checksums) {
            checksums_update(&props, id, util::base_name(f.image), f.hash);
        }

        // Get expected sha512sum
        ChecksumsGetResult ret = checksums_get(
                &props, id, util::base_name(f.image), &f.expected_hash);
        if (ret == ChecksumsGetResult::MALFORMED) {
            return SwitchRomResult::CHECKSUM_INVALID;
        }

        // Verify hashes if we have an expected hash
        if (ret == ChecksumsGetResult::FOUND && f.expected_hash != f.hash) {
            LOGE("%s: Checksum (%s) does not match expected (%s)",
                 f.image.c_str(), f.hash.c_str(), f.expected_hash.c_str());
            return SwitchRomResult::CHECKSUM_INVALID;
        }
    }

    // Fail if we're missing expected hashes. We do this last to make sure
    // CHECKSUM_INVALID is returned if some checksums don't match (for the ones
    // that aren't missing).
    for (Flashable &f : flashables) {
        if (f.expected_hash.empty()) {
            LOGE("%s: Checksum does not exist", f.image.c_str());
            return SwitchRomResult::CHECKSUM_NOT_FOUND;
        }
    }

    // Now we can flash the images
    for (Flashable &f : flashables) {
        // Cast is okay. The data is just passed to fwrite (ie. no signed
        // extension issues)
        if (!util::file_write_data(f.block_dev, (char *) f.data, f.size)) {
            LOGE("%s: Failed to write image: %s",
                 f.block_dev.c_str(), strerror(errno));
            return SwitchRomResult::FAILED;
        }
    }

    if (force_update_checksums) {
        LOGD("Updating checksums file");
        checksums_write(props);
    }

    if (!fix_permissions()) {
        return SwitchRomResult::FAILED;
    }

    return SwitchRomResult::SUCCEEDED;
}
示例#7
0
static int save_external_coredump(
                const char *info[_INFO_LEN],
                uid_t uid,
                char **ret_filename,
                int *ret_fd,
                uint64_t *ret_size) {

        _cleanup_free_ char *fn = NULL, *tmp = NULL;
        _cleanup_close_ int fd = -1;
        struct stat st;
        int r;

        assert(info);
        assert(ret_filename);
        assert(ret_fd);
        assert(ret_size);

        r = make_filename(info, &fn);
        if (r < 0)
                return log_error_errno(r, "Failed to determine coredump file name: %m");

        r = tempfn_random(fn, NULL, &tmp);
        if (r < 0)
                return log_error_errno(r, "Failed to determine temporary file name: %m");

        mkdir_p_label("/var/lib/systemd/coredump", 0755);

        fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
        if (fd < 0)
                return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);

        r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max, false);
        if (r == -EFBIG) {
                log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
                goto fail;
        } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
                log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
                goto fail;
        } else if (r < 0) {
                log_error_errno(r, "Failed to dump coredump to file: %m");
                goto fail;
        }

        if (fstat(fd, &st) < 0) {
                log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp);
                goto fail;
        }

        if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
                log_error_errno(errno, "Failed to seek on %s: %m", tmp);
                goto fail;
        }

#if defined(HAVE_XZ) || defined(HAVE_LZ4)
        /* If we will remove the coredump anyway, do not compress. */
        if (maybe_remove_external_coredump(NULL, st.st_size) == 0
            && arg_compress) {

                _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
                _cleanup_close_ int fd_compressed = -1;

                fn_compressed = strappend(fn, COMPRESSED_EXT);
                if (!fn_compressed) {
                        log_oom();
                        goto uncompressed;
                }

                r = tempfn_random(fn_compressed, NULL, &tmp_compressed);
                if (r < 0) {
                        log_error_errno(r, "Failed to determine temporary file name for %s: %m", fn_compressed);
                        goto uncompressed;
                }

                fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
                if (fd_compressed < 0) {
                        log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed);
                        goto uncompressed;
                }

                r = compress_stream(fd, fd_compressed, -1);
                if (r < 0) {
                        log_error_errno(r, "Failed to compress %s: %m", tmp_compressed);
                        goto fail_compressed;
                }

                r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
                if (r < 0)
                        goto fail_compressed;

                /* OK, this worked, we can get rid of the uncompressed version now */
                unlink_noerrno(tmp);

                *ret_filename = fn_compressed;     /* compressed */
                *ret_fd = fd;                      /* uncompressed */
                *ret_size = (uint64_t) st.st_size; /* uncompressed */

                fn_compressed = NULL;
                fd = -1;

                return 0;

        fail_compressed:
                unlink_noerrno(tmp_compressed);
        }

uncompressed:
#endif
        r = fix_permissions(fd, tmp, fn, info, uid);
        if (r < 0)
                goto fail;

        *ret_filename = fn;
        *ret_fd = fd;
        *ret_size = (uint64_t) st.st_size;

        fn = NULL;
        fd = -1;

        return 0;

fail:
        unlink_noerrno(tmp);
        return r;
}
示例#8
0
static int save_external_coredump(
                const char *context[_CONTEXT_MAX],
                int input_fd,
                char **ret_filename,
                int *ret_node_fd,
                int *ret_data_fd,
                uint64_t *ret_size) {

        _cleanup_free_ char *fn = NULL, *tmp = NULL;
        _cleanup_close_ int fd = -1;
        uint64_t rlimit, max_size;
        struct stat st;
        uid_t uid;
        int r;

        assert(context);
        assert(ret_filename);
        assert(ret_node_fd);
        assert(ret_data_fd);
        assert(ret_size);

        r = parse_uid(context[CONTEXT_UID], &uid);
        if (r < 0)
                return log_error_errno(r, "Failed to parse UID: %m");

        r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit);
        if (r < 0)
                return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]);
        if (rlimit <= 0) {
                /* Is coredumping disabled? Then don't bother saving/processing the coredump */
                log_info("Core Dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]);
                return -EBADSLT;
        }

        /* Never store more than the process configured, or than we actually shall keep or process */
        max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max));

        r = make_filename(context, &fn);
        if (r < 0)
                return log_error_errno(r, "Failed to determine coredump file name: %m");

        mkdir_p_label("/var/lib/systemd/coredump", 0755);

        fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp);
        if (fd < 0)
                return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);

        r = copy_bytes(input_fd, fd, max_size, false);
        if (r == -EFBIG) {
                log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
                goto fail;
        } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
                log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
                goto fail;
        } else if (r < 0) {
                log_error_errno(r, "Failed to dump coredump to file: %m");
                goto fail;
        }

        if (fstat(fd, &st) < 0) {
                log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp));
                goto fail;
        }

        if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
                log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp));
                goto fail;
        }

#if defined(HAVE_XZ) || defined(HAVE_LZ4)
        /* If we will remove the coredump anyway, do not compress. */
        if (maybe_remove_external_coredump(NULL, st.st_size) == 0
            && arg_compress) {

                _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
                _cleanup_close_ int fd_compressed = -1;

                fn_compressed = strappend(fn, COMPRESSED_EXT);
                if (!fn_compressed) {
                        log_oom();
                        goto uncompressed;
                }

                fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
                if (fd_compressed < 0) {
                        log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
                        goto uncompressed;
                }

                r = compress_stream(fd, fd_compressed, -1);
                if (r < 0) {
                        log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
                        goto fail_compressed;
                }

                r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
                if (r < 0)
                        goto fail_compressed;

                /* OK, this worked, we can get rid of the uncompressed version now */
                if (tmp)
                        unlink_noerrno(tmp);

                *ret_filename = fn_compressed;     /* compressed */
                *ret_node_fd = fd_compressed;      /* compressed */
                *ret_data_fd = fd;                 /* uncompressed */
                *ret_size = (uint64_t) st.st_size; /* uncompressed */

                fn_compressed = NULL;
                fd = fd_compressed = -1;

                return 0;

        fail_compressed:
                if (tmp_compressed)
                        (void) unlink(tmp_compressed);
        }

uncompressed:
#endif

        r = fix_permissions(fd, tmp, fn, context, uid);
        if (r < 0)
                goto fail;

        *ret_filename = fn;
        *ret_data_fd = fd;
        *ret_node_fd = -1;
        *ret_size = (uint64_t) st.st_size;

        fn = NULL;
        fd = -1;

        return 0;

fail:
        if (tmp)
                (void) unlink(tmp);
        return r;
}
示例#9
0
static int save_external_coredump(
                const char *context[_CONTEXT_MAX],
                int input_fd,
                char **ret_filename,
                int *ret_node_fd,
                int *ret_data_fd,
                uint64_t *ret_size) {

        _cleanup_free_ char *fn = NULL, *tmp = NULL;
        _cleanup_close_ int fd = -1;
        uint64_t rlimit, max_size;
        struct stat st;
        uid_t uid;
        int r;

        assert(context);
        assert(ret_filename);
        assert(ret_node_fd);
        assert(ret_data_fd);
        assert(ret_size);

        r = parse_uid(context[CONTEXT_UID], &uid);
        if (r < 0)
                return log_error_errno(r, "Failed to parse UID: %m");

        r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit);
        if (r < 0)
                return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]);
        if (rlimit < page_size()) {
                /* Is coredumping disabled? Then don't bother saving/processing the coredump.
                 * Anything below PAGE_SIZE cannot give a readable coredump (the kernel uses
                 * ELF_EXEC_PAGESIZE which is not easily accessible, but is usually the same as PAGE_SIZE. */
                log_info("Resource limits disable core dumping for process %s (%s).",
                         context[CONTEXT_PID], context[CONTEXT_COMM]);
                return -EBADSLT;
        }

        /* Never store more than the process configured, or than we actually shall keep or process */
        max_size = MIN(rlimit, MAX(arg_process_size_max, storage_size_max()));

        r = make_filename(context, &fn);
        if (r < 0)
                return log_error_errno(r, "Failed to determine coredump file name: %m");

        mkdir_p_label("/var/lib/systemd/coredump", 0755);

        fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp);
        if (fd < 0)
                return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);

        r = copy_bytes(input_fd, fd, max_size, false);
        if (r < 0) {
                log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]);
                goto fail;
        } else if (r == 1)
                log_struct(LOG_INFO,
                           LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
                           "SIZE_LIMIT=%zu", max_size,
                           LOG_MESSAGE_ID(SD_MESSAGE_TRUNCATED_CORE),
                           NULL);

        if (fstat(fd, &st) < 0) {
                log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp));
                goto fail;
        }

        if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
                log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp));
                goto fail;
        }

#if defined(HAVE_XZ) || defined(HAVE_LZ4)
        /* If we will remove the coredump anyway, do not compress. */
        if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) {

                _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
                _cleanup_close_ int fd_compressed = -1;

                fn_compressed = strappend(fn, COMPRESSED_EXT);
                if (!fn_compressed) {
                        log_oom();
                        goto uncompressed;
                }

                fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
                if (fd_compressed < 0) {
                        log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
                        goto uncompressed;
                }

                r = compress_stream(fd, fd_compressed, -1);
                if (r < 0) {
                        log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
                        goto fail_compressed;
                }

                r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
                if (r < 0)
                        goto fail_compressed;

                /* OK, this worked, we can get rid of the uncompressed version now */
                if (tmp)
                        unlink_noerrno(tmp);

                *ret_filename = fn_compressed;     /* compressed */
                *ret_node_fd = fd_compressed;      /* compressed */
                *ret_data_fd = fd;                 /* uncompressed */
                *ret_size = (uint64_t) st.st_size; /* uncompressed */

                fn_compressed = NULL;
                fd = fd_compressed = -1;

                return 0;

        fail_compressed:
                if (tmp_compressed)
                        (void) unlink(tmp_compressed);
        }

uncompressed:
#endif

        r = fix_permissions(fd, tmp, fn, context, uid);
        if (r < 0)
                goto fail;

        *ret_filename = fn;
        *ret_data_fd = fd;
        *ret_node_fd = -1;
        *ret_size = (uint64_t) st.st_size;

        fn = NULL;
        fd = -1;

        return 0;

fail:
        if (tmp)
                (void) unlink(tmp);
        return r;
}