OpenWRTの探索:UImageとSysupgradeイメージの違い



「OpenWRTでUpvel UR-313N4Gルーターをアップグレードする」という記事へのコメントで、謙虚なサーバントと尊敬されているMaysoftの間で、uImageとsysupgrade OpenWRTファームウェアイメージの構造の違いについて紛争が生じました。 Maysoftに問題を解決することを約束しました 。この記事はあなたの前にあります。



ご存じのように、OpenWRTダウンロードディレクトリでは、ほとんどの場合、uImageとsysupgradeの2種類のファームウェアを使用できます。



openwrt-15.05-rc3-ramips-rt305x-dir-320-b1-initramfs-uImage.bin

openwrt-15.05-rc3-ramips-rt305x-dir-320-b1-squashfs-sysupgrade.bin



公式FAQは、両者の違いについて非常に控えめに書いています:

異なる画像形式の違いは何ですか?

工場出荷時のイメージは、ブートローダーフラッシャーまたはストックソフトウェアフラッシャー用に構築されたものです。

sysupgradeイメージ(以前の名前はtrxイメージ)は、openwrt自体からフラッシュされるように設計されています

この2つのコンテンツは同じですが、ファクトリイメージには追加のヘッダー情報またはプラットフォームが必要とするものが含まれます。 一般的に、工場出荷時のイメージは、デバイスをOpenWrtに変換するために、OEM GUIまたはOEMフラッシュユーティリティで使用されます。 その後、sysupgradeイメージを使用します。


ドキュメントによると、工場出荷時のイメージに追加のヘッダーがあり、元のファームウェアのWebインターフェースを介してこのイメージをフラッシュできることを除いて、イメージの内容は同じです。



すばらしい、ファームウェアのサイズを比較してください:



openwrt-15.05-rc3-ramips-rt305x-dir-320-b1-initramfs-uImage.bin- 3253035バイト。



openwrt-15.05-rc3-ramips-rt305x-dir-320-b1-squashfs-sysupgrade.bin- 3407876バイト。



うわー、sysupgradeファームウェアはuImageよりもほぼ140 KB大きく、ドキュメントによると、ほぼ同じサイズである必要があり、この「余分なヘッダー情報」によるuImageは少し大きくなります。



もちろん、uImageとsysupgradeのイメージの違いを理解するには、アセンブリスクリプトを調べるだけで十分ですが、これはスポーツマンらしくないことを認めなければなりません。 今日は、ソースがないようにファームウェアの「額」を分析し、最後にアセンブリスクリプトを見て推測を確認します。



現時点でファームウェアを分析するための主要なツールは、Linuxで利用可能なbinwalkユーティリティです。 使いやすいように短いファームウェアファイルの名前を変更し、分析を開始します。



> binwalk ./uImage.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 uImage header, header size: 64 bytes, header CRC: 0x19DE1499, created: Fri Jul 3 22:16:00 2015, image size: 3252971 bytes, Data Address: 0x80000000, Entry Point: 0x80000000, data CRC: 0x886ADE01, OS: Linux, CPU: IPS, image type: OS Kernel Image, compression type: lzma, image name: "MIPS OpenWrt Linux-3.18.17" 64 0x40 LZMA compressed data, properties: 0x6D, dictionary size: 8388608 bytes, uncompressed size: 5479932 bytes
      
      





ファームウェア全体がuImageイメージであるように見えます-最初は64(0x40)バイトの長さのヘッダーがあり、その後はLZMAアルゴリズムによって圧縮されたサイズ3252971バイトのデータストリームです。 64と3252971を追加すると、3253035バイト、つまりダウンロードされたイメージのサイズが得られます。 したがって、uImageイメージを除き、ファイルには他に何もありません。 Binwalkは、見つかったLZMAストリームを解凍できます。 原則として、ファイルから最初の64バイトを手動で切り取り、lzma -dコマンドを使用して残りをアンパックできますが、なぜ便利なツールがあるのはなぜですか?



 > binwalk -e ./uImage.bin
      
      





