修改嵌入式 ARM Linux 内核映像中的文件系统

news2024/11/19 18:36:58

zImage 是编译内核后在 arch/arm/boot 目录下生成的一个已经压缩过的内核映像。通常我们不会使用编译生成的原始内核映像 vmlinux,因其体积很大。因此,zImage 是我们最常见的内核二进制,可以直接嵌入到固件,也可以直接使用 qemu 进行调试。当然,在 32 位嵌入式领域还能见到 uImage,这是在 zImage 首位增加 64B 的头,描述映像文件类型、加载位置、内核大小等信息。

有些嵌入式设备的文件系统直接嵌入到内核中,这种内置文件系统的机制被称为 ramdisk/initramfs,如果只是使用 extract-vmlinux/binwalk 解压固件,释放大量 shell 脚本和配置文件,是很容易做到的,但是如果想要修改这些文件,并进行重新打包,生成实际设备可以运行的 zImage 内核映像可能不是简单。

本文将演示如何在 32位 ARM zImage 中替换 piggy 中的文件系统,我们以 openWRT 的某个版本固件为例进行讲解。

初始设置

下载 OpenWRT ARM zImage-initramfs 映像,这是一个基于 ramdisk 的典型内核映像,不需要额外的文件系统,实际上也无法使用 binwalk 直接提取我们想要修改的操作系统启动提示信息。

$ wget https://downloads.openwrt.org/releases/17.01.0/targets/armvirt/generic/lede-17.01.0-r3205-59508e3-armvirt-zImage-initramfs -O zImage-initramfs
$ openssl dgst zImage-initramfs
SHA256(zImage-initramfs)= 5ad269e95b2db16aea3794dd0e97dabb6f9712184d79b0764bb10a810f8d7639

使用 qemu 启动

$ qemu-system-arm -M virt -m 1024 -kernel zImage-initramfs -append "console=ttyAMA0" -nographic

最小 shell 控制台

BusyBox v1.25.1 () built-in shell (ash)

     _________
    /        /\      _    ___ ___  ___
   /  LE    /  \    | |  | __|   \| __|
  /    DE  /    \   | |__| _|| |) | _|
 /________/  LE  \  |____|___|___/|___|                      lede-project.org
 \        \   DE /
  \    LE  \    /  -----------------------------------------------------------
   \  DE    \  /    Reboot (17.01.0, r3205-59508e3)
    \________\/    -----------------------------------------------------------

=== WARNING! =====================================
There is no root password defined on this device!
Use the "passwd" command to set up a new password
in order to prevent unauthorized SSH logins.
--------------------------------------------------
root@LEDE:/#

查看内核版本,找到对应的源码,因为我们有可能会根据内核解压缩的源码,调整重打包方式。

root@LEDE:/# uname -a
Linux LEDE 4.4.50 #0 SMP Mon Feb 20 17:13:44 2017 armv7l GNU/Linux

找到相应版本的内核,推荐在线浏览 https://elixir.bootlin.com/linux/v4.4.50/source/,版本匹配也没有那么重要,因为内核解压缩的核心代码其实一直以来变化不大,位于源码目录 arch/arm/boot/compressed

提取 Piggy

使用 binwalk 分析固件,就像我们在开始说的,binwalk 可能可以提取其中的配置文件,也有可能无法提取,即使提取,也都是归在一个文件夹下,并没有常见的 squashfs 文件系统

$ binwalk zImage-initramfs         

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Linux kernel ARM boot executable zImage (little-endian)
15400         0x3C28          xz compressed data
15632         0x3D10          xz compressed data

毫无疑问,固件开始部分是可以直接运行的未经压缩的用于解压内核的 head.omisc.o,使用 dd 命令提取该部分进行分析,或者直接将整个固件拖入 IDA,选择 arm,并只反汇编固件头部部分。
在这里插入图片描述
运行上述 IDC 脚本,即可得到解压内核代码部分。可以对比内核源码,我们需要找到固件中,内核压缩映像文件的起始地址和结束地址。piggy.S 使用 incbin 关键字引入 piggy.gz。其中全局变量 input_datainput_data_end 分别是 piggy 的起始地址和结束地址。

	.section .piggydata,#alloc
	.globl	input_data
