从 Rescue System 中提取 InitRamFs 映像文件

昨天晚上 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
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
   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

映像类型检查

file vmlinux
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
0000000 8b1f 0008 c9c2 4bfb 0302 5cec 740d 5514

我们看到当时打包的 gz 文件前四个字节是 8b1f 0008

解压 vmlinux 文件
从上面我们得知 vmlinux 实际是 gzip 压缩文件,我们解压它,得到了新的 vmlinux 文件。

mv vmlinux vmlinux.gz
gunzip vmlinux.gz

查找 ramdisk.cpio.gz 的开始地址
我们先使用 hexdump 将整个文件都 dump 出来保存到一个文本文件中,便于后面的查找。

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
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

解压 ramdisk.cpio.gz

gunzip ramdisk.cpio.gz

我们得到了完整的 ramdisk.cpio 文件了!

Over!

4 thoughts on “从 Rescue System 中提取 InitRamFs 映像文件”

  1. 楼主,我用你的resuce 运行起来了,但挂不上硬盘 (dev 下面没有那些设备文件)
    fdisk -l 没东西输出,然后手动建了几个设备文件
    mount /dev/sda1 /mnt //挂却上的是U盘

    我今天也做了一个resuce,就是在编译内核的时候把文件系统(用busybox1.18.1做的,在龙芯电脑上放在sda2可以跑)编译进去了,用pmon load 之后
    pmon> g console=tty
    提示VFS:unknow “root=” or unknow block(0,0)
    换成:
    pmon> g console=tty root=ram
    情况同上

Leave a Reply

Your email address will not be published. Required fields are marked *