把握行业变革关键节点,12 月 19 日 - 20 日,AICon北京站即将重磅启幕! 了解详情
写点什么

怎样 Hack Linux 的内核符号?

  • 2020-08-13
  • 本文字数:2726 字

    阅读完需:约 9 分钟

怎样Hack Linux的内核符号?

Linux 内核是不是坚不可摧?答案是 NO!尽管内核中存在诸多限制,但你只需要稍微花点心思,也可以想办法突破它们。下面我们将通过一个例子来展示这趟有趣的旅程。


首先简单介绍一下项目的背景。客户提供了一批嵌入式智能设备给我们,希望能够检测并且修复其中的安全漏洞。我们能够从设备中接触到二进制形式的固件,但却接触不到固件的源码。对于二进制固件的漏扫和加固是一个行业难题。此外为了减少人工成本,客户还希望我们提供一个自动化的漏扫和加固解决方案,这无疑成为了一件不可能完成的任务。


所谓固件,其实就是一个嵌入式操作系统,常见的有定制化的 Linux 和安卓系统。本质上它们都具有相似的结构:Bootloader、Kernel、根文件系统等。根文件系统中又包含了众多用户态程序、脚本、配置等。对于 Kernel 的 CVE 漏洞自动化扫描和修复是我们当前工作的主要内容。而自动化漏扫技术又可单独成文,本文将主要介绍自动化漏洞修复所用到的内核符号 Hack 技术。


所谓内核漏洞,其实就是 Linux 内核中存在的缺陷函数。所谓漏洞利用,就是在用户态通过一系列精巧的传参和调用,最终触发内核缺陷的过程。这里存在两种修复方式:


1)在触发缺陷的必由调用路径上设卡,做参数或调用关系过滤。比如 c 函数是缺陷函数,该漏洞触发的调用关系是 Func a-> Func b-> Func c,那么可以在 a 或 b 函数上做传参检查,一旦参数非法则立刻退出。这种方式的优点是修复过程简单,尤其当 c 函数调用非常深的时候,可以在表层易于打桩的函数中做传参检查;缺点是需要开发者深入理解漏洞的利用原理,同时不同漏洞的利用方式各不相同,修复方式也各异。


2)用与 c 函数功能相同,并且已经打好补丁的 c‘函数替换掉 c 函数。修补时只需要保证每次对 c 函数的调用都会无条件进入到 c’即可。这种方式的优点是修复方法统一,便于自动化,可不必深究不同漏洞的利用原理。


图示展示了方案二技术架构图:中间是 Hook 框架,提供缺陷函数拦截、函数跳板(Trampoline)、修复函数注册、内核代码区修改等基础功能;右侧是包含具体 CVE 漏洞修复业务的模块。 这里有很多核心问题需要解决,其中之一是修复函数使用未导出内核符号问题。



方案二技术架构图


我们都知道 Linux 是宏内核架构(Monolithic Kernel)。为了实现内核功能的动态扩展,Linux 又引入了内核模块。内核模块将不可避免的使用内核函数。正常情况下,Linux 内核代码会将一些基础功能性函数导出。如控制台输出函数 printk 等。所有被导出的函数都会通过 export_symbols 族的宏修饰。最后这些符号会被内核编译到特殊的段中。而针对我们漏洞修复的场景,内核缺陷函数可能存在于内核的任何地方,因此如果仅仅使用内核导出的少量符号,很多缺陷函数或其依赖函数将无法被解析到。


于是我们把目光放到了内核的 Kallsyms 功能上。这个功能是内核为了方便调试而引入的。当内核发生错误时会输出一系列 Stacktrace,后者其实是一系列函数地址。有了 Kallsyms,在输出 Stacktrace 的时候内核可以把地址解析成函数名输出,告诉开发人员错误发生在哪个函数的哪个位置:



export 导出的 printk 函数/kernel 的 stacktrace


