mirror of
https://github.com/pumpitupdev/pumptools.git
synced 2026-10-01 19:27:57 +03:00
Version 1.08 with public source code release.
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Common stuff for capnhook/hook module
|
||||
*
|
||||
* Based on original capnhook code for windows by decafcode:
|
||||
* https://github.com/decafcode/capnhook
|
||||
*/
|
||||
#ifndef CAPNHOOK_COMMON_H
|
||||
#define CAPNHOOK_COMMON_H
|
||||
|
||||
#define _countof(array) (sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,662 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "capnhook/hook/common.h"
|
||||
#include "capnhook/hook/filehook.h"
|
||||
#include "capnhook/hook/lib.h"
|
||||
|
||||
#include "util/time.h"
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Real funcs typedefs */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
typedef FILE* (*cnh_filehook_fopen_t)(const char* filename, const char* mode);
|
||||
typedef int (*cnh_filehook_fclose_t)(FILE* stream);
|
||||
typedef size_t (*cnh_filehook_fread_t)(void* ptr, size_t size, size_t nmemb, FILE* stream);
|
||||
typedef size_t (*cnh_filehook_fwrite_t)(const void* ptr, size_t size, size_t nmemb, FILE* stream);
|
||||
typedef char* (*cnh_filehook_fgets_t)(char* s, int size, FILE* stream);
|
||||
typedef int (*cnh_filehook_fseek_t)(FILE* stream, long offset, int whence);
|
||||
typedef long (*cnh_filehook_ftell_t)(FILE* stream);
|
||||
typedef int (*cnh_filehook_feof_t)(FILE* stream);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private helpers */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_filehook_init(void);
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real(struct cnh_filehook_irp *irp);
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fopen(struct cnh_filehook_irp *irp);
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fclose(struct cnh_filehook_irp *irp);
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fread(struct cnh_filehook_irp *irp);
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fwrite(struct cnh_filehook_irp *irp);
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fgets(struct cnh_filehook_irp *irp);
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fseek(struct cnh_filehook_irp *irp);
|
||||
static enum cnh_result _cnh_filehook_invoke_real_ftell(struct cnh_filehook_irp *irp);
|
||||
static enum cnh_result _cnh_filehook_invoke_real_feof(struct cnh_filehook_irp *irp);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private state */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static cnh_filehook_fopen_t _cnh_filehook_real_fopen;
|
||||
static cnh_filehook_fclose_t _cnh_filehook_real_fclose;
|
||||
static cnh_filehook_fread_t _cnh_filehook_real_fread;
|
||||
static cnh_filehook_fwrite_t _cnh_filehook_real_fwrite;
|
||||
static cnh_filehook_fgets_t _cnh_filehook_real_fgets;
|
||||
static cnh_filehook_fseek_t _cnh_filehook_real_fseek;
|
||||
static cnh_filehook_ftell_t _cnh_filehook_real_ftell;
|
||||
static cnh_filehook_feof_t _cnh_filehook_real_feof;
|
||||
|
||||
static const cnh_filehook_fn_t _cnh_filehook_real_handlers[8] = {
|
||||
[CNH_FILEHOOK_IRP_OP_OPEN] = _cnh_filehook_invoke_real_fopen,
|
||||
[CNH_FILEHOOK_IRP_OP_CLOSE] = _cnh_filehook_invoke_real_fclose,
|
||||
[CNH_FILEHOOK_IRP_OP_READ] = _cnh_filehook_invoke_real_fread,
|
||||
[CNH_FILEHOOK_IRP_OP_WRITE] = _cnh_filehook_invoke_real_fwrite,
|
||||
[CNH_FILEHOOK_IRP_OP_FGETS] = _cnh_filehook_invoke_real_fgets,
|
||||
[CNH_FILEHOOK_IRP_OP_SEEK] = _cnh_filehook_invoke_real_fseek,
|
||||
[CNH_FILEHOOK_IRP_OP_TELL] = _cnh_filehook_invoke_real_ftell,
|
||||
[CNH_FILEHOOK_IRP_OP_EOF] = _cnh_filehook_invoke_real_feof,
|
||||
};
|
||||
|
||||
static atomic_int _cnh_filehook_initted = ATOMIC_VAR_INIT(0);
|
||||
static atomic_int _cnh_filehook_init_in_progress = ATOMIC_VAR_INIT(0);
|
||||
static pthread_mutex_t _cnh_filehook_lock;
|
||||
static cnh_filehook_fn_t *_cnh_filehook_handlers;
|
||||
static size_t _cnh_filehook_nhandlers;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Public module functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
enum cnh_result cnh_filehook_push_handler(cnh_filehook_fn_t fn)
|
||||
{
|
||||
cnh_filehook_fn_t *new_array;
|
||||
size_t new_size;
|
||||
enum cnh_result result;
|
||||
|
||||
assert(fn != NULL);
|
||||
|
||||
_cnh_filehook_init();
|
||||
pthread_mutex_lock(&_cnh_filehook_lock);
|
||||
|
||||
new_size = _cnh_filehook_nhandlers + 1;
|
||||
new_array = realloc(_cnh_filehook_handlers, new_size * sizeof(cnh_filehook_fn_t));
|
||||
|
||||
if (new_array != NULL) {
|
||||
_cnh_filehook_handlers = new_array;
|
||||
_cnh_filehook_handlers[_cnh_filehook_nhandlers++] = fn;
|
||||
result = CNH_RESULT_SUCCESS;
|
||||
} else {
|
||||
result = CNH_RESULT_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_filehook_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_filehook_invoke_next(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
cnh_filehook_fn_t handler;
|
||||
enum cnh_result result;
|
||||
|
||||
assert(irp != NULL);
|
||||
assert(_cnh_filehook_initted > 0);
|
||||
|
||||
pthread_mutex_lock(&_cnh_filehook_lock);
|
||||
|
||||
assert(irp->next_handler <= _cnh_filehook_nhandlers);
|
||||
|
||||
if (irp->next_handler < _cnh_filehook_nhandlers) {
|
||||
handler = _cnh_filehook_handlers[irp->next_handler];
|
||||
irp->next_handler++;
|
||||
} else {
|
||||
handler = _cnh_filehook_invoke_real;
|
||||
irp->next_handler = (size_t) -1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_filehook_lock);
|
||||
|
||||
result = handler(irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
irp->next_handler = (size_t) -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FILE* cnh_filehook_open_dummy_file_handle()
|
||||
{
|
||||
_cnh_filehook_init();
|
||||
|
||||
/* We need to return some sort of valid FD to the
|
||||
caller, since we're simulating a device that
|
||||
doesn't exist. Open an FD on /dev/null. */
|
||||
|
||||
return _cnh_filehook_real_fopen("/dev/null", "r");
|
||||
}
|
||||
|
||||
void cnh_filehook_close_dummy_file_handle(FILE* file)
|
||||
{
|
||||
_cnh_filehook_init();
|
||||
|
||||
/* Avoid hooking pipeline when using dummy handles */
|
||||
_cnh_filehook_real_fclose(file);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooked functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
FILE* fopen(const char* filename, const char* mode)
|
||||
{
|
||||
struct cnh_filehook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_filehook_init();
|
||||
|
||||
if (filename == NULL || mode == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FILEHOOK_IRP_OP_OPEN;
|
||||
irp.file = NULL;
|
||||
irp.open_filename = filename;
|
||||
irp.open_mode = mode;
|
||||
|
||||
result = cnh_filehook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.file;
|
||||
}
|
||||
|
||||
int fclose(FILE* stream)
|
||||
{
|
||||
struct cnh_filehook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_filehook_init();
|
||||
|
||||
if (stream == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FILEHOOK_IRP_OP_CLOSE;
|
||||
irp.file = stream;
|
||||
|
||||
result = cnh_filehook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream)
|
||||
{
|
||||
struct cnh_filehook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_filehook_init();
|
||||
|
||||
if (ptr == NULL || stream == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FILEHOOK_IRP_OP_READ;
|
||||
irp.file = stream;
|
||||
irp.read.bytes = ptr;
|
||||
irp.read.nbytes = size * nmemb;
|
||||
irp.read.pos = 0;
|
||||
/* To ensure we don't mess up any calls that are just passed through, we have to drag along the original size
|
||||
and nmemb parameters and apply them to the real call at the end of the chain if the iobuffer was not
|
||||
modified */
|
||||
irp.orig_read_write_size = size;
|
||||
irp.orig_read_write_nmemb = nmemb;
|
||||
|
||||
result = cnh_filehook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.read.pos / size;
|
||||
}
|
||||
|
||||
size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream)
|
||||
{
|
||||
struct cnh_filehook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_filehook_init();
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FILEHOOK_IRP_OP_WRITE;
|
||||
irp.file = stream;
|
||||
irp.write.bytes = ptr;
|
||||
irp.write.nbytes = size * nmemb;
|
||||
irp.write.pos = 0;
|
||||
/* To ensure we don't mess up any calls that are just passed through, we have to drag along the original size
|
||||
and nmemb parameters and apply them to the real call at the end of the chain if the iobuffer was not
|
||||
modified */
|
||||
irp.orig_read_write_size = size;
|
||||
irp.orig_read_write_nmemb = nmemb;
|
||||
|
||||
result = cnh_filehook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.write.pos / size;
|
||||
}
|
||||
|
||||
char* fgets(char* s, int size, FILE* stream)
|
||||
{
|
||||
struct cnh_filehook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_filehook_init();
|
||||
|
||||
if (s == NULL || stream == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Redirect this to fread */
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FILEHOOK_IRP_OP_FGETS;
|
||||
irp.file = stream;
|
||||
irp.read.bytes = (uint8_t*) s;
|
||||
irp.read.nbytes = (size_t) size;
|
||||
irp.read.pos = 0;
|
||||
|
||||
result = cnh_filehook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return (char*) irp.read.bytes;
|
||||
}
|
||||
|
||||
int fseek(FILE* stream, long offset, int whence)
|
||||
{
|
||||
struct cnh_filehook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_filehook_init();
|
||||
|
||||
if (stream == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FILEHOOK_IRP_OP_SEEK;
|
||||
irp.file = stream;
|
||||
irp.seek_origin = whence;
|
||||
irp.seek_offset = offset;
|
||||
|
||||
result = cnh_filehook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rewind(FILE* stream)
|
||||
{
|
||||
struct cnh_filehook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_filehook_init();
|
||||
|
||||
if (stream == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Seek to beginning */
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FILEHOOK_IRP_OP_SEEK;
|
||||
irp.file = stream;
|
||||
irp.seek_origin = SEEK_SET;
|
||||
irp.seek_offset = 0;
|
||||
|
||||
result = cnh_filehook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
long ftell(FILE* stream)
|
||||
{
|
||||
struct cnh_filehook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_filehook_init();
|
||||
|
||||
if (stream == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FILEHOOK_IRP_OP_TELL;
|
||||
irp.file = stream;
|
||||
/* Return value */
|
||||
irp.tell_offset = 0;
|
||||
|
||||
result = cnh_filehook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return (long) irp.tell_offset;
|
||||
}
|
||||
|
||||
int feof(FILE* stream)
|
||||
{
|
||||
struct cnh_filehook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_filehook_init();
|
||||
|
||||
if (stream == NULL) {
|
||||
/* Must be EBADF according to man pages */
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FILEHOOK_IRP_OP_EOF;
|
||||
irp.file = stream;
|
||||
/* Return value */
|
||||
irp.eof = false;
|
||||
|
||||
result = cnh_filehook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
/* Function is never supposed to fail and EBADF is the only allowed error code */
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.eof ? 1 : 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Helper functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_filehook_init(void)
|
||||
{
|
||||
int expected;
|
||||
|
||||
if (atomic_load(&_cnh_filehook_initted) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
expected = 0;
|
||||
|
||||
if (!atomic_compare_exchange_strong(&_cnh_filehook_init_in_progress, &expected, 1)) {
|
||||
while (atomic_load(&_cnh_filehook_init_in_progress) > 1) {
|
||||
util_time_sleep_us(100);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
|
||||
if (atomic_load(&_cnh_filehook_initted) > 0) {
|
||||
/* Revert init in progress, because nothing to do anymore */
|
||||
atomic_store(&_cnh_filehook_init_in_progress, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_cnh_filehook_real_fopen = (cnh_filehook_fopen_t) cnh_lib_get_func_addr("fopen");
|
||||
_cnh_filehook_real_fclose = (cnh_filehook_fclose_t) cnh_lib_get_func_addr("fclose");
|
||||
_cnh_filehook_real_fread = (cnh_filehook_fread_t) cnh_lib_get_func_addr("fread");
|
||||
_cnh_filehook_real_fwrite = (cnh_filehook_fwrite_t) cnh_lib_get_func_addr("fwrite");
|
||||
_cnh_filehook_real_fgets = (cnh_filehook_fgets_t) cnh_lib_get_func_addr("fgets");
|
||||
_cnh_filehook_real_fseek = (cnh_filehook_fseek_t) cnh_lib_get_func_addr("fseek");
|
||||
_cnh_filehook_real_ftell = (cnh_filehook_ftell_t) cnh_lib_get_func_addr("ftell");
|
||||
_cnh_filehook_real_feof = (cnh_filehook_feof_t) cnh_lib_get_func_addr("feof");
|
||||
|
||||
pthread_mutex_init(&_cnh_filehook_lock, NULL);
|
||||
|
||||
atomic_store(&_cnh_filehook_initted, 1);
|
||||
atomic_store(&_cnh_filehook_init_in_progress, 0);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
cnh_filehook_fn_t handler;
|
||||
|
||||
assert(irp != NULL);
|
||||
assert(irp->op < _countof(_cnh_filehook_real_handlers));
|
||||
|
||||
handler = _cnh_filehook_real_handlers[irp->op];
|
||||
|
||||
assert(handler != NULL);
|
||||
|
||||
return handler(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fopen(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
FILE* file;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
file = _cnh_filehook_real_fopen(irp->open_filename, irp->open_mode);
|
||||
|
||||
if (!file) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->file = file;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fclose(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_filehook_real_fclose(irp->file);
|
||||
|
||||
if (res == EOF) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fread(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
size_t read;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
/* Check if any previous hook modified the buffer and use original parameters otherwise to ensure the original
|
||||
call is correctly applied to the real function */
|
||||
if (irp->read.pos != 0 || irp->read.nbytes != irp->orig_read_write_nmemb * irp->orig_read_write_size) {
|
||||
read = _cnh_filehook_real_fread(&irp->read.bytes[irp->read.pos], irp->read.nbytes - irp->read.pos, 1,
|
||||
irp->file);
|
||||
|
||||
if (read < 1 && ferror(irp->file) != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->read.pos += irp->read.nbytes - irp->read.pos;
|
||||
} else {
|
||||
read = _cnh_filehook_real_fread(irp->read.bytes, irp->orig_read_write_size, irp->orig_read_write_nmemb,
|
||||
irp->file);
|
||||
|
||||
if (read < 1 && ferror(irp->file) != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->read.pos = read * irp->orig_read_write_size;
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fwrite(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
size_t written;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
/* Check if any previous hook modified the buffer and use original parameters otherwise to ensure the original
|
||||
call is correctly applied to the real function */
|
||||
if (irp->write.pos != 0 || irp->write.nbytes != irp->orig_read_write_nmemb * irp->orig_read_write_size) {
|
||||
written = _cnh_filehook_real_fwrite(&irp->write.bytes[irp->write.pos], irp->write.nbytes - irp->write.pos, 1,
|
||||
irp->file);
|
||||
|
||||
if (written < 1 && ferror(irp->file) != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->write.pos += irp->write.nbytes - irp->write.pos;
|
||||
} else {
|
||||
written = _cnh_filehook_real_fwrite(irp->write.bytes, irp->orig_read_write_size, irp->orig_read_write_nmemb,
|
||||
irp->file);
|
||||
|
||||
if (written < 1 && ferror(irp->file) != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->write.pos += written * irp->orig_read_write_size;
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fgets(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
char* res;
|
||||
|
||||
res = _cnh_filehook_real_fgets((char*) irp->read.bytes, irp->read.nbytes, irp->file);
|
||||
|
||||
if (res == NULL) {
|
||||
/* eof */
|
||||
if (errno == 0) {
|
||||
irp->read.bytes = NULL;
|
||||
return CNH_RESULT_SUCCESS;
|
||||
} else {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
} else {
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real_fseek(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_filehook_real_fseek(irp->file, (long) irp->seek_offset, irp->seek_origin);
|
||||
|
||||
if (res != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real_ftell(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
long res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_filehook_real_ftell(irp->file);
|
||||
|
||||
if (res < 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->tell_offset = (uint64_t) res;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_filehook_invoke_real_feof(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_filehook_real_feof(irp->file);
|
||||
|
||||
if (res < 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->eof = res > 0;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Hook module for hooking file related calls, e.g. fopen, fread, fwrite, etc.
|
||||
*
|
||||
* Based on original capnhook code for windows by decafcode:
|
||||
* https://github.com/decafcode/capnhook
|
||||
*/
|
||||
#ifndef CAPNHOOK_FILEHOOK_H
|
||||
#define CAPNHOOK_FILEHOOK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "capnhook/hook/iobuf.h"
|
||||
|
||||
/**
|
||||
* Available operations to hook
|
||||
*/
|
||||
enum cnh_filehook_irp_op {
|
||||
CNH_FILEHOOK_IRP_OP_OPEN = 0,
|
||||
CNH_FILEHOOK_IRP_OP_CLOSE = 1,
|
||||
CNH_FILEHOOK_IRP_OP_READ = 2,
|
||||
CNH_FILEHOOK_IRP_OP_WRITE = 3,
|
||||
CNH_FILEHOOK_IRP_OP_FGETS = 4,
|
||||
CNH_FILEHOOK_IRP_OP_SEEK = 5,
|
||||
CNH_FILEHOOK_IRP_OP_TELL = 6,
|
||||
CNH_FILEHOOK_IRP_OP_EOF = 7,
|
||||
};
|
||||
|
||||
/**
|
||||
* I/O request packet
|
||||
*/
|
||||
struct cnh_filehook_irp {
|
||||
enum cnh_filehook_irp_op op;
|
||||
size_t next_handler;
|
||||
FILE* file;
|
||||
const char *open_filename;
|
||||
const char* open_mode;
|
||||
struct cnh_iobuf read;
|
||||
struct cnh_const_iobuf write;
|
||||
size_t orig_read_write_size;
|
||||
size_t orig_read_write_nmemb;
|
||||
int seek_origin;
|
||||
int64_t seek_offset;
|
||||
uint64_t tell_offset;
|
||||
bool eof;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook function type
|
||||
*/
|
||||
typedef enum cnh_result (*cnh_filehook_fn_t)(struct cnh_filehook_irp *irp);
|
||||
|
||||
/**
|
||||
* Add a new hook handler to the hook module.
|
||||
* The handler added is getting called on every hooked operation.
|
||||
*
|
||||
* @param fn Pointer to hook function to add
|
||||
* @return Result/error code if hooking was successful
|
||||
*/
|
||||
enum cnh_result cnh_filehook_push_handler(cnh_filehook_fn_t fn);
|
||||
|
||||
/**
|
||||
* Invoke the next hooked function registered in the hook module.
|
||||
* Call this from your hook handler if you want to pass on execution to further
|
||||
* hooked calls reaching the original function at the end of the hook chain.
|
||||
*
|
||||
* @param irp I/O request package to dispatch
|
||||
* @return Result of the following function dispatching the request package
|
||||
*/
|
||||
enum cnh_result cnh_filehook_invoke_next(struct cnh_filehook_irp *irp);
|
||||
|
||||
/**
|
||||
* Create a dummy file handle. Use this function for file access virtualization.
|
||||
* Do not execute real operations on the handle returned.
|
||||
*
|
||||
* @return Actual file handle instance pointing to a dummy endpoint.
|
||||
*/
|
||||
FILE* cnh_filehook_open_dummy_file_handle();
|
||||
|
||||
/**
|
||||
* Close a dummy file handle. Make sure to free your previously allocated handles
|
||||
* to avoid resource leaks. Do not free them using the real close function.
|
||||
*
|
||||
* @param file Dummy file handle to free
|
||||
*/
|
||||
void cnh_filehook_close_dummy_file_handle(FILE* file);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,447 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "capnhook/hook/common.h"
|
||||
#include "capnhook/hook/fshook.h"
|
||||
#include "capnhook/hook/lib.h"
|
||||
|
||||
#include "util/time.h"
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Real funcs typedefs */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
typedef DIR* (*cnh_fshook_opendir_t)(const char* name);
|
||||
typedef int (*cnh_fshook_lxstat_t)(int version, const char* file, struct stat* buf);
|
||||
typedef int (*cnh_fshook_xstat_t)(int version, const char* file, struct stat* buf);
|
||||
typedef int (*cnh_fshook_rename_t)(const char* old, const char* new);
|
||||
typedef int (*cnh_fshook_remove_t)(const char* pathname);
|
||||
typedef int (*cnh_fshook_access_t)(const char* path, int amode);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private helpers */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_fshook_init(void);
|
||||
|
||||
static enum cnh_result _cnh_fshook_invoke_real(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_fshook_invoke_real_opendir(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_fshook_invoke_real_lxstat(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_fshook_invoke_real_xstat(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_fshook_invoke_real_rename(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_fshook_invoke_real_remove(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_fshook_invoke_real_access(struct cnh_fshook_irp *irp);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private state */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static cnh_fshook_opendir_t _cnh_fshook_real_opendir;
|
||||
static cnh_fshook_lxstat_t _cnh_fshook_real_lxstat;
|
||||
static cnh_fshook_xstat_t _cnh_fshook_real_xstat;
|
||||
static cnh_fshook_rename_t _cnh_fshook_real_rename;
|
||||
static cnh_fshook_remove_t _cnh_fshook_real_remove;
|
||||
static cnh_fshook_access_t _cnh_fshook_real_access;
|
||||
|
||||
static const cnh_fshook_fn_t _cnh_fshook_real_handlers[6] = {
|
||||
[CNH_FSHOOK_IRP_OP_DIR_OPEN] = _cnh_fshook_invoke_real_opendir,
|
||||
[CNH_FSHOOK_IRP_OP_LXSTAT] = _cnh_fshook_invoke_real_lxstat,
|
||||
[CNH_FSHOOK_IRP_OP_XSTAT] = _cnh_fshook_invoke_real_xstat,
|
||||
[CNH_FSHOOK_IRP_OP_RENAME] = _cnh_fshook_invoke_real_rename,
|
||||
[CNH_FSHOOK_IRP_OP_REMOVE] = _cnh_fshook_invoke_real_remove,
|
||||
[CNH_FSHOOK_IRP_OP_ACCESS] = _cnh_fshook_invoke_real_access,
|
||||
};
|
||||
|
||||
static atomic_int _cnh_fshook_initted = ATOMIC_VAR_INIT(0);
|
||||
static atomic_int _cnh_fshook_init_in_progress = ATOMIC_VAR_INIT(0);
|
||||
static pthread_mutex_t _cnh_fshook_lock;
|
||||
static cnh_fshook_fn_t *_cnh_fshook_handlers;
|
||||
static size_t _cnh_fshook_nhandlers;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Public module functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
enum cnh_result cnh_fshook_push_handler(cnh_fshook_fn_t fn)
|
||||
{
|
||||
cnh_fshook_fn_t *new_array;
|
||||
size_t new_size;
|
||||
enum cnh_result result;
|
||||
|
||||
assert(fn != NULL);
|
||||
|
||||
_cnh_fshook_init();
|
||||
|
||||
pthread_mutex_lock(&_cnh_fshook_lock);
|
||||
|
||||
new_size = _cnh_fshook_nhandlers + 1;
|
||||
new_array = realloc(_cnh_fshook_handlers, new_size * sizeof(cnh_fshook_fn_t));
|
||||
|
||||
if (new_array != NULL) {
|
||||
_cnh_fshook_handlers = new_array;
|
||||
_cnh_fshook_handlers[_cnh_fshook_nhandlers++] = fn;
|
||||
result = CNH_RESULT_SUCCESS;
|
||||
} else {
|
||||
result = CNH_RESULT_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_fshook_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_fshook_invoke_next(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
cnh_fshook_fn_t handler;
|
||||
enum cnh_result result;
|
||||
|
||||
assert(irp != NULL);
|
||||
assert(_cnh_fshook_initted > 0);
|
||||
|
||||
pthread_mutex_lock(&_cnh_fshook_lock);
|
||||
|
||||
assert(irp->next_handler <= _cnh_fshook_nhandlers);
|
||||
|
||||
if (irp->next_handler < _cnh_fshook_nhandlers) {
|
||||
handler = _cnh_fshook_handlers[irp->next_handler];
|
||||
irp->next_handler++;
|
||||
} else {
|
||||
handler = _cnh_fshook_invoke_real;
|
||||
irp->next_handler = (size_t) -1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_fshook_lock);
|
||||
|
||||
result = handler(irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
irp->next_handler = (size_t) -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooked functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
DIR* opendir(const char* name)
|
||||
{
|
||||
struct cnh_fshook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_fshook_init();
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FSHOOK_IRP_OP_DIR_OPEN;
|
||||
irp.opendir_name = name;
|
||||
irp.opendir_ret = NULL;
|
||||
|
||||
result = cnh_fshook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.opendir_ret;
|
||||
}
|
||||
|
||||
int __lxstat(int version, const char* file, struct stat* buf)
|
||||
{
|
||||
struct cnh_fshook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_fshook_init();
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FSHOOK_IRP_OP_LXSTAT;
|
||||
irp.xstat_version = version;
|
||||
irp.xstat_file = file;
|
||||
irp.xstat_buf = buf;
|
||||
|
||||
result = cnh_fshook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __xstat(int version, const char* file, struct stat* buf)
|
||||
{
|
||||
struct cnh_fshook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_fshook_init();
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FSHOOK_IRP_OP_XSTAT;
|
||||
irp.xstat_version = version;
|
||||
irp.xstat_file = file;
|
||||
irp.xstat_buf = buf;
|
||||
|
||||
result = cnh_fshook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rename(const char* old, const char* new)
|
||||
{
|
||||
struct cnh_fshook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_fshook_init();
|
||||
|
||||
if (old == NULL || new == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FSHOOK_IRP_OP_RENAME;
|
||||
irp.rename_old = old;
|
||||
irp.rename_new = new;
|
||||
|
||||
result = cnh_fshook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int remove(const char* pathname)
|
||||
{
|
||||
struct cnh_fshook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_fshook_init();
|
||||
|
||||
if (pathname == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FSHOOK_IRP_OP_REMOVE;
|
||||
irp.remove_pathname = pathname;
|
||||
|
||||
result = cnh_fshook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int access(const char* path, int amode)
|
||||
{
|
||||
struct cnh_fshook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_fshook_init();
|
||||
|
||||
if (path == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_FSHOOK_IRP_OP_ACCESS;
|
||||
irp.access_path = path;
|
||||
irp.access_amode = amode;
|
||||
|
||||
result = cnh_fshook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Helper functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_fshook_init(void)
|
||||
{
|
||||
int expected;
|
||||
|
||||
if (atomic_load(&_cnh_fshook_initted) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
expected = 0;
|
||||
|
||||
if (!atomic_compare_exchange_strong(&_cnh_fshook_init_in_progress, &expected, 1)) {
|
||||
while (atomic_load(&_cnh_fshook_init_in_progress) > 1) {
|
||||
util_time_sleep_us(100);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
|
||||
if (atomic_load(&_cnh_fshook_initted) > 0) {
|
||||
/* Revert init in progress, because nothing to do anymore */
|
||||
atomic_store(&_cnh_fshook_init_in_progress, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_cnh_fshook_real_opendir = (cnh_fshook_opendir_t) cnh_lib_get_func_addr("opendir");
|
||||
_cnh_fshook_real_lxstat = (cnh_fshook_lxstat_t) cnh_lib_get_func_addr("__lxstat");
|
||||
_cnh_fshook_real_xstat = (cnh_fshook_xstat_t) cnh_lib_get_func_addr("__xstat");
|
||||
_cnh_fshook_real_rename = (cnh_fshook_rename_t) cnh_lib_get_func_addr("rename");
|
||||
_cnh_fshook_real_remove = (cnh_fshook_remove_t) cnh_lib_get_func_addr("remove");
|
||||
_cnh_fshook_real_access = (cnh_fshook_access_t) cnh_lib_get_func_addr("access");
|
||||
|
||||
pthread_mutex_init(&_cnh_fshook_lock, NULL);
|
||||
|
||||
atomic_store(&_cnh_fshook_initted, 1);
|
||||
atomic_store(&_cnh_fshook_init_in_progress, 0);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_fshook_invoke_real(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
cnh_fshook_fn_t handler;
|
||||
|
||||
assert(irp != NULL);
|
||||
assert(irp->op < _countof(_cnh_fshook_real_handlers));
|
||||
|
||||
handler = _cnh_fshook_real_handlers[irp->op];
|
||||
|
||||
assert(handler != NULL);
|
||||
|
||||
return handler(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_fshook_invoke_real_opendir(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
DIR* dir;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
dir = _cnh_fshook_real_opendir(irp->opendir_name);
|
||||
|
||||
if (dir == NULL) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->opendir_ret = dir;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_fshook_invoke_real_lxstat(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_fshook_real_lxstat(irp->xstat_version, irp->xstat_file, irp->xstat_buf);
|
||||
|
||||
if (res != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_fshook_invoke_real_xstat(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_fshook_real_xstat(irp->xstat_version, irp->xstat_file, irp->xstat_buf);
|
||||
|
||||
if (res != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_fshook_invoke_real_rename(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_fshook_real_rename(irp->rename_old, irp->rename_new);
|
||||
|
||||
if (res != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_fshook_invoke_real_remove(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_fshook_real_remove(irp->remove_pathname);
|
||||
|
||||
if (res != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_fshook_invoke_real_access(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_fshook_real_access(irp->access_path, irp->access_amode);
|
||||
|
||||
if (res != 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Hook module for hooking file system related calls,
|
||||
* not counting calls operating on actual opened files, e.g.
|
||||
* fopen, fread, fwrite etc.
|
||||
*
|
||||
* Based on original capnhook code for windows by decafcode:
|
||||
* https://github.com/decafcode/capnhook
|
||||
*/
|
||||
#ifndef CAPNHOOK_FSHOOK_H
|
||||
#define CAPNHOOK_FSHOOK_H
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "capnhook/hook/result.h"
|
||||
|
||||
/**
|
||||
* Available operations to hook
|
||||
*/
|
||||
enum cnh_fshook_irp_op {
|
||||
CNH_FSHOOK_IRP_OP_DIR_OPEN = 0,
|
||||
CNH_FSHOOK_IRP_OP_LXSTAT = 1,
|
||||
CNH_FSHOOK_IRP_OP_XSTAT = 2,
|
||||
CNH_FSHOOK_IRP_OP_RENAME = 3,
|
||||
CNH_FSHOOK_IRP_OP_REMOVE = 4,
|
||||
CNH_FSHOOK_IRP_OP_ACCESS = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* I/O request packet
|
||||
*/
|
||||
struct cnh_fshook_irp {
|
||||
enum cnh_fshook_irp_op op;
|
||||
size_t next_handler;
|
||||
const char* opendir_name;
|
||||
DIR* opendir_ret;
|
||||
int xstat_version;
|
||||
const char* xstat_file;
|
||||
struct stat* xstat_buf;
|
||||
const char* rename_old;
|
||||
const char* rename_new;
|
||||
const char* remove_pathname;
|
||||
const char* access_path;
|
||||
int access_amode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook function type
|
||||
*/
|
||||
typedef enum cnh_result (*cnh_fshook_fn_t)(struct cnh_fshook_irp *irp);
|
||||
|
||||
/**
|
||||
* Add a new hook handler to the hook module.
|
||||
* The handler added is getting called on every hooked operation.
|
||||
*
|
||||
* @param fn Pointer to hook function to add
|
||||
* @return Result/error code if hooking was successful
|
||||
*/
|
||||
enum cnh_result cnh_fshook_push_handler(cnh_fshook_fn_t fn);
|
||||
|
||||
/**
|
||||
* Invoke the next hooked function registered in the hook module.
|
||||
* Call this from your hook handler if you want to pass on execution to further
|
||||
* hooked calls reaching the original function at the end of the hook chain.
|
||||
*
|
||||
* @param irp I/O request package to dispatch
|
||||
* @return Result of the following function dispatching the request package
|
||||
*/
|
||||
enum cnh_result cnh_fshook_invoke_next(struct cnh_fshook_irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,253 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "capnhook/hook/iobuf.h"
|
||||
|
||||
void cnh_iobuf_flip(struct cnh_const_iobuf *child, struct cnh_iobuf *parent)
|
||||
{
|
||||
assert(child != NULL);
|
||||
assert(parent != NULL);
|
||||
|
||||
child->bytes = parent->bytes;
|
||||
child->pos = 0;
|
||||
child->nbytes = parent->pos;
|
||||
}
|
||||
|
||||
size_t cnh_iobuf_move(struct cnh_iobuf *dest, struct cnh_const_iobuf *src)
|
||||
{
|
||||
size_t dest_avail;
|
||||
size_t src_avail;
|
||||
size_t chunksz;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
|
||||
assert(src != NULL);
|
||||
assert(src->bytes != NULL || src->nbytes == 0);
|
||||
assert(src->pos <= src->nbytes);
|
||||
|
||||
dest_avail = dest->nbytes - dest->pos;
|
||||
src_avail = src->nbytes - src->pos;
|
||||
chunksz = dest_avail < src_avail ? dest_avail : src_avail;
|
||||
|
||||
memcpy(&dest->bytes[dest->pos], &src->bytes[src->pos], chunksz);
|
||||
|
||||
dest->pos += chunksz;
|
||||
src->pos += chunksz;
|
||||
|
||||
return chunksz;
|
||||
}
|
||||
|
||||
size_t cnh_iobuf_shift(struct cnh_iobuf *dest, struct cnh_iobuf *src)
|
||||
{
|
||||
struct cnh_const_iobuf span;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
cnh_iobuf_flip(&span, src);
|
||||
cnh_iobuf_move(dest, &span);
|
||||
|
||||
memmove(src->bytes, &src->bytes[span.pos], span.nbytes - span.pos);
|
||||
src->pos -= span.pos;
|
||||
|
||||
return span.pos;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_read(struct cnh_const_iobuf *src, void *bytes, size_t nbytes)
|
||||
{
|
||||
assert(src != NULL);
|
||||
assert(bytes != NULL || nbytes == 0);
|
||||
|
||||
if (src->pos + nbytes > src->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
memcpy(bytes, &src->bytes[src->pos], nbytes);
|
||||
src->pos += nbytes;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_read_8(struct cnh_const_iobuf *src, uint8_t *out)
|
||||
{
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint8_t) > src->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
*out = src->bytes[src->pos++];
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_read_be16(struct cnh_const_iobuf *src, uint16_t *out)
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint16_t) > src->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
value = src->bytes[src->pos++] << 8;
|
||||
value |= src->bytes[src->pos++];
|
||||
|
||||
*out = value;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_read_be32(struct cnh_const_iobuf *src, uint32_t *out)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint32_t) > src->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
value = src->bytes[src->pos++] << 24;
|
||||
value |= src->bytes[src->pos++] << 16;
|
||||
value |= src->bytes[src->pos++] << 8;
|
||||
value |= src->bytes[src->pos++];
|
||||
|
||||
*out = value;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_read_le16(struct cnh_const_iobuf *src, uint16_t *out)
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint16_t) > src->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
value = src->bytes[src->pos++];
|
||||
value |= src->bytes[src->pos++] << 8;
|
||||
|
||||
*out = value;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_read_le32(struct cnh_const_iobuf *src, uint32_t *out)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint32_t) > src->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
value = src->bytes[src->pos++];
|
||||
value |= src->bytes[src->pos++] << 8;
|
||||
value |= src->bytes[src->pos++] << 16;
|
||||
value |= src->bytes[src->pos++] << 24;
|
||||
|
||||
*out = value;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_write(struct cnh_iobuf *dest, const void *bytes, size_t nbytes)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
assert(bytes != NULL || nbytes == 0);
|
||||
|
||||
if (dest->pos + nbytes > dest->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
memcpy(&dest->bytes[dest->pos], bytes, nbytes);
|
||||
dest->pos += nbytes;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_write_8(struct cnh_iobuf *dest, uint8_t value)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + sizeof(uint8_t) > dest->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = value;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_write_be16(struct cnh_iobuf *dest, uint16_t value)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + sizeof(uint16_t) > dest->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = (uint8_t) (value >> 8);
|
||||
dest->bytes[dest->pos++] = (uint8_t) value;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_write_be32(struct cnh_iobuf *dest, uint32_t value)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + sizeof(uint32_t) > dest->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = (uint8_t) (value >> 24);
|
||||
dest->bytes[dest->pos++] = (uint8_t) (value >> 16);
|
||||
dest->bytes[dest->pos++] = (uint8_t) (value >> 8);
|
||||
dest->bytes[dest->pos++] = (uint8_t) value;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_write_le16(struct cnh_iobuf *dest, uint16_t value)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + sizeof(uint16_t) > dest->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = (uint8_t) value;
|
||||
dest->bytes[dest->pos++] = (uint8_t) (value >> 8);
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iobuf_write_le32(struct cnh_iobuf *dest, uint32_t value)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + sizeof(uint32_t) > dest->nbytes) {
|
||||
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = (uint8_t) value;
|
||||
dest->bytes[dest->pos++] = (uint8_t) (value >> 8);
|
||||
dest->bytes[dest->pos++] = (uint8_t) (value >> 16);
|
||||
dest->bytes[dest->pos++] = (uint8_t) (value >> 24);
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Implementation of an I/O buffer used by hooking modules
|
||||
*
|
||||
* Based on original capnhook code for windows by decafcode:
|
||||
* https://github.com/decafcode/capnhook
|
||||
*/
|
||||
#ifndef CAPNHOOK_IOBUF_H
|
||||
#define CAPNHOOK_IOBUF_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "capnhook/hook/result.h"
|
||||
|
||||
/**
|
||||
* I/O buffer
|
||||
*/
|
||||
struct cnh_iobuf {
|
||||
uint8_t *bytes;
|
||||
size_t nbytes;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* I/O buffer
|
||||
*/
|
||||
struct cnh_const_iobuf {
|
||||
const uint8_t *bytes;
|
||||
size_t nbytes;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* Flip the I/O buffer. Set the current pos as size and reset the position.
|
||||
*
|
||||
* @param child Flipped I/O buffer
|
||||
* @param parent I/O buffer to flip
|
||||
*/
|
||||
void cnh_iobuf_flip(struct cnh_const_iobuf *child, struct cnh_iobuf *parent);
|
||||
|
||||
/**
|
||||
* Move the contents currently enclosed by pos and end of an I/O buffer to a destination.
|
||||
*
|
||||
* @param dest Destination buffer to move the source to, starting at current pos
|
||||
* @param src Source to move to the destination, starting at current pos up to end of the buffer
|
||||
* @return Number of bytes copied to destination
|
||||
*/
|
||||
size_t cnh_iobuf_move(struct cnh_iobuf *dest, struct cnh_const_iobuf *src);
|
||||
|
||||
size_t cnh_iobuf_shift(struct cnh_iobuf *dest, struct cnh_iobuf *src);
|
||||
|
||||
/**
|
||||
* Read data from an I/O buffer
|
||||
*
|
||||
* @param src Source to read from (starting at current position)
|
||||
* @param bytes Target to read to
|
||||
* @param nbytes Number of bytes to read
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_read(struct cnh_const_iobuf *src, void *bytes, size_t nbytes);
|
||||
|
||||
/**
|
||||
* Read a byte from an I/O buffer
|
||||
*
|
||||
* @param src Source to read from (starting at current position)
|
||||
* @param value Pointer to variable to read the data into
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_read_8(struct cnh_const_iobuf *src, uint8_t *value);
|
||||
|
||||
/**
|
||||
* Read a 2 byte value, big endian byte order, from an I/O buffer
|
||||
*
|
||||
* @param src Source to read from (starting at current position)
|
||||
* @param value Pointer to variable to read the data into
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_read_be16(struct cnh_const_iobuf *src, uint16_t *value);
|
||||
|
||||
/**
|
||||
* Read a 4 byte value, big endian byte order, from an I/O buffer
|
||||
*
|
||||
* @param src Source to read from (starting at current position)
|
||||
* @param value Pointer to variable to read the data into
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_read_be32(struct cnh_const_iobuf *src, uint32_t *value);
|
||||
|
||||
/**
|
||||
* Read a 2 byte value, little endian byte order, from an I/O buffer
|
||||
*
|
||||
* @param src Source to read from (starting at current position)
|
||||
* @param value Pointer to variable to read the data into
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_read_le16(struct cnh_const_iobuf *src, uint16_t *value);
|
||||
|
||||
/**
|
||||
* Read a 4 byte value, little endian byte order, from an I/O buffer
|
||||
*
|
||||
* @param src Source to read from (starting at current position)
|
||||
* @param value Pointer to variable to read the data into
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_read_le32(struct cnh_const_iobuf *src, uint32_t *value);
|
||||
|
||||
/**
|
||||
* Write data to an I/O buffer
|
||||
*
|
||||
* @param dest I/O buffer to write to (starting at current position)
|
||||
* @param bytes Pointer to data to write
|
||||
* @param nbytes Number of bytes to write
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_write(struct cnh_iobuf *dest, const void *bytes, size_t nbytes);
|
||||
|
||||
/**
|
||||
* Write a byte to an I/O buffer
|
||||
*
|
||||
* @param dest Destination I/O buffer to write to (starting at current position)
|
||||
* @param value Value to write
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_write_8(struct cnh_iobuf *dest, uint8_t value);
|
||||
|
||||
/**
|
||||
* Write a 2 byte value, big endian byte order, to an I/O buffer
|
||||
*
|
||||
* @param dest Destination I/O buffer to write to (starting at current position)
|
||||
* @param value Value to write
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_write_be16(struct cnh_iobuf *dest, uint16_t value);
|
||||
|
||||
/**
|
||||
* Write a 4 byte value, big endian byte order, to an I/O buffer
|
||||
*
|
||||
* @param dest Destination I/O buffer to write to (starting at current position)
|
||||
* @param value Value to write
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_write_be32(struct cnh_iobuf *dest, uint32_t value);
|
||||
|
||||
/**
|
||||
* Write a 2 byte value, little endian byte order, to an I/O buffer
|
||||
*
|
||||
* @param dest Destination I/O buffer to write to (starting at current position)
|
||||
* @param value Value to write
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_write_le16(struct cnh_iobuf *dest, uint16_t value);
|
||||
|
||||
/**
|
||||
* Write a 4 byte value, little endian byte order, to an I/O buffer
|
||||
*
|
||||
* @param dest Destination I/O buffer to write to (starting at current position)
|
||||
* @param value Value to write
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iobuf_write_le32(struct cnh_iobuf *dest, uint32_t value);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,552 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "capnhook/hook/common.h"
|
||||
#include "capnhook/hook/iohook.h"
|
||||
#include "capnhook/hook/lib.h"
|
||||
|
||||
#include "util/time.h"
|
||||
|
||||
#define INVALID_FILE_DEVICE (-1)
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Real funcs typedefs */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
typedef int (*cnh_iohook_open_t)(const char* path, int oflag);
|
||||
typedef FILE* (*cnh_iohook_fdopen_t)(int fd, const char* mode);
|
||||
typedef ssize_t (*cnh_iohook_write_t)(int fd, const void* buf, size_t count);
|
||||
typedef ssize_t (*cnh_iohook_read_t)(int fd, void* buf, size_t count);
|
||||
typedef off_t (*cnh_iohook_lseek_t)(int fildes, off_t offset, int whence);
|
||||
typedef int (*cnh_iohook_ioctl_t)(int fd, int request, void* data);
|
||||
typedef int (*cnh_iohook_close_t)(int fd);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private helpers */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_iohook_init(void);
|
||||
|
||||
static enum cnh_result _cnh_iohook_invoke_real(struct cnh_iohook_irp *irp);
|
||||
static enum cnh_result _cnh_iohook_invoke_real_open(struct cnh_iohook_irp *irp);
|
||||
static enum cnh_result _cnh_iohook_invoke_real_fdopen(struct cnh_iohook_irp *irp);
|
||||
static enum cnh_result _cnh_iohook_invoke_real_close(struct cnh_iohook_irp *irp);
|
||||
static enum cnh_result _cnh_iohook_invoke_real_read(struct cnh_iohook_irp *irp);
|
||||
static enum cnh_result _cnh_iohook_invoke_real_write(struct cnh_iohook_irp *irp);
|
||||
static enum cnh_result _cnh_iohook_invoke_real_seek(struct cnh_iohook_irp *irp);
|
||||
static enum cnh_result _cnh_iohook_invoke_real_ioctl(struct cnh_iohook_irp *irp);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private state */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static cnh_iohook_open_t _cnh_iohook_real_open;
|
||||
static cnh_iohook_fdopen_t _cnh_iohook_real_fdopen;
|
||||
static cnh_iohook_close_t _cnh_iohook_real_close;
|
||||
static cnh_iohook_read_t _cnh_iohook_real_read;
|
||||
static cnh_iohook_write_t _cnh_iohook_real_write;
|
||||
static cnh_iohook_lseek_t _cnh_iohook_real_lseek;
|
||||
static cnh_iohook_ioctl_t _cnh_iohook_real_ioctl;
|
||||
|
||||
static const cnh_iohook_fn_t _cnh_iohook_real_handlers[7] = {
|
||||
[CNH_IOHOOK_IRP_OP_OPEN] = _cnh_iohook_invoke_real_open,
|
||||
[CNH_IOHOOK_IRP_OP_FDOPEN] = _cnh_iohook_invoke_real_fdopen,
|
||||
[CNH_IOHOOK_IRP_OP_CLOSE] = _cnh_iohook_invoke_real_close,
|
||||
[CNH_IOHOOK_IRP_OP_READ] = _cnh_iohook_invoke_real_read,
|
||||
[CNH_IOHOOK_IRP_OP_WRITE] = _cnh_iohook_invoke_real_write,
|
||||
[CNH_IOHOOK_IRP_OP_SEEK] = _cnh_iohook_invoke_real_seek,
|
||||
[CNH_IOHOOK_IRP_OP_IOCTL] = _cnh_iohook_invoke_real_ioctl,
|
||||
};
|
||||
|
||||
static atomic_int _cnh_iohook_initted = ATOMIC_VAR_INIT(0);
|
||||
static atomic_int _cnh_iohook_init_in_progress = ATOMIC_VAR_INIT(0);
|
||||
static pthread_mutex_t _cnh_iohook_lock;
|
||||
static cnh_iohook_fn_t *_cnh_iohook_handlers;
|
||||
static size_t _cnh_iohook_nhandlers;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Public module functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
enum cnh_result cnh_iohook_push_handler(cnh_iohook_fn_t fn)
|
||||
{
|
||||
cnh_iohook_fn_t *new_array;
|
||||
size_t new_size;
|
||||
enum cnh_result result;
|
||||
|
||||
assert(fn != NULL);
|
||||
|
||||
_cnh_iohook_init();
|
||||
pthread_mutex_lock(&_cnh_iohook_lock);
|
||||
|
||||
new_size = _cnh_iohook_nhandlers + 1;
|
||||
new_array = realloc(_cnh_iohook_handlers, new_size * sizeof(cnh_iohook_fn_t));
|
||||
|
||||
if (new_array != NULL) {
|
||||
_cnh_iohook_handlers = new_array;
|
||||
_cnh_iohook_handlers[_cnh_iohook_nhandlers++] = fn;
|
||||
result = CNH_RESULT_SUCCESS;
|
||||
} else {
|
||||
result = CNH_RESULT_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_iohook_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_iohook_invoke_next(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
cnh_iohook_fn_t handler;
|
||||
enum cnh_result result;
|
||||
|
||||
assert(irp != NULL);
|
||||
assert(_cnh_iohook_initted > 0);
|
||||
|
||||
pthread_mutex_lock(&_cnh_iohook_lock);
|
||||
|
||||
assert(irp->next_handler <= _cnh_iohook_nhandlers);
|
||||
|
||||
if (irp->next_handler < _cnh_iohook_nhandlers) {
|
||||
handler = _cnh_iohook_handlers[irp->next_handler];
|
||||
irp->next_handler++;
|
||||
} else {
|
||||
handler = _cnh_iohook_invoke_real;
|
||||
irp->next_handler = (size_t) -1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_iohook_lock);
|
||||
|
||||
result = handler(irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
irp->next_handler = (size_t) -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int cnh_iohook_open_dummy_fd()
|
||||
{
|
||||
_cnh_iohook_init();
|
||||
|
||||
/* We need to return some sort of valid FD to the
|
||||
caller, since we're simulating a device that
|
||||
doesn't exist. Open an FD on /dev/null. */
|
||||
|
||||
/* Open read only */
|
||||
return _cnh_iohook_real_open("/dev/null", 0);
|
||||
}
|
||||
|
||||
void cnh_iohook_close_dummy_fd(int fd)
|
||||
{
|
||||
_cnh_iohook_init();
|
||||
|
||||
/* Avoid hooking pipeline when using dummy handles */
|
||||
_cnh_iohook_real_close(fd);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooked functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
int open(const char* path, int oflag)
|
||||
{
|
||||
struct cnh_iohook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_iohook_init();
|
||||
|
||||
if (path == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return INVALID_FILE_DEVICE;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_IOHOOK_IRP_OP_OPEN;
|
||||
irp.fd = INVALID_FILE_DEVICE;
|
||||
irp.open_filename = path;
|
||||
irp.open_flags = oflag;
|
||||
|
||||
result = cnh_iohook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return INVALID_FILE_DEVICE;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.fd;
|
||||
}
|
||||
|
||||
FILE* fdopen(int fd, const char* mode)
|
||||
{
|
||||
struct cnh_iohook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_iohook_init();
|
||||
|
||||
if (fd == -1 || mode == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_IOHOOK_IRP_OP_FDOPEN;
|
||||
irp.fdopen_fd = fd;
|
||||
irp.fdopen_mode = mode;
|
||||
irp.fdopen_res = NULL;
|
||||
|
||||
result = cnh_iohook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.fdopen_res;
|
||||
}
|
||||
|
||||
int close(int fd)
|
||||
{
|
||||
struct cnh_iohook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_iohook_init();
|
||||
|
||||
if (fd == INVALID_FILE_DEVICE) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_IOHOOK_IRP_OP_CLOSE;
|
||||
irp.fd = fd;
|
||||
|
||||
result = cnh_iohook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t read(int fd, void* buf, size_t count)
|
||||
{
|
||||
struct cnh_iohook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_iohook_init();
|
||||
|
||||
if (fd == INVALID_FILE_DEVICE || buf == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_IOHOOK_IRP_OP_READ;
|
||||
irp.fd = fd;
|
||||
irp.read.bytes = buf;
|
||||
irp.read.nbytes = count;
|
||||
irp.read.pos = 0;
|
||||
|
||||
result = cnh_iohook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(irp.read.pos <= irp.read.nbytes);
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.read.pos;
|
||||
}
|
||||
|
||||
ssize_t write(int fd, const void* buf, size_t count)
|
||||
{
|
||||
struct cnh_iohook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_iohook_init();
|
||||
|
||||
if (fd == INVALID_FILE_DEVICE || buf == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_IOHOOK_IRP_OP_WRITE;
|
||||
irp.fd = fd;
|
||||
irp.write.bytes = buf;
|
||||
irp.write.nbytes = count;
|
||||
irp.write.pos = 0;
|
||||
|
||||
result = cnh_iohook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(irp.write.pos <= irp.write.nbytes);
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.write.pos;
|
||||
}
|
||||
|
||||
off_t lseek(int fd, off_t offset, int whence)
|
||||
{
|
||||
struct cnh_iohook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_iohook_init();
|
||||
|
||||
if (fd == INVALID_FILE_DEVICE) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_IOHOOK_IRP_OP_SEEK;
|
||||
irp.fd = fd;
|
||||
irp.seek_origin = whence;
|
||||
irp.seek_offset = offset;
|
||||
|
||||
result = cnh_iohook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return (off_t) irp.seek_pos;
|
||||
}
|
||||
|
||||
int ioctl(int fd, int request, void* data)
|
||||
{
|
||||
struct cnh_iohook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_iohook_init();
|
||||
|
||||
if (fd == INVALID_FILE_DEVICE) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_IOHOOK_IRP_OP_IOCTL;
|
||||
irp.fd = fd;
|
||||
irp.ioctl_req = request;
|
||||
irp.ioctl.bytes = data;
|
||||
|
||||
result = cnh_iohook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return irp.ioctl.pos;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Helper functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_iohook_init(void)
|
||||
{
|
||||
int expected;
|
||||
|
||||
if (atomic_load(&_cnh_iohook_initted) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
expected = 0;
|
||||
|
||||
if (!atomic_compare_exchange_strong(&_cnh_iohook_init_in_progress, &expected, 1)) {
|
||||
while (atomic_load(&_cnh_iohook_init_in_progress) > 1) {
|
||||
util_time_sleep_us(100);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
|
||||
if (atomic_load(&_cnh_iohook_initted) > 0) {
|
||||
/* Revert init in progress, because nothing to do anymore */
|
||||
atomic_store(&_cnh_iohook_init_in_progress, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_cnh_iohook_real_open = (cnh_iohook_open_t) cnh_lib_get_func_addr("open");
|
||||
_cnh_iohook_real_fdopen = (cnh_iohook_fdopen_t) cnh_lib_get_func_addr("fdopen");
|
||||
_cnh_iohook_real_close = (cnh_iohook_close_t) cnh_lib_get_func_addr("close");
|
||||
_cnh_iohook_real_read = (cnh_iohook_read_t) cnh_lib_get_func_addr("read");
|
||||
_cnh_iohook_real_write = (cnh_iohook_write_t) cnh_lib_get_func_addr("write");
|
||||
_cnh_iohook_real_lseek = (cnh_iohook_lseek_t) cnh_lib_get_func_addr("lseek");
|
||||
_cnh_iohook_real_ioctl = (cnh_iohook_ioctl_t) cnh_lib_get_func_addr("ioctl");
|
||||
|
||||
pthread_mutex_init(&_cnh_iohook_lock, NULL);
|
||||
|
||||
atomic_store(&_cnh_iohook_initted, 1);
|
||||
atomic_store(&_cnh_iohook_init_in_progress, 0);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_iohook_invoke_real(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
cnh_iohook_fn_t handler;
|
||||
|
||||
assert(irp != NULL);
|
||||
assert(irp->op < _countof(_cnh_iohook_real_handlers));
|
||||
|
||||
handler = _cnh_iohook_real_handlers[irp->op];
|
||||
|
||||
assert(handler != NULL);
|
||||
|
||||
return handler(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_iohook_invoke_real_open(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
int fd;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
fd = _cnh_iohook_real_open(irp->open_filename, irp->open_flags);
|
||||
|
||||
if (fd == INVALID_FILE_DEVICE) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->fd = fd;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_iohook_invoke_real_fdopen(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
FILE* file;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
file = _cnh_iohook_real_fdopen(irp->fdopen_fd, irp->fdopen_mode);
|
||||
|
||||
if (file == NULL) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->fdopen_res = file;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_iohook_invoke_real_close(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
int ok;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
ok = _cnh_iohook_real_close(irp->fd);
|
||||
|
||||
if (ok < 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_iohook_invoke_real_read(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
ssize_t read;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
read = _cnh_iohook_real_read(irp->fd, &irp->read.bytes[irp->read.pos], irp->read.nbytes - irp->read.pos);
|
||||
|
||||
if (read < 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->read.pos += read;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_iohook_invoke_real_write(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
ssize_t written;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
written = _cnh_iohook_real_write(irp->fd, &irp->write.bytes[irp->write.pos], irp->write.nbytes - irp->write.pos);
|
||||
|
||||
if (written < 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->write.pos += written;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_iohook_invoke_real_seek(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
off_t result;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
result = _cnh_iohook_real_lseek(irp->fd, (off_t) irp->seek_offset, irp->seek_origin);
|
||||
|
||||
if (result < 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->seek_pos = result;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_iohook_invoke_real_ioctl(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
int result;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
result = _cnh_iohook_real_ioctl(irp->fd, irp->ioctl_req, irp->ioctl.bytes);
|
||||
|
||||
if (result < 0) {
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->ioctl.pos = (size_t) result;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Hook module for hooking I/O related calls, e.g. open, read, write etc.
|
||||
*
|
||||
* Based on original capnhook code for windows by decafcode:
|
||||
* https://github.com/decafcode/capnhook
|
||||
*/
|
||||
#ifndef CAPNHOOK_IOHOOK_H
|
||||
#define CAPNHOOK_IOHOOK_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "capnhook/hook/iobuf.h"
|
||||
|
||||
/**
|
||||
* Available operations to hook
|
||||
*/
|
||||
enum cnh_iohook_irp_op {
|
||||
CNH_IOHOOK_IRP_OP_OPEN = 0,
|
||||
CNH_IOHOOK_IRP_OP_FDOPEN = 1,
|
||||
CNH_IOHOOK_IRP_OP_CLOSE = 2,
|
||||
CNH_IOHOOK_IRP_OP_READ = 3,
|
||||
CNH_IOHOOK_IRP_OP_WRITE = 4,
|
||||
CNH_IOHOOK_IRP_OP_SEEK = 5,
|
||||
CNH_IOHOOK_IRP_OP_IOCTL = 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* I/O request packet
|
||||
*/
|
||||
struct cnh_iohook_irp {
|
||||
enum cnh_iohook_irp_op op;
|
||||
size_t next_handler;
|
||||
int fd;
|
||||
const char *open_filename;
|
||||
int open_flags;
|
||||
int fdopen_fd;
|
||||
const char* fdopen_mode;
|
||||
FILE* fdopen_res;
|
||||
struct cnh_iobuf read;
|
||||
struct cnh_const_iobuf write;
|
||||
int seek_origin;
|
||||
int64_t seek_offset;
|
||||
uint64_t seek_pos;
|
||||
int ioctl_req;
|
||||
struct cnh_iobuf ioctl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook function type
|
||||
*/
|
||||
typedef enum cnh_result (*cnh_iohook_fn_t)(struct cnh_iohook_irp *irp);
|
||||
|
||||
/**
|
||||
* Add a new hook handler to the hook module.
|
||||
* The handler added is getting called on every hooked operation.
|
||||
*
|
||||
* @param fn Pointer to hook function to add
|
||||
* @return Result/error code if hooking was successful
|
||||
*/
|
||||
enum cnh_result cnh_iohook_push_handler(cnh_iohook_fn_t fn);
|
||||
|
||||
/**
|
||||
* Invoke the next hooked function registered in the hook module.
|
||||
* Call this from your hook handler if you want to pass on execution to further
|
||||
* hooked calls reaching the original function at the end of the hook chain.
|
||||
*
|
||||
* @param irp I/O request package to dispatch
|
||||
* @return Result of the following function dispatching the request package
|
||||
*/
|
||||
enum cnh_result cnh_iohook_invoke_next(struct cnh_iohook_irp *irp);
|
||||
|
||||
/**
|
||||
* Create a dummy file handle. Use this function for file access virtualization.
|
||||
* Do not execute real operations on the handle returned.
|
||||
*
|
||||
* @return Actual file handle instance pointing to a dummy endpoint.
|
||||
*/
|
||||
int cnh_iohook_open_dummy_fd();
|
||||
|
||||
/**
|
||||
* Close a dummy file handle. Make sure to free your previously allocated handles
|
||||
* to avoid resource leaks. Do not free them using the real close function.
|
||||
*
|
||||
* @param file Dummy file handle to free
|
||||
*/
|
||||
void cnh_iohook_close_dummy_fd(int fd);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Provides a set of macros to hook into an application right before its
|
||||
* main function is called.
|
||||
*/
|
||||
#ifndef CNH_LIB_MAIN_H
|
||||
#define CNH_LIB_MAIN_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#define LIB_MAIN_CONSTRUCTOR(func) __attribute__((constructor)) void lib_constructor() { func(); }
|
||||
|
||||
#define LIB_MAIN_DESTRUCTOR(func) __attribute__((destructor)) void lib_destructor() { func(); }
|
||||
|
||||
#define LIB_MAIN_TRAP_MAIN(func_before_main, func_after_main) \
|
||||
typedef int (*func_libc_start_main_t)(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end)); \
|
||||
typedef int* (*func_main_t)(int, char**, char**); \
|
||||
typedef void (*func_init_t)(void); \
|
||||
static func_main_t orig_main; \
|
||||
static func_init_t orig_init; \
|
||||
static bool trapped; \
|
||||
\
|
||||
int* trap_main(int argc, char** argv, char** envp) \
|
||||
{ \
|
||||
log_debug("Trap before main (%s): argc %d", argv[0], argc); \
|
||||
func_before_main(argc, argv); \
|
||||
log_debug("Calling orig_init"); \
|
||||
orig_init(); \
|
||||
log_debug("Calling real main (%s): argc %d", argv[0], argc); \
|
||||
int* ret = orig_main(argc, argv, envp); \
|
||||
log_debug("Calling cleanup after main (%s): ret %d", argv[0], ret); \
|
||||
func_after_main(); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
void dummy_init(void) {} \
|
||||
\
|
||||
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end)) \
|
||||
{ \
|
||||
func_libc_start_main_t start = (func_libc_start_main_t) dlsym(RTLD_NEXT, "__libc_start_main"); \
|
||||
if (start == NULL) { \
|
||||
printf("ERROR finding orig func __libc_start_main\n"); \
|
||||
} \
|
||||
orig_main = main; \
|
||||
main = trap_main; \
|
||||
orig_init = init; \
|
||||
init = dummy_init; \
|
||||
trapped = true; \
|
||||
return (*start)(main, argc, ubp_av, init, fini, rtld_fini, stack_end); \
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,179 @@
|
||||
#define LOG_MODULE "hook-lib"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "capnhook/hook/lib.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/rand.h"
|
||||
|
||||
typedef void* (*cnh_lib_load_t)(const char* lib);
|
||||
typedef void* (*cnh_lib_get_func_addr_handle_t)(void* lib_handle, const char* func_name);
|
||||
typedef void (*cnh_lib_unload_t)(void* handle);
|
||||
typedef void* (*cnh_lib_get_func_addr_t)(const char* func_name);
|
||||
|
||||
static void* _cnh_lib_load(const char* lib);
|
||||
static void* _cnh_lib_get_func_addr_handle(void* lib_handle, const char* func_name);
|
||||
static void _cnh_lib_unload(void* handle);
|
||||
static void* _cnh_lib_get_func_addr(const char* func_name);
|
||||
|
||||
static void* _cnh_lib_load_test(const char* lib);
|
||||
static void* _cnh_lib_get_func_addr_handle_test(void* lib_handle, const char* func_name);
|
||||
static void _cnh_lib_unload_test(void* handle);
|
||||
static void* _cnh_lib_get_func_addr_test(const char* func_name);
|
||||
|
||||
struct cnh_lib_interface {
|
||||
cnh_lib_load_t load;
|
||||
cnh_lib_get_func_addr_handle_t get_func_addr_handle;
|
||||
cnh_lib_unload_t unload;
|
||||
cnh_lib_get_func_addr_t get_func_addr;
|
||||
};
|
||||
|
||||
static struct cnh_lib_interface _cnh_lib_interface = {
|
||||
.load = _cnh_lib_load,
|
||||
.get_func_addr_handle = _cnh_lib_get_func_addr_handle,
|
||||
.unload = _cnh_lib_unload,
|
||||
.get_func_addr = _cnh_lib_get_func_addr,
|
||||
};
|
||||
|
||||
static struct cnh_lib_unit_test_func_mocks* _cnh_lib_func_mocks;
|
||||
size_t _cnh_lib_func_mocks_cnt;
|
||||
|
||||
static void* _cnh_lib_load(const char* lib)
|
||||
{
|
||||
void* so = dlopen(lib, RTLD_NOW);
|
||||
|
||||
if (so == NULL) {
|
||||
log_warn("Failed to open library %s: %s", lib, dlerror());
|
||||
} else {
|
||||
log_debug("Opened lib %s, handle %p", lib, so);
|
||||
}
|
||||
|
||||
return so;
|
||||
}
|
||||
|
||||
static void* _cnh_lib_get_func_addr_handle(void* lib_handle, const char* func_name)
|
||||
{
|
||||
void* ret = dlsym(lib_handle, func_name);
|
||||
|
||||
if (ret == NULL) {
|
||||
log_warn("Could not find func %s in library %p", func_name, lib_handle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _cnh_lib_unload(void* handle)
|
||||
{
|
||||
dlclose(handle);
|
||||
log_debug("Closed lib handle %p", handle);
|
||||
}
|
||||
|
||||
static void* _cnh_lib_get_func_addr(const char* func_name)
|
||||
{
|
||||
void* ret = dlsym(RTLD_NEXT, func_name);
|
||||
|
||||
if (ret == NULL) {
|
||||
log_warn("Could not find func %s", func_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void* _cnh_lib_load_test(const char* lib)
|
||||
{
|
||||
log_warn("Loading lib %s not supported in test", lib);
|
||||
|
||||
if (sizeof(void*) == 4) {
|
||||
return (void*) util_rand_gen_32();
|
||||
} else {
|
||||
log_die_illegal_state();
|
||||
}
|
||||
}
|
||||
|
||||
static void* _cnh_lib_get_func_addr_handle_test(void* lib_handle, const char* func_name)
|
||||
{
|
||||
return _cnh_lib_get_func_addr_test(func_name);
|
||||
}
|
||||
|
||||
static void _cnh_lib_unload_test(void* handle)
|
||||
{
|
||||
log_warn("Unloading lib %p not supported in test", handle);
|
||||
}
|
||||
|
||||
static void* _cnh_lib_get_func_addr_test(const char* func_name)
|
||||
{
|
||||
for (size_t i = 0; i < _cnh_lib_func_mocks_cnt; i++) {
|
||||
if (!strcmp(_cnh_lib_func_mocks[i].name, func_name)) {
|
||||
return _cnh_lib_func_mocks->func;
|
||||
}
|
||||
}
|
||||
|
||||
log_warn("Could not find func %s", func_name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct cnh_lib_unit_test_func_mocks* cnh_lib_allocate_func_mocks(size_t count)
|
||||
{
|
||||
log_assert(count >= 0);
|
||||
|
||||
struct cnh_lib_unit_test_func_mocks* func_mocks;
|
||||
|
||||
func_mocks = util_xmalloc(sizeof(struct cnh_lib_unit_test_func_mocks) * count);
|
||||
|
||||
memset(func_mocks, 0, sizeof(struct cnh_lib_unit_test_func_mocks) * count);
|
||||
|
||||
return func_mocks;
|
||||
}
|
||||
|
||||
void cnh_lib_init_unit_test(struct cnh_lib_unit_test_func_mocks* func_mocks, size_t func_mocks_cnt)
|
||||
{
|
||||
log_warn("Setting up %d unit test func mocks", func_mocks_cnt);
|
||||
|
||||
_cnh_lib_func_mocks = func_mocks;
|
||||
_cnh_lib_func_mocks_cnt = func_mocks_cnt;
|
||||
|
||||
_cnh_lib_interface.load = _cnh_lib_load_test;
|
||||
_cnh_lib_interface.get_func_addr_handle = _cnh_lib_get_func_addr_handle_test;
|
||||
_cnh_lib_interface.unload = _cnh_lib_unload_test;
|
||||
_cnh_lib_interface.get_func_addr = _cnh_lib_get_func_addr_test;
|
||||
}
|
||||
|
||||
void cnh_lib_init()
|
||||
{
|
||||
log_warn("Setting up real interface");
|
||||
|
||||
_cnh_lib_interface.load = _cnh_lib_load;
|
||||
_cnh_lib_interface.get_func_addr_handle = _cnh_lib_get_func_addr_handle;
|
||||
_cnh_lib_interface.unload = _cnh_lib_unload;
|
||||
_cnh_lib_interface.get_func_addr = _cnh_lib_get_func_addr;
|
||||
}
|
||||
|
||||
void cnh_lib_shutdown_unit_test()
|
||||
{
|
||||
log_assert(_cnh_lib_func_mocks);
|
||||
|
||||
util_xfree((void**) &_cnh_lib_func_mocks);
|
||||
}
|
||||
|
||||
void* cnh_lib_load(const char* lib)
|
||||
{
|
||||
return _cnh_lib_interface.load(lib);
|
||||
}
|
||||
|
||||
void* cnh_lib_get_func_addr_handle(void* lib_handle, const char* func_name)
|
||||
{
|
||||
return _cnh_lib_interface.get_func_addr_handle(lib_handle, func_name);
|
||||
}
|
||||
|
||||
void cnh_lib_unload(void* handle)
|
||||
{
|
||||
_cnh_lib_interface.unload(handle);
|
||||
}
|
||||
|
||||
void* cnh_lib_get_func_addr(const char* func_name)
|
||||
{
|
||||
return _cnh_lib_interface.get_func_addr(func_name);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Various functions related to library hooking
|
||||
*/
|
||||
#ifndef CAPNHOOK_LIB_H
|
||||
#define CAPNHOOK_LIB_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Provide a mock function key'd by it's name and pointing to your actual mock implementation.
|
||||
*/
|
||||
struct cnh_lib_unit_test_func_mocks {
|
||||
const char* name;
|
||||
void* func;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate memory for a list of mock functions to pass to cnh_lib_init_unit_test.
|
||||
*
|
||||
* @param count The size of the list.
|
||||
* @return Allocated memory with the specified number of entries.
|
||||
*/
|
||||
struct cnh_lib_unit_test_func_mocks* cnh_lib_allocate_func_mocks(size_t count);
|
||||
|
||||
/**
|
||||
* Initialize this module when using it in a unit-test. This allows you to add function mocks that are returned on the
|
||||
* various calls to get function pointers from real library functions. Use this to mock them to test various patching
|
||||
* modules.
|
||||
*
|
||||
* Use the cnh_lib_allocate_func_mocks function to allocate a list that you can fill in.
|
||||
*
|
||||
* @param func_mocks A list of functions mocks with function names and pointers to the test mocks you set up. Ensure
|
||||
* that the function signatures match, otherwise things might crash when getting called. The module
|
||||
* will cleanup the memory allocated of this for you.
|
||||
* @param func_mocks_cnt The length of the list of func_mocks.
|
||||
*/
|
||||
void cnh_lib_init_unit_test(struct cnh_lib_unit_test_func_mocks* func_mocks, size_t func_mocks_cnt);
|
||||
|
||||
/**
|
||||
* Optional, used for unit testing. Switch to the default backend that utilizes loading real libraries and getting real
|
||||
* function pointers from them. This is enabled by default.
|
||||
*/
|
||||
void cnh_lib_init();
|
||||
|
||||
/**
|
||||
* Cleanup any memory allocated/used on cnh_lib_init_unit_test.
|
||||
*/
|
||||
void cnh_lib_shutdown_unit_test();
|
||||
|
||||
/**
|
||||
* Load a dynamic library
|
||||
*
|
||||
* @param lib Path to library file to load
|
||||
* @return Pointer to a handle on success, NULL on failure
|
||||
*/
|
||||
void* cnh_lib_load(const char* lib);
|
||||
|
||||
/**
|
||||
* Get the address of a function from a library
|
||||
*
|
||||
* @param lib_handle Handle of an opened library
|
||||
* @param func_name Name of the function to get
|
||||
* @return Function ptr on success, false on error (function not found)
|
||||
*/
|
||||
void*cnh_lib_get_func_addr_handle(void* lib_handle, const char* func_name);
|
||||
|
||||
/**
|
||||
* Unload a loaded dynamic library
|
||||
*
|
||||
* @param handle Handle of the loaded lib to unload
|
||||
*/
|
||||
void cnh_lib_unload(void* handle);
|
||||
|
||||
/**
|
||||
* Get the adress of a function (from a dynamic library)
|
||||
*
|
||||
* Note: This will return the first function found matching the specified name
|
||||
*
|
||||
* @param func_name Name of the function
|
||||
* @return Valid ptr to function on success, false on error (not found)
|
||||
*/
|
||||
void* cnh_lib_get_func_addr(const char* func_name);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
#define LOG_MODULE "capnhook"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "capnhook/hook/result.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
int cnh_result_to_errno(enum cnh_result result)
|
||||
{
|
||||
switch (result) {
|
||||
case CNH_RESULT_SUCCESS:
|
||||
return 0;
|
||||
case CNH_RESULT_ERROR_INSUFFICIENT_BUFFER:
|
||||
return EIO;
|
||||
case CNH_RESULT_INVALID_PARAMETER:
|
||||
return EINVAL;
|
||||
case CNH_RESULT_OUT_OF_MEMORY:
|
||||
return ENOMEM;
|
||||
case CNH_RESULT_REPEAT_OPERATION:
|
||||
return EAGAIN;
|
||||
case CNH_RESULT_NO_SUCH_FILE_OR_DIR:
|
||||
return ENOENT;
|
||||
case CNH_RESULT_NOT_TTY:
|
||||
return ENOTTY;
|
||||
case CNH_RESULT_BUSY:
|
||||
return EBUSY;
|
||||
case CNH_RESULT_IS_A_DIRECTORY:
|
||||
return EISDIR;
|
||||
case CNH_RESULT_PERMISSION_DENIED:
|
||||
return EACCES;
|
||||
case CNH_RESULT_DIRECTORY_NOT_EMPTY:
|
||||
return ENOTEMPTY;
|
||||
case CNH_RESULT_OPERATION_NOT_PERMITTED:
|
||||
return EPERM;
|
||||
case CNH_RESULT_NO_SUCH_DEVICE:
|
||||
return ENODEV;
|
||||
case CNH_RESULT_BAD_FILE_NUMBER:
|
||||
return EBADF;
|
||||
case CNH_RESULT_FILE_DESCRIPTOR_IN_BAD_STATE:
|
||||
return EBADFD;
|
||||
case CNH_RESULT_VALUE_TOO_LARGE_FOR_DEFINED_DATATYPE:
|
||||
return EOVERFLOW;
|
||||
case CNH_RESULT_NO_SUCH_DEVICE_OR_ADDRESS:
|
||||
return ENXIO;
|
||||
case CNH_RESULT_BROKEN_PIPE:
|
||||
return EPIPE;
|
||||
case CNH_RESULT_TIMER_EXPIRED:
|
||||
return ETIME;
|
||||
default:
|
||||
log_warn("Unhandled other error %d to errno convert might cause bugs", result);
|
||||
return EIO;
|
||||
}
|
||||
}
|
||||
|
||||
enum cnh_result cnh_errno_to_result(int errn)
|
||||
{
|
||||
switch (errn) {
|
||||
case 0:
|
||||
return CNH_RESULT_SUCCESS;
|
||||
case EINVAL:
|
||||
return CNH_RESULT_INVALID_PARAMETER;
|
||||
case ENOMEM:
|
||||
return CNH_RESULT_OUT_OF_MEMORY;
|
||||
case EAGAIN:
|
||||
return CNH_RESULT_REPEAT_OPERATION;
|
||||
case ENOENT:
|
||||
return CNH_RESULT_NO_SUCH_FILE_OR_DIR;
|
||||
case ENOTTY:
|
||||
return CNH_RESULT_NOT_TTY;
|
||||
case EBUSY:
|
||||
return CNH_RESULT_BUSY;
|
||||
case EISDIR:
|
||||
return CNH_RESULT_IS_A_DIRECTORY;
|
||||
case EACCES:
|
||||
return CNH_RESULT_PERMISSION_DENIED;
|
||||
case ENOTEMPTY:
|
||||
return CNH_RESULT_DIRECTORY_NOT_EMPTY;
|
||||
case EPERM:
|
||||
return CNH_RESULT_OPERATION_NOT_PERMITTED;
|
||||
case ENODEV:
|
||||
return CNH_RESULT_NO_SUCH_DEVICE;
|
||||
case EBADF:
|
||||
return CNH_RESULT_BAD_FILE_NUMBER;
|
||||
case EBADFD:
|
||||
return CNH_RESULT_FILE_DESCRIPTOR_IN_BAD_STATE;
|
||||
case EOVERFLOW:
|
||||
return CNH_RESULT_VALUE_TOO_LARGE_FOR_DEFINED_DATATYPE;
|
||||
case ENXIO:
|
||||
return CNH_RESULT_NO_SUCH_DEVICE_OR_ADDRESS;
|
||||
case EPIPE:
|
||||
return CNH_RESULT_BROKEN_PIPE;
|
||||
case ETIME:
|
||||
return CNH_RESULT_TIMER_EXPIRED;
|
||||
default:
|
||||
log_warn("Unhandled errno to general error convert might cause bugs, errno: %d", errn);
|
||||
return CNH_RESULT_OTHER_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Results for returning errors on hook modules, converting them to errno and vice versa
|
||||
*
|
||||
* Based on original capnhook code for windows by decafcode:
|
||||
* https://github.com/decafcode/capnhook
|
||||
*/
|
||||
#ifndef CAPNHOOK_RESULT_H
|
||||
#define CAPNHOOK_RESULT_H
|
||||
|
||||
/**
|
||||
* Result values for hook functions
|
||||
*/
|
||||
enum cnh_result {
|
||||
CNH_RESULT_SUCCESS = 0,
|
||||
CNH_RESULT_ERROR_INSUFFICIENT_BUFFER = 1,
|
||||
CNH_RESULT_INVALID_PARAMETER = 2,
|
||||
CNH_RESULT_OUT_OF_MEMORY = 3,
|
||||
CNH_RESULT_REPEAT_OPERATION = 4,
|
||||
CNH_RESULT_NO_SUCH_FILE_OR_DIR = 5,
|
||||
CNH_RESULT_OTHER_ERROR = 6,
|
||||
CNH_RESULT_NOT_TTY = 7,
|
||||
CNH_RESULT_BUSY = 8,
|
||||
CNH_RESULT_IS_A_DIRECTORY = 9,
|
||||
CNH_RESULT_PERMISSION_DENIED = 10,
|
||||
CNH_RESULT_DIRECTORY_NOT_EMPTY = 11,
|
||||
CNH_RESULT_OPERATION_NOT_PERMITTED = 12,
|
||||
CNH_RESULT_NO_SUCH_DEVICE = 13,
|
||||
CNH_RESULT_BAD_FILE_NUMBER = 14,
|
||||
CNH_RESULT_FILE_DESCRIPTOR_IN_BAD_STATE = 15,
|
||||
CNH_RESULT_VALUE_TOO_LARGE_FOR_DEFINED_DATATYPE = 16,
|
||||
CNH_RESULT_NO_SUCH_DEVICE_OR_ADDRESS = 17,
|
||||
CNH_RESULT_BROKEN_PIPE = 18,
|
||||
CNH_RESULT_TIMER_EXPIRED = 19,
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a hook function result to errno
|
||||
*
|
||||
* @param result Result to convert
|
||||
* @return errno value
|
||||
*/
|
||||
int cnh_result_to_errno(enum cnh_result result);
|
||||
|
||||
/**
|
||||
* Convert an errno value to a hook function result
|
||||
*
|
||||
* @param errn errno value to convert
|
||||
* @return Hook function result
|
||||
*/
|
||||
enum cnh_result cnh_errno_to_result(int errn);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
#define LOG_MODULE "cnh-sig"
|
||||
|
||||
#include "capnhook/hook/lib.h"
|
||||
#include "capnhook/hook/sig.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
typedef int (*cnh_sig_handler_sigaction_t) (int sig, const struct sigaction* act, struct sigaction* oact);
|
||||
|
||||
static void _cnh_signal_handler(int sig);
|
||||
|
||||
static cnh_sig_handler_sigaction_t _cnh_sig_handler_real_sigaction;
|
||||
|
||||
static cnh_sig_handler_t _cnh_signal_handlers[32];
|
||||
static __sighandler_t _cnh_signal_orig_handlers[32];
|
||||
|
||||
void cnh_sig_install(int sig, cnh_sig_handler_t handler)
|
||||
{
|
||||
if (sig < 0 || sig >= 32) {
|
||||
log_die("Can't register handler for invalid signal number %d", sig);
|
||||
}
|
||||
|
||||
/* Ensure that the application does not install it's own handler */
|
||||
_cnh_signal_handlers[sig] = handler;
|
||||
|
||||
/* Install a little detour as our signal handler */
|
||||
signal(sig, _cnh_signal_handler);
|
||||
|
||||
log_info("Installed signal handler %p for %d", handler, sig);
|
||||
}
|
||||
|
||||
int sigaction(int sig, const struct sigaction* act, struct sigaction* oact)
|
||||
{
|
||||
if (_cnh_signal_handlers[sig]) {
|
||||
log_info("Enforce own signal handler on signal: %d", sig);
|
||||
|
||||
/* save the original handler, we might need it */
|
||||
_cnh_signal_orig_handlers[sig] = ((struct sigaction*) act)->sa_handler;
|
||||
|
||||
/* swap */
|
||||
((struct sigaction*) act)->sa_handler = (__sighandler_t) _cnh_signal_handler;
|
||||
|
||||
log_debug("Original handler %p for signal %d", _cnh_signal_orig_handlers[sig], sig);
|
||||
}
|
||||
|
||||
if (!_cnh_sig_handler_real_sigaction) {
|
||||
_cnh_sig_handler_real_sigaction = (cnh_sig_handler_sigaction_t) cnh_lib_get_func_addr("sigaction");
|
||||
}
|
||||
|
||||
return _cnh_sig_handler_real_sigaction(sig, act, oact);
|
||||
}
|
||||
|
||||
static void _cnh_signal_handler(int sig)
|
||||
{
|
||||
if (_cnh_signal_handlers[sig]) {
|
||||
_cnh_signal_handlers[sig](sig, _cnh_signal_orig_handlers[sig]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Hook and intercept signal handlers
|
||||
*/
|
||||
#ifndef CAPNHOOK_SIG_H
|
||||
#define CAPNHOOK_SIG_H
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
/**
|
||||
* Special callback for signal allowing access to a signal handler installed
|
||||
* by the application
|
||||
*
|
||||
* @param signal Signal to handle
|
||||
* @param orig_handler Original handler of application or NULL if non exists
|
||||
*/
|
||||
typedef void (*cnh_sig_handler_t)(int signal, __sighandler_t orig_handler);
|
||||
|
||||
/**
|
||||
* Install our own signal handler (and possibly override existing handlers)
|
||||
*
|
||||
* @param sig Signal to handle
|
||||
* @param handler Handler to install for specified signal
|
||||
*/
|
||||
void cnh_sig_install(int sig, cnh_sig_handler_t handler);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,624 @@
|
||||
#define LOG_MODULE "cnh-usbhook"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <string.h>
|
||||
#include <usb.h>
|
||||
|
||||
#include "capnhook/hook/common.h"
|
||||
#include "capnhook/hook/lib.h"
|
||||
#include "capnhook/hook/usbhook.h"
|
||||
|
||||
#include "util/time.h"
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Real funcs typedefs */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
typedef int (*cnh_usbhook_usb_init_t)(void);
|
||||
typedef int (*cnh_usbhook_usb_find_devices_t)(void);
|
||||
typedef int (*cnh_usbhook_usb_find_busses_t)(void);
|
||||
typedef usb_dev_handle* (*cnh_usbhook_usb_open_t)(struct usb_device* dev);
|
||||
typedef int (*cnh_usbhook_usb_close_t)(usb_dev_handle* dev);
|
||||
typedef int (*cnh_usbhook_usb_reset_t)(usb_dev_handle* dev);
|
||||
typedef int (*cnh_usbhook_usb_set_altinterface_t)(usb_dev_handle* dev, int interface);
|
||||
typedef int (*cnh_usbhook_usb_set_configuration_t)(usb_dev_handle* dev, int configuration);
|
||||
typedef int (*cnh_usbhook_usb_claim_interface_t)(usb_dev_handle* dev, int interface);
|
||||
typedef int (*cnh_usbhook_usb_control_msg_t)(usb_dev_handle* dev, int requesttype, int request, int value, int index,
|
||||
char* bytes, int nbytes, int timeout);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private helpers */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_usbhook_init(void);
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_init(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_find_busses(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_find_devices(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_open(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_close(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_reset(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_set_altinterface(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_set_configuration(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_claim_interface(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_ctrl_msg(struct cnh_usbhook_irp* irp);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private state */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static cnh_usbhook_usb_init_t _cnh_usbhook_real_init;
|
||||
static cnh_usbhook_usb_find_devices_t _cnh_usbhook_real_find_devices;
|
||||
static cnh_usbhook_usb_find_busses_t _cnh_usbhook_real_find_busses;
|
||||
static cnh_usbhook_usb_open_t _cnh_usbhook_real_open;
|
||||
static cnh_usbhook_usb_close_t _cnh_usbhook_real_close;
|
||||
static cnh_usbhook_usb_reset_t _cnh_usbhook_real_reset;
|
||||
static cnh_usbhook_usb_set_altinterface_t _cnh_usbhook_real_set_altinterface;
|
||||
static cnh_usbhook_usb_set_configuration_t _cnh_usbhook_real_set_configuration;
|
||||
static cnh_usbhook_usb_claim_interface_t _cnh_usbhook_real_claim_interface;
|
||||
static cnh_usbhook_usb_control_msg_t _cnh_usbhook_real_control_msg;
|
||||
|
||||
static const cnh_usbhook_fn_t _cnh_usbhook_real_handlers[10] = {
|
||||
[CNH_USBHOOK_IRP_OP_INIT] = _cnh_usbhook_invoke_real_init,
|
||||
[CNH_USBHOOK_IRP_OP_FIND_BUSSES ]= _cnh_usbhook_invoke_real_find_busses,
|
||||
[CNH_USBHOOK_IRP_OP_FIND_DEVICES] = _cnh_usbhook_invoke_real_find_devices,
|
||||
[CNH_USBHOOK_IRP_OP_OPEN] = _cnh_usbhook_invoke_real_open,
|
||||
[CNH_USBHOOK_IRP_OP_CLOSE] = _cnh_usbhook_invoke_real_close,
|
||||
[CNH_USBHOOK_IRP_OP_RESET] = _cnh_usbhook_invoke_real_reset,
|
||||
[CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE] = _cnh_usbhook_invoke_real_set_altinterface,
|
||||
[CNH_USBHOOK_IRP_OP_SET_CONFIGURATION] = _cnh_usbhook_invoke_real_set_configuration,
|
||||
[CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE] = _cnh_usbhook_invoke_real_claim_interface,
|
||||
[CNH_USBHOOK_IRP_OP_CTRL_MSG] = _cnh_usbhook_invoke_real_ctrl_msg,
|
||||
};
|
||||
|
||||
static atomic_int _cnh_usbhook_initted = ATOMIC_VAR_INIT(0);
|
||||
static atomic_int _cnh_usbhook_init_in_progress = ATOMIC_VAR_INIT(0);
|
||||
static pthread_mutex_t _cnh_usbhook_lock;
|
||||
static cnh_usbhook_fn_t *_cnh_usbhook_handlers;
|
||||
static size_t _cnh_usbhook_nhandlers;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Public module functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
enum cnh_result cnh_usbhook_push_handler(cnh_usbhook_fn_t fn)
|
||||
{
|
||||
cnh_usbhook_fn_t *new_array;
|
||||
size_t new_size;
|
||||
enum cnh_result result;
|
||||
|
||||
assert(fn != NULL);
|
||||
|
||||
_cnh_usbhook_init();
|
||||
pthread_mutex_lock(&_cnh_usbhook_lock);
|
||||
|
||||
new_size = _cnh_usbhook_nhandlers + 1;
|
||||
new_array = realloc(_cnh_usbhook_handlers, new_size * sizeof(cnh_usbhook_fn_t));
|
||||
|
||||
if (new_array != NULL) {
|
||||
_cnh_usbhook_handlers = new_array;
|
||||
_cnh_usbhook_handlers[_cnh_usbhook_nhandlers++] = fn;
|
||||
result = CNH_RESULT_SUCCESS;
|
||||
} else {
|
||||
result = CNH_RESULT_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_usbhook_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_usbhook_invoke_next(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
cnh_usbhook_fn_t handler;
|
||||
enum cnh_result result;
|
||||
|
||||
assert(irp != NULL);
|
||||
assert(_cnh_usbhook_initted > 0);
|
||||
|
||||
pthread_mutex_lock(&_cnh_usbhook_lock);
|
||||
|
||||
assert(irp->next_handler <= _cnh_usbhook_nhandlers);
|
||||
|
||||
if (irp->next_handler < _cnh_usbhook_nhandlers) {
|
||||
handler = _cnh_usbhook_handlers[irp->next_handler];
|
||||
irp->next_handler++;
|
||||
} else {
|
||||
handler = _cnh_usbhook_invoke_real;
|
||||
irp->next_handler = (size_t) -1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_usbhook_lock);
|
||||
|
||||
result = handler(irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
irp->next_handler = (size_t) -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooked functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
void usb_init(void)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_INIT;
|
||||
|
||||
cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
/* No return value, results ignored */
|
||||
}
|
||||
|
||||
int usb_find_busses(void)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_FIND_BUSSES;
|
||||
irp.find_busses_res_num_busses = 0;
|
||||
|
||||
cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
/* No errors processed */
|
||||
|
||||
return irp.find_busses_res_num_busses;
|
||||
}
|
||||
|
||||
int usb_find_devices(void)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_FIND_DEVICES;
|
||||
irp.find_devices_res_num_devices = 0;
|
||||
|
||||
cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
/* No errors processed */
|
||||
|
||||
return irp.find_devices_res_num_devices;
|
||||
}
|
||||
|
||||
usb_dev_handle* usb_open(struct usb_device* dev)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
if (dev == NULL) {
|
||||
/* No errno is set here (from libusbcompat impl) */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_OPEN;
|
||||
irp.open_usb_dev = dev;
|
||||
irp.handle = NULL;
|
||||
|
||||
result = cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return irp.handle;
|
||||
}
|
||||
|
||||
int usb_close(usb_dev_handle* dev)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
if (dev == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_CLOSE;
|
||||
irp.handle = dev;
|
||||
|
||||
result = cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_reset(usb_dev_handle* dev)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
if (dev == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_RESET;
|
||||
irp.handle = dev;
|
||||
|
||||
result = cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_set_altinterface(usb_dev_handle* dev, int interface)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
if (dev == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE;
|
||||
irp.handle = dev;
|
||||
irp.set_altinterface = interface;
|
||||
|
||||
result = cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_set_configuration(usb_dev_handle* dev, int configuration)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
if (dev == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_SET_CONFIGURATION;
|
||||
irp.handle = dev;
|
||||
irp.set_configuration = configuration;
|
||||
|
||||
result = cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_claim_interface(usb_dev_handle* dev, int interface)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
if (dev == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE;
|
||||
irp.handle = dev;
|
||||
irp.claim_interface = interface;
|
||||
|
||||
result = cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_control_msg(usb_dev_handle* dev, int requesttype, int request, int value, int index, char* bytes, int nbytes,
|
||||
int timeout)
|
||||
{
|
||||
struct cnh_usbhook_irp irp;
|
||||
enum cnh_result result;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_usbhook_init();
|
||||
|
||||
if (dev == NULL || bytes == NULL) {
|
||||
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = CNH_USBHOOK_IRP_OP_CTRL_MSG;
|
||||
irp.handle = dev;
|
||||
irp.ctrl_req_type = requesttype;
|
||||
irp.ctrl_req = request;
|
||||
irp.ctrl_value = value;
|
||||
irp.ctrl_index = index;
|
||||
irp.ctrl_buffer.bytes = (uint8_t*) bytes;
|
||||
irp.ctrl_buffer.nbytes = (size_t) nbytes;
|
||||
irp.ctrl_buffer.pos = 0;
|
||||
irp.ctrl_timeout = timeout;
|
||||
|
||||
result = cnh_usbhook_invoke_next(&irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
errno = cnh_result_to_errno(result);
|
||||
/* Always return negative errno on errors */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return irp.ctrl_buffer.pos;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Helper functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_usbhook_init(void)
|
||||
{
|
||||
int expected;
|
||||
|
||||
if (atomic_load(&_cnh_usbhook_initted) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
expected = 0;
|
||||
|
||||
if (!atomic_compare_exchange_strong(&_cnh_usbhook_init_in_progress, &expected, 1)) {
|
||||
while (atomic_load(&_cnh_usbhook_init_in_progress) > 1) {
|
||||
util_time_sleep_us(100);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
|
||||
if (atomic_load(&_cnh_usbhook_initted) > 0) {
|
||||
/* Revert init in progress, because nothing to do anymore */
|
||||
atomic_store(&_cnh_usbhook_init_in_progress, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_cnh_usbhook_real_init = (cnh_usbhook_usb_init_t) cnh_lib_get_func_addr("usb_init");
|
||||
_cnh_usbhook_real_find_devices = (cnh_usbhook_usb_find_devices_t) cnh_lib_get_func_addr("usb_find_devices");
|
||||
_cnh_usbhook_real_find_busses = (cnh_usbhook_usb_find_busses_t) cnh_lib_get_func_addr("usb_find_busses");
|
||||
_cnh_usbhook_real_open = (cnh_usbhook_usb_open_t) cnh_lib_get_func_addr("usb_open");
|
||||
_cnh_usbhook_real_close = (cnh_usbhook_usb_close_t) cnh_lib_get_func_addr("usb_close");
|
||||
_cnh_usbhook_real_reset = (cnh_usbhook_usb_reset_t) cnh_lib_get_func_addr("usb_reset");
|
||||
_cnh_usbhook_real_set_altinterface =
|
||||
(cnh_usbhook_usb_set_altinterface_t) cnh_lib_get_func_addr("usb_set_altinterface");
|
||||
_cnh_usbhook_real_set_configuration =
|
||||
(cnh_usbhook_usb_set_configuration_t) cnh_lib_get_func_addr("usb_set_configuration");
|
||||
_cnh_usbhook_real_claim_interface =
|
||||
(cnh_usbhook_usb_claim_interface_t) cnh_lib_get_func_addr("usb_claim_interface");
|
||||
_cnh_usbhook_real_control_msg = (cnh_usbhook_usb_control_msg_t) cnh_lib_get_func_addr("usb_control_msg");
|
||||
|
||||
pthread_mutex_init(&_cnh_usbhook_lock, NULL);
|
||||
|
||||
atomic_store(&_cnh_usbhook_initted, 1);
|
||||
atomic_store(&_cnh_usbhook_init_in_progress, 0);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
cnh_usbhook_fn_t handler;
|
||||
|
||||
assert(irp != NULL);
|
||||
assert(irp->op < _countof(_cnh_usbhook_real_handlers));
|
||||
|
||||
handler = _cnh_usbhook_real_handlers[irp->op];
|
||||
|
||||
assert(handler != NULL);
|
||||
|
||||
return handler(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_init(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
_cnh_usbhook_real_init();
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_find_busses(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_usbhook_real_find_busses();
|
||||
|
||||
irp->find_busses_res_num_busses = res;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_find_devices(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_usbhook_real_find_devices();
|
||||
|
||||
irp->find_devices_res_num_devices = res;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_open(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
usb_dev_handle* res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_usbhook_real_open(irp->open_usb_dev);
|
||||
|
||||
if (res == NULL) {
|
||||
irp->handle = NULL;
|
||||
return cnh_errno_to_result(errno);
|
||||
}
|
||||
|
||||
irp->handle = res;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_close(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_usbhook_real_close(irp->handle);
|
||||
|
||||
if (res < 0) {
|
||||
return cnh_errno_to_result(res);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_reset(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_usbhook_real_reset(irp->handle);
|
||||
|
||||
if (res < 0) {
|
||||
return cnh_errno_to_result(res);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_set_altinterface(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_usbhook_real_set_altinterface(irp->handle, irp->set_altinterface);
|
||||
|
||||
if (res < 0) {
|
||||
return cnh_errno_to_result(res);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_set_configuration(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_usbhook_real_set_configuration(irp->handle, irp->set_configuration);
|
||||
|
||||
if (res < 0) {
|
||||
return cnh_errno_to_result(res);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_claim_interface(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_usbhook_real_claim_interface(irp->handle, irp->claim_interface);
|
||||
|
||||
if (res < 0) {
|
||||
return cnh_errno_to_result(res);
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbhook_invoke_real_ctrl_msg(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
res = _cnh_usbhook_real_control_msg(irp->handle, irp->ctrl_req_type, irp->ctrl_req, irp->ctrl_value,
|
||||
irp->ctrl_index, (char*) irp->ctrl_buffer.bytes, irp->ctrl_buffer.nbytes, irp->ctrl_timeout);
|
||||
|
||||
irp->ctrl_buffer.pos = 0;
|
||||
|
||||
if (res < 0) {
|
||||
return cnh_errno_to_result(res);
|
||||
}
|
||||
|
||||
irp->ctrl_buffer.pos = (size_t) res;
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Hook module for hooking libusb 0.1
|
||||
*
|
||||
* Based on original capnhook code for windows by decafcode:
|
||||
* https://github.com/decafcode/capnhook
|
||||
*/
|
||||
#ifndef CAPNHOOK_USBHOOK_H
|
||||
#define CAPNHOOK_USBHOOK_H
|
||||
|
||||
#include <usb.h>
|
||||
|
||||
#include "capnhook/hook/iobuf.h"
|
||||
|
||||
/**
|
||||
* Available operations to hook
|
||||
*/
|
||||
enum cnh_usbhook_irp_op {
|
||||
CNH_USBHOOK_IRP_OP_INIT = 0,
|
||||
CNH_USBHOOK_IRP_OP_FIND_BUSSES = 1,
|
||||
CNH_USBHOOK_IRP_OP_FIND_DEVICES = 2,
|
||||
CNH_USBHOOK_IRP_OP_OPEN = 3,
|
||||
CNH_USBHOOK_IRP_OP_CLOSE = 4,
|
||||
CNH_USBHOOK_IRP_OP_RESET = 5,
|
||||
CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE = 6,
|
||||
CNH_USBHOOK_IRP_OP_SET_CONFIGURATION = 7,
|
||||
CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE = 8,
|
||||
CNH_USBHOOK_IRP_OP_CTRL_MSG = 9
|
||||
};
|
||||
|
||||
/**
|
||||
* I/O request packet
|
||||
*/
|
||||
struct cnh_usbhook_irp {
|
||||
enum cnh_usbhook_irp_op op;
|
||||
size_t next_handler;
|
||||
int find_busses_res_num_busses;
|
||||
int find_devices_res_num_devices;
|
||||
struct usb_device* open_usb_dev;
|
||||
usb_dev_handle* handle;
|
||||
int set_altinterface;
|
||||
int set_configuration;
|
||||
int claim_interface;
|
||||
int ctrl_req_type;
|
||||
int ctrl_req;
|
||||
int ctrl_value;
|
||||
int ctrl_index;
|
||||
struct cnh_iobuf ctrl_buffer;
|
||||
int ctrl_timeout;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook function type
|
||||
*/
|
||||
typedef enum cnh_result (*cnh_usbhook_fn_t)(struct cnh_usbhook_irp *irp);
|
||||
|
||||
/**
|
||||
* Add a new hook handler to the hook module.
|
||||
* The handler added is getting called on every hooked operation.
|
||||
*
|
||||
* @param fn Pointer to hook function to add
|
||||
* @return Result/error code if hooking was successful
|
||||
*/
|
||||
enum cnh_result cnh_usbhook_push_handler(cnh_usbhook_fn_t fn);
|
||||
|
||||
/**
|
||||
* Invoke the next hooked function registered in the hook module.
|
||||
* Call this from your hook handler if you want to pass on execution to further
|
||||
* hooked calls reaching the original function at the end of the hook chain.
|
||||
*
|
||||
* @param irp I/O request package to dispatch
|
||||
* @return Result of the following function dispatching the request package
|
||||
*/
|
||||
enum cnh_result cnh_usbhook_invoke_next(struct cnh_usbhook_irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#define LOG_MODULE "cnh-filehook-mon"
|
||||
|
||||
#include "capnhook/hooklib/filehook-mon.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static const char* _cnh_filehook_mon_str_empty = "";
|
||||
static const char* _cnh_filehook_mon_irp_op_str[] = {
|
||||
"fopen",
|
||||
"fclose",
|
||||
"fread",
|
||||
"fwrite",
|
||||
"fgets",
|
||||
"fseek",
|
||||
"ftell",
|
||||
"feof"
|
||||
};
|
||||
|
||||
enum cnh_result cnh_filehook_mon(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
|
||||
/* Skip writes to log file because this results in infinite recursion */
|
||||
if (irp->file == util_log_get_file_handle()) {
|
||||
return cnh_filehook_invoke_next(irp);
|
||||
}
|
||||
|
||||
log_debug("[before][%s] next_handler %d, file %p, open_filename %s, open_mode %s, read(bytes %p, nbytes %d, pos %d)"
|
||||
", write(bytes %p, nbytes %d, pos %d), orig_read_write_size %d, orig_read_write_nmemb %d, seek_origin %d, "
|
||||
"seek_offset %d, tell_offset %llu, eof %d", _cnh_filehook_mon_irp_op_str[irp->op], irp->next_handler,
|
||||
irp->file, irp->open_filename ? irp->open_filename : _cnh_filehook_mon_str_empty,
|
||||
irp->open_mode ? irp->open_mode : _cnh_filehook_mon_str_empty, irp->read.bytes, irp->read.nbytes,
|
||||
irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->orig_read_write_size,
|
||||
irp->orig_read_write_nmemb, irp->seek_origin, irp->seek_offset, irp->tell_offset, irp->eof);
|
||||
|
||||
result = cnh_filehook_invoke_next(irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
log_error("[after][%s] result %d, next_handler %d, file %p, open_filename %s, open_mode %s, read(bytes %p, "
|
||||
"nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), orig_read_write_size %d, orig_read_write_nmemb %d,"
|
||||
" seek_origin %d, seek_offset %d, tell_offset %llu, eof %d", _cnh_filehook_mon_irp_op_str[irp->op],
|
||||
result, irp->next_handler, irp->file, irp->open_filename ? irp->open_filename : _cnh_filehook_mon_str_empty,
|
||||
irp->open_mode ? irp->open_mode : _cnh_filehook_mon_str_empty, irp->read.bytes, irp->read.nbytes,
|
||||
irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->orig_read_write_size,
|
||||
irp->orig_read_write_nmemb, irp->seek_origin, irp->seek_offset, irp->tell_offset, irp->eof);
|
||||
} else {
|
||||
log_debug("[after][%s] result %d, next_handler %d, file %p, open_filename %s, open_mode %s, read(bytes %p, "
|
||||
"nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), orig_read_write_size %d, orig_read_write_nmemb %d,"
|
||||
" seek_origin %d, seek_offset %d, tell_offset %llu, eof %d", _cnh_filehook_mon_irp_op_str[irp->op],
|
||||
result, irp->next_handler, irp->file, irp->open_filename ? irp->open_filename : _cnh_filehook_mon_str_empty,
|
||||
irp->open_mode ? irp->open_mode : _cnh_filehook_mon_str_empty, irp->read.bytes, irp->read.nbytes,
|
||||
irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->orig_read_write_size,
|
||||
irp->orig_read_write_nmemb, irp->seek_origin, irp->seek_offset, irp->tell_offset, irp->eof);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Hook implementation to monitor file related calls, e.g. fopen, fread, fwrite etc.
|
||||
*/
|
||||
#ifndef CAPNHOOK_FILEHOOK_MON_H
|
||||
#define CAPNHOOK_FILEHOOK_MON_H
|
||||
|
||||
#include "capnhook/hook/filehook.h"
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for monitoring file related calls
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_filehook_mon(struct cnh_filehook_irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "capnhook/hook/filehook.h"
|
||||
#include "capnhook/hook/fshook.h"
|
||||
#include "capnhook/hook/iohook.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
enum cnh_result cnh_fileopen_mon_filehook(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
|
||||
if (irp->op == CNH_FILEHOOK_IRP_OP_OPEN) {
|
||||
log_debug("[before][fopen]: %s %s", irp->open_filename, irp->open_mode);
|
||||
|
||||
result = cnh_filehook_invoke_next(irp);
|
||||
|
||||
log_debug("[after][fopen]: %s %s -> %d (%d)", irp->open_filename, irp->open_mode, irp->file, result);
|
||||
} else {
|
||||
result = cnh_filehook_invoke_next(irp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_fileopen_mon_fshook(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
|
||||
switch (irp->op) {
|
||||
case CNH_FSHOOK_IRP_OP_DIR_OPEN:
|
||||
log_debug("[before][diropen]: %s", irp->opendir_name);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_LXSTAT:
|
||||
log_debug("[before][lxstat]: %s", irp->xstat_file);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_XSTAT:
|
||||
log_debug("[before][xstat]: %s", irp->xstat_file);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_RENAME:
|
||||
log_debug("[before][rename]: %s %s", irp->rename_old, irp->rename_new);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_REMOVE:
|
||||
log_debug("[before][remove]: %s", irp->remove_pathname);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_ACCESS:
|
||||
log_debug("[before][access]: %s", irp->access_path);
|
||||
break;
|
||||
default:
|
||||
log_die("Unhandled case: %d", irp->op);
|
||||
break;
|
||||
}
|
||||
|
||||
result = cnh_fshook_invoke_next(irp);
|
||||
|
||||
switch (irp->op) {
|
||||
case CNH_FSHOOK_IRP_OP_DIR_OPEN:
|
||||
log_debug("[after][diropen]: %s -> %d", irp->opendir_name, result);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_LXSTAT:
|
||||
log_debug("[after][lxstat]: %s -> %d", irp->xstat_file, result);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_XSTAT:
|
||||
log_debug("[after][xstat]: %s -> %d", irp->xstat_file, result);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_RENAME:
|
||||
log_debug("[after][rename]: %s %s -> %d", irp->rename_old, irp->rename_new, result);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_REMOVE:
|
||||
log_debug("[after][remove]: %s -> %d", irp->remove_pathname, result);
|
||||
break;
|
||||
case CNH_FSHOOK_IRP_OP_ACCESS:
|
||||
log_debug("[after][access]: %s -> %d", irp->access_path, result);
|
||||
break;
|
||||
default:
|
||||
log_die("Unhandled case: %d", irp->op);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
enum cnh_result cnh_fileopen_mon_iohook(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
|
||||
if (irp->op == CNH_IOHOOK_IRP_OP_OPEN) {
|
||||
log_debug("[before][open]: %s %d", irp->open_filename, irp->open_flags);
|
||||
|
||||
result = cnh_iohook_invoke_next(irp);
|
||||
|
||||
log_debug("[after][open]: %s %d -> %d (%d)", irp->open_filename, irp->open_flags, irp->fd, result);
|
||||
} else {
|
||||
result = cnh_iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Hook implementation to monitor different kinds of operations opening a file
|
||||
*/
|
||||
#ifndef CAPNHOOK_FILEOPEN_MON_H
|
||||
#define CAPNHOOK_FILEOPEN_MON_H
|
||||
|
||||
#include "capnhook/hook/filehook.h"
|
||||
#include "capnhook/hook/fshook.h"
|
||||
#include "capnhook/hook/iohook.h"
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for monitoring open file calls on file operations
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_fileopen_mon_filehook(struct cnh_filehook_irp *irp);
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for monitoring open file calls on file system related operations
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_fileopen_mon_fshook(struct cnh_fshook_irp *irp);
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for monitoring open file/device calls on I/O operations
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_fileopen_mon_iohook(struct cnh_iohook_irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#define LOG_MODULE "cnh-fshook-mon"
|
||||
|
||||
#include "capnhook/hooklib/fshook-mon.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static const char* _cnh_fshook_mon_irp_op_str[] = {
|
||||
"diropen",
|
||||
"lxstat",
|
||||
"xstat",
|
||||
"rename",
|
||||
"remove",
|
||||
"access"
|
||||
};
|
||||
|
||||
enum cnh_result cnh_fshook_mon(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
|
||||
log_debug("[before][%s] next_handler %d, opendir_name %s, opendir_ret %p, xstat_version %d, xstat_file %s, "
|
||||
"xstat_buf %p, rename_old %s, rename_new %s, remove_pathname %s, access_path %s, access_amode %d",
|
||||
_cnh_fshook_mon_irp_op_str[irp->op], irp->next_handler, irp->opendir_name, irp->opendir_ret,
|
||||
irp->xstat_version, irp->xstat_file, irp->xstat_buf, irp->rename_old, irp->rename_new, irp->remove_pathname,
|
||||
irp->access_path, irp->access_amode);
|
||||
|
||||
result = cnh_fshook_invoke_next(irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
log_error("[before][%s] result %d, next_handler %d, opendir_name %s, opendir_ret %p, xstat_version %d, "
|
||||
"xstat_file %s, xstat_buf %p, rename_old %s, rename_new %s, remove_pathname %s, access_path %s, "
|
||||
"access_amode %d", _cnh_fshook_mon_irp_op_str[irp->op], result, irp->next_handler, irp->opendir_name,
|
||||
irp->opendir_ret, irp->xstat_version, irp->xstat_file, irp->xstat_buf, irp->rename_old, irp->rename_new,
|
||||
irp->remove_pathname, irp->access_path, irp->access_amode);
|
||||
} else {
|
||||
log_debug("[before][%s] result %d, next_handler %d, opendir_name %s, opendir_ret %p, xstat_version %d, "
|
||||
"xstat_file %s, xstat_buf %p, rename_old %s, rename_new %s, remove_pathname %s, access_path %s, "
|
||||
"access_amode %d", _cnh_fshook_mon_irp_op_str[irp->op], result, irp->next_handler, irp->opendir_name,
|
||||
irp->opendir_ret, irp->xstat_version, irp->xstat_file, irp->xstat_buf, irp->rename_old, irp->rename_new,
|
||||
irp->remove_pathname, irp->access_path, irp->access_amode);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Hook implementation to monitor file system related calls
|
||||
*/
|
||||
#ifndef CAPNHOOK_FSHOOK_MON_H
|
||||
#define CAPNHOOK_FSHOOK_MON_H
|
||||
|
||||
#include "capnhook/hook/fshook.h"
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for monitoring file system related calls
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_fshook_mon(struct cnh_fshook_irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
#define LOG_MODULE "cnh-iohook-mon"
|
||||
|
||||
#include "capnhook/hooklib/iohook-mon.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static const char* _cnh_iohook_mon_str_empty = "";
|
||||
static const char* _cnh_iohook_mon_irp_op_str[] = {
|
||||
"open",
|
||||
"fdopen",
|
||||
"close",
|
||||
"read",
|
||||
"write",
|
||||
"seek",
|
||||
"ioctl"
|
||||
};
|
||||
|
||||
enum cnh_result cnh_iohook_mon(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
|
||||
log_debug("[before][%s] next_handler %d, fd %d, open_filename %s, open_flags %d, fdopen_fd %d, fdopen_mode %s, "
|
||||
"fdopen_res %p, read(bytes %p, nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), seek_origin %d, "
|
||||
"seek_offset %d, seek_pos %llu, ioctl_req %d, ioctl(bytes %p, nbytes %d, pos %d)",
|
||||
_cnh_iohook_mon_irp_op_str[irp->op], irp->next_handler, irp->fd,
|
||||
irp->open_filename ? irp->open_filename : _cnh_iohook_mon_str_empty, irp->open_flags, irp->fdopen_fd,
|
||||
irp->fdopen_mode ? irp->fdopen_mode : _cnh_iohook_mon_str_empty, irp->fdopen_res, irp->read.bytes,
|
||||
irp->read.nbytes, irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->seek_origin,
|
||||
irp->seek_offset, irp->seek_pos, irp->ioctl_req, irp->ioctl.bytes, irp->ioctl.nbytes, irp->ioctl.pos);
|
||||
|
||||
result = cnh_iohook_invoke_next(irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
log_error("[after][%s] next_handler %d, res: %d, fd %d, open_filename %s, open_flags %d, fdopen_fd %d, "
|
||||
"fdopen_mode %s, fdopen_res %p, read(bytes %p, nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), "
|
||||
"seek_origin %d, seek_offset %d, seek_pos %llu, ioctl_req %d, ioctl(bytes %p, nbytes %d, pos %d)",
|
||||
_cnh_iohook_mon_irp_op_str[irp->op], irp->next_handler, result, irp->fd,
|
||||
irp->open_filename ? irp->open_filename : _cnh_iohook_mon_str_empty, irp->open_flags, irp->fdopen_fd,
|
||||
irp->fdopen_mode ? irp->fdopen_mode : _cnh_iohook_mon_str_empty, irp->fdopen_res, irp->read.bytes,
|
||||
irp->read.nbytes, irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->seek_origin,
|
||||
irp->seek_offset, irp->seek_pos, irp->ioctl_req, irp->ioctl.bytes, irp->ioctl.nbytes, irp->ioctl.pos);
|
||||
} else {
|
||||
log_debug("[after][%s] next_handler %d, res %d, fd %d, open_filename %s, open_flags %d, fdopen_fd %d, "
|
||||
"fdopen_mode %s, fdopen_res %p, read(bytes %p, nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), "
|
||||
"seek_origin %d, seek_offset %d, seek_pos %llu, ioctl_req %d, ioctl(bytes %p, nbytes %d, pos %d)",
|
||||
_cnh_iohook_mon_irp_op_str[irp->op], irp->next_handler, result, irp->fd,
|
||||
irp->open_filename ? irp->open_filename : _cnh_iohook_mon_str_empty, irp->open_flags, irp->fdopen_fd,
|
||||
irp->fdopen_mode ? irp->fdopen_mode : _cnh_iohook_mon_str_empty, irp->fdopen_res, irp->read.bytes,
|
||||
irp->read.nbytes, irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->seek_origin,
|
||||
irp->seek_offset, irp->seek_pos, irp->ioctl_req, irp->ioctl.bytes, irp->ioctl.nbytes, irp->ioctl.pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Hook implementation to monitor I/O calls, e.g. open, read, write etc.
|
||||
*/
|
||||
#ifndef CAPNHOOK_IOHOOK_MON_H
|
||||
#define CAPNHOOK_IOHOOK_MON_H
|
||||
|
||||
#include "capnhook/hook/iohook.h"
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for monitoring I/O calls
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_iohook_mon(struct cnh_iohook_irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,371 @@
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "capnhook/hook/fshook.h"
|
||||
#include "capnhook/hook/filehook.h"
|
||||
#include "capnhook/hook/iohook.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
#include "util/time.h"
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooks to fshook module */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static enum cnh_result _cnh_redir_fshook_diropen(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_redir_fshook_lxstat(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_redir_fshook_xstat(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_redir_fshook_rename(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_redir_fshook_remove(struct cnh_fshook_irp *irp);
|
||||
static enum cnh_result _cnh_redir_fshook_access(struct cnh_fshook_irp *irp);
|
||||
|
||||
static const cnh_fshook_fn_t _cnh_redir_fshook_handlers[6] = {
|
||||
[CNH_FSHOOK_IRP_OP_DIR_OPEN] = _cnh_redir_fshook_diropen,
|
||||
[CNH_FSHOOK_IRP_OP_LXSTAT] = _cnh_redir_fshook_lxstat,
|
||||
[CNH_FSHOOK_IRP_OP_XSTAT] = _cnh_redir_fshook_xstat,
|
||||
[CNH_FSHOOK_IRP_OP_RENAME] = _cnh_redir_fshook_rename,
|
||||
[CNH_FSHOOK_IRP_OP_REMOVE] = _cnh_redir_fshook_remove,
|
||||
[CNH_FSHOOK_IRP_OP_ACCESS] = _cnh_redir_fshook_access,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooks to filehook module */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static enum cnh_result _cnh_redir_filehook_default(struct cnh_filehook_irp *irp);
|
||||
static enum cnh_result _cnh_redir_filehook_fopen(struct cnh_filehook_irp *irp);
|
||||
|
||||
static const cnh_filehook_fn_t _cnh_redir_filehook_handlers[8] = {
|
||||
[CNH_FILEHOOK_IRP_OP_OPEN] = _cnh_redir_filehook_fopen,
|
||||
[CNH_FILEHOOK_IRP_OP_CLOSE] = _cnh_redir_filehook_default,
|
||||
[CNH_FILEHOOK_IRP_OP_READ] = _cnh_redir_filehook_default,
|
||||
[CNH_FILEHOOK_IRP_OP_WRITE] = _cnh_redir_filehook_default,
|
||||
[CNH_FILEHOOK_IRP_OP_FGETS] = _cnh_redir_filehook_default,
|
||||
[CNH_FILEHOOK_IRP_OP_SEEK] = _cnh_redir_filehook_default,
|
||||
[CNH_FILEHOOK_IRP_OP_TELL] = _cnh_redir_filehook_default,
|
||||
[CNH_FILEHOOK_IRP_OP_EOF] = _cnh_redir_filehook_default,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooks to iohook module */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static enum cnh_result _cnh_redir_iohook_default(struct cnh_iohook_irp *irp);
|
||||
static enum cnh_result _cnh_redir_iohook_open(struct cnh_iohook_irp *irp);
|
||||
|
||||
static const cnh_iohook_fn_t _cnh_redir_iohook_handlers[7] = {
|
||||
[CNH_IOHOOK_IRP_OP_OPEN] = _cnh_redir_iohook_open,
|
||||
[CNH_IOHOOK_IRP_OP_FDOPEN] = _cnh_redir_iohook_default,
|
||||
[CNH_IOHOOK_IRP_OP_CLOSE] = _cnh_redir_iohook_default,
|
||||
[CNH_IOHOOK_IRP_OP_READ] = _cnh_redir_iohook_default,
|
||||
[CNH_IOHOOK_IRP_OP_WRITE] = _cnh_redir_iohook_default,
|
||||
[CNH_IOHOOK_IRP_OP_SEEK] = _cnh_redir_iohook_default,
|
||||
[CNH_IOHOOK_IRP_OP_IOCTL] = _cnh_redir_iohook_default,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private state */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
struct cnh_redir_redir {
|
||||
char* src;
|
||||
char* dest;
|
||||
};
|
||||
|
||||
static atomic_int _cnh_redir_initted = ATOMIC_VAR_INIT(0);
|
||||
static atomic_int _cnh_redir_init_in_progress = ATOMIC_VAR_INIT(0);
|
||||
static pthread_mutex_t _cnh_redir_lock;
|
||||
static struct cnh_redir_redir* _cnh_redir_redirs;
|
||||
static size_t _cnh_redir_nredirs;
|
||||
|
||||
static void _cnh_redir_init();
|
||||
static char* _cnh_redir_check(const char* src);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Public module functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
void cnh_redir_add(const char* src, const char* dest)
|
||||
{
|
||||
struct cnh_redir_redir *new_array;
|
||||
size_t new_size;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(dest != NULL);
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_redir_init();
|
||||
|
||||
pthread_mutex_lock(&_cnh_redir_lock);
|
||||
|
||||
new_size = _cnh_redir_nredirs + 1;
|
||||
new_array = realloc(_cnh_redir_redirs, new_size * sizeof(struct cnh_redir_redir));
|
||||
|
||||
if (new_array != NULL) {
|
||||
_cnh_redir_redirs = new_array;
|
||||
}
|
||||
|
||||
_cnh_redir_redirs[_cnh_redir_nredirs].src = util_str_dup(src);
|
||||
_cnh_redir_redirs[_cnh_redir_nredirs].dest = util_str_dup(dest);
|
||||
|
||||
_cnh_redir_nredirs++;
|
||||
|
||||
pthread_mutex_unlock(&_cnh_redir_lock);
|
||||
|
||||
log_info("Added path redirect %s -> %s", src, dest);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooks to fshook module */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
enum cnh_result cnh_redir_fshook(struct cnh_fshook_irp* irp)
|
||||
{
|
||||
cnh_fshook_fn_t handler;
|
||||
|
||||
handler = _cnh_redir_fshook_handlers[irp->op];
|
||||
|
||||
assert(handler != NULL);
|
||||
|
||||
return handler(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_fshook_diropen(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
char* res;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_redir_init();
|
||||
|
||||
res = _cnh_redir_check(irp->opendir_name);
|
||||
|
||||
if (res != NULL) {
|
||||
irp->opendir_name = res;
|
||||
}
|
||||
|
||||
return cnh_fshook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_fshook_lxstat(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
char* res;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_redir_init();
|
||||
|
||||
res = _cnh_redir_check(irp->xstat_file);
|
||||
|
||||
if (res != NULL) {
|
||||
irp->xstat_file = res;
|
||||
}
|
||||
|
||||
return cnh_fshook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_fshook_xstat(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
char* res;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_redir_init();
|
||||
|
||||
res = _cnh_redir_check(irp->xstat_file);
|
||||
|
||||
if (res != NULL) {
|
||||
irp->xstat_file = res;
|
||||
}
|
||||
|
||||
return cnh_fshook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_fshook_rename(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
char* res_old;
|
||||
char* res_new;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_redir_init();
|
||||
|
||||
res_old = _cnh_redir_check(irp->rename_old);
|
||||
res_new = _cnh_redir_check(irp->rename_old);
|
||||
|
||||
if (res_old != NULL) {
|
||||
irp->rename_old = res_old;
|
||||
}
|
||||
|
||||
if (res_new != NULL) {
|
||||
irp->rename_new = res_new;
|
||||
}
|
||||
|
||||
return cnh_fshook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_fshook_remove(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
char* res;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_redir_init();
|
||||
|
||||
res = _cnh_redir_check(irp->remove_pathname);
|
||||
|
||||
if (res != NULL) {
|
||||
irp->remove_pathname = res;
|
||||
}
|
||||
|
||||
return cnh_fshook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_fshook_access(struct cnh_fshook_irp *irp)
|
||||
{
|
||||
char* res;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_redir_init();
|
||||
|
||||
res = _cnh_redir_check(irp->access_path);
|
||||
|
||||
if (res != NULL) {
|
||||
irp->access_path = res;
|
||||
}
|
||||
|
||||
return cnh_fshook_invoke_next(irp);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooks to iohook module */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
enum cnh_result cnh_redir_iohook(struct cnh_iohook_irp* irp)
|
||||
{
|
||||
cnh_iohook_fn_t handler;
|
||||
|
||||
handler = _cnh_redir_iohook_handlers[irp->op];
|
||||
|
||||
assert(handler != NULL);
|
||||
|
||||
return handler(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_iohook_default(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
return cnh_iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_iohook_open(struct cnh_iohook_irp *irp)
|
||||
{
|
||||
char* res;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_redir_init();
|
||||
|
||||
res = _cnh_redir_check(irp->open_filename);
|
||||
|
||||
if (res != NULL) {
|
||||
irp->open_filename = res;
|
||||
}
|
||||
|
||||
return cnh_iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooks to filehook module */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
enum cnh_result cnh_redir_filehook(struct cnh_filehook_irp* irp)
|
||||
{
|
||||
cnh_filehook_fn_t handler;
|
||||
|
||||
handler = _cnh_redir_filehook_handlers[irp->op];
|
||||
|
||||
assert(handler != NULL);
|
||||
|
||||
return handler(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_filehook_default(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
return cnh_filehook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_redir_filehook_fopen(struct cnh_filehook_irp *irp)
|
||||
{
|
||||
char* res;
|
||||
|
||||
/* Ensure module is initialized */
|
||||
_cnh_redir_init();
|
||||
|
||||
res = _cnh_redir_check(irp->open_filename);
|
||||
|
||||
if (res != NULL) {
|
||||
irp->open_filename = res;
|
||||
}
|
||||
|
||||
return cnh_filehook_invoke_next(irp);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Helper functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_redir_init()
|
||||
{
|
||||
int expected;
|
||||
|
||||
if (atomic_load(&_cnh_redir_initted) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
expected = 0;
|
||||
|
||||
if (!atomic_compare_exchange_strong(&_cnh_redir_init_in_progress, &expected, 1)) {
|
||||
while (atomic_load(&_cnh_redir_init_in_progress) > 1) {
|
||||
util_time_sleep_us(100);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
|
||||
if (atomic_load(&_cnh_redir_initted) > 0) {
|
||||
/* Revert init in progress, because nothing to do anymore */
|
||||
atomic_store(&_cnh_redir_init_in_progress, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_init(&_cnh_redir_lock, NULL);
|
||||
|
||||
atomic_store(&_cnh_redir_initted, 1);
|
||||
atomic_store(&_cnh_redir_init_in_progress, 0);
|
||||
}
|
||||
|
||||
static char* _cnh_redir_check(const char* src)
|
||||
{
|
||||
const char* redir_src;
|
||||
const char* redir_dest;
|
||||
char* res;
|
||||
|
||||
res = NULL;
|
||||
|
||||
pthread_mutex_lock(&_cnh_redir_lock);
|
||||
|
||||
for (size_t i = 0; i < _cnh_redir_nredirs; i++) {
|
||||
redir_src = _cnh_redir_redirs[i].src;
|
||||
redir_dest = _cnh_redir_redirs[i].dest;
|
||||
|
||||
if (strncmp(src, redir_src, strlen(redir_src)) == 0) {
|
||||
// FIXME memory leak
|
||||
res = util_str_merge(redir_dest, &src[strlen(redir_src)]);
|
||||
log_debug("Redirect %s -> %s", src, res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_redir_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Hook implementation to redirect files/paths passed as parameters on various file, file system and I/O calls
|
||||
*/
|
||||
#ifndef CAPNHOOK_REDIR_H
|
||||
#define CAPNHOOK_REDIR_H
|
||||
|
||||
#include "capnhook/hook/fshook.h"
|
||||
#include "capnhook/hook/filehook.h"
|
||||
#include "capnhook/hook/iohook.h"
|
||||
|
||||
/**
|
||||
* Add a new path/file redirect to the module
|
||||
*
|
||||
* @param src Source path/file to redirect. This does not have to match the full path.
|
||||
* Postfix, e.g. just the filename is sufficient as well
|
||||
* @param dest Destination to redirect the original path to. Recommended to use absolute paths here.
|
||||
*/
|
||||
void cnh_redir_add(const char* src, const char* dest);
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for redirecting file system related calls
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_redir_fshook(struct cnh_fshook_irp* irp);
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for redirecting I/O calls
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_redir_iohook(struct cnh_iohook_irp* irp);
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for redirecting file related calls
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_redir_filehook(struct cnh_filehook_irp* irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,494 @@
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <util/mem.h>
|
||||
|
||||
#include "capnhook/hooklib/usb-emu.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/time.h"
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private helpers */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static void _cnh_usb_emu_init(void);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooks to usbhook module */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_default(struct cnh_usbhook_irp *irp);
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_noop(struct cnh_usbhook_irp *irp);
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_find_busses(struct cnh_usbhook_irp *irp);
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_find_devices(struct cnh_usbhook_irp *irp);
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_open(struct cnh_usbhook_irp *irp);
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_close(struct cnh_usbhook_irp *irp);
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_reset(struct cnh_usbhook_irp *irp);
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_ctrl_msg(struct cnh_usbhook_irp *irp);
|
||||
|
||||
static const cnh_usbhook_fn_t _cnh_usb_emu_usb_handlers[10] = {
|
||||
[CNH_USBHOOK_IRP_OP_INIT] = _cnh_usb_emu_usbhook_default,
|
||||
[CNH_USBHOOK_IRP_OP_FIND_BUSSES] = _cnh_usb_emu_usbhook_find_busses,
|
||||
[CNH_USBHOOK_IRP_OP_FIND_DEVICES] = _cnh_usb_emu_usbhook_find_devices,
|
||||
[CNH_USBHOOK_IRP_OP_OPEN] = _cnh_usb_emu_usbhook_open,
|
||||
[CNH_USBHOOK_IRP_OP_CLOSE] = _cnh_usb_emu_usbhook_close,
|
||||
[CNH_USBHOOK_IRP_OP_RESET] = _cnh_usb_emu_usbhook_reset,
|
||||
[CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE] = _cnh_usb_emu_usbhook_noop,
|
||||
[CNH_USBHOOK_IRP_OP_SET_CONFIGURATION] = _cnh_usb_emu_usbhook_noop,
|
||||
[CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE] = _cnh_usb_emu_usbhook_noop,
|
||||
[CNH_USBHOOK_IRP_OP_CTRL_MSG] = _cnh_usb_emu_usbhook_ctrl_msg,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Private state */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
struct usb_dev_handle {
|
||||
struct cnh_usb_emu_virtdev* virtdev;
|
||||
};
|
||||
|
||||
struct cnh_usb_emu_virtdev {
|
||||
const struct cnh_usb_emu_virtdev_ep* ep;
|
||||
bool real_dev_avail;
|
||||
bool enumerated;
|
||||
struct usb_dev_handle usb_dev_handle;
|
||||
};
|
||||
|
||||
static atomic_int _cnh_usb_emu_initted = ATOMIC_VAR_INIT(0);
|
||||
static atomic_int _cnh_usb_emu_init_in_progress = ATOMIC_VAR_INIT(0);
|
||||
static pthread_mutex_t _cnh_usb_emu_virtdevs_lock;
|
||||
static struct cnh_usb_emu_virtdev *_cnh_usb_emu_virtdevs;
|
||||
static size_t _cnh_usb_emu_nvirtdevs;
|
||||
|
||||
static struct cnh_usb_emu_virtdev* _cnh_usb_emu_get_virtdev(uint16_t vid, uint16_t pid);
|
||||
static struct cnh_usb_emu_virtdev* _cnh_usb_emu_get_virtdev_by_handle(usb_dev_handle* handle);
|
||||
static struct usb_device* _cnh_usb_emu_create_fakedev(uint16_t vid, uint16_t pid);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Public module functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
void cnh_usb_emu_add_virtdevep(const struct cnh_usb_emu_virtdev_ep* virtdevep)
|
||||
{
|
||||
struct cnh_usb_emu_virtdev *new_array;
|
||||
size_t new_size;
|
||||
|
||||
assert(virtdevep->enumerate);
|
||||
assert(virtdevep->open);
|
||||
assert(virtdevep->reset);
|
||||
assert(virtdevep->control_msg);
|
||||
assert(virtdevep->close);
|
||||
|
||||
/* Ensure initialized */
|
||||
_cnh_usb_emu_init();
|
||||
|
||||
pthread_mutex_lock(&_cnh_usb_emu_virtdevs_lock);
|
||||
|
||||
new_size = _cnh_usb_emu_nvirtdevs + 1;
|
||||
new_array = realloc(_cnh_usb_emu_virtdevs, new_size * sizeof(struct cnh_usb_emu_virtdev));
|
||||
|
||||
if (new_array != NULL) {
|
||||
_cnh_usb_emu_virtdevs = new_array;
|
||||
|
||||
_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs].enumerated = false;
|
||||
_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs].real_dev_avail = false;
|
||||
_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs].ep = virtdevep;
|
||||
_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs].usb_dev_handle.virtdev = &_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs];
|
||||
_cnh_usb_emu_nvirtdevs++;
|
||||
} else {
|
||||
log_die("Out of memory");
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_usb_emu_virtdevs_lock);
|
||||
}
|
||||
|
||||
enum cnh_result cnh_usb_emu(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
cnh_usbhook_fn_t handler;
|
||||
|
||||
/* Ensure initialized */
|
||||
_cnh_usb_emu_init();
|
||||
|
||||
handler = _cnh_usb_emu_usb_handlers[irp->op];
|
||||
|
||||
assert(handler != NULL);
|
||||
|
||||
return handler(irp);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Hooked functions to usbhook module */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_default(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
return cnh_usbhook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_noop(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
struct cnh_usb_emu_virtdev* virtdev;
|
||||
|
||||
assert(irp->handle);
|
||||
|
||||
virtdev = _cnh_usb_emu_get_virtdev_by_handle(irp->handle);
|
||||
|
||||
/* Not a virtual device handle, probably real handle from libusb */
|
||||
if (virtdev == NULL) {
|
||||
return cnh_usbhook_invoke_next(irp);
|
||||
}
|
||||
|
||||
/* No-op */
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_find_busses(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
|
||||
result = cnh_usbhook_invoke_next(irp);
|
||||
|
||||
// inject another custom bus for virtual devices
|
||||
if (result == CNH_RESULT_SUCCESS) {
|
||||
log_debug("Injecting usb bus for virtual devices");
|
||||
irp->find_busses_res_num_busses++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_find_devices(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
struct usb_bus* it;
|
||||
int fakedev_count;
|
||||
|
||||
fakedev_count = 0;
|
||||
|
||||
/* first, populate the list with real devices */
|
||||
result = cnh_usbhook_invoke_next(irp);
|
||||
|
||||
/* Iterate to the end of the list and add our stuff keeping real devices */
|
||||
it = usb_get_busses();
|
||||
|
||||
/* Iterate all busses */
|
||||
do {
|
||||
/* First, iterate all devices on each bus and check for all real
|
||||
devices connected which are also part of our hook list */
|
||||
if (it != NULL) {
|
||||
/* Iterate devices on the current bus */
|
||||
struct usb_device* dev = it->devices;
|
||||
|
||||
while (dev != NULL) {
|
||||
struct cnh_usb_emu_virtdev* virt_dev = _cnh_usb_emu_get_virtdev((uint16_t) dev->descriptor.idVendor,
|
||||
(uint16_t) dev->descriptor.idProduct);
|
||||
|
||||
/* Real device connected for a hook */
|
||||
if (virt_dev) {
|
||||
log_debug("Detected real %04X:%04X device for virtual one", dev->descriptor.idVendor,
|
||||
dev->descriptor.idProduct);
|
||||
virt_dev->real_dev_avail = true;
|
||||
}
|
||||
|
||||
dev = dev->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* it == NULL, nothing found on current bus which is our last bus
|
||||
we added on usb_find_busses. We put our fakedevs on that bus */
|
||||
if (it == NULL || it->next == NULL) {
|
||||
struct usb_bus* fakebus;
|
||||
struct usb_device* fakebus_dev_tail;
|
||||
|
||||
/* Create an additional bus for our fakedevs */
|
||||
fakebus = (struct usb_bus*) util_xmalloc(sizeof(struct usb_bus));
|
||||
memset(fakebus, 0, sizeof(struct usb_bus));
|
||||
|
||||
/* Make sure to modify the linked list correctly. If we fuck up,
|
||||
this works fine until Fiesta where they started to iterate
|
||||
everything in a different manner (-> crash) */
|
||||
if (it == NULL) {
|
||||
usb_busses = fakebus;
|
||||
/* Bus empty, no prev bus */
|
||||
fakebus->prev = NULL;
|
||||
} else {
|
||||
/* Append at end of bus chain */
|
||||
it->next = fakebus;
|
||||
/* Keep doubly linked list intact */
|
||||
fakebus->prev = it->next;
|
||||
}
|
||||
|
||||
fakebus_dev_tail = NULL;
|
||||
|
||||
pthread_mutex_lock(&_cnh_usb_emu_virtdevs_lock);
|
||||
|
||||
for (size_t i = 0; i < _cnh_usb_emu_nvirtdevs; i++) {
|
||||
struct cnh_usb_emu_virtdev* virtdev = &_cnh_usb_emu_virtdevs[i];
|
||||
|
||||
virtdev->enumerated = virtdev->ep->enumerate(virtdev->real_dev_avail);
|
||||
|
||||
if (virtdev->enumerated) {
|
||||
if (virtdev->real_dev_avail) {
|
||||
/* Suppress real device and detour everything to
|
||||
our hook */
|
||||
log_debug("Suppressing real device %04X:%04X", virtdev->ep->vid, virtdev->ep->pid);
|
||||
} else {
|
||||
/* Create a fakedev and add it to the bus */
|
||||
struct usb_device* fakedev;
|
||||
|
||||
{
|
||||
/* Print this once. Pump Pro calls this
|
||||
function every second */
|
||||
static int counter = 0;
|
||||
|
||||
if (counter++ == 0) {
|
||||
log_info("Injecting fake usb device %04X:%04X into bus", virtdev->ep->vid,
|
||||
virtdev->ep->pid);
|
||||
} else {
|
||||
counter = 1;
|
||||
}
|
||||
}
|
||||
|
||||
fakedev = _cnh_usb_emu_create_fakedev(virtdev->ep->vid, virtdev->ep->pid);
|
||||
|
||||
/* Append to linked list */
|
||||
if (!fakebus->devices) {
|
||||
/* Set root on bus */
|
||||
fakebus->devices = fakedev;
|
||||
}
|
||||
|
||||
/* Cur element */
|
||||
fakedev->prev = fakebus_dev_tail;
|
||||
fakedev->next = NULL;
|
||||
|
||||
/* Update prev element's next */
|
||||
if (fakebus_dev_tail) {
|
||||
fakebus_dev_tail->next = fakedev;
|
||||
}
|
||||
|
||||
/* tail update */
|
||||
fakebus_dev_tail = fakedev;
|
||||
|
||||
fakedev_count++;
|
||||
}
|
||||
} else {
|
||||
log_debug("Reject enumerate hook for %04X:%04X", virtdev->ep->vid, virtdev->ep->pid);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_usb_emu_virtdevs_lock);
|
||||
|
||||
break;
|
||||
}
|
||||
/* End of if block which handles fakedevs and hooking */
|
||||
|
||||
it = it->next;
|
||||
}
|
||||
while (it != NULL);
|
||||
|
||||
{
|
||||
/* Print this once. Pump Pro calls this
|
||||
function every second */
|
||||
static int counter = 0;
|
||||
|
||||
if (counter++ == 0) {
|
||||
log_info("Added %d fakedevs to usb bus", fakedev_count);
|
||||
} else {
|
||||
counter = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* include our fakedev count as well */
|
||||
return result + fakedev_count;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_open(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
struct cnh_usb_emu_virtdev* virtdev;
|
||||
enum cnh_result result;
|
||||
|
||||
virtdev = _cnh_usb_emu_get_virtdev((uint16_t) irp->open_usb_dev->descriptor.idVendor,
|
||||
(uint16_t) irp->open_usb_dev->descriptor.idProduct);
|
||||
|
||||
if (virtdev == NULL) {
|
||||
return cnh_usbhook_invoke_next(irp);
|
||||
}
|
||||
|
||||
if (!virtdev->enumerated) {
|
||||
log_debug("Virtdev %04X:%04X not enumerated, skipping open", virtdev->ep->vid, virtdev->ep->pid);
|
||||
return cnh_usbhook_invoke_next(irp);
|
||||
}
|
||||
|
||||
result = virtdev->ep->open();
|
||||
|
||||
if (result == CNH_RESULT_SUCCESS) {
|
||||
/* Return handle, use virtual device as fake handle */
|
||||
irp->handle = &virtdev->usb_dev_handle;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_close(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
struct cnh_usb_emu_virtdev* virtdev;
|
||||
|
||||
assert(irp->handle);
|
||||
|
||||
virtdev = _cnh_usb_emu_get_virtdev_by_handle(irp->handle);
|
||||
|
||||
/* Not a virtual device handle, probably real handle from libusb */
|
||||
if (virtdev == NULL) {
|
||||
return cnh_usbhook_invoke_next(irp);
|
||||
}
|
||||
|
||||
virtdev->ep->close();
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_reset(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
struct cnh_usb_emu_virtdev* virtdev;
|
||||
|
||||
assert(irp->handle);
|
||||
|
||||
virtdev = _cnh_usb_emu_get_virtdev_by_handle(irp->handle);
|
||||
|
||||
/* Not a virtual device handle, probably real handle from libusb */
|
||||
if (virtdev == NULL) {
|
||||
return cnh_usbhook_invoke_next(irp);
|
||||
}
|
||||
|
||||
return virtdev->ep->reset();
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usb_emu_usbhook_ctrl_msg(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
struct cnh_usb_emu_virtdev* virtdev;
|
||||
|
||||
assert(irp->handle);
|
||||
|
||||
virtdev = _cnh_usb_emu_get_virtdev_by_handle(irp->handle);
|
||||
|
||||
/* Not a virtual device handle, probably real handle from libusb */
|
||||
if (virtdev == NULL) {
|
||||
return cnh_usbhook_invoke_next(irp);
|
||||
}
|
||||
|
||||
return virtdev->ep->control_msg(irp->ctrl_req_type, irp->ctrl_req, irp->ctrl_value, irp->ctrl_index,
|
||||
&irp->ctrl_buffer, irp->ctrl_timeout);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* Helper functions */
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
void _cnh_usb_emu_init(void)
|
||||
{
|
||||
int expected;
|
||||
|
||||
if (atomic_load(&_cnh_usb_emu_initted) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
expected = 0;
|
||||
|
||||
if (!atomic_compare_exchange_strong(&_cnh_usb_emu_init_in_progress, &expected, 1)) {
|
||||
while (atomic_load(&_cnh_usb_emu_init_in_progress) > 1) {
|
||||
util_time_sleep_us(100);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
|
||||
if (atomic_load(&_cnh_usb_emu_initted) > 0) {
|
||||
/* Revert init in progress, because nothing to do anymore */
|
||||
atomic_store(&_cnh_usb_emu_init_in_progress, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_init(&_cnh_usb_emu_virtdevs_lock, NULL);
|
||||
|
||||
atomic_store(&_cnh_usb_emu_initted, 1);
|
||||
atomic_store(&_cnh_usb_emu_init_in_progress, 0);
|
||||
}
|
||||
|
||||
static struct cnh_usb_emu_virtdev* _cnh_usb_emu_get_virtdev(uint16_t vid, uint16_t pid)
|
||||
{
|
||||
struct cnh_usb_emu_virtdev* virtdev;
|
||||
struct cnh_usb_emu_virtdev* tmp;
|
||||
|
||||
virtdev = NULL;
|
||||
|
||||
pthread_mutex_lock(&_cnh_usb_emu_virtdevs_lock);
|
||||
|
||||
for (size_t i = 0; i < _cnh_usb_emu_nvirtdevs; i++) {
|
||||
tmp = &_cnh_usb_emu_virtdevs[i];
|
||||
|
||||
if (tmp->ep->vid == vid && tmp->ep->pid == pid) {
|
||||
virtdev = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_usb_emu_virtdevs_lock);
|
||||
|
||||
return virtdev;
|
||||
}
|
||||
|
||||
static struct cnh_usb_emu_virtdev* _cnh_usb_emu_get_virtdev_by_handle(usb_dev_handle* handle)
|
||||
{
|
||||
struct cnh_usb_emu_virtdev* virtdev;
|
||||
struct cnh_usb_emu_virtdev* tmp;
|
||||
|
||||
virtdev = NULL;
|
||||
|
||||
pthread_mutex_lock(&_cnh_usb_emu_virtdevs_lock);
|
||||
|
||||
for (size_t i = 0; i < _cnh_usb_emu_nvirtdevs; i++) {
|
||||
tmp = &_cnh_usb_emu_virtdevs[i];
|
||||
|
||||
if (tmp == handle->virtdev) {
|
||||
virtdev = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_cnh_usb_emu_virtdevs_lock);
|
||||
|
||||
return virtdev;
|
||||
}
|
||||
|
||||
static struct usb_device* _cnh_usb_emu_create_fakedev(uint16_t vid, uint16_t pid)
|
||||
{
|
||||
struct usb_device* fakedev = (struct usb_device*) util_xmalloc(sizeof(struct usb_device));
|
||||
memset(fakedev, 0, sizeof(struct usb_device));
|
||||
|
||||
/* Populate fakedev */
|
||||
fakedev->descriptor.idVendor = vid;
|
||||
fakedev->descriptor.idProduct = pid;
|
||||
|
||||
/* Mark as fakedev */
|
||||
fakedev->descriptor.bcdDevice = 0xBEEF;
|
||||
|
||||
fakedev->config = (struct usb_config_descriptor*) util_xmalloc(
|
||||
sizeof(struct usb_config_descriptor));
|
||||
memset(fakedev->config, 0, sizeof(struct usb_config_descriptor));
|
||||
|
||||
fakedev->config->interface = (struct usb_interface*) util_xmalloc(
|
||||
sizeof(struct usb_interface));
|
||||
memset(fakedev->config->interface, 0, sizeof(struct usb_interface));
|
||||
|
||||
fakedev->config->interface->altsetting = (struct usb_interface_descriptor*)
|
||||
util_xmalloc(sizeof(struct usb_interface_descriptor));
|
||||
memset(fakedev->config->interface->altsetting, 0,
|
||||
sizeof(struct usb_interface_descriptor));
|
||||
|
||||
return fakedev;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Hook implementation for emulating usb devices on libusb 0.1
|
||||
*/
|
||||
#ifndef CNH_USB_EMU_H
|
||||
#define CNH_USB_EMU_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "capnhook/hook/iobuf.h"
|
||||
#include "capnhook/hook/usbhook.h"
|
||||
|
||||
/**
|
||||
* Virtual device endpoint
|
||||
*/
|
||||
struct cnh_usb_emu_virtdev_ep {
|
||||
uint16_t pid;
|
||||
uint16_t vid;
|
||||
bool (*enumerate)(bool real_exists);
|
||||
enum cnh_result (*open)(void);
|
||||
enum cnh_result (*reset)(void);
|
||||
enum cnh_result (*control_msg)(int request_type, int request, int value, int index, struct cnh_iobuf* buffer,
|
||||
int timeout);
|
||||
void (*close)(void);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a pointer to a virtual device endpoint implementing handler functions
|
||||
*
|
||||
* @param virtdevep Virtual device endpoint to add
|
||||
*/
|
||||
void cnh_usb_emu_add_virtdevep(const struct cnh_usb_emu_virtdev_ep* virtdevep);
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for emulating usb devices
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_usb_emu(struct cnh_usbhook_irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "capnhook/hooklib/usb-init-fix.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static enum cnh_result _cnh_usb_init_fix_default(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usb_init_fix_init(struct cnh_usbhook_irp* irp);
|
||||
static enum cnh_result _cnh_usbio_emu_find_busses(struct cnh_usbhook_irp* irp);
|
||||
|
||||
static const cnh_usbhook_fn_t _hook_usb_init_fix_handlers[10] = {
|
||||
[CNH_USBHOOK_IRP_OP_INIT] = _cnh_usb_init_fix_init,
|
||||
[CNH_USBHOOK_IRP_OP_FIND_BUSSES] = _cnh_usbio_emu_find_busses,
|
||||
[CNH_USBHOOK_IRP_OP_FIND_DEVICES] = _cnh_usb_init_fix_default,
|
||||
[CNH_USBHOOK_IRP_OP_OPEN] = _cnh_usb_init_fix_default,
|
||||
[CNH_USBHOOK_IRP_OP_CLOSE] = _cnh_usb_init_fix_default,
|
||||
[CNH_USBHOOK_IRP_OP_RESET] = _cnh_usb_init_fix_default,
|
||||
[CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE] = _cnh_usb_init_fix_default,
|
||||
[CNH_USBHOOK_IRP_OP_SET_CONFIGURATION] = _cnh_usb_init_fix_default,
|
||||
[CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE] = _cnh_usb_init_fix_default,
|
||||
[CNH_USBHOOK_IRP_OP_CTRL_MSG] = _cnh_usb_init_fix_default,
|
||||
};
|
||||
|
||||
static int _cnh_usb_init_fix_init_count = 0;
|
||||
|
||||
enum cnh_result cnh_usb_init_fix(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
cnh_usbhook_fn_t handler;
|
||||
|
||||
handler = _hook_usb_init_fix_handlers[irp->op];
|
||||
|
||||
assert(handler != NULL);
|
||||
|
||||
return handler(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usb_init_fix_default(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
return cnh_usbhook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usb_init_fix_init(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
/* On NXA, one programmer/multiple programmers had the bright idea to call
|
||||
usb_init twice for reasons...
|
||||
This breaks stuff in the usb-0.1 backend resulting in usb_find_busses
|
||||
not even finding native devices anymore.
|
||||
Make sure we call the real usb_init ONCE ONLY to fix that fuck up */
|
||||
_cnh_usb_init_fix_init_count++;
|
||||
|
||||
if (_cnh_usb_init_fix_init_count <= 1) {
|
||||
return cnh_usbhook_invoke_next(irp);
|
||||
} else {
|
||||
/* Pump Pro is even worse and calls usb_init approx. every second...
|
||||
avoid overflow of counter if we really leave this running forever.
|
||||
Furthermore, this avoids printing the log message below with ever
|
||||
init call */
|
||||
if (_cnh_usb_init_fix_init_count > 2) {
|
||||
_cnh_usb_init_fix_init_count = 2;
|
||||
} else {
|
||||
log_info("usb_init called multiple times, blocking further calls...");
|
||||
}
|
||||
|
||||
return CNH_RESULT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
static enum cnh_result _cnh_usbio_emu_find_busses(struct cnh_usbhook_irp* irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
|
||||
/* Fixes quirks with usb_init called multiple times on NXA */
|
||||
if (_cnh_usb_init_fix_init_count <= 1) {
|
||||
result = cnh_usbhook_invoke_next(irp);
|
||||
} else {
|
||||
result = CNH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Hook implementation to fix buggy applications that call usb_init repeatetly
|
||||
*/
|
||||
#ifndef CNH_USB_INIT_FIX_H
|
||||
#define CNH_USB_INIT_FIX_H
|
||||
|
||||
#include "capnhook/hook/usbhook.h"
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module to fix the usb_init bug
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_usb_init_fix(struct cnh_usbhook_irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#define LOG_MODULE "cnh-usbhook-mon"
|
||||
|
||||
#include "capnhook/hooklib/usbhook-mon.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static const char* _cnh_usbhook_mon_irp_op_str[] = {
|
||||
"init",
|
||||
"find_busses",
|
||||
"find_devices",
|
||||
"open",
|
||||
"close",
|
||||
"reset",
|
||||
"set_altinterface",
|
||||
"set_configuration",
|
||||
"claim_interface",
|
||||
"ctrl_msg"
|
||||
};
|
||||
|
||||
enum cnh_result cnh_usbhook_mon(struct cnh_usbhook_irp *irp)
|
||||
{
|
||||
enum cnh_result result;
|
||||
|
||||
log_debug("[before][%s] next_handler %d, find_busses_res_num_busses %d, find_devices_res_num_devices %d, "
|
||||
"open_usb_dev %p, handle %p, set_altinterface %d, set_configuration %d, claim_interface %d, ctrl_req_type %d, "
|
||||
"ctrl_req %d, ctrl_value %d, ctrl_index %d, ctrl_buffer(bytes %p, nbytes %d, pos %d), ctrl_timeout %d",
|
||||
_cnh_usbhook_mon_irp_op_str[irp->op], irp->next_handler, irp->find_busses_res_num_busses,
|
||||
irp->find_devices_res_num_devices, irp->open_usb_dev, irp->handle, irp->set_altinterface,
|
||||
irp->set_configuration, irp->claim_interface, irp->ctrl_req_type, irp->ctrl_req, irp->ctrl_value,
|
||||
irp->ctrl_index, irp->ctrl_buffer.bytes, irp->ctrl_buffer.nbytes, irp->ctrl_buffer.pos, irp->ctrl_timeout);
|
||||
|
||||
result = cnh_usbhook_invoke_next(irp);
|
||||
|
||||
if (result != CNH_RESULT_SUCCESS) {
|
||||
log_error("[after][%s] result %d, next_handler %d, find_busses_res_num_busses %d, "
|
||||
"find_devices_res_num_devices %d, open_usb_dev %p, handle %p, set_altinterface %d, set_configuration %d, "
|
||||
"claim_interface %d, ctrl_req_type %d, ctrl_req %d, ctrl_value %d, ctrl_index %d, ctrl_buffer(bytes %p, "
|
||||
"nbytes %d, pos %d), ctrl_timeout %d", _cnh_usbhook_mon_irp_op_str[irp->op], result, irp->next_handler,
|
||||
irp->find_busses_res_num_busses, irp->find_devices_res_num_devices, irp->open_usb_dev, irp->handle,
|
||||
irp->set_altinterface, irp->set_configuration, irp->claim_interface, irp->ctrl_req_type, irp->ctrl_req,
|
||||
irp->ctrl_value, irp->ctrl_index, irp->ctrl_buffer.bytes, irp->ctrl_buffer.nbytes, irp->ctrl_buffer.pos,
|
||||
irp->ctrl_timeout);
|
||||
} else {
|
||||
log_debug("[after][%s] result %d, next_handler %d, find_busses_res_num_busses %d, "
|
||||
"find_devices_res_num_devices %d, open_usb_dev %p, handle %p, set_altinterface %d, set_configuration %d, "
|
||||
"claim_interface %d, ctrl_req_type %d, ctrl_req %d, ctrl_value %d, ctrl_index %d, ctrl_buffer(bytes %p, "
|
||||
"nbytes %d, pos %d), ctrl_timeout %d", _cnh_usbhook_mon_irp_op_str[irp->op], result, irp->next_handler,
|
||||
irp->find_busses_res_num_busses, irp->find_devices_res_num_devices, irp->open_usb_dev, irp->handle,
|
||||
irp->set_altinterface, irp->set_configuration, irp->claim_interface, irp->ctrl_req_type, irp->ctrl_req,
|
||||
irp->ctrl_value, irp->ctrl_index, irp->ctrl_buffer.bytes, irp->ctrl_buffer.nbytes, irp->ctrl_buffer.pos,
|
||||
irp->ctrl_timeout);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Hook implementation to monitor libusb 0.1 calls
|
||||
*/
|
||||
#ifndef CAPNHOOK_USBHOOK_MON_H
|
||||
#define CAPNHOOK_USBHOOK_MON_H
|
||||
|
||||
#include "capnhook/hook/usbhook.h"
|
||||
|
||||
/**
|
||||
* Hook function to add to the hook module for monitoring libusb calls
|
||||
*
|
||||
* @param irp I/O request package of hook module received
|
||||
* @return Result of operation
|
||||
*/
|
||||
enum cnh_result cnh_usbhook_mon(struct cnh_usbhook_irp *irp);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user