input_data:
	.incbin	"arch/arm/boot/compressed/piggy.gz"
	.globl	input_data_end
input_data_end:

毫无疑问,内核解压代码需要这些全局变量,这样才能够解压真正压缩的内核。

putstr("Uncompressing Linux...");
ret = do_decompress(input_data, input_data_end - input_data,
    output_data, error);
if (ret)
    error("decompressor returned an error");
else
    putstr(" done, booting the kernel.\n");

IDA 反编译的固件头部,寻找 Uncompressing Linux...,对比源码很容易知道 piggy 的实际偏移。
在这里插入图片描述
继续分析汇编,找到全局变量存放的位置
在这里插入图片描述
对比原始固件二进制时,发现压缩结束 magic YZ 后面多出了 4B 数据,这 4B 其实是原始未经压缩的xz大小。实际上 YZ 才是压缩文件的结尾。因此使用 xz 解压时,估计会出现 Unexpected end of input 错误,只需要添加参数即可。
在这里插入图片描述
dd 截取 piggy

$ dd if=zImage-initramfs of=vmlinux.xz bs=1 skip=$[0x3d10] count=$[0x2bb404]                     
2864132+0 records in
2864132+0 records out
2864132 bytes (2.9 MB, 2.7 MiB) copied, 13.595 s, 211 kB/s

解压 piggy

$ unxz --verbose --single-stream < vmlinux.xz > /tmp/vmlinux
  100 %   2,797.0 KiB / 8,883.5 KiB = 0.315 

我们发现解压后的 vmlinux 内核映像大小果然是 28 c3 8a 00

$ ls -l /tmp/vmlinux                
-rw-r--r-- 1 kali kali 9096744 Dec 20 04:04 /tmp/vmlinux

$ python -c "print(0x8ace28)"
9096744

重打包

修改 vmlinux,例如修改启动界面字符串,找到需要修改信息的地址。这些信息显示 initramfs 嵌入在解压后的 vmlinux 中,该部分由一个没有校验和的未经压缩的 CPIO 文档组成(binwalk 可以识别)。

$ strings -t x /tmp/vmlinux | grep "WARNING\!" 
 76ac3a === WARNING! =====================================

使用 hexedit 编辑,回车键可快速定位此地址,tab 可切换 16 进制 / ASCII 码,ctrl+x 保存并退出。

0076AC3C   3D 20 57 41  52 4E 49 4E  47 21 20 4D  6F 64 69 66  69 63 61 74  = WARNING! Modificat
0076AC50   69 6F 6E 20  73 75 63 63  65 65 64 65  64 21 21 21  3D 3D 3D 3D  ion succeeded!!!====

如果直接使用 xz 压缩,我们会发现压缩后大小大于原始压缩文件 0x2bb404(2864132),通过 Linux 源码可以找到压缩命令位于 xz_wrap.sh

xz --check=crc32 --arm --lzma2=$LZMA2OPTS,dict=32MiB

仅仅使用上述命令压缩还是不够的,压缩后的文件仍然较大,nice 可以达到最大压缩比。最终压缩命令如下

$ xz --check=crc32 --arm --lzma2=,dict=32MiB,nice=128 < /tmp/vmlinux > /tmp/vmlinux.xz             

$ ls -l /tmp/vmlinux.xz 
-rw-r--r-- 1 kali kali 2863832 Dec 20 04:26 /tmp/vmlinux.xz

显然小于原始压缩文件,符合要求。要记住,piggy 末尾 4 字节存放原始文件大小,而我们只是修改启动信息,并没有改变原始 vmlinux 大小

$ echo -en "\x28\xce\x8a\x00" >> /tmp/vmlinux.xz # piggy.gz

替换 piggy

