follow me

MZK-WNH(OpenWrt)に潜むEdimaxヘッダー20バイトの罠

MZK-WNHに潜んでいるEdimaxヘッダー20バイトの罠について少しメモをしておこうと思います。
メーカー製ルータにありがちな独自ヘッダー付加による、ファームの書換えロックですが、MZK-WNHはかなり変態仕様である。
管理画面のアプリ側でチェックして、Flashには独自ヘッダーを削った形で書き込む方がスマートに思えるのだが、MZK-WNHはEdimaxヘッダーも含めてFlashに書き込むという仕様で、かつ書き込まれているu-bootはこのヘッダーが必要という訳ではない仕様であるという不思議が存在している。
このEdimaxヘッダーの為に、どれほどのMZK-WNHが文鎮として扱われてしまったのだろうか。


MZK-WNH文鎮化生成機能として働いたEdimaxヘッダー20バイトは以下の様な20バイトの事をいいます。(正確にはu-bootは生きているし、WPSボタン押しながらの復旧はできるので文鎮ではないが)
CSYSから始まり、固定システム名称が入る、解析済みのヘッダー構造でOpenWrtでもfactoryイメージを生成できるものの、2015/08/29時点でのtrunkではヘッダーは生成するものの、rootfsの位置をヘッダ分ずらせずにrootfsマウントに失敗します。
        00010203 04050607 08090A0B 0C0D0E0F  0123456789ABCDEF
000000  43535953 00000C00 00000500 524E3532  CSYS........RN52
000010  06002C00                             ..,.            
意外にも気が付くのに時間を取られてしまったので、このヘッダーにより発生する問題と、調べた結果をメモしておきます。
この結果を元に綺麗なパッチが生まれる事があれば良いのですが。。。
他のデバイスへの影響が生まれない様なパッチが思い浮かばなかったので、とりあえず起動できる段階までのパッチを載せておこうかと思います。


①はじめに、現時点OpenWrt trunkに含まれるfactoryイメージを試した場合の起動ログを見てみましょう。
[    0.510000] Amd/Fujitsu Extended Query Table at 0x0040
[    0.520000]   Amd/Fujitsu Extended Query version 1.1.
[    0.530000] number of CFI chips: 1
[    0.550000] 5 ofpart partitions found on MTD device 1f000000.cfi
[    0.560000] Creating 5 MTD partitions on "1f000000.cfi":
[    0.570000] 0x000000000000-0x000000030000 : "u-boot"
[    0.580000] 0x000000030000-0x000000040000 : "u-boot-env"
[    0.600000] 0x000000040000-0x000000050000 : "factory"
[    0.610000] 0x0000003e0000-0x000000400000 : "cimage"
[    0.620000] 0x000000050000-0x0000003e0000 : "firmware"
[    0.640000] ralink_soc_eth 10100000.ethernet eth0: ralink at 0xb0100000, irq 5
[    0.660000] rt2880_wdt 10000120.watchdog: Initialized
[    0.670000] TCP: cubic registered
[    0.680000] NET: Registered protocol family 17
[    0.680000] bridge: automatic filtering via arp/ip/ip6tables has been deprecated. Update your scripts to load br_netfilter if you need this.
[    0.710000] 8021q: 802.1Q VLAN Support v1.8
[    0.740000] VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
[    0.750000] Please append a correct "root=" boot option; here are the available partitions:
[    0.770000] 1f00             192 mtdblock0  (driver?)
[    0.780000] 1f01              64 mtdblock1  (driver?)
[    0.790000] 1f02              64 mtdblock2  (driver?)
[    0.800000] 1f03             128 mtdblock3  (driver?)
[    0.810000] 1f04            3648 mtdblock4  (driver?)
[    0.820000] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
[    0.820000] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
この様にrootfsをマウントできない以前にmtdデバイスの見え方が既におかしくなっています。
また、"firmware"パーティションが、"kernel"と"rootfs"パーティションに分かれていないのが味噌になります。
"firmware"パーティションには、"kernel"と"rootfs"の両方が含まれており、それぞれmtdblockが見えないといけない為、rootfsがマウントできる訳もなく、このあたりを調べていく必要があります。


