修改嵌入式 ARM Linux 内核映像中的文件系统_armv7l gnu/linux-程序员宅基地

技术标签: arm开发  linux  Linux内核与嵌入式C  

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 在内核中的作用与实现

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/song_lee/article/details/128345166

智能推荐

前端开发之vue-grid-layout的使用和实例-程序员宅基地

文章浏览阅读1.1w次,点赞7次,收藏34次。vue-grid-layout的使用、实例、遇到的问题和解决方案_vue-grid-layout

Power Apps-上传附件控件_powerapps点击按钮上传附件-程序员宅基地

文章浏览阅读218次。然后连接一个数据源,就会在下面自动产生一个添加附件的组件。把这个控件复制粘贴到页面里,就可以单独使用来上传了。插入一个“编辑”窗体。_powerapps点击按钮上传附件

C++ 面向对象(Object-Oriented)的特征 & 构造函数& 析构函数_"object(cnofd[\"ofdrender\"])十条"-程序员宅基地

文章浏览阅读264次。(1) Abstraction (抽象)(2) Polymorphism (多态)(3) Inheritance (继承)(4) Encapsulation (封装)_"object(cnofd[\"ofdrender\"])十条"

修改node_modules源码,并保存,使用patch-package打补丁,git提交代码后,所有人可以用到修改后的_修改 node_modules-程序员宅基地

文章浏览阅读133次。删除node_modules,重新npm install看是否成功。在 package.json 文件中的 scripts 中加入。修改你的第三方库的bug等。然后目录会多出一个目录文件。_修改 node_modules

【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure-程序员宅基地

文章浏览阅读883次。【代码】【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure

整理5个优秀的微信小程序开源项目_微信小程序开源模板-程序员宅基地

文章浏览阅读1w次,点赞13次,收藏97次。整理5个优秀的微信小程序开源项目。收集了微信小程序开发过程中会使用到的资料、问题以及第三方组件库。_微信小程序开源模板

随便推点

Centos7最简搭建NFS服务器_centos7 搭建nfs server-程序员宅基地

文章浏览阅读128次。Centos7最简搭建NFS服务器_centos7 搭建nfs server

Springboot整合Mybatis-Plus使用总结(mybatis 坑补充)_mybaitis-plus ruledataobjectattributemapper' and '-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏3次。前言mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQL语句进行,就必须写大量的xml文件,很是麻烦。mybatis-plus就很好的解决了这个问题。..._mybaitis-plus ruledataobjectattributemapper' and 'com.picc.rule.management.d

EECE 1080C / Programming for ECESummer 2022 Laboratory 4: Global Functions Practice_eece1080c-程序员宅基地

文章浏览阅读325次。EECE 1080C / Programming for ECESummer 2022Laboratory 4: Global Functions PracticePlagiarism will not be tolerated:Topics covered:function creation and call statements (emphasis on global functions)Objective:To practice program development b_eece1080c

洛谷p4777 【模板】扩展中国剩余定理-程序员宅基地

文章浏览阅读53次。被同机房早就1年前就学过的东西我现在才学,wtcl。设要求的数为\(x\)。设当前处理到第\(k\)个同余式,设\(M = LCM ^ {k - 1} _ {i - 1}\) ,前\(k - 1\)个的通解就是\(x + i * M\)。那么其实第\(k\)个来说,其实就是求一个\(y\)使得\(x + y * M ≡ a_k(mod b_k)\)转化一下就是\(y * M ...

android 退出应用没有走ondestory方法,[Android基础论]为何Activity退出之后,系统没有调用onDestroy方法?...-程序员宅基地

文章浏览阅读1.3k次。首先,问题是如何出现的?晚上复查代码,发现一个activity没有调用自己的ondestroy方法我表示非常的费解,于是我检查了下代码。发现再finish代码之后接了如下代码finish();System.exit(0);//这就是罪魁祸首为什么这样写会出现问题System.exit(0);////看一下函数的原型public static void exit (int code)//Added ..._android 手动杀死app,activity不执行ondestroy

SylixOS快问快答_select函数 导致堆栈溢出 sylixos-程序员宅基地

文章浏览阅读894次。Q: SylixOS 版权是什么形式, 是否分为<开发版税>和<运行时版税>.A: SylixOS 是开源并免费的操作系统, 支持 BSD/GPL 协议(GPL 版本暂未确定). 没有任何的运行时版税. 您可以用她来做任何 您喜欢做的项目. 也可以修改 SylixOS 的源代码, 不需要支付任何费用. 当然笔者希望您可以将使用 SylixOS 开发的项目 (不需要开源)或对 SylixOS 源码的修改及时告知笔者.需要指出: SylixOS 本身仅是笔者用来提升自己水平而开发的_select函数 导致堆栈溢出 sylixos

推荐文章

热门文章

相关标签