基于linux 5.0内核添加一个系统调用,但是单纯添加一个系统调用会显得有些单调,所以这里把系统调用,工作队列,修改内核,内核编译,内核模块的编写,插入等结合起来。

要添加的是一个系统调用日志收集系统

系统调用是用户程序与系统打交道的入口,系统调用的安全直接关系到系统的安全,假设一个用户它恶意的不断的调用fork,将会导致系统负载增加,所以,如果能收集到是谁调用了一些有危险的系统调用,以及系统调用的时间和其它信息,将有助于系统管理员进行事后的追踪,从而提高系统的安全性,下面这张图就是本次要添加的系统调用日志收集系统示意图。

当用户进程进行系统调用的时候,当执行到do_syscall_64,我们判断是否是我们需要记录的系统调用,如果是则拦截需要记录的系统调用,通过my_audit这一函数,将相关信息写入到内核中的buffer里,同时我们编写用户测试程序。

在用户测试程序中,通过我们本次添加的335号系统调用执行my_sysaudit这一函数,该函数把内核buffer里的信息拿到我们的用户buffer当中,其中my_audit和my_sysaudit这两个函数是钩子函数,并且在我们的my_audit内核模块当中进行具体的实现,接下来看具体的实现步骤

添加系统调用第一步:打开系统调用表表项文件,增加系统调用表表项

/arch/x86/entry/syscalls/syscall_64.tbl

要按照上面的格式添加。

第二步,添加系统调用函数。/arch/x86/kernel/myaudit.c

#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/kernel.h>
#include <asm/current.h>void (*my_audit) (int,int) = 0;int(*my_sysaudit)(u8,u8 *,u16,u8) = 0;
SYSCALL_DEFINE4(myaudit,u8,type,u8 *,us_buf,u16,us_buf_size,u8,reset)
{if (my_audit){printk("IN KENEL:my system call sys_myaudit() working\n");return (*my_sysaudit)(type,us_buf,us_buf_size,reset);} elseprintk("my_audit is not exist\n");return 1;}EXPORT_SYMBOL(my_audit);
EXPORT_SYMBOL(my_sysaudit);

这里使用内核所给的SYSCALL_DEFINE宏,定义了系统调用在内核的入口。同时声明了两个钩子函数在内核模块中等待实现。

第三步,修改Makefile文件,把myaudit.c添加到内核编译中去

/arch/x86/kernel/Makefile

也就是这里, obj-y += myaudit.o

第四步,增加函数声明

第五步,拦截相关系统调用

首先找到do_syscall_64这个函数,

在这个地方加入对系统调用号nr的判断

如果是我们需要拦截的系统调用号我们就将它拦截下来

我们可以看到,要记录的系统调用号有2号open,3号close,39号get_pid,56号clone,57号fork,59号exit,如果是我们需要记录的系统调用函数,我们就调用之前的钩子函数my_audit,如果我们还没有在内核模块中实现my_audit,就打印日志my_audit is not exist,然后保存退出

#ifdef CONFIG_X86_64
__visible void do_syscall_64(unsigned long nr, struct pt_regs *regs)
{struct thread_info *ti;enter_from_user_mode();local_irq_enable();ti = current_thread_info();if (READ_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY)nr = syscall_trace_enter(regs);/** NB: Native and x32 syscalls are dispatched from the same* table.  The only functional difference is the x32 bit in* regs->orig_ax, which changes the behavior of some syscalls.*/nr &= __SYSCALL_MASK;if (likely(nr < NR_syscalls)) {nr = array_index_nospec(nr, NR_syscalls);regs->ax = sys_call_table[nr](regs);}syscall_return_slowpath(regs);
}
#endif

接下来编译内核,当前内核是4.1.15,我们把当前内核的.config文件copy到当前目录下,之后执行make menuconfig

加载.config文件,

顺序是load,ok,save,ok,exit,exit

接着执行make olddefconfig

之后开始编译内核

如果直接使用的是ubuntu系统,直接make menuconfig有时会报错,需要安装几个依赖的库

sudo make modules装内核,并修改内核引导sudo make modules_installsudo make installsudo update-grub2sudo reboot

现在,内核版本就替换位5.0,接下来要做的是添加实现钩子函数的内核模块。