$ cp zImage-initramfs zImage-initramfs-warnmod
$ dd if=/tmp/vmlinux.xz of=zImage-initramfs-warnmod bs=1 seek=$[0x3d10] conv=notrunc
2863836+0 records in
2863836+0 records out
2863836 bytes (2.9 MB, 2.7 MiB) copied, 11.1713 s, 256 kB/s

修改内核解压代码中的 piggy 结束地址,input_data_end = hex(0x3d10+2863836) = 0x2befec,原始大小为 0x2bf114

002BF124   EC EF 2B 00  68 F5 2B 00  10 3D 00 00  64 F5 2B 00  64 F1 2B 00  ..+.h.+..=..d.+.d.+.

尝试启动内核,修改成功!

BusyBox v1.25.1 () built-in shell (ash)

     _________
    /        /\      _    ___ ___  ___
   /  LE    /  \    | |  | __|   \| __|
  /    DE  /    \   | |__| _|| |) | _|
 /________/  LE  \  |____|___|___/|___|                      lede-project.org
 \        \   DE /
  \    LE  \    /  -----------------------------------------------------------
   \  DE    \  /    Reboot (17.01.0, r3205-59508e3)
    \________\/    -----------------------------------------------------------

=== WARNING! Modification succeeded!!!============
There is no root password defined on this device!
Use the "passwd" command to set up a new password
in order to prevent unauthorized SSH logins.
--------------------------------------------------
root@LEDE:/# 

小结

如果需要增加而不是修改 initramfs 的内容,可能就没那么简单了。因为你需要准确掌握固件的每一个部分,而且需要注意的是 piggy 的 inflated size 也就是 xz 实际大小其实是 input_data_end - 4,这一部分代码位于 misc.cLC0 对象

LC0:	.word	LC0			@ r1
		.word	__bss_start		@ r2
		.word	_end			@ r3
		.word	_edata			@ r6
		.word	input_data_end - 4	@ r10 (inflated size location)
		.word	_got_start		@ r11
		.word	_got_end		@ ip
		.word	.L_user_stack_end	@ sp
		.word	_end - restart + 16384 + 1024*1024
		.size	LC0, . - LC0

以本文中的固件为例,piggy 实际大小 0x2bf110,位于固件偏移 0x258,因此如果修改了 piggy 的大小,还需要修改此处地址对应的数据。

在这里插入图片描述
当然,实际还需要考虑各个部分的偏移,可参考 https://gist.github.com/jamchamb/243e6973aeb5c9a2e302a4d4f57f16e1

如果你需要增加内核内容并且改变了原有内核大小,而不只是简单修改,则需要掌握内核解压缩的详细流程,在这里,我们只将内核压缩映像生成流程简单呈现如下,详细流程可参见

vmlinux
   │
   │ -R.note-R.comment
   │
   └─arch/arm/boot/Image
       │
       │ gzip -f -9 < Image > piggy.gz
       │
       └─arch/arm/boot/compressed/piggy.gz
           │
           │ piggy.S 直接引入piggy.gz
           │
           └─arch/arm/boot/compressed/piggy.o
               │
               │ +head.o
               │ +misc.o
               │
               └─arch/arm/boot/compressed/vmlinux
                   │
                   │ -debuginfo
                   │
                   └─arch/arm/boot/compressed/zImage

内核代码中的 head.Smisc.c 用于内核自解压,所以,如果我们需要直接通过修改内核二进制的方式打 patch,则需要了解内核压缩和解压的流程。从上图也可以看出来,piggy 就是压缩过的内核的一部分,其实也是内核的主体部分。

参考文献

Modifying Embedded Filesystems in ARM Linux zImages
Linux内核源码分析–内核启动之zImage自解压过程
Linux2.6 内核启动分析
initramfs 在内核中的作用与实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/106761.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++的OpenCV中cv::minAreaRect的返回角度的数值范围是多少?

版本不一样的时候&#xff0c;返回也不一样。 我使用opencv/4.5.5。 下图是使用minAreaRect判定的角度&#xff0c;可以看到&#xff0c;数值范围是[0,90]&#xff0c;看起来很离谱。 画出这张图使用的程序如下&#xff1a; C int main() {std::string prefix1 "/mn…