由于内核错误可能发生在任何地方,因此 Kallsyms 单独保存了一份函数符号和函数地址的对应关系,其中的符号数量远远多于 export_symbols 宏导出的符号量。即使内核开启了地址随机化(Address Space Layout Randomization)功能,Kallsyms 也能在运行时解析到符号正确地址。如果在内核模块中想使用未导出的符号,可以使用 Kallsyms 提供的 kallsyms_lookup_name 函数将符号名解析到函数地址,再以函数指针的形式调用即可,如:



Kallsyms


普通需求到这里就完事了,但是针对客户的特殊场景,稍微思考一下就会发现有很大缺陷。假如修复补丁中一共涉及到了数百个未导出的函数,我们则要在修复代码中把所有使用到这些函数的地方全部修改成函数指针调用的形式,工作量增加了不少。最简单的解决办法是内核加载修复模块时,单独走 Kallsyms 解析模块符号,而绕过 export_symbols 这个符号子集(前提是不引入新的内核安全风险)。


Linux 内核模块的加载过程其实跟可执行程序加载动态链接库的过程是一样的。举个简单例子,在 printf(“hello world”)中,我们其实并没有实现 printf(由 puts 函数封装而来)。它实际是由 Libc 库实现。当我们运行 HelloWorld 程序的时候,操作系统会解析程序符号,载入依赖的动态链接库(每次加载的基址可能不同),计算重定位符号地址,并把地址填回 HelloWorld 程序中。我们可以通过下图过程来验证:



Linux 内核模块的加载过程


对于 Linux 内核模块而言,它本质上也是动态链接库,因此加载模块时必然存在解析符号地址的函数。于是我们的思路是,动态拦截该函数,重定向到我们的替换函数中,并在替换函数中添加 Kallsyms 查找符号地址的逻辑即可:左图为我们的替换函数,右图为内核原始函数。这样达到的效果是,我们可以在 CVE 修复代码中直接使用诸如 d_absolute_path 这样的未导出函数,而不用做任何函数指针形式的改造,便于漏洞修复过程的自动化。



符号解析替换函数/符号解析原始函数


可能会有同学感兴趣我们是如何实现内核函数拦截的,即如何从 find_symbol_in_section 跳转到 hook_find_symbol_in_section,这里以 ARM64 架构 CPU 为例简单说明。我们在内核的 find_symbol_in_section 函数开头插入了下图所示的汇编指令(以二进制形式修改内核代码区)。这里借用了 x0 寄存器作远距离跳转(从内核跳转到内核模块)。由于无条件跳转不应该产生任何副作用(即栈帧和寄存器不能改变),因此我们需要先保存 x0 的值到栈上,远跳转后再恢复 x0 内容。


ldr 指令从.addr(low)和.addr(high)中把跳板函数地址装载进 x0,注意到 ARM64 的地址长度为 64 位,而 ARM64 的指令长度为 32 位,因此跳板函数地址被折成低 32 位和高 32 位。进入跳板函数后先恢复 x0 寄存器值,再做近距离跳转(内核模块内部跳转),注意前图 hook_find_symbol_in_section 函数末尾有一行 HOOK_FUNC_TEMPLATE(find_symbol_in_section),即为宏定义的 find_symbol_in_section 的跳板函数。这样经过连续无条件跳转后,执行流被拦截到我们的 HOOK 函数中。



HOOK 函数


此外顺便多提一下,上述使用 Inline Hook 技术的拦截方式跟 CPU 架构是强相关的,如果想实现 ARM32 或 x86 架构的函数拦截,则需要分别单独实现。


作者介绍


刘涛,5 年 Linux 内核开发经历,熟悉操作系统原理,擅长 C 语言、汇编,热爱底层技术,曾在业余时间独立开发过操作系统。目前是 ThoughtWorks 中国安全团队核心成员。我的个人 github 地址是https://github.com/liutgnu


本文转载自 ThoughtWorks 洞见


原文链接


https://insights.thoughtworks.cn/how-to-hack-linux-kernel-symbols/


2020-08-13 14:051953

评论

发布
暂无评论
发现更多内容

Fabarta个人专属智能体多版本上线:覆盖多领域,诚邀免费体验!

Fabarta

人工智能 智能体 agent

