linux 安全模块开发

一、内核编译与调试

1. 编译内核

  1. 进入到下载好的Linux内核文件中,将配置文件中的选项清空

    make allnoconfig
    
  2. make menuconfig 进入配置文件菜单选项,将以下选择勾选

    64-bit kernel ---> yes
    General setup ---> Initial RAM filesystem and RAM disk (initramfs/initrd) support ---> yes
    General setup ---> Configure standard kernel features ---> Enable support for printk ---> yes
    Executable file formats / Emulations ---> Kernel support for ELF binaries ---> yes
    Executable file formats / Emulations ---> Kernel support for scripts starting with #! ---> yes
    Device Drivers ---> Generic Driver Options ---> Maintain a devtmpfs filesystem to mount at /dev ---> yes
    Device Drivers ---> Generic Driver Options ---> Automount devtmpfs at /dev, after the kernel mounted the rootfs ---> yes
    Device Drivers ---> Character devices ---> Enable TTY ---> yes
    Device Drivers ---> Character devices ---> Serial drivers ---> 8250/16550 and compatible serial support ---> yes
    Device Drivers ---> Character devices ---> Serial drivers ---> Console on 8250/16550 and compatible serial port ---> yes
    File systems ---> Pseudo filesystems ---> /proc file system support ---> yes
    File systems ---> Pseudo filesystems ---> sysfs file system support ---> yes
    

  1. 编译内核,差不多只有1M

    sudo make -j8
    

2. 使用busybox构建根文件系统

  1. 进入busybox文件夹,使用默认的配置文件

    make defconfig
    
  2. make menuconfig 编辑配置文件,一定开启静态编译

    Busybox Settings ---> Build Options ---> Build BusyBox as a static binary (no shared libs) ---> yes
    
  3. 编译busybox

    time make -j 8
    
  4. 安装busybox, install之后出现_install 文件夹

    make install
    
  5. cd _install 创建一些文件夹以及文件

    • mkdir -p proc sys dev etc etc/init.d lib tmp
      
    • ln -sf linuxrc init
      
    • cat > etc/init.d/rcS <<EOF
      #!/bin/sh
      mount -t proc none /proc
      mount -t sysfs none /sys
      /sbin/mdev -s
      ifconfig lo up
      EOF
      
    • chmod +x etc/init.d/rcS
      
    • cat > etc/inittab <<EOF
      # /etc/inittab
      ::sysinit:/etc/init.d/rcS
      ::askfirst:-/bin/sh
      ::ctrlaltdel:/sbin/reboot
      ::shutdown:/bin/umount -a -r
      EOF
      
  6. 使用cpio生成根文件系统

    find . -print0 | cpio --null -ov --format=newc   | gzip -9 > ../initramfs.cpio.gz
    
  7. busybox文件夹中,解压刚才生成的initramfs.cpio.gzinitramfs.cpio

3. 使用qemu启动编译好的内核

  • qemu-system-x86_64 -kernel ~/Desktop/linux-4.14/arch/x86/boot/bzImage -initrd initramfs.cpio
    

二、linux 内核增加系统调用

1. 前提知识(系统调用如何执行)

  • 我们通常写的c,有相对应的开源的标准库glibc,(2.23的glibc中还是有很多缺陷的)其中的头文件unistd.h就包含了许多的系统调用,如read/write/open等等
  • 系统调用是通过的修改寄存器eax的值,然后通过触发 软中断使系统进入内核空间,比如32位下经典的int 80以及64位的syscall。如果你想更清楚的了解系统调用可以查看我之前写的 ret2syscall
  • 所以内核的中断处理函数是根据系统调用号来调用相应的内核函数,c中的read/write,其实调用了内核函数sys_read/sys_write
  • 因此,添加一个系统调用需要注册 系统调用号 以及相应的 中断处理函数,如此,内核才能找到这个系统调用和执行对应的内核函数。内核的汇编代码最终会在include/generated/asm/syscalls_64.h中查找调用号,不过我们并不修改这里,x86平台提供了一个专门用来注册系统调用的文件/arch/x86/entry/syscalls/syscall_64.tbl,在编译时会运行同目录下的syscalltbl.sh脚本,将这个文件中登记过的系统调用都生成到前面的syscalls_64.h文件中。因此我们后面要添加系统调用就是修改这个tbl文件。
  • 系统执行相应功能,调用的是C代码编写的函数,而不是汇编代码,减轻了编写实现系统调用的负担。系统调用的函数定义include/linux/syscalls.h中,因为汇编代码到C代码的参数传递是通过栈实现的,所以可以看到所有系统调用的函数前面都使用了asmlinkage宏,它意味着编译时限制只使用栈来传递参数。
  • 系统调用函数的实现kernel/sys.c 中,当我们注册了相应的系统调用号以及定义了 系统调用的函数 之后,我们就可以在此文件的代码的最后添加自己的函数,这个文件中有很多已经实现的系统调用的函数作为参考。

