Linux Kernel CFI机制简介及测试禁用
PS:要转载请注明出处,本人版权所有。
PS: 这个只是基于《我自己》的理解,
如果和你的原则及想法相冲突,请谅解,勿喷。
环境说明
无
前言
当我们为android移植linux的驱动程序的时候,总会遇到一些错误,这些错误有一部分就是android 内核开启的安全的机制导致的。本文就会介绍一种内核的安全机制:Kernel Control Flow Integrity(kCFI)。
此外,这里还要说明一下,Control Flow Integrity(CFI)与 Kernel Control Flow Integrity(kCFI)是不一样的,kCFI只检查函数指针,CFI还具备其他很多的检查,详情请参考:https://clang.llvm.org/docs/ControlFlowIntegrity.html 。
Kernel Control Flow Integrity(kCFI)原理简单介绍
Control Flow Integrity的翻译是控制流完整性,从直译来看,其实就是用一些方法保证来保证我们的指令执行到正确的位置。我们从clang官方文档知道,kCFI只检查函数指针,那么其实kCFI就是保证函数指针跳转到正确的位置,并且返回到正确的位置。
从这里来看,其实我们可以看到对于函数指针来说,我们需要保护两个地方:跳转到正确的位置、返回到正确的位置。这两个地方有两个专有名词:
- forward-edge
 - backward-edge
 
此外,我们还应该知道,在编写代码的时候,分为直接函数调用(direct function call),间接函数调用(indirect function call)。他们的示例如下:
void target(void)
{
    //... ...
}
typedef void(*fn)(void);
int main(int argc, char * argv[])
{
    // direct function call
    target();
    //indirect function call
    fn _id_fn = target;
    _id_fn();
}
从示例可以知道,indirect function call其实就是函数指针这种调用形式。
此外,我们还要知道,如果我们想破坏代码的执行流,那么我们必须在可写、可读、可执行的内存里面写入shellcode,并跳转到这个shellcode,否则我们的代码是无法工作的。那么显而易见的事情是,通过函数指针来调用函数,我们的目标是明确的,因此我们可以校验这些目标的原型、地址等等信息。
因为我们需要验证目标的原型、地址等等信息,所以,当我们在生成可执行文件的时候,需要知道所有的函数目标的信息,这个时候,就需要一个叫做Link Time Optimization(LTO)的功能,因为只有最终可执行文件链接时,才知道所有的函数目标信息。
kCFI演示示例
首先在qemu中运行一个arm64的linux模拟器,然后为linux内核配置如下内核选项:
# General architecture-dependent options -> LTO
CONFIG_CFI_CLANG=y
CONFIG_CFI_PERMISSIVE=y
我们的测试驱动例子:
#include <linux/module.h>  // 必须的头文件,定义了MODULE_*宏
#include <linux/kernel.h>  // 包含内核信息头文件
#include <linux/init.h>    // 包含 __init 和 __exit 宏
static int param_int = 0;
module_param(param_int, int, 0644);
static void hello_cfi_i(int i){
    printk(KERN_INFO "hello_cfi_i\n");
}
static void hello_cfi_f(float i){
    printk(KERN_INFO "hello_cfi_f\n");
}
typedef void (*hello_cfi_func_i)(int);
typedef void (*hello_cfi_func_f)(float);
struct node {
    hello_cfi_func_i i0[1];
    hello_cfi_func_f f0[1];
    hello_cfi_func_i i1[1];
    hello_cfi_func_f f1[1];
    hello_cfi_func_i i2[1];
    hello_cfi_func_f f2[1];
};
struct node fn_arr = {
    .i0 = {hello_cfi_i},
    .f0 = {hello_cfi_f},
    .i1 = {hello_cfi_i},
    .f1 = {hello_cfi_f},
    .i2 = {hello_cfi_i},
    .f2 = {hello_cfi_f},
};
// 模块初始化函数
static int __init hello_init(void)
{
    fn_arr.i0[param_int](param_int);
    printk(KERN_INFO "Hello, World!\n");
    return 0;  // 返回0表示加载成功
}
// 模块清理函数
static void __exit hello_exit(void)
{
    printk(KERN_INFO "Goodbye, World!\n");
}
// 注册模块初始化和清理函数
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");  // 模块许可证
MODULE_AUTHOR("Your Name");  // 模块作者
MODULE_DESCRIPTION("A simple Hello World Module");  // 模块描述
我们传入参数0,执行fn_arr.i0[0],测试正常跳转
    我们传入参数1,执行fn_arr.i0[1],测试传入参数原型不匹配(本来应该调用hello_cfi_i,实际调用hello_cfi_f)
    测试数组越界访问
    后记
从上面来看,kCFI一般会对调用类型、调用的目标地址进行判断,更多细节,去看CFI的具体原理。
参考文献
- https://clang.llvm.org/docs/ControlFlowIntegrity.html
 - https://source.android.com/docs/security/test/kcfi
 - https://outflux.net/slides/2020/lca/cfi.pdf
 
打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
    PS: 请尊重原创,不喜勿喷。
