static inline void rw_enable( void ) { asm( "cli \n" "pushl %eax \n" "movl %cr0, %eax \n" "andl $0xfffeffff, %eax \n" "movl %eax, %cr0 \n" "popl %eax" ); } static inline void rw_disable( void ) { asm( "pushl %eax \n" "movl %cr0, %eax \n" "orl $0x00010000, %eax \n" "movl %eax, %cr0 \n" "popl %eax \n" "sti " ); }
#ifdef __i386__ // ... , #else static inline void rw_enable( void ) { asm( "cli \n" "pushq %rax \n" "movq %cr0, %rax \n" "andq $0xfffffffffffeffff, %rax \n" "movq %rax, %cr0 \n" "popq %rax " ); } static inline void rw_disable( void ) { asm( "pushq %rax \n" "movq %cr0, %rax \n" "xorq $0x0000000000001000, %rax \n" "movq %rax, %cr0 \n" "popq %rax \n" "sti " ); } #endif
#include <linux/preempt.h> #include <asm/paravirt.h> #include <asm-generic/bug.h> #include <linux/version.h> static inline unsigned long native_pax_open_kernel( void ) { unsigned long cr0; preempt_disable(); barrier(); cr0 = read_cr0() ^ X86_CR0_WP; BUG_ON( unlikely( cr0 & X86_CR0_WP ) ); write_cr0( cr0 ); return cr0 ^ X86_CR0_WP; } static inline unsigned long native_pax_close_kernel( void ) { unsigned long cr0; cr0 = read_cr0() ^ X86_CR0_WP; BUG_ON( unlikely( !( cr0 & X86_CR0_WP ) ) ); write_cr0( cr0 ); barrier(); #if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) preempt_enable_no_resched(); #else preempt_count_dec(); #endif return cr0 ^ X86_CR0_WP; }
#ifdef MODULE /* * Modules have no business playing preemption tricks. */ #undef sched_preempt_enable_no_resched #undef preempt_enable_no_resched
[ 337.230937] ------------[ cut here ]------------ [ 337.230949] WARNING: CPU: 1 PID: 3410 at /build/buildd/linux-lts-utopic-3.16.0/init/main.c:802 do_one_initcall+0x1cb/0x1f0() [ 337.230955] initcall rw_init+0x0/0x1000 [srw] returned with preemption imbalance
static inline unsigned long native_pai_open_kernel( void ) { unsigned long cr0; local_irq_disable(); barrier(); cr0 = read_cr0() ^ X86_CR0_WP; BUG_ON( unlikely( cr0 & X86_CR0_WP ) ); write_cr0( cr0 ); return cr0 ^ X86_CR0_WP; } static inline unsigned long native_pai_close_kernel( void ) { unsigned long cr0; cr0 = read_cr0() ^ X86_CR0_WP; BUG_ON( unlikely( !( cr0 & X86_CR0_WP ) ) ); write_cr0( cr0 ); barrier(); local_irq_enable(); return cr0 ^ X86_CR0_WP; }
#include <asm/pgtable_types.h> #include <asm/tlbflush.h> static inline void mem_setrw( void **table ) { unsigned int l; pte_t *pte = lookup_address( (long unsigned int)table, &l ); pte->pte |= _PAGE_RW; __flush_tlb_one( (unsigned long)table ); } static inline void mem_setro( void **table ) { unsigned int l; pte_t *pte = lookup_address( (long unsigned int)table, &l ); pte->pte &= ~_PAGE_RW; __flush_tlb_one( (unsigned long)table ); }
static void *map_writable( void *addr, size_t len ) { void *vaddr; int nr_pages = DIV_ROUND_UP( offset_in_page( addr ) + len, PAGE_SIZE ); struct page **pages = kmalloc( nr_pages * sizeof(*pages), GFP_KERNEL ); void *page_addr = (void*)( (unsigned long)addr & PAGE_MASK ); int i; if( pages == NULL ) return NULL; for( i = 0; i < nr_pages; i++ ) { if( __module_address( (unsigned long)page_addr ) == NULL ) { pages[ i ] = virt_to_page( page_addr ); WARN_ON( !PageReserved( pages[ i ] ) ); } else { pages[i] = vmalloc_to_page(page_addr); } if( pages[ i ] == NULL ) { kfree( pages ); return NULL; } page_addr += PAGE_SIZE; } vaddr = vmap( pages, nr_pages, VM_MAP, PAGE_KERNEL ); kfree( pages ); if( vaddr == NULL ) return NULL; return vaddr + offset_in_page( addr ); } static void unmap_writable( void *addr ) { void *page_addr = (void*)( (unsigned long)addr & PAGE_MASK ); vfree( page_addr ); }
#include "rw_cr0.c" #include "rw_pte.c" #include "rw_pax.c" #include "rw_map.c" #include "rw_pai.c" #define PREFIX "! " #define LOG(...) printk( KERN_INFO PREFIX __VA_ARGS__ ) #define ERR(...) printk( KERN_ERR PREFIX __VA_ARGS__ ) #define __NR_rw_test 31 // sys_call_table static int mode = 0; module_param( mode, uint, 0 ); #define do_write( addr, val ) { \ LOG( "writing address %p\n", addr ); \ *addr = val; \ } static bool write( void** addr, void* val ) { switch( mode ) { case 0: rw_enable(); do_write( addr, val ); rw_disable(); return true; case 1: native_pax_open_kernel(); do_write( addr, val ); native_pax_close_kernel(); return true; case 2: mem_setrw( addr ); do_write( addr, val ); mem_setro( addr ); return true; case 3: addr = map_writable( (void*)addr, sizeof( val ) ); if( NULL == addr ) { ERR( "wrong mapping\n" ); return false; } do_write( addr, val ); unmap_writable( addr ); return true; case 4: native_pai_open_kernel(); do_write( addr, val ); native_pai_close_kernel(); return true; default: ERR( "illegal mode %d\n", mode ); return false; } } static int __init rw_init( void ) { void **taddr; // sys_call_table asmlinkage long (*sys_ni_syscall) ( void ); // __NR_rw_test if( NULL == ( taddr = (void**)kallsyms_lookup_name( "sys_call_table" ) ) ) { ERR( "sys_call_table not found\n" ); return -EFAULT; } LOG( "sys_call_table address = %p\n", taddr ); sys_ni_syscall = (void*)taddr[ __NR_rw_test ]; // if( !write( taddr + __NR_rw_test, (void*)0x12345 ) ) return -EINVAL; LOG( "modified sys_call_table[%d] = %p\n", __NR_rw_test, taddr[ __NR_rw_test ] ); if( !write( taddr + __NR_rw_test, (void*)sys_ni_syscall ) ) return -EINVAL; LOG( "restored sys_call_table[%d] = %p\n", __NR_rw_test, taddr[ __NR_rw_test ] ); return -EPERM; } module_init( rw_init );
$ uname -r 3.16.0-48-generic $ uname -m x86_64 $ sudo insmod srw.ko mode=0 insmod: ERROR: could not insert module srw.ko: Operation not permitted $ dmesg | tail -n6 [ 7258.575977] ! detected 64-bit platform [ 7258.584504] ! sys_call_table address = ffffffff81801460 [ 7258.584579] ! writing address ffffffff81801558 [ 7258.584653] ! modified sys_call_table[31] = 0000000000012345 [ 7258.584654] ! writing address ffffffff81801558 [ 7258.584666] ! restored sys_call_table[31] = ffffffff812db550 $ sudo insmod srw.ko mode=2 insmod: ERROR: could not insert module srw.ko: Operation not permitted $ dmesg | tail -n6 [ 7282.625539] ! detected 64-bit platform [ 7282.633020] ! sys_call_table address = ffffffff81801460 [ 7282.633129] ! writing address ffffffff81801558 [ 7282.633178] ! modified sys_call_table[31] = 0000000000012345 [ 7282.633228] ! writing address ffffffff81801558 [ 7282.633291] ! restored sys_call_table[31] = ffffffff812db550 $ sudo insmod srw.ko mode=3 insmod: ERROR: could not insert module srw.ko: Operation not permitted $ dmesg | tail -n6 [ 7297.040272] ! detected 64-bit platform [ 7297.059764] ! sys_call_table address = ffffffff81801460 [ 7297.065930] ! writing address ffffc900001e6558 [ 7297.066000] ! modified sys_call_table[31] = 0000000000012345 [ 7297.066035] ! writing address ffffc9000033d558 [ 7297.066073] ! restored sys_call_table[31] = ffffffff812db550 $ sudo insmod srw.ko mode=4 insmod: ERROR: could not insert module srw.ko: Operation not permitted $ dmesg | tail -n6 [ 7309.831119] ! detected 64-bit platform [ 7309.836299] ! sys_call_table address = ffffffff81801460 [ 7309.836311] ! writing address ffffffff81801558 [ 7309.836359] ! modified sys_call_table[31] = 0000000000012345 [ 7309.836368] ! writing address ffffffff81801558 [ 7309.836424] ! restored sys_call_table[31] = ffffffff812db550
Source: https://habr.com/ru/post/269193/
All Articles