昨天晚上 Jactry 使用 Rescue for Lemote2f 打包龙芯笔记本上的操作系统,发现 Ext4 分区挂载不了,检查发现是内核没有编译进 Ext4 文件系统的支持(当时太粗心,忘记了 :))。
准备重新编译这个 Rescue System,但是当时制作的 InitRamFs 已经早就没有了,在 Wuzhangjin 的帮助下成功的从 Rescue System 中提取出了映像文件。
Rescue System
这是为 Lemote2f 机器编译制作的紧急修复系统,用于在操作系统不能正常启动的情况下修复硬盘中的系统,也可以用于打包硬盘中的操作系统等等。
Rescue System 的实质就是一个 Linux 内核且编译进了一个 RamDisk 文件系统的映像文件,运行时解压到内存盘中用作根文件系统。它是一个 ELF 格式的文件,可以使用 readelf 程序读取其中的信息。
检查是否压缩
readelf -S rescue-lemote2f
readelf -S rescue-lemote2f
There are 26 section headers, starting at offset 0x668610:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS ffffffff80de0000 00010000
0000000000000b10 0000000000000000 AX 0 0 16
[ 2] .text.zlib_update PROGBITS ffffffff80de0b10 00010b10
0000000000000190 0000000000000000 AX 0 0 8
[ 3] .text.nofill PROGBITS ffffffff80de0ca0 00010ca0
0000000000000018 0000000000000000 AX 0 0 8
[ 4] .text.error PROGBITS ffffffff80de0cb8 00010cb8
0000000000000038 0000000000000000 AX 0 0 8
[ 5] .text.memcpy PROGBITS ffffffff80de0cf0 00010cf0
0000000000000048 0000000000000000 AX 0 0 8
[ 6] .text.zlib_inflat PROGBITS ffffffff80de0d38 00010d38
0000000000000978 0000000000000000 AX 0 0 8
[ 7] .text.inflate_fas PROGBITS ffffffff80de16b0 000116b0
0000000000000c80 0000000000000000 AX 0 0 8
[ 8] .text.zlib_inflat PROGBITS ffffffff80de2330 00012330
0000000000000018 0000000000000000 AX 0 0 8
[ 9] .text.zlib_inflat PROGBITS ffffffff80de2348 00012348
0000000000000098 0000000000000000 AX 0 0 8
[10] .text.zlib_inflat PROGBITS ffffffff80de23e0 000123e0
00000000000000e8 0000000000000000 AX 0 0 8
[11] .text.zlib_inflat PROGBITS ffffffff80de24c8 000124c8
00000000000018a8 0000000000000000 AX 0 0 8
[12] .text.zlib_inflat PROGBITS ffffffff80de3d70 00013d70
0000000000000038 0000000000000000 AX 0 0 8
[13] .text.zlib_inflat PROGBITS ffffffff80de3da8 00013da8
0000000000000308 0000000000000000 AX 0 0 8
[14] .text.decompress_ PROGBITS ffffffff80de40b0 000140b0
00000000000004f0 0000000000000000 AX 0 0 8
[15] .text.putc PROGBITS ffffffff80de45a0 000145a0
0000000000000018 0000000000000000 AX 0 0 8
[16] .text.puts PROGBITS ffffffff80de45b8 000145b8
0000000000000078 0000000000000000 AX 0 0 8
[17] .text.puthex PROGBITS ffffffff80de4630 00014630
0000000000000120 0000000000000000 AX 0 0 8
[18] .text.main PROGBITS ffffffff80de4750 00014750
0000000000000018 0000000000000000 AX 0 0 8
[19] .rodata.str1.8 PROGBITS ffffffff80de4768 00014768
00000000000002f8 0000000000000001 AMS 0 0 8
[20] .data PROGBITS ffffffff80de4a60 00014a60
0000000000653a0d 0000000000000000 WA 0 0 16
[21] .bss NOBITS ffffffff81438470 0066846d
0000000000402030 0000000000000000 WA 0 0 16
[22] .gnu.attributes LOOS+ffffff5 0000000000000000 0066846d
0000000000000010 0000000000000000 0 0 1
[23] .shstrtab STRTAB 0000000000000000 0066847d
000000000000018e 0000000000000000 0 0 1
[24] .symtab SYMTAB 0000000000000000 00668c90
0000000000002a00 0000000000000018 25 417 8
[25] .strtab STRTAB 0000000000000000 0066b690
0000000000000a70 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are 26 section headers, starting at offset 0x668610:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS ffffffff80de0000 00010000
0000000000000b10 0000000000000000 AX 0 0 16
[ 2] .text.zlib_update PROGBITS ffffffff80de0b10 00010b10
0000000000000190 0000000000000000 AX 0 0 8
[ 3] .text.nofill PROGBITS ffffffff80de0ca0 00010ca0
0000000000000018 0000000000000000 AX 0 0 8
[ 4] .text.error PROGBITS ffffffff80de0cb8 00010cb8
0000000000000038 0000000000000000 AX 0 0 8
[ 5] .text.memcpy PROGBITS ffffffff80de0cf0 00010cf0
0000000000000048 0000000000000000 AX 0 0 8
[ 6] .text.zlib_inflat PROGBITS ffffffff80de0d38 00010d38
0000000000000978 0000000000000000 AX 0 0 8
[ 7] .text.inflate_fas PROGBITS ffffffff80de16b0 000116b0
0000000000000c80 0000000000000000 AX 0 0 8
[ 8] .text.zlib_inflat PROGBITS ffffffff80de2330 00012330
0000000000000018 0000000000000000 AX 0 0 8
[ 9] .text.zlib_inflat PROGBITS ffffffff80de2348 00012348
0000000000000098 0000000000000000 AX 0 0 8
[10] .text.zlib_inflat PROGBITS ffffffff80de23e0 000123e0
00000000000000e8 0000000000000000 AX 0 0 8
[11] .text.zlib_inflat PROGBITS ffffffff80de24c8 000124c8
00000000000018a8 0000000000000000 AX 0 0 8
[12] .text.zlib_inflat PROGBITS ffffffff80de3d70 00013d70
0000000000000038 0000000000000000 AX 0 0 8
[13] .text.zlib_inflat PROGBITS ffffffff80de3da8 00013da8
0000000000000308 0000000000000000 AX 0 0 8
[14] .text.decompress_ PROGBITS ffffffff80de40b0 000140b0
00000000000004f0 0000000000000000 AX 0 0 8
[15] .text.putc PROGBITS ffffffff80de45a0 000145a0
0000000000000018 0000000000000000 AX 0 0 8
[16] .text.puts PROGBITS ffffffff80de45b8 000145b8
0000000000000078 0000000000000000 AX 0 0 8
[17] .text.puthex PROGBITS ffffffff80de4630 00014630
0000000000000120 0000000000000000 AX 0 0 8
[18] .text.main PROGBITS ffffffff80de4750 00014750
0000000000000018 0000000000000000 AX 0 0 8
[19] .rodata.str1.8 PROGBITS ffffffff80de4768 00014768
00000000000002f8 0000000000000001 AMS 0 0 8
[20] .data PROGBITS ffffffff80de4a60 00014a60
0000000000653a0d 0000000000000000 WA 0 0 16
[21] .bss NOBITS ffffffff81438470 0066846d
0000000000402030 0000000000000000 WA 0 0 16
[22] .gnu.attributes LOOS+ffffff5 0000000000000000 0066846d
0000000000000010 0000000000000000 0 0 1
[23] .shstrtab STRTAB 0000000000000000 0066847d
000000000000018e 0000000000000000 0 0 1
[24] .symtab SYMTAB 0000000000000000 00668c90
0000000000002a00 0000000000000018 25 417 8
[25] .strtab STRTAB 0000000000000000 0066b690
0000000000000a70 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
从上面我们看出有几个代码段包含 zlib 关键词,说明内核编译进了压缩支持,多数是压缩内核。
提取映像
readelf -s rescue-lemote2f | grep image
readelf -s rescue-lemote2f | grep image
424: ffffffff81438480 8 OBJECT GLOBAL DEFAULT 21 zimage_start
425: ffffffff8143846d 0 NOTYPE GLOBAL DEFAULT 20 __image_end
429: ffffffff80de4a60 0 NOTYPE GLOBAL DEFAULT 20 __image_begin
424: ffffffff81438480 8 OBJECT GLOBAL DEFAULT 21 zimage_start
425: ffffffff8143846d 0 NOTYPE GLOBAL DEFAULT 20 __image_end
429: ffffffff80de4a60 0 NOTYPE GLOBAL DEFAULT 20 __image_begin
从上面的数据看到 __image_begin 和 __image_end 两个符号没有类型且没有长度,应该是静态常量用于标记映像文件的位置地址,结合上面的 Section Headers 发现标记的这段数据正好处于 .data 段,而 zimage_start 则是一个 .bss 段的变量,一定不是我们需要的信息。
我们看到 __image_begin 的内存地址是 ffffffff80de4a60,__image_end 内存地址是 ffffffff8143846d,计算出长度是 ffffffff8143846d – ffffffff80de4a60 = 653a0d,从 Section Headers 检查发现整个 .data 段就是这个映像文件。从 Section Headers 我们找到了 .data 段在文件中的偏移地址 00014a60。有了这些信息我们就可以使用 dd 命令提取出映像文件了。
全部转换成十进制数
偏移地址 00014a60 = 84576
长度 653a0d = 6633997
dd if=rescue-lemote2f of=vmlinux skip=84576 bs=1 count=6633997
dd if=rescue-lemote2f of=vmlinux skip=84576 bs=1 count=6633997
映像类型检查
file vmlinux
file vmlinux
vmlinux: gzip compressed data, from Unix, last modified: Tue May 25 20:59:46 2010, max compression
vmlinux: gzip compressed data, from Unix, last modified: Tue May 25 20:59:46 2010, max compression
获取 gz 文件头
获取文件头用于后面查找 ramdisk.cpio.gz 文件的开始地址
hexdump vmlinux | grep 0000000
hexdump vmlinux | grep 0000000
0000000 8b1f 0008 c9c2 4bfb 0302 5cec 740d 5514
0000000 8b1f 0008 c9c2 4bfb 0302 5cec 740d 5514
我们看到当时打包的 gz 文件前四个字节是 8b1f 0008
解压 vmlinux 文件
从上面我们得知 vmlinux 实际是 gzip 压缩文件,我们解压它,得到了新的 vmlinux 文件。
mv vmlinux vmlinux.gz
gunzip vmlinux.gz
mv vmlinux vmlinux.gz
gunzip vmlinux.gz
查找 ramdisk.cpio.gz 的开始地址
我们先使用 hexdump 将整个文件都 dump 出来保存到一个文本文件中,便于后面的查找。
hexdump vmlinux > vmlinux.txt
vim vmlinux.txt
hexdump vmlinux > vmlinux.txt
vim vmlinux.txt
通过搜索,我们在这个文件中找到了两处 8b1f 0008,分别是
03b4520 4d50 8028 ffff ffff 47f8 8028 ffff ffff
03b4530 4b80 8028 ffff ffff 4810 8028 ffff ffff
03b4540 4b49 4643 5f47 5453 8b1f 0008 c8c2 4bfb
03b4520 4d50 8028 ffff ffff 47f8 8028 ffff ffff
03b4530 4b80 8028 ffff ffff 4810 8028 ffff ffff
03b4540 4b49 4643 5f47 5453 8b1f 0008 c8c2 4bfb
04b6c80 0000 0000 0000 0000 0000 0000 0000 0000
*
04b8000 8b1f 0008 6c56 4bd5 0302 5ab4 707d 555b
04b6c80 0000 0000 0000 0000 0000 0000 0000 0000
*
04b8000 8b1f 0008 6c56 4bd5 0302 5ab4 707d 555b
我猜测后者的可能性比较大,也可以每个都 dd 出解压看看。
提取 ramdisk.cpio.gz
从上面我们得到了ramdisk.cpio.gz在文件中的偏移地址 04b8000 = ,但是没有长度,实际也不需要长度,因为 gunzip 肯定能够从 gz 文件中得知需要读取多少个字节,就让它交给 gunzip 处理吧。我们从这个地址开始取到文件结束。
dd if=vmlinux of=ramdisk.cpio.gz skip=4947968 bs=1
dd if=vmlinux of=ramdisk.cpio.gz skip=4947968 bs=1
解压 ramdisk.cpio.gz
gunzip ramdisk.cpio.gz
gunzip ramdisk.cpio.gz
我们得到了完整的 ramdisk.cpio 文件了!
Over!