2. 添加系统调用号

  • 编辑调用号注册表
cd  arch/x86/entry/syscalls/
vi syscall_64.tbl
  • 添加系统调用号

    #上面省略
    328     64      pwritev2                sys_pwritev2
    329     common  pkey_mprotect           sys_pkey_mprotect
    330     common  pkey_alloc              sys_pkey_alloc
    331     common  pkey_free               sys_pkey_free
    332     common  statx                   sys_statx
    333     common  ps_counter              sys_ps_counter
    # 下面的我们x32先不管,系统调用号,就在330左右,在此行上面添加
    # x32-specific system call numbers start at 512 to avoid cache impact
    # for native 64-bit operation.
    #

3. 定义系统调用函数

  • 编辑系统调用函数原型定义

    vi include/linux/syscalls.h
    
  • 添加函数原型(__user 是为了向用户空间传值)

    #以上省略
    asmlinkage long sys_pkey_mprotect(unsigned long start, size_t len,unsigned long prot, int pkey);
    asmlinkage long sys_pkey_alloc(unsigned long flags, unsigned long init_val);
    asmlinkage long sys_pkey_free(int pkey);
    asmlinkage long sys_ps_counter(int __user *num);
    asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags,unsigned mask, struct statx __user *buffer);#endif

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-keJMnAb3-1646184138966)(https://cdn.jsdelivr.net/gh/huzai9527/blog_pics@master/uPic/image-20210517082803018.png)]

4. 编写实现函数

  • 实现函数文件

    vi kernel/sys.c
    
  • 实现函数(这里就只要统计task数量到用户空间,我顺便输出了内核相关的task的信息,可以做调试用)

    SYSCALL_DEFINE1(ps_counter, int __user*, num)
    {struct task_struct *task; int counter=0;printk("[Syscall] ps_counter\n");printk("Now pid = %ld",current->pid);#current是宏,指向当前taskprintk("Pid    Parent->pid"); for_each_process(task){counter++;printk(" %ld    %ld\n",task->pid,task->parent->pid);}copy_to_user(num, &counter, sizeof(int)); #结果显示到用户空间return 0;
    }
    

5. 编写测试代码

  • 源文件
vi test_syscall.c
#include <unistd.h>
#include <syscall.h>
#include <stdio.h>
int main(void){int result;syscall(333,&result); //系统调用号,是你自己在`tbl`中注册的printf("process number is %d\n",result);return 0;
}
  • 编译(一定静态编译,因为你的qemu下的linux可能没有这些库)
gcc -static -o get_ps_num get_ps_num.c
  • 将编译好的二进制,放到busybox下的_install 下的tmp文件夹中,重新压缩gpio文件,这一步看之前的文章 裁剪Linux内核,用qemu进行调试
    find . -print0 | cpio --null -ov --format=newc   | gzip -9 > ../initramfs.cpio.gz

6. 运行测试程序

  • 编译添加完系统调用号,定义了系统调用函数,完成了函数实现之后,编译linux内核

    sudo make -j8
    
  • 使用新编译的内核以及刚打包的根文件系统,启动虚拟机

    qemu-system-x86_64 -kernel ~/Desktop/linux-4.14/arch/x86/boot/bzImage -initrd ~/Desktop/busybox-1.32.1/initramfs.cpio
    

三、新建一个LSM模块

1. 文件组织框架

  • security

    • HUHU_LSM(工作目录)

      • lsm.c(主要的的代码)
      • Makefile(自己创建)
      • Kconfig(自己创建)
    • Kconfig(需要修改)
    • Makefile(需要修改)

2. 基本函数框架

  • hook_implement_function(自己编写的安全函数)

  • static struct security_hook_list gmlsm_hooks[] = {LSM_HOOK_INIT(inode_rename,gmlsm_inode_rename),LSM_HOOK_INIT(inode_create,gmlsm_inode_create),
    }
    
    • LSM_HOOK_INIT(将自己编写的函数与内核中的接口进行绑定)
  • void __init gmlsm_add_hooks(void)
    {security_add_hooks(gmlsm_hooks, ARRAY_SIZE(gmlsm_hooks));
    }
    
    • security_add_hooks(将hook插入内核)
  • static __init int gmlsm_init(void){gmlsm_add_hooks();return 0;
    }
    security_initcall(gmlsm_init);
    
    • security_initcall(初始化)

3. 一个小的demo

https://github.com/guomo233/LSM-based-RBAC

四、为LSM模块实现功能

1. 选择相应的hook函数,并实现

  • 实现task_alloc函数(首先实现的自己的huhu_task_alloc函数,与lsm中的task_alloc对应)

    int huhu_task_alloc(struct task_struct *task,unsigned long clone_flags) // 2. implement relevant function
    {printk("[+huhu] call task_create(). count=%llu\n", ++count);    return 0;
    }
    

2. 将实现的hook函数与lsm_hook函数绑定

  • 绑定,将实现了的函数与lsm中的hook函数绑定,并放入hooks[]中

    static struct security_hook_list huhu_hooks[] = {LSM_HOOK_INIT(task_alloc,huhu_task_alloc),LSM_HOOK_INIT(file_alloc_security,huhu_file_alloc_security),LSM_HOOK_INIT(inode_create,huhu_inode_create), //3. add to security_hook_listLSM_HOOK_INIT(file_permission,huhu_file_permission),LSM_HOOK_INIT(inode_rename,huhu_inode_rename),LSM_HOOK_INIT(inode_alloc_security,huhu_inode_alloc_security),LSM_HOOK_INIT(inode_free_security,huhu_inode_free_security),};
    

3. 初始化这些hook函数

  • 将这些hook函数,放入到security框架中

    void __init huhu_add_hooks(void)
    {pr_info("Demo: becoming mindful.\n");        //print relevant mesg, cat by dmesg | grep huhu  security_add_hooks(huhu_hooks, ARRAY_SIZE(huhu_hooks),"huhu");   //add security model function
    }
    

4. 设置security初始化函数

  • 安全模块的初始化工作

    static __init int huhu_init(void){huhu_add_hooks();return 0;
    }
    security_initcall(huhu_init); //4. register this hook function
    

五、内核与用户之间的通信

1. /proc文件系统

  • /proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。

  • 相关链接(详细阅读)

    https://www.cnblogs.com/cute/archive/2011/04/20/2022280.html
    https://blog.csdn.net/sty23122555/article/details/51638697
    https://www.cnblogs.com/bakari/p/10966303.html
    

2. /proc文件创建

  • 首先指定该文件具有哪些操作

    static const struct file_operations huhu_policy_fops = {.owner = THIS_MODULE,.read = my_read,.write = my_write,
    };
    huhu_policy_file = proc_create(NODE_POLICY, 0, NULL, &huhu_policy_fops);
    • my_read、my_write是自己实现的读写方式

3. /proc文件的读写方式

  • 读写方式根据自己的实际需求进行修改

    static ssize_t
    my_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
    {int nbytes = sprintf(huhu_policy_buf, "policy->level %d\npolicy->pepartment %d\npolicy->project %d\n", huhu_policy.level,huhu_policy.department,huhu_policy.project);return simple_read_from_buffer(buf, lbuf, ppos, huhu_policy_buf, nbytes);
    }static ssize_t
    my_write(struct file *file, const char __user *buf, size_t lbuf,loff_t *ppos)
    {ssize_t rc;rc = simple_write_to_buffer(huhu_policy_buf, lbuf, ppos, buf, lbuf);sscanf(huhu_policy_buf, "%d%d%d", &huhu_policy.level, &huhu_policy.department, &huhu_policy.project);pr_info("levle has been set to %d\n", huhu_policy.level);pr_info("department has been set to %d\n", huhu_policy.department);pr_info("project has been set to %d\n", huhu_policy.project);return rc;
    }

linux 安全模块开发基础知识相关推荐

  1. linux课程_【课程完结】嵌入式Linux应用/驱动开发基础知识两大篇章已全部录制完毕 共72集...

    完结撒花 <第四篇嵌入式Linux应用开发基础知识> <第五篇嵌入式Linux驱动开发基础知识> 两大篇章已全部录制完毕 共计 72 集 01 嵌入式Linux应用开发基础知识 ...

  2. Linux应用程序开发 基础知识

     Linux应用程序开发 本文讲述了linux应用程序开发的基本内容.值得学习! Copyright © 2006 本文遵从GNU 的自由文档许可证(Free Documentation Lice ...

  3. 服务器架设笔记——Apache模块开发基础知识

    通过上节的例子,我们发现Apache插件开发的一个门槛便是学习它自成体系的一套API.虽然Apache的官网上有对这些API的详细介绍,但是空拿着一些零散的说明书,是很难快速建立起一套可以运行的系统. ...

  4. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之Pinctrl子系统和GPIO子系统的使用

    文章目录 前言 1.Pinctrl子系统 1.1.为什么有Pinctrl子系统 1.2.重要的概念 1.3.代码中怎么引用pinctrl 2.GPIO子系统 2.1.为什么有GPIO子系统 2.2.在 ...

  5. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之设备树模型

    文章目录 前言 1.设备树的作用 2.设备树的语法 2.1.设备树的逻辑图和dts文件.dtb文件 2.1.1.1Devicetree格式 1DTS文件的格式 node的格式 properties的格 ...

  6. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型

    文章目录 前言 1.驱动编写的三种方法 1.1.传统写法 1.2.总线驱动模型 1.3.设备树驱动模型 2.Linux实现分离:Bus/Dev/Drv模型 2.1.Bus/Dev/Drv模型 2.2. ...

  7. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之第一个驱动

    文章目录 前言 1.Hello驱动 1.1.APP打开的文件在内核中如何表示? 1.2.打开字符设备节点时,内核中也有对应的struct file 1.3.如何编写驱动程序? 1.4.驱动程序代码 1 ...

  8. 【嵌入式Linux】嵌入式Linux应用开发基础知识之输入系统应用编程

    文章目录 前言 1.输入系统应用编程 1.1.输入系统框架及调试 1.1.1.框架概述 1.1.2.编写APP需要的基础知识 1.2.调试技巧 1.2.1.查看设备信息 1.2.2.使用命令查看节点数 ...

  9. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之按键驱动框架

    文章目录 前言 1.APP怎么读取按键值 1.1.查询方式 1.2.休眠-唤醒方式 1.3.poll方式 1.3.异步通知方式 1.5. 驱动程序提供能力,不提供策略 2.按键驱动程序框架--查询方式 ...

最新文章

  1. 2020年,最适合AI的5种编程语言
  2. Python3-onvif协议之相机截图
  3. matlab从入门到精通:图像可视化基础操作
  4. php7 imagick安装,php扩展imagick安装for windows7
  5. php如何写log日志,用php生成log日志
  6. SIP应答消息状态码与功能
  7. 将dll制作成控件_如何将皮料剪切成想要的大小?制作皮具几种裁剪工具和使用方法...
  8. Html5-canvas
  9. 小白初解Linux基础相关
  10. C语言实现任意两种进制之间互相转换
  11. 在Exchange Server 2007中修改邮件接受域
  12. v6使用手册 天正电气t20_天正电气T20手册
  13. 房天下搜房网二手房_【杭州二手房|杭州二手房出售】 - 杭州房天下
  14. TensorFlow Session 中关于 GPU 的配置项解析 ——转自 慢慢学TensorFlow 微信公众号
  15. 2012年秋季,斯皮维大厅音乐会的亮点
  16. nginx反向代理是什么意思
  17. halcon19.11深度学习关于分类入门案例
  18. 【css】 让文字换行的样式
  19. 基于COF智能屏的桌面式3D打印机方案
  20. 数据库基础知识点汇总(事务,索引)

热门文章

  1. MCS-51单片机存储器结构-特殊功能寄存器 :堆栈指针SP(Stack Pointer)
  2. Linux命令之查找命令
  3. 怎么调用onenet平台的API从而读取我们的设备数据和下发命令,做到控制开关
  4. 多多情报通:拼多多店铺不交保证金能卖货吗?有什么影响吗?
  5. 登录outlook显示无法登录服务器,无法登录到 Outlook Web Access
  6. 海豚php增加后台页面,Ajax功能配置步骤 · 从DolphinPHP(海豚PHP)中把ZBuilder移植到oscshop2.0教程 · 看云...
  7. IOS版aplayer使用教程_享声数播APP使用指南【ios版】
  8. 骨传导耳机健康吗?骨传导耳机对身体好不好?
  9. 论系统的整体与部分的关系
  10. 魔兽一直显示世界服务器无法连接,魔兽世界怀旧服世界服务器无法连接解决办法...