अपवादों का उपयोग करके लिनक्स कर्नेल फ़ंक्शन को इंटरसेप्ट करना (do-it-yourself kprobes)

कर्नेल फ़ंक्शंस का अवरोधन एक बुनियादी तरीका है जो आपको इसके विभिन्न तंत्रों को फिर से परिभाषित करने (पूरक) करने की अनुमति देता है। इस तथ्य के आधार पर कि, छोटे आर्किटेक्चर-निर्भर भागों के अपवाद के साथ, लिनक्स कर्नेल लगभग पूरी तरह से सी में लिखा गया है, यह तर्क दिया जा सकता है कि अधिकांश कर्नेल घटकों में एम्बेड करने के लिए, यह या उस तर्क को लागू करने वाले संबंधित जावा कार्यों को बाधित करने में सक्षम होने के लिए पर्याप्त है।

यह लेख पहले के लेखों का व्यावहारिक सारांश है:
  1. लिनक्स कर्नेल में प्रबंधित पेजफॉल्ट
  2. लिनक्स कर्नेल के लेखन-संरक्षित क्षेत्रों को संशोधित करने का कोषेर तरीका

अगला, हम विचार करेंगे कि इन सामग्रियों का उपयोग लिनक्स कर्नेल के कार्यों को बाधित करने की क्षमता प्रदान करने में कैसे लागू हो सकता है।

अंतरविरोध के बारे में संक्षेप में



किसी भी फ़ंक्शन को इंटरसेप्ट करने का उद्देश्य उसके कॉल के समय पर नियंत्रण प्राप्त करना है। आगे की क्रियाएं विशिष्ट कार्यों पर निर्भर करती हैं। कुछ मामलों में, अपने स्वयं के साथ एल्गोरिदम के सिस्टम कार्यान्वयन को बदलने के लिए आवश्यक है, दूसरों में - पूरक करने के लिए। इस मामले में, अपने स्वयं के प्रयोजनों के लिए इंटरसेप्टेड फ़ंक्शन का उपयोग करने की संभावना को छोड़ना महत्वपूर्ण है।

"रैपर" की अवधारणा के उपयोग के लिए दृष्टिकोण अंतरविरोध के कार्यान्वयन में पारंपरिक हो गया है, जो पूर्व-और बाद के प्रसंस्करण को लागू करने की अनुमति देता है, जबकि अंतःक्रियात्मक फ़ंक्शन द्वारा प्रस्तुत मूल कार्यक्षमता तक पहुंचने की क्षमता बनाए रखता है।

जैसा कि आप जानते हैं, इंटरसेप्टिंग फ़ंक्शन के अधिकांश तरीकों का आधार पैचिंग है - कर्नेल कोड को इस तरह से संशोधित करना कि टारगेट फ़ंक्शन को कॉल करने पर इंटरसेप्टर फ़ंक्शन को नियंत्रण स्थानांतरित करने की क्षमता प्रदान करें। इसी समय, विकसित x86 आर्किटेक्चर कमांड सिस्टम के कारण, निष्पादन प्रवाह में कई संभावित भिन्नताएं हैं (हां,) 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/In206778/


All Articles