②u-bootのメニューからtftpでsquashfs-sysupgradeなイメージを流し込んだ場合は、正常に起動できる為、そのログも見てみましょう。
[    0.510000] Amd/Fujitsu Extended Query Table at 0x0040
[    0.520000]   Amd/Fujitsu Extended Query version 1.1.
[    0.530000] number of CFI chips: 1
[    0.550000] 5 ofpart partitions found on MTD device 1f000000.cfi
[    0.560000] Creating 5 MTD partitions on "1f000000.cfi":
[    0.570000] 0x000000000000-0x000000030000 : "u-boot"
[    0.580000] 0x000000030000-0x000000040000 : "u-boot-env"
[    0.600000] 0x000000040000-0x000000050000 : "factory"
[    0.610000] 0x0000003e0000-0x000000400000 : "cimage"
[    0.620000] 0x000000050000-0x0000003e0000 : "firmware"
[    0.630000] 2 uimage-fw partitions found on MTD device firmware
[    0.650000] 0x000000050000-0x000000157b69 : "kernel"
[    0.660000] 0x000000157b69-0x0000003e0000 : "rootfs"
[    0.670000] mtd: device 6 (rootfs) set to be root filesystem
[    0.680000] 1 squashfs-split partitions found on MTD device rootfs
[    0.690000] 0x000000300000-0x0000003e0000 : "rootfs_data"
[    0.710000] ralink_soc_eth 10100000.ethernet eth0: ralink at 0xb0100000, irq 5
[    0.730000] rt2880_wdt 10000120.watchdog: Initialized
[    0.740000] TCP: cubic registered
[    0.750000] NET: Registered protocol family 17
"firmware"パーティションが、"kernel"と"rootfs"が別れています。


③パーティションを分ける処理を見ていきます。
別けているのは、kernelのdrivers/mtd/mtdsplit/mtdsplit_uimage.cになります。
ヘッダーのMagic Numberを見てイメージ検出と"kernel"と"rootfs"の分離を行うのですが、Edimaxヘッダーに対する処理は明らかに、今回使用しているPlanex MZK-WNHのものには合いません。
他のEdimax機器に対するコードなのか、何かしらのミスが入っているのかは現時点不明ですが、動作はしません。
static ssize_t uimage_find_edimax(u_char *buf, size_t len)
この処理の中では、"firmware"パーティションの20バイト目に対して、EdimaxヘッダーのMagic Number検出を行おうとしていますが、実際のEdimaxヘッダーのMagic Numberは0バイト目に存在するので、検出できる筈がありません。
また、このヘッダー検出の呼び出しは、全バイトに対し捜索するのではなく、1000バイト単位に捜索する為、最後まで検出されることなくエラーとなってしまう訳です。


④Planex MZK-WNHのEdimaxヘッダー検出にはこうじゃないのか?
という事で、コードを少し直してみます。
他のEdimax機器のヘッダーが解らない為、パッチというよりは質問をOpenWrtのMLには投げようかと思いますが。
始めにEdimaxヘッダーのMagic Numberを0バイト目に検出した上で、20バイトずらしてu-bootイメージヘッダーとなる為、その箇所にkernelが存在するのかでEdimaxであると判断してしまうコードにしてみました。
static ssize_t uimage_find_edimax(u_char *buf, size_t len)
{
        struct uimage_header *header;

        if (len < FW_EDIMAX_OFFSET + sizeof(*header)) {
                pr_err("Buffer too small for checking Edimax header\n");
                return -ENOSPC;
        }

        //header = (struct uimage_header *)(buf + FW_EDIMAX_OFFSET);
        header = (struct uimage_header *)(buf);

        switch be32_to_cpu(header->ih_magic) {
        case FW_MAGIC_EDIMAX:
                break;
        default:
                return -EINVAL;
        }

        header = (struct uimage_header *)(buf + FW_EDIMAX_OFFSET);
        if (header->ih_os != IH_OS_LINUX ||
            //header->ih_type != IH_TYPE_FILESYSTEM)
            header->ih_type != IH_TYPE_KERNEL)
                return -EINVAL;

        return FW_EDIMAX_OFFSET;
}
が、これだけでは、rootfsはマウントできず、Flash上で20バイトずれたままの状態が発生してしまいます。
上記最後の行で、FW_EDIMAX_OFFSETを返しているにも関わらず、このOFFSETが機能していない模様です。


