📜 ⬆️ ⬇️

Interception of Linux kernel functions using exceptions (do-it-yourself kprobes)

Intercepting kernel functions is a basic method that allows you to redefine (complement) its various mechanisms. Taking into account the fact that, with the exception of small architecture-dependent parts, the Linux kernel is written almost entirely in C, it can be argued that to embed into most of the kernel components, it is enough to have the ability to intercept the relevant ULA functions that implement this or that logic.

This article is a practical generalization of the articles presented earlier:
  1. Managed by PageFault in the Linux kernel
  2. A kosher way to modify write-protected areas of the Linux kernel

Further, it will be discussed how the use of these materials can be applied in providing the ability to intercept functions of the Linux kernel.

Short about interception



The purpose of intercepting any function is to get control at the time it is called. Further actions depend on specific tasks. In some cases, it is necessary to replace the system implementation of the algorithm with its own, in others - to supplement it. In this case, it is important to leave the possibility of using the intercepted function for its own purposes.
')
The traditional approach to intercepting is to use the concept of “wrappers”, which allows pre-and post-processing to be implemented while preserving the ability to access the original functionality represented by the intercepted function.

As is known, the basis of most methods for intercepting functions is patching - modifying the kernel code so that it can transfer control to the interceptor function when the target function is called. In this case, due to the developed command system of the x86 architecture, it is possible that there are many options for changing the flow JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .

JMP - : ).

, , , . , , " ".



, , , , .

, UD2 . ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .

JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .

JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .

JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .

JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .

JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
   JMP -    : ). 

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .
JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .

JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .

JMP - : ).

, , , . , , " ".



, , , , .

, UD2
. ( #UD ) . Linux kprobes , , INT3 .

, inode_permission :

ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 55 push %rbp ffffffff8118dd81: 48 89 e5 mov %rsp,%rbp ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

,
ffffffff8118dd80 <inode_permission>: ffffffff8118dd80: 0f b0 ud2 => #UD ffffffff8118dd82: 89 e5 ??? ffffffff8118dd84: e8 f7 b7 4f 00 callq ffffffff81689580 <mcount> ffffffff8118dd89: 40 f6 c6 02 test $0x2,%sil

UD2 , ffffffff8118dd80 , , .



, , . , , . (), - . , , , , .

, :

#include <linux/fs.h> // inode_permission() prototype lives here DECLARE_KHOOK(inode_permission); int khook_inode_permission(struct inode * inode, int mode) { int result; KHOOK_USAGE_INC(inode_permission); ... result = KHOOK_ORIGIN(inode_permission, inode, mode); ... KHOOK_USAGE_DEC(inode_permission); return result; }

, inode_permission . , :
DECLARE_KHOOK KHOOK_ORIGIN KHOOK_USAGE_INC KHOOK_USAGE_DEC
, .

DECLARE_KHOOK(...) , , -, , khook_...

KHOOK_ORIGIN(...) () , , .

KHOOK_USAGE_INC(...) KHOOK_USAGE_DEC(...) , , , .



, DECLARE_KHOOK(...) :

typedef struct { /* tagret's name */ char * name; /* target's insn length */ int length; /* target's handler address */ void * handler; /* target's address and rw-mapping */ void * target; void * target_map; /* origin's address and rw-mapping */ void * origin; void * origin_map; atomic_t usage; } khookstr_t;

: name - , length - , handler - -, target - , target_map - , origin - -, , origin_map - , usage - "", .

DECLARE_KHOOK(...) , :

#define __DECLARE_TARGET_ALIAS(t) \ void __attribute__((alias("khook_"#t))) khook_alias_##t(void) #define __DECLARE_TARGET_ORIGIN(t) \ void notrace khook_origin_##t(void) { \ asm volatile ( \ ".rept 0x20\n" \ ".byte 0x90\n" \ ".endr\n" \ ); \ } #define __DECLARE_TARGET_STRUCT(t) \ khookstr_t __attribute__((unused,section(".khook"),aligned(1))) __khook_##t #define DECLARE_KHOOK(t) \ __DECLARE_TARGET_ALIAS(t); \ __DECLARE_TARGET_ORIGIN(t); \ __DECLARE_TARGET_STRUCT(t) = { \ .name = #t, \ .handler = khook_alias_##t, \ .origin = khook_origin_##t, \ .usage = ATOMIC_INIT(0), \ }

__DECLARE_TARGET_ALIAS(...) , __DECLARE_TARGET_ORIGIN(...) (32 nop'). __DECLARE_TARGET_STRUCT(...) , section (".khook"). DECLARE_KHOOK(...) .

, , . :

extern khookstr_t __khook_start[], __khook_finish[]; #define khook_for_each(item) \ for (item = __khook_start; item < __khook_finish; item++)

, . , , . :

static int init_hooks(void) { khookstr_t * s; int num_exentries = 0; struct exception_table_entry * extable; extable = (void *)pfnModuleAlloc(sizeof(*extable) * (__khook_finish - __khook_start)); if (extable == NULL) { debug("Memory allocation failed\n"); return -ENOMEM; } khook_for_each(s) { s->target = get_symbol_address(s->name); if (s->target) { s->target_map = map_writable(s->target, 32); s->origin_map = map_writable(s->origin, 32); if (s->target_map && s->origin_map) { if (init_origin_stub(s) == 0) { struct exception_table_entry * entry = &extable[num_exentries++]; /* OK, the stub is initialized */ atomic_inc(&s->usage); extable_make_insn(entry, (unsigned long)s->target); extable_make_fixup(entry, (unsigned long)s->handler); continue; } } } debug("Failed to initalize \"%s\" hook", s->name); } pfnSortExtable(extable, extable + num_exentries); THIS_MODULE->extable = extable; THIS_MODULE->num_exentries = num_exentries; /* apply patches */ stop_machine(do_init_hooks, NULL, NULL); return 0; }

, , stop_machine :

static int do_init_hooks(void * arg) { khookstr_t * s; khook_for_each(s) { if (atomic_read(&s->usage) == 1) x86_put_ud2(s->target_map); } return 0; }

do_init_hooks , . , - UD2 .

, "", - - ( khook_origin_... ). , ( JMP ) "" . , , , :

static int init_origin_stub(khookstr_t * s) { ud_t ud; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, (void *)s->target, 32); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Iud2 || ud.mnemonic == UD_Iint3) { debug("It seems that \"%s\" is not a hooking virgin\n", s->name); return -EINVAL; } #define UD2_INSN_LEN 2 s->length += ud_insn_len(&ud); if (s->length >= UD2_INSN_LEN) { memcpy(s->origin_map, s->target, s->length); x86_put_jmp(s->origin_map + s->length, s->origin + s->length, s->target + s->length); break; } } return 0; }

, :

#define KHOOK_ORIGIN(t, ...) \ ((typeof(t) *)__khook_##t.origin)(__VA_ARGS__)



, Linux. , github , .

Source: https://habr.com/ru/post/206778/


All Articles