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