LSM实现的原理
LSM
在内核中的关键位置插入了一系列的检测点, 即security_xxxx()
的函数
例如在用户执行一个新程序时, sys_execve
执行到search_binary_handler
时, 会调用LSM
的security_bprm_check()
函数检测是否允许继续执行
int search_binary_handler(struct linux_binprm *bprm)
{
...
retval = security_bprm_check(bprm);
if (retval)
return retval;
...
}
security_bprm_check
中会使用call_int_hook
宏调用所有注册到系统中的LSM hook
int security_bprm_check(struct linux_binprm *bprm)
{
int ret;
ret = call_int_hook(bprm_check_security, 0, bprm);
if (ret)
return ret;
return ima_bprm_check(bprm);
}
call_int_hook
实现如下
#define call_int_hook(FUNC, IRC, ...) ({ \
int RC = IRC; \
do { \
struct security_hook_list *P; \
\
list_for_each_entry(P, &security_hook_heads.FUNC, list) { \
RC = P->hook.FUNC(__VA_ARGS__); \
if (RC != 0) \
break; \
} \
} while (0); \
RC; \
})
security_hook_heads
里保存了所有注册到lsm的hook函数, 这些hook函数使用list串联起来,
例如在security_bprm_check
检测点中, 就会调用security_hook_heads
中的bprm_check_security
链表中的所有hook函数
在独立模块中使用LSM
正常使用LSM
需要使用security_add_hooks
接口函数
由于LSM
相关的接口并没有导出, 正常情况下是没办法在独立模块中使用LSM
的
但是, 通过上面的原理分析可以看到, 我们只要把需要hook的函数手动插入到系统security_hook_heads
中即可实现security_add_hooks
一样的效果
步骤如下:
- 获取
security_hook_heads
结构的地址, 该结构里存放了LSM
系统所有的hook函数 - 初始化我们自己的
security_hook_list
- 把我们的
security_hook_list
插入到LSM
的security_hook_heads
中即可
具体代码如下
/**
* @file main.c
* @author lee
* @brief 使用LSM监控进程的启动
* @version 0.1
* @date 2022-08-24
*
* @copyright Copyright (c) 2022
*
*/
// 由于系统没有导出security_hook_heads
// 需要我们自己获取, 使用kallsyms_lookup_name
struct security_hook_heads* my_security_hook_heads;
//
// LSM Hook函数
// security_bprm_check在execve->search_binary_handler实际加载可执行文件之前被调用
// 此时cmdline已经被复制到进程地址空间
// 在这里实现进程监控
//
static int my_security_bprm_check(struct linux_binprm *bprm)
{
char *cmd = kstrdup_quotable_cmdline(current, GFP_KERNEL);
// klogw(log_tag "IN");
klogw(log_tag "[%s] [%s] [%s]", current->comm, bprm->filename, cmd);
kfree(cmd);
return 0;
}
// 定义我们的hook结构, 用于插入到系统的hook链表
static struct security_hook_list my_hook_list;
/*
* 模块初始化
*/
static __init int main_init(void)
{
int rv = -1;
long oldcr0;
// 获取security_hook_heads结构的地址
my_security_hook_heads = (struct security_hook_heads*)kallsyms_lookup_name("security_hook_heads");
// 初始化security_hook_list
my_hook_list.head = &my_security_hook_heads->bprm_check_security;
my_hook_list.hook.bprm_check_security = my_security_bprm_check;
// 关闭写保护, 把我们的hook插入到security_hook_heads
oldcr0 = write_protect_disable();
// 5.4内核是hash list
hlist_add_tail_rcu(&my_hook_list.list, my_hook_list.head);
write_protect_enable(oldcr0);
klogw(log_tag "kernel module load.");
// 设置成功标识
rv = 0x0;
goto out;
out:
return rv;
}
static __exit void main_exit(void)
{
long oldcr0;
oldcr0 = write_protect_disable();
hlist_del_rcu(&my_hook_list.list);
write_protect_enable(oldcr0);
klogw(log_tag "kernel module unload.");
}
module_init(main_init);
module_exit(main_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Kernel Module");
MODULE_AUTHOR("lee");