私たちが得たものを見てみましょう

 > ls -l ./_uImage.bin.extracted/  8532 -rw-r--r-- 1 user user 5479932  8 23:10 40 -rw-r--r-- 1 user user 3252971  8 23:10 40.7z
      
      





40という名前のファイル(ソースファイルのオフセット)は、展開されたストリームです。 binwalkを設定しましょう:



 binwalk ./_uImage.bin.extracted/40 DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 2812692 0x2AEB14 Linux kernel version "3.18.17 (buildbot@builder1) (gcc version 4.8.3 (OpenWrt/Linaro c version 4.8.3 (OpenWrt/Linaro GCC 4.8-2014.04 r46018) ) #2 Fr" 2932132 0x2CBDA4 LZMA compressed data, properties: 0x5D, dictionary size: 16777216 bytes, missing uncompressed size 2936592 0x2CCF10 xz compressed data 3400392 0x33E2C8 LZMA compressed data, properties: 0x6D, dictionary size: 1048576 bytes, uncompressed size: -1 bytes
      
      





そして、ここでは一見理解できないものがあります。binwalkは、Linuxカーネルがオフセット0x2AEB14で、カーネルに続く3つの圧縮データストリームを発見しました。 実際、binwalkは分析にヒューリスティックを使用し、それが出力するのは最終的な真実ではなく、検討のための情報です。

常識的には、カーネルはオフセット0から開始し、圧縮ストリームは1で、initramfs(RAMにロードされる初期ファイルシステム)を含む必要があります。 コアドキュメントには同じことが記載されています

initramfsとは何ですか?

-すべての2.6 Linuxカーネルには、gzip圧縮された「cpio」形式のアーカイブが含まれています。これは、カーネルの起動時にrootfsに抽出されます。 抽出後、カーネルはrootfsにファイル「init」が含まれているかどうかを確認し、含まれている場合はPID 1として実行します。


さらに

initramfsの設定:

-2.6カーネルビルドプロセスは、常にgzip圧縮されたcpio形式のinitramfsアーカイブを作成し、結果のカーネルバイナリにリンクします。 デフォルトでは、このアーカイブは空です(x86では134バイトを消費します)。


ストリーム形式もここに記載されています-CPIO。



binwalkが画像から抽出できるものを見てみましょう。



 > binwalk -e ./_uImage.bin.extracted/40 ls -l ./_uImage.bin.extracted/_40.extracted/  14028 -rw-r--r-- 1 user user 2547808  8 23:25 2CBDA4.7z -rw-r--r-- 1 user user 2543340  8 23:25 2CCF10.tar -rw-r--r-- 1 user user 7186944  8 23:25 33E2C8 -rw-r--r-- 1 user user 2079540  8 23:25 33E2C8.7z
      
      





そのため、オフセット33E2C8のストリームのみが正常にアンパックされました。 すべてを正しく行う場合、これはファイルシステムを備えたCPIOコンテナである必要があります。



 > binwalk _uImage.bin.extracted/_40.extracted/33E2C8 DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 ASCII cpio archive (SVR4 with no CRC), file name: "dev", file name length: "0x00000004", file size: "0x00000000" 116 0x74 ASCII cpio archive (SVR4 with no CRC), file name: "dev/console", file name length: "0x0000000C", file size: "0x00000000" 240 0xF0 ASCII cpio archive (SVR4 with no CRC), file name: "lib", file name length: "0x00000004", file size: "0x00000000" 356 0x164 ASCII cpio archive (SVR4 with no CRC), file name: "lib/netifd", file name length: "0x0000000B", file size: "0x00000000" 480 0x1E0 ASCII cpio archive (SVR4 with no CRC), file name: "lib/netifd/netifd-wireless.sh", file name length: "0x0000001E", file size: "0x00001638" *********** *********** 7186416 0x6DA7F0 ASCII cpio archive (SVR4 with no CRC), file name: "dev/urandom", file name length: "0x0000000C", file size: "0x00000000" 7186540 0x6DA86C ASCII cpio archive (SVR4 with no CRC), file name: "dev/pts", file name length: "0x00000008", file size: "0x00000000" 7186660 0x6DA8E4 ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!", file name length: "0x0000000B", file size: "0x00000000"
      
      