PS: 要转载请注明出处,本人版权所有。
PS: 有问题请留言,看到后我会第一时间回复。
Linux Kernel CFI机制简介及测试禁用的更多相关文章
- Linux kernel workqueue机制分析
		
Linux kernel workqueue机制分析 在内核编程中,workqueue机制是最常用的异步处理方式.本文主要基于linux kernel 3.10.108的workqueue文档分析其基 ...
 - Linux kernel 同步机制
		
Linux kernel同步机制(上篇) https://mp.weixin.qq.com/s/mosYi_W-Rp1-HgdtxUqSEgLinux kernel 同步机制(下篇) https:// ...
 - Linux kernel workqueue机制分析【转】
		
转自:http://www.linuxsir.org/linuxjcjs/15346.html 在内核编程中,workqueue机制是最常用的异步处理方式.本文主要基于linux kernel 3.1 ...
 - linux netlink通信机制简介
		
一.什么是Netlink通信机制 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口. Netlink 是一种特殊的 s ...
 - linux kernel同步机制的思考
		
在学习内核同步机制的时候,书中介绍了同步方法:原子操作(atomic).自旋锁(spinlock).信号量(semaphore).互斥锁(mutex).完成变量(completion).大内核(BLK ...
 - Android休眠唤醒机制简介(一)【转】
		
本文转载自:http://blog.csdn.net/zhaoxiaoqiang10_/article/details/24408129 Android休眠唤醒机制简介(一) ************ ...
 - Linux Kernel 0.12 启动简介,调试记录(Ubuntu1804, Bochs, gdb)
		
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
 - 【嵌入式开发】 Linux Kernel 下载 配置 编译 安装 及 驱动简介
		
作者 : 韩曙亮 转载请出名出处 : http://blog.csdn.net/shulianghan/article/details/38636827 一. Linux 内核简介 1. 内核功能简介 ...
 - linux kernel内存回收机制
		
转:http://www.wowotech.net/linux_kenrel/233.html linux kernel内存回收机制 作者:itrocker 发布于:2015-11-12 20:37 ...
 - Android HAL层与Linux Kernel层驱动开发简介
		
近日稍微对Android中的驱动开发做了一些简要的了解. HAL:Hardware Abstract Layer 硬件抽象层,由于Linux Kernel需要遵循GPL开源协议,硬件厂商为了保护自己硬 ...
 
随机推荐
- 面试官:说说Netty的核心组件?
			
Netty 核心组件是指 Netty 在执行过程中所涉及到的重要概念,这些核心组件共同组成了 Netty 框架,使 Netty 框架能够正常的运行. Netty 核心组件包含以下内容: 启动器 Boo ...
 - 在 Rainbond 中一键安装高可用 Nacos 集群
			
描述如何通过云原生应用管理平台 Rainbond 一键安装高可用 Nacos 集群.这种方式适合不太了解 Kubernetes.容器化等复杂技术的用户使用,降低了在 Kubernetes 中部署 Na ...
 - 218. The Skyline Problem-Hard
			
一.题目描述 给定建筑的轮廓坐标,求叠加之后的轮廓结果 二.解法 这个题目最容易想到的思路是扫描法 https://briangordon.github.io/2014/08/the-skyline- ...
 - ReplayKit2 采集音视频回调格式
			
一.音频 ReplayKit2 RPSampleBufferTypeAudioApp sampleBuffer = CMSampleBuffer 0x100500c50 retainCount: 1 ...
 - uniapp 富文本图片100%显示
			
下面的这个方法是在百度上找的,但是老是报错,一直没找到原因,索性自己就挨个检查了下,发现在过滤之前需要对传过来的值进行检测,是字符串才能让这个方法执行 filters: { formatRichTex ...
 - C# 指针简单使用
			
1. 使用unsafe C# 支持 unsafe 上下文,你可在其中编写不可验证的代码. 在 unsafe 上下文中,代码可使用指针.分配和释放内存块,以及使用函数指针调用方法. C# 中的不安全代码 ...
 - OPA Gatekeeper:Kubernetes的策略和管理
			
目录 一.系统环境 二.前言 三.OPA Gatekeeper简介 四.在kubernetes上安装OPA Gatekeeper 五.gatekeeper规则 5.1 使用gatekeeper禁止某些 ...
 - 开发视频会议系统:使用GPU解码渲染视频
			
现在,使用视频会议系统远程协同办公.沟通交流,已经非常普遍了.如果我们要开发自己的视频会议系统,那么,GPU解码渲染技术是不可缺少的. 在视频会议系统中,经常需要同时观看会议中多个参会人员的视频图像, ...
 - 铭瑄 USB 供电不足
			
铭瑄 USB 供电不足 可能是USB固件开了节能,节能状态和某些设备会不兼容,更新固件试试固件链接:链接:https://pan.baidu.com/s/1RxHEddYe6TWMDlMJ3PQB1Q ...
 - 使用Wesky.Net.OpenTools包来快速实现嵌套型结构体数据转换功能
			
今天遇到有人提到结构体和byte数组互转的问题,我就顺便拿来水一篇.这是一个冷门的问题,估计使用的人不多.既然有需求,应该就有使用场景,那就顺便整一波. 为了达到效果,结构体.复杂结构体嵌套等都能实现 ...