很久以前接触到日系手机内核里面各式各样的LSM的时候,就对LSM背后到底是什么,产生了一些奇怪的兴趣。
最近有些得空,读了一些关于LSM,钩子,内核相关的东西,再搭上以前对FJSEC这些OEM LSM的逆向的经验。
尝试胡乱弄个LSM出来看看?也许有用也也许没用,又成为了一个慢慢挖坑再填坑的作品。
为啥叫NGLSM?NG是我很早用的一个网名,全称是Nango,不懂当初为什么会想到这个名字,但是既然想到了,那么就这样叫吧。
// 2023.06.08-00.00.01/FIRST RELEASE
// 2023.06.11-00.01.01/ADD PROTECT OF SETSOCKOPT/GETSOCKOPT
// 2025.03.16-00.02.01/ADD PROTECT OF /DATA/LOCAL/TMP FOLDER
#include <linux/lsm_hooks.h>
#include <linux/binfmts.h>
#include <linux/path.h>
#include <linux/cred.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/bprm.h>
#include <linux/socket.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/string.h>
#define STATUS_FILE "/data/security/lsm/status"
#define STATUS_PERMISSIVE "PERMISSIVE"
#define STATUS_ENFORCING "ENFORCING"
#define STATUS_MAX 100
#define TASK_FLAG_RESTRICTED (1 << 30)
static inline bool is_restricted_task(struct task_struct *task)
{
return (task->flags & TASK_FLAG_RESTRICTED) != 0;
}
static int nglsm_status(void)
{
struct file *f;
char buf[STATUS_MAX];
loff_t pos = 0;
int status = -1;
ssize_t ret;
f = filp_open(STATUS_FILE, O_RDONLY, 0);
if (IS_ERR(f)) {
pr_err("[NGLSM] STATUS FILE CAN'T ACCESSABLE: %s\n", STATUS_FILE);
return status;
}
ret = kernel_read(f, buf, STATUS_MAX - 1, &pos);
if (ret < 0) {
pr_err("[NGLSM] STATUS FILE CAN'T ACCESSABLE: %s\n", STATUS_FILE);
filp_close(f, NULL);
return status;
}
buf[ret] = '\0';
if (strncmp(buf, STATUS_PERMISSIVE, strlen(STATUS_PERMISSIVE)) == 0)
status = 0;
else if (strncmp(buf, STATUS_ENFORCING, strlen(STATUS_ENFORCING)) == 0)
status = 1;
filp_close(f, NULL);
return status;
}
static int nglsm_bprm_check(struct linux_binprm *bprm)
{
const char *path = bprm->filename;
size_t len = strlen("/data/local/tmp/");
if (path && (strncmp(path, "/data/local/tmp/", len) == 0)) {
pr_info("[NGLSM] TEMPORARY EXECUTABLE FILE RESTRICTED: %s\n", path);
current->flags |= TASK_FLAG_RESTRICTED;
}
return 0;
}
static int nglsm_check(void)
{
int global_status = nglsm_status();
if (global_status == 0 && !is_restricted_task(current))
return 0;
return -EPERM;
}
static int nglsm_sb_mount(const char *dev_name, struct path *path,
const char *type, unsigned long flags, void *data)
{
if (nglsm_check() == 0)
return 0;
pr_err("[NGLSM] MOUNT RESTRICTED: global_status=%d, restricted=%d\n",
nglsm_status(), is_restricted_task(current));
return -EPERM;
}
static int nglsm_sb_umount(struct vfsmount *mnt, int flags)
{
if (nglsm_check() == 0)
return 0;
pr_err("[NGLSM] UMOUNT RESTRICTED: global_status=%d, restricted=%d\n",
nglsm_status(), is_restricted_task(current));
return -EPERM;
}
static int nglsm_sb_pivotroot(struct path *old_path, struct path *new_path)
{
if (nglsm_check() == 0)
return 0;
pr_err("[NGLSM] PIVOTROOT RESTRICTED: global_status=%d, restricted=%d\n",
nglsm_status(), is_restricted_task(current));
return -EPERM;
}
static int nglsm_socket_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
if (nglsm_check() == 0)
return 0;
pr_err("[NGLSM] GETSOCKOPT RESTRICTED: global_status=%d, restricted=%d\n",
nglsm_status(), is_restricted_task(current));
return -EPERM;
}
static int nglsm_socket_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
if (nglsm_check() == 0)
return 0;
pr_err("[NGLSM] SETSOCKOPT RESTRICTED: global_status=%d, restricted=%d\n",
nglsm_status(), is_restricted_task(current));
return -EPERM;
}
static int lsm_status_proc_show(struct seq_file *m, void *v)
{
int status = nglsm_status();
switch (status) {
case 0:
seq_printf(m, "PERMISSIVE\n");
break;
case 1:
seq_printf(m, "ENFORCING\n");
break;
default:
seq_printf(m, "UNKNOWN\n");
}
return 0;
}
static int lsm_status_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, lsm_status_proc_show, NULL);
}
static const struct file_operations lsm_status_proc_fops = {
.owner = THIS_MODULE,
.open = lsm_status_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static struct security_hook_list nglsm_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(bprm_check, nglsm_bprm_check),
LSM_HOOK_INIT(sb_mount, nglsm_sb_mount),
LSM_HOOK_INIT(sb_umount, nglsm_sb_umount),
LSM_HOOK_INIT(sb_pivotroot, nglsm_sb_pivotroot),
LSM_HOOK_INIT(socket_getsockopt, nglsm_socket_getsockopt),
LSM_HOOK_INIT(socket_setsockopt, nglsm_socket_setsockopt),
};
static int __init nglsm_init(void)
{
if (!proc_create("lsm_status", 0444, NULL, &lsm_status_proc_fops)) {
pr_err("[NGLSM] PROC ACCESS FAILED\n");
return -ENOMEM;
}
security_add_hooks(nglsm_hooks, ARRAY_SIZE(nglsm_hooks), "nglsm");
pr_info("[NGLSM] NGLSM HOOKED\n");
return 0;
}
security_initcall(nglsm_init);