进入相应的目录下打开my_audit.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/uaccess.h>#define COMM_SIZE 16
#define AUDIT_BUF_SIZE 20MODULE_LICENSE("GPL v2");struct syscall_buf{
u32 serial;
u64 ts_sec;
u64 ts_micro;
u32 syscall;
u32 status;
pid_t pid;
uid_t uid;
u8 comm[COMM_SIZE];
};DECLARE_WAIT_QUEUE_HEAD(buffer_wait);static struct  syscall_buf  audit_buf[AUDIT_BUF_SIZE];
static int current_pos = 0;
static u32 serial = 0;void syscall_audit(int syscall,int return_status)
{   struct syscall_buf *ppb_temp;struct timespec64 nowtime;ktime_get_real_ts64(&nowtime);if(current_pos<AUDIT_BUF_SIZE){ppb_temp = &audit_buf[current_pos];ppb_temp->serial = serial++;ppb_temp->ts_sec = nowtime.tv_sec;ppb_temp->ts_micro = nowtime.tv_nsec;ppb_temp->syscall = syscall;ppb_temp->status = return_status;ppb_temp->pid = current->pid;ppb_temp->uid = current->tgid; memcpy(ppb_temp->comm,current->comm,COMM_SIZE);if(++current_pos ==AUDIT_BUF_SIZE * 6/10){printk("IN MODULE_AUDIT:yes,it near full\n");wake_up_interruptible(&buffer_wait);}}}int sys_audit(u8 type,u8 *us_buf,u16 us_buf_size,u8 reset)
{int ret = 0;if(!type){if(clear_user(us_buf,us_buf_size)){printk("Error:clear_user\n");return 0;}printk("IN MODULE_systemcall:starting...\n");ret = wait_event_interruptible(buffer_wait,current_pos >= AUDIT_BUF_SIZE *6/10);printk("IN MODULE_systemcall:over,current_pos is %d\n",current_pos);if(copy_to_user(us_buf,audit_buf,(current_pos)*sizeof(struct syscall_buf))){printk("Error:copy error\n");return 0;}ret = current_pos;current_pos = 0;}return ret;
}extern void(*my_audit)(int,int);
extern int (*my_sysaudit)(u8,u8*,u16,u8);
static int __init audit_init(void)
{my_sysaudit = sys_audit;my_audit = syscall_audit;printk("Exiting System Call Auditing\n");return;
}module_init(audit_init);static void __exit audit_exit(void)
{my_audit = NULL;my_sysaudit = NULL;printk("Exiting System Calling Auditing\n");return;
}module_exit(audit_exit);

先定义了一个结构体,这个结构体就存放了我们需要获取的一些系统调用的属性,

接下来实现了两个函数,syscall_audit这个函数,实现了把拦截下来的系统调用参数赋值给我们定义的结构体,

sys_audit这个函数则实现了把内核的buffer拿到用户的buffer中,

my_sysaudit,my_audit这两个函数就挂在我们之前在内核中定义的两个钩子函数上。

可以看到模块加载的时候会把它赋过去,模块卸载的时候就会把这个钩子函数重新置为空

编写Makefile

把my_audit.c编译成内核模块。接着对my_audit.c进行编译make,加载模块

查看日志,demesg

最后一行可以看到,内核buffer快满了

编写用户态测试程序

vim test_syscall.c

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/types.h>#define COMM_SIZE 16typedef unsigned char u8;
typedef unsigned int u32;
typedef unsigned long long u64;struct syscall_buf{u32 serial;u64 ts_sec;u64 ts_micro;u32 syscall;u32 status;pid_t pid;uid_t uid;u8 comm[COMM_SIZE];
};#define AUDIT_BUF_SIZE (20*sizeof(struct syscall_buf))int main(int argc,char *argv[])
{   u8 col_buf[AUDIT_BUF_SIZE];unsigned char reset = 1;int num = 0;int i,j;struct syscall_buf *p;while(1){num = syscall(335,0,col_buf,AUDIT_BUF_SIZE,reset);printf("num :%d\n",num);p = (struct syscall_buf *)col_buf;for(i=0;i<num;i++){printf("num[%d],serial:%d,\t syscall :%d,\t pid:%d,\t comm:%s,\t time:%s\n",i,p[i].serial,p[i].syscall,p[i].pid,p[i].comm,ctime(&p[i].ts_sec));}}return 1;
}

这里有相同的结构体

查看执行结果

./test_syscall

我们可以看到测试程序一运行就打印出了许多拦截到的系统调用的相关日志,并随着系统的运行,也在不断的记录一些信息,也在不断的打印

用户进行我们需要记录的系统调用时,会被my_audit进行拦截,存在内核的buffer中,我们使用用户态测试程序进行335号系统调用,把内核buffer里面的信息拿出到用户态buffer里面进行打印,这就是我们本次添加日志收集系统的全过程。

如果对您有帮助,麻烦点赞、收藏或者关注哦~