SpringMvc+Thymeleaf实现数据渲染

Thymeleaf是spring boot推荐使用的模板语法&#xff0c;它可以完全替代 JSP 。 从代码层次上讲&#xff1a;Thymeleaf是一个java类库&#xff0c;它是一个xml/xhtml/html5的模板引擎&#xff0c;可以作为mvc的web应用的view层。 Thymeleaf 提供spring标准方言和一个与 SpringMV…

Ui自动化概念+Web自动化测试框架介绍

目录 UI 1.UI自动化测试概念:我们先明确什么是UI 2.为什么对UI采用自动化测试? 3.什么项目适合做UI自动化测试? 4.UI自动化测试介入时机 5.UI自动化测试所属分类 Web自动化测试框架介绍 2.Selenium框架介绍及特点: Web自动化测试环境搭建 2.元素定位(一) idclassna…

【数据结构】栈与集合类Stack

目录 一、栈 二、Java中的集合类之Stack 1、介绍 2、构造方法 3、常用方法 1.push 2.pop 3.peek 4.search 5.empty 三、实现Stack 1、准备字段 2、实现判空 3、实现压栈 4、实现出栈 5、实现获取栈尾元素 6、指定元素到栈顶的距离 一、栈 栈(stack)是一种比较…

Redis高可用之哨兵模式(第二部分)

引言 接上一篇&#xff0c;今天我们来聊一聊Redis的高可用的第二个解决方案&#xff1a;哨兵模式。 一、Redis哨兵模式 哨兵模式&#xff08;sentinel&#xff09;是Redis提供的高可用的解决方案之一。由一个或者多个sentinel示例组成的sentinel系统&#xff0c;可以监听任意…

(Java高级教程)第二章Java多线程常见面试题-第二节:JUC(java.util.concurrent)

文章目录一&#xff1a;Callable接口二&#xff1a;ReentrantLock三&#xff1a;原子类四&#xff1a;信号量SemaphoreJUC&#xff1a;JUC是java.util.concurrent包的简称&#xff0c;目的就是为了更好的支持高并发任务。让开发者进行多线程编程时减少竞争条件和死锁的问题 一…

智己LS7发布,预售价格区间35-50万元

12月20日&#xff0c;智己首款中大型大五座SUV 智己LS7开启预售。动力配置&#xff1a; •最大零百加速4.5S&#xff1b; •峰值公里425kw&#xff0c;峰值扭矩725Nm&#xff1b; •提供90度和100度电池选项&#xff1b; •最大CLTC续航660km&#xff1b;空间配置&#xff1a; …

06. http协议基础,带你了解网络访问

06. http协议基础&#xff0c;带你了解网络访问 渗透测试学习路径 计算机基础网络基础WEB漏洞渗透测试 渗透测试和WEB安全漏洞的区别&#xff1f; 渗透测试包含WEB安全漏洞 WEB网站只是单一的网站服务&#xff0c;在渗透测试过程中可能不是攻击网站&#xff0c;而是寻找其他…

ElasticSearch全文检索原理及过程

倒排索引 ElasticSearch的搜索引擎中&#xff0c;每个文档都有一个对应的文档 ID&#xff0c;文档内容被表示为一系列关键词的集合。例如文档 1 经过分词&#xff0c;提取了 20 个关键词&#xff0c;每个关键词都会记录它在文档中出现的次数和出现位置。那么&#xff0c;倒排索…

差分---(小明的彩灯)蓝桥杯真题,差分思想很明确的模板

小明的彩灯题目描述暴力解法差分的思路和模板差分解法题目描述 小明拥有 N个彩灯&#xff0c;第 i个彩灯的初始亮度为 ai​。 小明将进行 Q次操作&#xff0c;每次操作可选择一段区间&#xff0c;并使区间内彩灯的亮度 x&#xff08;x可能为负数&#xff09;。 求 Q次操作后…

