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

Linux 应急安全重启

当 Linux 系统发生死机时,通过下面的方法可以安全的保存数据并重新启动。

1. 同时按住 Alt 和 Sys Rq 键。
2. 依次输入 reisub 六个键,注意速度不要太快。

reisub 依次表示如下的意思,可以独立操作:

unraw 将键盘控制从 X Server 那里抢回来
terminate 给所有进程发送 SIGTERM 信号,让他们自己解决善后
kill 给所有进程发送 SIGKILL 信号,强制他们马上关闭
sync 将所有数据同步至磁盘
unmount 将所有分区挂载为只读模式
reboot 重启

From: http://linuxtoy.org/archives/what-to-do-if-linux-crash.html

Over!

ArchLinux 系统安装郑码输入法

郑码输入法是由北京中易公司开发,是中文形码输入法中最成熟和高效的,在 Windows 系统中也是默认安装。可能由于版权的问题,开源操作系统中几乎没有集成。

从源代码安装
郑码输入法的 ibus table 已经在 AUR 中发布,链接 http://aur.archlinux.org/packages.php?ID=45795

wget http://aur.archlinux.org/packages/ibus-table-zhengma/ibus-table-zhengma.tar.gz
tar xzf ibus-table-zhengma.tar.gz
cd ibus-table-zhengma
makepkg
sudo pacman -U ibus-table-zhengma-1.2.0-1-any.pkg.tar.xz

从预编译包安装

sudo pacman -U http://heiher.info/sftp/ime/zhengma/ibus-table-zhengma-1.2.0-1-any.pkg.tar.xz

Tip: 重启 IBus 生效。

Over!

将 USB 设备映射到 KVM 里

有时需要在 KVM 的 Guest OS 里使用 USB 设备,记录一下映射方法。以 HTC Dream 手机为例。

获取设备ID

lsusb
Bus 002 Device 014: ID 0bb4:0c02 High Tech Computer Corp. 
Bus 002 Device 013: ID 1ea7:000e  
Bus 002 Device 006: ID 0e8f:0022 GreenAsia Inc. 
Bus 002 Device 002: ID 8087:0020  
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 004: ID 0c45:6452 Microdia 
Bus 001 Device 003: ID 0bda:0138 Realtek Semiconductor Corp. 
Bus 001 Device 002: ID 8087:0020  
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

这里 0bb4:0c02 就是我的 HTC Dream 的设备ID,大家可以通过对比设备插入前后的结果获得正确的ID。

设置访问权限
由于 KVM 多数是进行于普通用户,这类用户一般没有对 USB 设备的直接访问权限,需要增加一个 udev 规则增加权限。这里需要使用到前面获取到的设备ID的前段,也就是厂商ID,如这里的 0bb4,在执行完下面的命令后,重新插入一下设备。

echo "SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"0bb4\", MODE=\"0666\"" > /tmp/51-android.rules
sudo mv /tmp/51-android.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules

映射 USB 设备

kvm ... -usb -usbdevice host:0bb4:0c02

现在在 KVM 里的 Guest OS 中就可以看到设备了。

Over!

Ubuntu 10.04 系统上构建 Android 应用开发环境

安装 JDK
从源里安装 OpenJDK

sudo apt-get install openjdk-6-jdk

下载、安装 Android SDK

cd ~
wget http://dl.google.com/android/android-sdk_r08-linux_86.tgz
tar xzf android-sdk_r08-linux_86.tgz
mv android-sdk_r08-linux_86 asdk
test -e bin || mkdir bin
cd bin
find ../asdk/tools -maxdepth 1 -type f -executable -exec ln -s {} \;
find ../asdk/platform-tools -maxdepth 1 -type f -executable -exec ln -s {} \;

安装、升级 Android 平台

android

先升级SDK,再根据需要安装平台。

创建 AVD

android create avd -n Android -t 1 -c 100M

安装 Ant

sudo apt-get install ant1.8

至此,再安装一个自己喜欢的代码编辑器(如 vim )就完成开发环境的建立。

创建、编译和调试 Hello World!

# 创建项目
android create project --package com.android.helloandroid --activity HelloAndroid --target 1 --path HelloAndroid
# 编译调试版本
cd HelloAndroid
ant debug
# 进行模拟器
emulator -avd Android
# 安装 Hello World 程序到模拟器
adb -s emulator-5554 install bin/HelloAndroid-debug.apk

Over!

Windows XP 客户系统的 KVM 参数

在 KVM 里安装了一个 Windows XP,觉得 KVM 切换挺麻烦的,还需要远程控制,就映像了3389端口到宿主系统,关闭了KVM的图形输出。记录一下我的 KVM 参数。

kvm -cpu core2duo -smp 2 -m 512 -soundhw all -rtc base=localtime,clock=host -net nic -net user,hostfwd=tcp::3389-:3389 -daemonize -nographic -hda /dev/sdc2

Over!