アーカイブの最後に、TRAILER !!! ..というファイルがあります。 ドキュメントによると、それはアーカイブの終わりの印です。



したがって、initramfs-uImageファームウェアの構造は次のとおりです。







次に、squashfs-sysupgradeイメージを取得します。 名前は、イメージに(カーネルを除く)squashfsファイルシステムが含まれることを意味します。 これがそうかどうか見てみましょう:



 > binwalk -e ./sysupgrade.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 uImage header, header size: 64 bytes, header CRC: 0x66CC90D2, created: Mon Jul 6 21:54:35 2015, image size: 1142606 bytes, Data Address: 0x80000000, Entry Point: 0x80000000, data CRC: 0x91B77696, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "MIPS OpenWrt Linux-3.18.17" 64 0x40 LZMA compressed data, properties: 0x6D, dictionary size: 8388608 bytes, uncompressed size: 3396940 bytes 1142670 0x116F8E Squashfs filesystem, little endian, version 4.0, compression:lzma (non-standard type definition), size: 2221946 bytes, 1132 inodes, blocksize: 262144 bytes, created: Mon Jul 6 21:54:02 2015
      
      







64 + 1142606(画像サイズ)= 1142670、このオフセットでsquashfs画像が開始し、オフセット1142670 + 2221946 = 3364616で終了します。一方、画像のサイズは3407876バイトであり、3407876バイトあります。 -3364616 = 43260バイトの身元不明情報。 そこに何があるか見てみましょう、hexdump

 > hexdump -s 3364616 ./sysupgrade.bin 0335708 ffff ffff ffff ffff ffff ffff ffff ffff * 0335ff8 ffff ffff ffff ffff adde dec0 ffff ffff 0336008 ffff ffff ffff ffff ffff ffff ffff ffff * 0337ff8 ffff ffff ffff ffff adde dec0 ffff ffff 0338008 ffff ffff ffff ffff ffff ffff ffff ffff * 033fff8 ffff ffff ffff ffff adde dec0 0340004
      
      





明らかにある種のスタブがあります。 後で彼女に戻ってきます。



展開された画像があるディレクトリにあるものを見てみましょう:



 > ls -l _sysupgrade.bin.extracted/  8820 -rw-r--r-- 1 user user 2221946  8 23:40 116F8E.squashfs -rw-r--r-- 1 user user 3396940  8 23:40 40 -rw-r--r-- 1 user user 3407812  8 23:40 40.7z
      
      





LZMAストリームをオフセット40で解凍します。



 binwalk -e _sysupgrade.bin.extracted/40 DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 2812644 0x2AEAE4 Linux kernel version "3.18.17 (buildbot@builder1) (gcc version 4.8.3 (OpenWrt/Linaro c version 4.8.3 (OpenWrt/Linaro GCC 4.8-2014.04 r46018) ) #1 Fr" 2932068 0x2CBD64 LZMA compressed data, properties: 0x5D, dictionary size: 16777216 bytes, missing uncompressed size 2936444 0x2CCE7C xz compressed data 3396424 0x33D348 ASCII cpio archive (SVR4 with no CRC), file name: "dev", file name length: "0x00000004", file size: "0x00000000" 3396540 0x33D3BC ASCII cpio archive (SVR4 with no CRC), file name: "dev/console", file name length: "0x0000000C", file size: "0x00000000" 3396664 0x33D438 ASCII cpio archive (SVR4 with no CRC), file name: "root", file name length: "0x00000005", file size: "0x00000000" 3396780 0x33D4AC ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!", file name length: "0x0000000B", file size: "0x00000000"
      
      





ここには、Linuxカーネルと4つのファイルを持つ小さなinitramfsイメージがあります。 残りは明らかにsquashfsイメージに移動しました:



 > unsquashfs -i ./_sysupgrade.bin.extracted/116F8E.squashfs Parallel unsquashfs: Using 4 processors 1033 inodes (1034 blocks) to write squashfs-root squashfs-root/bin squashfs-root/bin/ash squashfs-root/bin/board_detect squashfs-root/bin/busybox *********** *********** squashfs-root/www/luci-static/resources/load.svg squashfs-root/www/luci-static/resources/wifirate.svg squashfs-root/www/luci-static/resources/wireless.svg squashfs-root/www/luci-static/resources/xhr.js created 848 files created 99 directories created 184 symlinks created 0 devices created 0 fifos
      
      