Linux内核学习8——添加系统调用相关推荐

  1. 操作系统实验(linux内核编译,添加系统调用,windows进程创建,脚本程序编写)

    <操作系统原理>实验报告 一.实验目的 (1)理解操作系统生成的概念和过程; (2)理解操作系统两类用户界面(操作界面,系统调用)概念; 二.实验内容 (1)在Unbantu或Fedora ...

  2. 杭电操作系统实验一 --- Linux内核编译及添加系统调用(arm架构华为云)

    实验要求  掌握Linux 内核的编译与安装 掌握Linux 系统调用基本概念 设计和添加linux系统调用 (1)修改或返回指定进程的优先级(nice值和prio值)(详见教材P328)提示:可能参 ...

  3. 杭电操作系统实验一----Linux内核编译及添加系统调用(完整实验报告)

    一 题目介绍 Linux是开源操作系统.在系统中根据需要添加新的系统调用是修改内核的一种常用手段,通过本次实验,我们可以理解Linux系统处理系统调用的流程以及增加系统调用的方法.Linux系统提供了 ...

  4. linux内核编译及添加系统调用(hdu)_浅谈关于Linux内核write系统调用操作的原子性

    Linux系统的write调用到底是不是原子的.网上能搜出一大堆文章,基本上要么是翻译一些文献,要么就是胡扯,本文中我来结合实例来试着做一个稍微好一点的回答. 先摆出结论吧.结论包含两点,即write ...

  5. 我的Linux内核学习笔记

    在开始今天的内容之前,其实有一些题外话可以和大家分享一下.自从工作以来,我个人一直都有一个观点.那就是怎么样利用简单的代码来说明开发中的问题,或者是解释软件中的原理,这是一个很高的学问.有些道理看上去 ...

  6. Linux内核学习--内存管理模块

    Linux内核学习--内存管理模块 首先,Linux内核主要由五个部分组成,他们分别是:进程调度模块.内存管理模块.文件系统模块.进程间通信模块和网络接口模块. 本部分所讲的内存是内存管理模块,其主要 ...

  7. 操作系统进程学习(Linux 内核学习笔记)

    操作系统进程学习(Linux 内核学习笔记) 进程优先级 并非所有进程都具有相同的重要性.除了大多数我们所熟悉的进程优先级之外,进程还有不同的关键度类别,以满足不同需求.首先进程比较粗糙的划分,进程可 ...

  8. linux网卡配子接口,linux 内核学习(2).

    linux 内核学习(2). (2011-07-18 01:45:46) 标签: 杂谈 linux内核源码树基本构造 由于linux的原代码继续在改变,因而不可能给出太翔实的内容,只能指出一个特异的驱 ...

  9. linux 内核 课程,Linux内核分析课程-全面剖析Linux内核技术 揭开Linux内核的面纱 Linux内核学习视频教 ......

    课程名称 Linux内核分析课程-全面剖析Linux内核技术 揭开Linux内核的面纱 Linux内核学习视频 课程目录 (1)\1, 计算机是如何工作的?:目录中文件数:0个 (2)\2, 操作系统 ...

最新文章

  1. SilverLight学习笔记--Silverlight中WebRequest通讯
  2. 行为型模式之责任链模式
  3. ubuntu下c 访问mysql_Ubuntu下用C语言访问MySQL数据库
  4. 国外公司技术博客盘点
  5. 黑马程序员之SQL server基础学习笔记(三)
  6. oracle几何体数据类型,Oracle数据库之spatial操作geometry方法
  7. STL常用函数总结-map
  8. SWJTU 2208 最大覆盖
  9. 源码pub:C#实现IPv6地址的二进制输出
  10. 360急速浏览器有道词典屏幕取词问题
  11. 关于过程改进和能力提升
  12. pythonmt4通讯swot矩阵_swot分析矩阵范例
  13. Linux XFS 文件系统文件的删除恢复
  14. 软件需求的薛定谔之猫
  15. Shell脚本文本三剑客之Sed
  16. 天正对应cad版本_天正建筑t20适用哪个版本cad
  17. 从实战学习微信小程序-电商星星评分功能(五)
  18. 甘特图:项目进度管理中的跟踪工具
  19. 那些会讲ppt的技术人有多爽?演讲的6个步骤
  20. Python之Django 模型Model

热门文章

  1. c#关于chart控件的使用方法
  2. 今天来聊聊什么类型的APP不能开发
  3. 百付宝携手瑞星 打造零风险支付平台
  4. SASS使用CSS3动画并使动画暂停和停止在最后一帧的简单例子
  5. [转载]欧洲杯四强之魔兽争霸版
  6. PHP怎样写延时队列(定时器)
  7. a-select下拉展示中英文,选择回填中文
  8. 13.6.2 定制邀请函,保存为 Word 文档
  9. 字体图标从下载到使用
  10. arduino平衡小车教程