Version 1.08 with public source code release.

This commit is contained in:
icex2
2020-10-03 20:56:55 +02:00
parent 656148121d
commit 762bf140c0
555 changed files with 55502 additions and 3 deletions
+12
View File
@@ -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
+662
View File
@@ -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;
}
+90
View File
@@ -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
+447
View File
@@ -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;
}
+72
View File
@@ -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
+253
View File
@@ -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;
}
+162
View File
@@ -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
+552
View File
@@ -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;
}
+90
View File
@@ -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
+57
View File
@@ -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
+179
View File
@@ -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);
}
+84
View File
@@ -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
+99
View File
@@ -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;
}
}
+52
View File
@@ -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
+58
View File
@@ -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]);
}
}
+26
View File
@@ -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
+624
View File
@@ -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;
}
+75
View File
@@ -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
+57
View File
@@ -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;
}
+17
View File
@@ -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
+96
View File
@@ -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;
}
+35
View File
@@ -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
+43
View File
@@ -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;
}
+17
View File
@@ -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
+54
View File
@@ -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;
}
+17
View File
@@ -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
+371
View File
@@ -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;
}
+44
View File
@@ -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
+494
View File
@@ -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;
}
+42
View File
@@ -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
+80
View File
@@ -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;
}
+17
View File
@@ -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
+55
View File
@@ -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;
}
+17
View File
@@ -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