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       ,    . 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