自动控制原理笔记-传递函数

目录 拉普拉斯反变换&#xff1a; 用拉普拉斯变换求解常微分方程的步骤&#xff1a; 部分分式展开法&#xff1a; 留数法&#xff1a; 零极点图: 传递函数 定义&#xff1a; 传递函数的标准形式&#xff1a; 传递函数的性质&#xff1a; 传递函数的局限性&#xff1a…

SOT23-6封装 小封装 超精简外围PD Sink端取电协议芯片

PD协议&#xff08;USB-PD&#xff09;的全名是USB Power Delivery&#xff0c;是由 USB-IF 组织制定的一种快速充电规范&#xff0c;是目前主流的快充协议之一&#xff0c;USB-PD 快充协议是以 Type-C 接口输出的&#xff0c;我们经常看到的华为笔记本配的Type-C 65W充电器就是…

【C语言】函数栈帧的创建和销毁

目录 1.函数栈帧的含义 概念 要用到的汇编语言的知识 示例 2.理解栈帧 2.1 main函数栈帧的创建 2.2 局部变量的创建 2.3 函数传参 2.4 调用函数 2.5 函数返回 一个.c文件在调用函数的时候&#xff08;包括main 函数&#xff09;&#xff0c;其内存中的栈区有什么变…

Qt之实现工具箱界面程序

最近终于有点空闲时间了&#xff0c;就写写博客&#xff0c;就把上次给客户实现的一个程序开发过程写出来&#xff1b;客户要求的是在主界面上能有几个很好看的按钮&#xff0c;单击各个按钮能弹出不同的应用窗口&#xff0c;如游戏窗口&#xff0c;显示图像窗口等等&#xff0…

pcl 基本操作汇总

目录 PCLVisualizer简单的点云可视化 createViewPort创建视窗 代码 效果 点云视窗上打印文本信息 使用addText 合并多个点云 xyzxyz xyz nxnynz 新建自己的Point类型 点云的刚体变换&#xff08;旋转平移&#xff09; 以下是pcl点云基本操作&#xff0c;后面会慢慢…

C++--数据结构--最小生成树-- Kruskal--Prim--高阶0713

注&#xff1a;本次修改了添加边的一些其他情况可以采用坐标来添加边 void _AddEdge(size_t srci, size_t dsti, const W& w) {_matrix[srci][dsti] w;// 无向图if (Direction false){_matrix[dsti][srci] w;} }void AddEdge(const V& src, const V& dst, const…

【学习笔记03】vue的组件

目录一、组件二、组件的分类&#xff08;一&#xff09;全局组件&#xff08;二&#xff09;局部组件1、为什么vue组件 data函数返回一个对象2、bootstrap的使用三、父组件传值给子组件1、父传子实现进度条2、 props的属性四、子组件传值给父组件五、兄弟组件传值一、组件 可以…

【Javassist】快速入门系列04 使用Javassist更改整个方法体

系列文章目录 01 在方法体的开头或结尾插入代码 02 使用Javassist实现方法执行时间统计 03 使用Javassist实现方法异常处理 04 使用Javassist更改整个方法体 文章目录系列文章目录前言引入Javassist jar包使用Javassist更改整个方法体总结说明前言 上一章我们介绍了使用Javas…

2022全年度白酒十大热门品牌销量榜单

白酒为中国特有的一种蒸馏酒&#xff0c;是世界六大蒸馏酒之一&#xff0c;中国是全球最大的蒸馏酒市场&#xff0c;中国的白酒消费也位列世界烈酒行业领先地位。近几年来&#xff0c;由于市场需求的不断提升及居民的消费升级&#xff0c;高档白酒价格也不断增长&#xff0c;从…

会员管理系统可行性研究

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 1.引言 1.1编写目的 1.2项目背景 1.3定义 1.4参考资料 2.可行性研究前提 2.1要求 2.2目标 2.3条件、假定和限制 2.4决定可行性的因素 3.现有小程序分析 3.1经…