⑤位置がずれてないコードになってる?
同じコード内に、このEdimaxヘッダー検出を呼び出している箇所で戻り値は取っているものの、位置をずらす様にはなっていません。
位置がずれる様に変更してみます。
                ret = find_header(buf, MAX_HEADER_LEN);
                if (ret < 0) {
                        pr_err("no valid uImage found in \"%s\" at offset %llx\n",
                                 master->name, (unsigned long long) offset);
                        continue;
                }
                header = (struct uimage_header *)(buf + ret);

                //uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size);
                uimage_size = sizeof(*header) + ret + be32_to_cpu(header->ih_size);
                if ((offset + uimage_size) > master->size) {
                        pr_err("uImage exceeds MTD device \"%s\"\n",
                                 master->name);
                        continue;
                }
                break;
この様に変更する事で、rootfsの先頭位置を補正します。
これによりrootfsのマウントが可能となります。


この修正内容はパッチにしましたので、ご利用下さい。
この後書いていますが、以下パッチ、イメージにはまだ問題箇所が存在します。
本Facroryイメージは、純正ファームからの書換えに利用し、そのままの利用はおすすめしません。再度、sysupgradeイメージで再書き込みする事をお勧めします。
また、Luciを入れた場合、メモリ容量不足も存在しファームのアップグレードが正常に完了できない模様の為、Luci無しのイメージとしています。
ファームのアップグレードはコマンド(sysupgrade)で実施してください。
r46749_mzk-wnh_header_offset.patch
openwrt-ramips-rt305x-mzk-wnh-squashfs-factory.bin
openwrt-ramips-rt305x-mzk-wnh-squashfs-sysupgrade.bin


⑥最後に、まだ抱えている問題
OpenWrtでは、初回起動時にrootfs_dataをrootfsに対しオーバーライドする形で書き込み可能領域をマウントする事で設定を保存したりする訳ですが、なぜかこちらの初期化のフラグ検出に失敗します。
こちらは、jffs2のMagic Numberの検出失敗が原因となります。
Magic Numberの検出も、1000バイト単位で捜索しているのですが、この際20バイトずらして検出をかけようとしてしまいます。
rootfs_dataについては、Edimaxヘッダーを意識せず実施してほしいにも関わらず、なぜか20バイトずらしてしまいます。
jffs2のMagic Numberが書き込まれている箇所は0バイト目です、20バイト目を探しても見つかるはずがありません。
今回MZK-WNHの為に入れたコードの戻り値がずらしている訳ではないので、また別の箇所に20バイトずらしている原因がありそうですが、追い切れていません。
jffs2markやjffs2reset等を使えば使用できてしまうので、追うメリットがそこまで感じないというか。。。

初回起動時のjffs初期化にミスっているログも載せておきます。
直したい方の参考になればよいかと。
[   24.780000] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00000014: 0xadde instead
[   24.840000] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00010014: 0xadde instead
[   24.900000] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00030014: 0xadde instead
[   24.960000] jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00030018: 0xc70a instead
[   24.990000] jffs2: Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes
[   25.010000] jffs2: empty_blocks 13, bad_blocks 0, c->nr_blocks 16
MZK-WNH(OpenWrt)に潜むEdimaxヘッダー20バイトの罠 | 2 件のコメント | アカウント登録
サイト管理者はコメントに関する責任を負いません。
MZK-WNH(OpenWrt)に潜むEdimaxヘッダー20バイトの罠
投稿者: sig9 on 2015年8月31日(月) 15:46 JST

 これ、全然知りませんでした。気をつけないと、また文鎮が…

MZK-WNH(OpenWrt)に潜むEdimaxヘッダー20バイトの罠
投稿者: Tochiro on 2015年9月 2日(水) 00:09 JST

> sig9さん
ほんと見事な罠です。 まぁ、開封、ピン立てする人は、なんとでもなるんですがねwww