実際、メインファイルシステムはsquashfsイメージに含まれています。



次に、画像の最後にあるスタブを扱います。 sysupgradeイメージがフラッシュされてからdmesgにロードされると、次の行が表示されるため、何らかの形でJFFS2 FSに関連している疑いがあります。



 [ 29.550000] jffs2_scan_eraseblock(): End of filesystem marker found at 0x0 [ 29.580000] jffs2_build_filesystem(): unlocking the mtd device... done. [ 29.590000] jffs2_build_filesystem(): erasing all blocks after the end marker...
      
      





また、uImageイメージをフラッシュしてロードするとき、これらの行はそうではありません。 これらの行を「バニラ」カーネルで検索しても何も得られませんが、 OpenWRTソースツリーには次のような行があります。



 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -114,6 +114,16 @@ static int jffs2_build_filesystem(struct dbg_fsbuild("scanned flash completely\n"); jffs2_dbg_dump_block_lists_nolock(c); + if (c->flags & (1 << 7)) { + printk("%s(): unlocking the mtd device... ", __func__); + mtd_unlock(c->mtd, 0, c->mtd->size); + printk("done.\n"); + + printk("%s(): erasing all blocks after the end marker... ", __func__); + jffs2_erase_pending_blocks(c, -1); + printk("done.\n"); + } + dbg_fsbuild("pass 1 starting\n"); c->flags |= JFFS2_SB_FLAG_BUILDING; /* Now scan the directory tree, increasing nlink according to every dirent found. */ --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_in /* reset summary info for next eraseblock scan */ jffs2_sum_reset_collected(s); - ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), - buf_size, s); + if (c->flags & (1 << 7)) { + if (mtd_block_isbad(c->mtd, jeb->offset)) + ret = BLK_STATE_BADBLOCK; + else + ret = BLK_STATE_ALLFF; + } else + ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), + buf_size, s); if (ret < 0) goto out; @@ -561,6 +567,17 @@ full_scan: return err; } + if ((buf[0] == 0xde) && + (buf[1] == 0xad) && + (buf[2] == 0xc0) && + (buf[3] == 0xde)) { + /* end of filesystem. erase everything after this point */ + printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); + c->flags |= (1 << 7); + + return BLK_STATE_ALLFF;```` + } + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ ofs = 0; max_ofs = EMPTY_SCAN_SIZE(c->sector_size);
      
      





ええ、これが0xDEADCODEスタブです。 JFFS2ドライバーはこのタグを見つけると、それを前のファイルシステムの終わりと見なし、タグのゼロバイトからドライブの終わりまでをすべて消去します。 したがって、ラベル自体も上書きされます。

その後、ドライバーはこの場所にJFFS2の新しいインスタンスを作成します。

したがって、次の画像構造が取得されます。







このように:

  1. uImageにはOpenWRTを実行するために最低限必要な機能が含まれているため、元のファームウェアのWebインターフェイスで検証チェックに合格するように、その構造を簡単に変更できます。
  2. Sysupgradeはより複雑で、Linux固有のツールであるSquashFSおよびJFFS2を使用します。
  3. どちらのタイプのイメージにもブートローダーが含まれていません(含まれてはなりません)。
  4. (UARTまたは災害復旧を介して)ブートローダーを介してフラッシュする場合、すぐにsysupradeを縫うことができます。
  5. mtd_writeを介してフラッシュするときに、公式ファームウェアからtelnetを介してこのユーティリティを使用できる場合、sysupgradeをすぐにフラッシュすることもできます。




結論として、OpenWRTビルドスクリプトを見て、結論を確認してください。 ファイル/ target / linux / ramips / Makefileから始めましょう。 しかし、これが通常のGNU Makefileであると思われる場合は、そうではありません。 開発者自身改善されたMakefileをどのように説明するかを以下に示します。