华为开发者空间-云开发环境,实现本地VSCode远程开发小程序

华为云开发者联盟

vscode 华为开发者空间

瑞士开源大语言模型今夏发布

qife122

大语言模型 多语言处理

Web3 项目外包开发的类型

北京木奇移动技术有限公司

区块链开发 软件外包公司 web3开发

小红书笔记详情API响应数据解析

tbapi

小红书 小红书笔记详情接口 小红书API 小红书数据采集

JimuReport 积木报表 v2.1.1 版本发布,免费开源的报表和大屏设计

JEECG低代码

数据可视化 报表 数据大屏 报表工具 数据BI

开源赋能双碳:MyEMS 能源管理系统的架构与实践价值

开源能源管理系统

开源 开源能源管理系统 国能国标

为什么海外舆情监测将成为品牌出海的底层能力?

沃观Wovision

出海企业 海外舆情监控 沃观Wovision 舆情监测系统

基于华为开发者空间云开发环境部署Coze Studio + Maas构建智能体应用

华为云开发者联盟

华为云ModelArts DeepSeek v3 华为开发者空间 Coze开源 cozestudio

生成式AI实现多模态信息检索新突破

qife122

机器学习 生成式AI

基于华为开发者空间,使用MySQL MCP Server对数据源进行获取等操作

华为云开发者联盟

华为开发者空间 DeepSekk mysql'

SharePoint漏洞被利用传播勒索软件

qife122

网络安全 SharePoint

我试用了5款文本配音工具后的真实感受

石臻臻的杂货铺

AI TTS 文本转语音

Mac系统编程入门指南:从环境配置到高效编码

qife122

命令行工具 Mac开发

本地CodeArts IDE连接开发者空间 - 云开发环境,完成小游戏开发

华为云开发者联盟

华为CodeArts 华为开发者空间

覆铜板工厂新一代AI智能化MES系统:数字化转型的关键

万界星空科技

mes 制造业数字化 覆铜板行业 智能化MES 覆铜板mes

基于华为开发者空间,使用DeepSeek+Dify构建财务报表分析模型

华为云开发者联盟

dify 华为开发者空间 DeepSekk

高防IP:数字化时代企业网络安全的核心盾牌

德迅云安全杨德俊

RocketMQ 消息集成:多类型业务消息——定时消息

Apache RocketMQ

阿里云 RocketMQ 云原生 消息队列 定时消息

CST补丁安装教程-CST Studio Suite 2022 SP4 补丁包

思茂信息

cst CST软件 CST Studio Suite

分库分表之后如何使用?面试可以参考这些话术

王中阳Go

面试 分库分表

[大厂实践] 利用 TCP 拥塞控制算法增强分布式系统服务降级

俞凡

架构 大厂实践

Apache RocketMQ 在阿里云大规模商业化实践之路

Apache RocketMQ

阿里云 RocketMQ 云原生 消息队列

区块链Web3项目开发框架

北京木奇移动技术有限公司

区块链开发 软件外包公司 web3开发

AI少儿英语背单词APP的开发流程

北京木奇移动技术有限公司

软件外包公司 APP外包公司 AI英语学习

海外舆情监测的核心技术三件套

沃观Wovision

海外舆情监控 沃观Wovision 舆情监测系统

阿里云基于全新 RocketMQ 5.0 内核的落地实践

Apache RocketMQ

阿里云 RocketMQ 云原生 消息队列

解析 RocketMQ 业务消息--顺序消息

Apache RocketMQ

RocketMQ 云原生 消息队列 消息集成 mq消息集成

解析 RocketMQ 业务消息——事务消息

Apache RocketMQ

RocketMQ 云原生 消息队列 事务消息

从技术架构到场景落地:JetLinks 与 MyEMS 的差异化路径与价值解析

开源能源管理系统

开源 开源能源管理系统

开源驱动下的能源管理革新:安全自主可控与 MyEMS 的实践路径

开源能源管理系统

开源 开源能源管理系统

怎样Hack Linux的内核符号?_架构_张凯峰_InfoQ精选文章