パッケージmakefileの1つを見ると、それをmakefileとして認識することはほとんどありません。 従来のmake形式の露骨な無視と濫用としか言いようのないものを通して、makefileは試練全体を単純化するオブジェクト指向テンプレートに変換されました。


イメージビルドは564行で実行されるようです。



 Image/Build/Profile/DIR-320-B1=$(call BuildFirmware/Default8M/$(1),$(1),dir-320-b1,DIR-320-B1)
      
      





BuildFirmware / Default8Mの目的は、行195および196で説明されています。



 BuildFirmware/Default8M/squashfs=$(call BuildFirmware/OF,$(1),$(2),$(3),$(ralink_default_fw_size_8M),$(4)) BuildFirmware/Default8M/initramfs=$(call BuildFirmware/OF/initramfs,$(1),$(2),$(3),$(4))
      
      





これは、squashfsとinitramfsの2つの位置で構成されています。 BuildFirmware / OFおよびBuildFirmware / OF / initramfsの目標は、上記の同じファイルに記載されています。



 149 # $(1), Rootfs type, eg squashfs 150 # $(2), lowercase board name 151 # $(3), DTS filename without .dts extension 152 # $(4), maximum size of sysupgrade image 153 # $(5), uImage header's ih_name field 154 define BuildFirmware/OF 155 $(call MkImageLzmaDtb,$(2),$(3),$(5)) 156 $(call MkImageSysupgrade/$(1),$(1),$(2),$(4),$(6)) 157 endef
      
      





そして



 169 # $(1), squashfs/initramfs 170 # $(2), lowercase board name 171 # $(3), DTS filename without .dts extension 172 # $(4), ih_name field of uImage header 173 define BuildFirmware/OF/initramfs 174 $(call MkImageLzmaDtb,$(2),$(3),$(4),-initramfs) 175 $(CP) $(KDIR)/vmlinux-$(2)-initramfs.uImage $(call imgname,$(1),$(2))-uImage.bin 176 endef
      
      





ターゲットMkImageLzmaDtbの名前から、DTSファイルの説明からuImageイメージが作成されると推測できます。 BuildFirmware / OF / initramfsの目的で、メインファイルシステムを含むinitramfsセクションがこのイメージに追加され、その後、イメージが出力ディレクトリにコピーされます。 BuildFirmware / OFターゲットの場合、ソースイメージはMkImageSysupgrade関数によって処理されます。この関数は、「cat」コマンドを使用して、squashfsセクションをアタッチし、 image.mkファイルで定義されたprepare_generic_squashfs関数を呼び出します。



 # pad to 4k, 8k, 16k, 64k, 128k, 256k and add jffs2 end-of-filesystem mark 110 define prepare_generic_squashfs 111 $(STAGING_DIR_HOST)/bin/padjffs2 $(1) 4 8 16 64 128 256 112 endef
      
      





また、 C記述されたpadjffs2ユーティリティは、イメージファイルの最後に0xDEADCODEマークを書き込みます。



 static unsigned char eof_mark[4] = {0xde, 0xad, 0xc0, 0xde}; *** static unsigned char *pad = eof_mark; *** /* write out the JFFS end-of-filesystem marker */ t = write(fd, pad, pad_len); if (t != pad_len) { ERRS("Unable to write to %s", name); goto close; }
      
      





一般に、OpenWRTの世界は美しく、特に次のようなコメントがすばらしいです。

#これらのテンプレート内で本当の魔法が発生します


または

/ *元のドライバーからのvodoo * /


結論として、OpenWRTをソースからビルドする場合、ビルド環境に注意を払う必要があります。たとえば、Debian 8 + Sidのこのリポジトリでは、クロスコンパイル用のツールセットもビルドされません。 しかし、Debian 7では、初期設定を編集しない限り、このすべての富は驚くべきものになります。 修正すると、アセンブリスクリプトがソースコードをダウンロードするWebソースの一部が既に「腐敗」しており、新しいソースを探したり、スクリプトを修正したりする必要がある場合があります。



All Articles