Translated using Gemini 3.1 Pro. 人工添削あり.
ていうか文面や表現が固すぎない?ひどく硬いと思います。次回でまたoptimizeしますm(_ _)m
日本でなかなか入手しがたい製品ですがまぁコスパ抜群ですのでできれば AliExpress などでぜひ。ただし MAP-E や DS-Lite に未対応ですので OpenWRT でも、日本のネット環境に利用には工夫が必要かもしれないです。
Disclaimer: この記事はリバースエンジニアリング技術の学習のみを目的としています。権利者の方でクレームがある場合は、直接メールでご連絡いただければ記事を削除しますので、弁護士からの警告書は送らないでくださいにゃん、降参しましゅにゃ
あけましておめでとうございます。
TL; DR
分解せずに直接 OpenWRT を焼きたいだけ、試行錯誤の過程を見たくない方は、以下の手順に従って操作してください(ある程度の Linux 操作スキルが必要です。注意:筆者はコメント欄やメールなどいかなる形での1対1の技術サポートも受け付けていません。記事の手順説明に問題があることが確実な場合、またはメーカーからさらなるアップデートがあった場合のみご連絡ください)。誰かが One Click Script やもっとわかりやすいチュートリアルを作ってくれることをお楽しみ(もしかしたら前のバージョンと似た RCE がないか見つけるかもしれねぇし)。
まずいくつかのスクリプトを準備し、Python と Nu Shell をインストールしておきます(後者は結構使いやすい Shell なのでおすすめです)。
xor_tool.py
#!/usr/bin/python3
import sys
import os
def xor_file(input_path):
# 1. Validation: Ensure the file exists
if not os.path.isfile(input_path):
print(f"Error: File '{input_path}' not found.")
return
# 2. Define the output filename
base_name = os.path.basename(input_path)
output_path = f"decoded-{base_name}"
try:
# 3. Read the input file in binary mode ('rb')
with open(input_path, 'rb') as f:
data = f.read()
# 4. Perform XOR 0x55 on each byte
# We use a bytearray for efficient processing
processed_data = bytearray(b ^ 0x55 for b in data)
# 5. Write the result to the new file in binary mode ('wb')
with open(output_path, 'wb') as f:
f.write(processed_data)
print(f"Success! Processed file saved as: {output_path}")
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
# Check if a filename was provided as a command-line argument
if len(sys.argv) < 2:
print("Usage: python script_name.py <filename>")
else:
xor_file(sys.argv[1])
enable-telnet.sh
#!/bin/sh
telnetd -l /bin/sh -p 7788
packedconf.nu
#!/usr/bin/nu
cd NX30Pro/mnt/config
rm -rf cfgmd5/firewall*
let fwmd5 = open firewall | hash md5
print $fwmd5
touch $"cfgmd5/firewall-($fwmd5)"
cd ../..
tar -czf ../NX30Pro-config/NX30Pro.tar.gz *
cd ../NX30Pro-config
let cfmd5 = open NX30Pro.tar.gz | hash md5
print $cfmd5
$"NX30ProV100R010\n($cfmd5)" | save -f NX30Pro.info
tar -czf ../NX30Pro-finished.cfg *
cd ..
python xor_tool.py NX30Pro-finished.cfg
- 現在のシステムから設定をバックアップします。上部の「もっと(更多)」 - 左側の「デバイス管理(设备管理)」 - デバイス管理 - 設定バックアップ(配置备份) から、
NX30Pro.cfgファイルを取得します。 - ファイルをデコードします
python xor_tool.py NX30Pro.cfg - root ユーザーまたは fake root 環境に入り(ファイルの権限を維持するため)、その中で得られた圧縮ファイル
decoded-NX30Pro.cfgを NX30Pro-config フォルダに権限付き(-xpzf)で解凍します。このフォルダにはNX30Pro.infoとNX30Pro.tar.gzの2つのファイルがあるはずです。 - さらに内側の圧縮ファイルを、NX30Pro-config と同じ階層の NX30Pro フォルダに解凍します(後続の操作をしやすくするため、直接 NX30Pro-config 内には解凍しません)。この時点でのディレクトリ構造は以下のようになるはずです(さらに
xor_tool.pyとpackedconf.nuがこれら2つのディレクトリと同じ階層にあります)
NX30Pro/mnt/config/scriptディレクトリを作成し、上記のenable-telnet.shをその中に入れます。NX30Pro/mnt/config/firewallを編集し、冒頭に以下のスニペットを追加します
config include 'custom'
option type 'script'
option path '/etc/config/script/enable-telnet.sh'
option family 'any'
option enable '1'
option reload '1'
- 上記の packedconf.nu を実行して
decoded-NX30Pro-finished.cfgを取得します。 - 同じ画面で上記のファイルを選択して設定を復元し、電源を再投入します。
- 完全に起動したら、Telnet を使用してルーターの 7788 ポートに接続します。パスワードなしで直接 root に入れます。
- https://blog.qust.me/nx30pro や他の任意のチュートリアルに従って操作します。
きっかけ
ここ数ヶ月、自分の All in One Homelab 全体を再構築しており、自宅サーバーで Internet と dn42 の Software Router を動かし、さらにすべての Cloud Service をローカルに移行する計画で、場合によっては Anycast もやる予定でした。Hypervisor Host の性能は実はかなりひどいものですが(Consumer 向けプラットフォーム、i3-12100+32GB シングル DDR4)、それでも意地を張って構築するつもりでした。年末のハードウェア価格の高騰と最近金欠により、しばらくはハードウェアを交換する機会はなさそうですから。時間ができたら、一連の試行錯誤の記録か、少なくともその結果を公開しようと思います。
その計画の一環として新しい無線 AP の導入がありました。VLAN とパブリック Wi-Fi への接続(つまり無線から有線へ)のニーズがあったため、メーカーからの純正ファームウェアでは足りませんでした。筆者が以前学部の寮で使っていたのは、定番の MT7621 を搭載した Redmi AC2100 でした。しかしスマホもすでに Wi-Fi 7 に対応し、自宅の回線も上下ギガビットを引いたので、少なくとも AX3000 の速度を満たすものに替えようと思いました。友人が以前 H3C Magic NX30 Pro を焼いたことがあり、筆者に勧めてくれました。ネット上に多くのチュートリアル(例えば https://blog.qust.me/nx30pro や https://www.right.com.cn/forum/thread-8356975-1-1.html など)がある上、この機種の純正ファームウェアはなんと Telnet が開放されており、非常に簡単に分解なしでファームウェアを焼くことができるからです。また、このルーターの価格もかなり安く、筆者は中国のフリマアプリ(goofish)で120元(≒ 2400 日本円)ほどで入手しましたが、新品でも JD で149元(≒ 3000 日本円)ほどです。この機器は MediaTek MT7981 プラットフォームをベースにしており、大変人気で資料もたくさんあります。
しかし、手に入れてから気づいたのですが、この機種の新しい純正 FW では Telnet が塞がれていました。V100R009 および V100R010 バージョンの FW はいずれもデフォルトで Telnet が開いていないようです。ネット上で推奨されている最初の焼き方は、分解して TTL ケーブルを接続するというものだったので、筆者もまずは分解を試みました。
分解して焼く
分解と接続
NX30 Pro のケースは上下2つの部品しかありませんが、分解するのは非常に難しく、爪が多くて非常に硬いです。アンテナと本体が一体になっているため、上部の柔らかくて爪がキツく噛み合っている部分は特に注意が必要です。裏蓋の6本のネジを外し、下の方から少しずつこじ開けツールで爪を外していきます。
筆者の手元には、以前使っていたような使いやすくて長めの金属製こじ開け棒がなかったので、ピンセット、マイナスドライバー、そして不要なゲームカードを使って分解するしかありませんでした。凶器一覧を添付しておきます。
そういえばこのカードは 2024年初めの韓国・日本・香港旅行 の際に、香港で maimai DX をやった時に作ったカードでしたが、結局1回使っただけで中の数香港ドルの残高とともに無効になってしまいました。このような「未使用のカードは2ヶ月後に残高ごと無効になる」という規定が仮に中国本土で行われたら、消費者権益に関する何らかの規定に違反する恐れがあるでしょうね(笑)。筆者が爪を外した順番は、まず底部の2つの角から始めて底辺全体に広げることでした。見事に指に怪我をした。
底辺が緩み、すべての爪が外れたら、(1枚目の写真の視点から)左側から側面の爪を外します。その後、力任せに一番左のアンテナの根元をこじ開け、ピンセットの先端でアンテナ上の爪をこじ開け、一番左側の上下2つの部分を分離し、ずらします。アンテナの PCB が中にあるので、アンテナの PCB を傷つけないよう注意してください。その後、一番右を直接外すのはかなり困難になります。これは左側がずれたことで2枚のケースがよりきつく噛み合ってしまったためかもしれません。そのため、まず左から2番目のアンテナを外してアンテナの根元(つまり本体の上辺)を少し緩め、そこから右側から右から1番目と2番目のアンテナを外し、最後にケースの2つの部分をこじ開けることをしてみてください。ここでの分解順序はあくまで参考ですので、実際の手触りに従って行ってください。
分解後の上記の2枚目の写真の右側が TTL です。本来であればルーターの電源を接続し、ここの GND、TX、RX を FT232RL や CH340/1 に挿せばファームウェアを焼けるはずですが、筆者にははんだごてもなく、まともな焼き込みチップクリップもなかったため、安定した接続を行うのは困難でした。PCB 上には4つの SMT パッドしか用意されておらず、DIP ピンヘッダ用の穴がないため、オスのデュポンケーブルを直接 DIP 穴に挿し込むことはできません。少なくとも焼き込みのデータが安定して伝送できるように(シリアルポート経由の X/Y/ZMODEM を使わずに済むように)、TFTP 用にもう1本 LAN ケーブルを接続して、任意の LAN ポートを PC に直接接続せざるを得ませんでした。
筆者はビニールテープやセロハンテープなど様々な方法を試した結果、最終的に採用したのは、3本のオスデュポンケーブルを束ねて、洗濯バサミで PCB ボードに挟むという方法でした。接続は極めて不安定だったので、片手でクリップの先端を押さえておかないと正常に通信できませんでした。ここでさらに致命的だったのは、追加で LAN ケーブルを挿さなければならない上、LAN ケーブルのコネクタがケースの穴を完全に通り抜けにくく(本来はケース越しに PCB ボードのポートに挿さる)、同時にアンテナがケースに固定されているためケースと PCB を完全に分離するのも不便で、さらに TTL パッドの真下にちょうどケースのプラスチックの支柱がいくつか押し付けられているため、挟むのが難しく手で補助するしかなかったことです。
最終的な接続は、ルーターの任意の LAN ポートを PC の LAN ポートに接続し、電源をアダプタに接続し、GND/TX/RX を CH340 に接続しました。CH340 の TTL レベルは 3.3V に設定しました。
ホスト側の操作で uboot を焼く
ドライバ等については割愛します。筆者は OS を再インストールしてから初めてこの USB-TTL モジュールを使ったため、手動で WCH の公式サイトから最新の(2024年版の)ドライバをダウンロードしました。WCH のドライバの品質にはいつも言葉を失いますし、オープンソースでもありませんからなかなか困ります(Linux 版のいくつかのバージョンはソースコードが公開されていますが)。幸い、24年版ではホスト側で BSoD が発生したという人はいないようです。インストール後、以下の接続設定を使用しました:3.3V レベル、115200bps 8-N-1 フロー制御なし。
電源投入前に、まず NIC と TFTP の設定を行い、uboot イメージを準備します。ネットワークカードには適当なプライベート IPv4 アドレスを設定し、ゲートウェイはなしにします(ここは"1"が欠けてタイポしていますが、筆者が実際に使ったのは 192.168.11.2 です。後続の手順を参照してください)。
TFTP サーバーには定番の TFTPd64 を使用します。
ここで使用した uboot.bin は、最初 hanwckf 氏のレポジトリ https://github.com/hanwckf/bl-mt798x/ からダウンロードしました(実際には複数の選択肢があります。以下の詳細な説明を参照してください)。Release の中から最新のものを見つけ、その中の mt7981_h3c_magic-nx30-pro-fip-fixed-parts.bin を uboot.bin にリネームします。
正常にシリアルポートを開いた後、電源を入れるとコードが流れるはずです。電源を入れた直後に上下キーを連打すると、以下のようなメニューに入ります。
4 番の ATF FIP の更新、つまり uboot 更新を選択し、次に 0 番を選択して、TFTP から uboot データを引っ張ってきます。安定したシリアル接続がある場合は、Xmodem や Ymodem の使用を検討しても良いですが、そうでなければ大人しく TFTP を使いましょう。
同様に同一サブネットの IP アドレスを適当に選び、他の情報を入力すると、書き込みが開始されます。
この <H3C> のプロンプトに入ったら、電源を切ってから再投入します。電源を入れる際に RESET ボタンを押し続け、PC NIC のアドレスを 192.168.1.x に変更し、192.168.1.1 にアクセスして uboot の画面に入れれば、uboot の焼き込みは成功です。
分解せずに焼く
しかし筆者はこの時点ではまだ満足していませんでした。なぜなら、この状態では H 氏が提供する uboot の Web インターフェースからもシリアルコンソールからも、MTD ストレージ内のコンテンツを dump して純正 FW を Backup する方法が提供されていなかったからです。また一方で、H3C は公式サイトでの純正 FW のダウンロードを閉鎖してしまったため、唯一の方法はルーターからバックアップを取ることでした。そこで筆者は依然として、純正システムを break-in する手段を探ろうと試みました。root ユーザーの真のパスワードは筆者には分かりません(Web のバックグラウンド画面の管理者パスワードや H3C のようなものではありません)。そして tty ログインにはパスワードが必要であるため、この道は基本的に無理でした。
筆者は以下の手がかりを見つけました:
- 恩山 BBS で、設置バックアップをエクスポートし、フリマアプリでお金を払って誰かに修正してもらった後、それをインポートすればば Telnet を有効にできるという投稿があり、Telnet が有効にできることが分かりました。ただし、このファイルは「暗号化」されている、と言われています。
- 分析するために最新版の を入手できなかFW ったため、筆者は恩山のこのスレで V100R002 の旧版ファームウェアを見つけました。
- ZIKH26 氏が CVE-2025-2726 を提出しており、その中には RCE の脆弱性が含まれており、H3C FW の Web API に対するいくつかの分析が含まれていました。元のスレは削除されてしまいましたが、Web Archive にはバックアップがあります。
実際に紐解いてみると、想像していたより少し簡単であることが分かりました。一番基礎的な分析だけが必要でした。メーカーのセキュリティ対策は依然として不十分ですが、それはむしろ我々にとっては好都合です。
ファームウェアの分解
上記のスレッドから backup.img ファイルをダウンロードして解凍し、そのバックアップからルーター実行設定 NX30Pro.cfg を取得します。この backup.img が UBI イメージであることが見て取れます:
chariri@chariri-arch ~/stock> file backup.img
backup.img: UBI image, version 1
苦労して MTD ツールを使ってカーネルにマウントするよりは、ubi_reader を使用して直接解凍する方が良いです。uv tool を使ってこのツールをインストールした後、直接 ubireader_extract_images backup.img を実行すると、カレントディレクトリに ubifs-root/backup.img/ が生成され、その中にいくつかの ubifs ファイルができます。
chariri@chariri-arch ~/stock> ls ubifs-root/backup.img/
img-1660220975_vol-kernel2.ubifs img-1660220975_vol-rootfs2.ubifs img-1660220975_vol-rootfs.ubifsimg-1660220975_vol-kernel.ubifs img-1660220975_vol-rootfs_data.ubifs img-1660220975_vol-u-boot-env.ubifs
chariri@chariri-arch ~/stock> cd ubifs-root/backup.img/
chariri@chariri-arch ~/s/u/backup.img> file img-1660220975_vol-rootfs.ubifs
img-1660220975_vol-rootfs.ubifs: Squashfs filesystem, little endian, version 4.0, xz compressed, 15089502 bytes, 2361 inodes, blocksize: 262144 bytes, created: Wed Dec 7 17:18:33 2022
ubifs ファイルの内部が squashfs ファイルシステムであることが分かったので、unsquashfs を使って解凍します。
chariri@chariri-arch ~/s/u/backup.img> unsquashfs -mem 128M -f -d rootfs1 img-1660220975_vol-rootfs.ubifs
Parallel unsquashfs: Using 8 processors
2256 inodes (2105 blocks) to write
create_inode: could not create character device rootfs1/dev/console, because you're not superuser!
[=================================================================================================================================================================| ] 4360/4361 99%
created 1962 files
created 105 directories
created 293 symlinks
created 0 devices
created 0 fifos
created 0 sockets
created 0 hardlinks
ルートディレクトリの下に /www があり、その中に Web ページと API バックエンド(/www/api)があるのを発見しました。これをローカルにコピーして IDA に読み込ませて分析(F5 だけ押したwwww)します。直接パケットキャプチャと組み合わせて、重要なインターフェースが /esps であることを発見しました。文字列を検索し、上記の CVE で開示された詳細情報と組み合わせることで以下のコードを特定しました。
ここにある execcmd は最終的に popen されます。上記の CVE はどうやら最新バージョンで塞がれてしまったらしく(コードからの検証は未実施)、筆者は ZIKH26 氏が書いた PoC を直接使って Telnet を取得できなかったため、/usr/lib/lua/protol_cvt.lua をさらに分析する必要がありました。
対応するファイルは ubifs-root/backup.img/rootfs1/usr/lib/lua/magic_link/magic_link.lua であり、その中で直接 OpenWRT ubus 上のコマンドが呼び出されていることを発見しました。筆者は細かく調べるのが面倒だったので、ファイルシステム全体から実行される method を直接検索しました:
chariri@chariri-arch ~/s/u/b/rootfs1> grep -abor "backupprofile" .
./usr/libexec/rpcd/esps.system:5010:backupprofile
./usr/libexec/rpcd/esps.system:8313:backupprofile
./usr/bin/outinfo.sh:580:backupprofile
./www/assets/equip.ade96d1d.js:1137:backupprofile
./www/assets/portal.efed5bda.js:1633:backupprofile
chariri@chariri-arch ~/s/u/b/rootfs1> file ./usr/libexec/rpcd/esps.system
./usr/libexec/rpcd/esps.system: POSIX shell script, Unicode text, UTF-8 text executable
その中でバックアップを書き出すのに関連するコードは cfgFileFinal=$(cfg_backup) の1行しかありません。さらに /usr/sbin/cfg_backup を特定します。
chariri@chariri-arch ~/s/u/b/rootfs1> find -name "cfg_backup"
./usr/sbin/cfg_backup
chariri@chariri-arch ~/s/u/b/rootfs1> file ./usr/sbin/cfg_backup
./usr/sbin/cfg_backup: POSIX shell script, ASCII text executable
同様に、その中で重要なのは file_encrypt "${cfgFileOrg}" "${cfgFileFinal}" の1行だけです。
file_encrypt は ELF ファイルでした。取り出して見てみたら、笑ってしまいました。
なんとファイル全体を 0x55 で XOR しているだけでした。直接 Python スクリプトを書けば済みます(実は NuShell でもできますが、NuShell では特に便利というわけでもないので)。記事冒頭の xor_tool.py を参照してください。これをデコードして上記の ./usr/libexec/rpcd/esps.system のコードと照らし合わせると、tar.gz の中に tar.gz が入れ子になっている構造です。1層目の tar.gz の中には info ファイルがあり、それに hostname と2層目の tar.gz の MD5 情報が紐付いています。このファイルは復元時に検証されるため、2層目の tar.gz を再パッケージした後、この info ファイルもそれに合わせて修正する必要があります。
設定の分解と注入
解凍時、筆者は root 権限で操作しました。これは主に tar ファイルの権限を維持するためです。解凍してみると予想通り、定番の OpenWRT の UCI ファイルでしたが、標準の /etc/config ではなく /mnt/config にありました。ネット上の記事によると、この機器は /mnt/ 内部のものしか電源再投入時に保存されず、H3C の FW は起動時に /mnt/config を /etc/config にコピーするようです。とにかく、この中から telnet をオン/オフにできるオプションを見つければよいのです。筆者は rc.d ディレクトリを作成して、その中に telnetd へのシンボリックリンクを貼り、再パッケージして設定をインポートしてみましたが、成功しませんでした。
この時、筆者は OpenWRT UCI で任意コード実行の gadget が利用できないかと強く望んでいました。渡りに船というか、恩山 BBS のこの記事で見つけました。具体的なドキュメントは OpenWRT 公式 Wiki にあります。つまり、ファイアウォールの UCI の中に任意で実行できるスクリプトを書くことができるので、/mnt/config/firewall の中に以下のような記述を追加するだけで済みます:
config include 'custom'
option type 'script'
option path '/etc/config/script/enable-telnet.sh'
option family 'any'
option enable '1'
option reload '1'
そして enable-telnet.sh を /etc/config/script ディレクトリに置き、権限を適切に設定すれば、起動時に自動的にスクリプトが実行されます。enable-telnet.sh の具体的な内容について、筆者は以下の試みを行いました:
service telnet startまたは/etc/init.d/telnet startはだめでした。後で気づいたのですが、telnet のサービススクリプト自体が新 FW に変更されていたためであり、先ほど rc.d に S50telnet を追加して無効だったのと同じ理由です。- ping と wget をホストに対して実行して成功したため、スクリプトが確かに実行されていることが分かりました。
- 以前のあの CVE の詳細から拾ってきた
telnetd -l /bin/sh -p 7788の一文が成功しました。
インポート可能な設定ファイルを自動的に再パッケージ化するために、筆者は短い nushell ファイルを作成しました(記事冒頭の packedconf.nu)。読者が reproduce したい場合は、直接 DL して呼び出すか、中身の具体的な実装を参考に各自で操作してください。なお、/mnt/config の中には cfgmd5 というディレクトリがあり、各設定ファイルの MD5 が(ファイル名に直接)書き込まれています。ここの MD5 が正しく更新されないと設定がロールバックされる恐れがあるため、ついでにここの MD5 も更新しておきました。
一通りの操作を経て、ついに 7788 ポートで root shell を取得することができました。
バックアップと uboot の書き込み
筆者はこの shell の中で telnet の init.d サービスファイルを読んでみましたが、中によくわからないロジックが追加されているのを発見しました。uci -q set telnetcfg.telnet.enable=1 を設定してから /etc/init.d/telnet start をしても telnet を正常に開くことができなかったため、試行錯誤するのが面倒になり、まずは SSH を開くことを優先しました。チュートリアルの方法を使って Dropbear をダウンロードし、root パスワードをリセットします。
curl -o /tmp/dropbear.ipk http://192.168.124.2:8000/dropbear_2019.78-2_aarch64_cortex-a53.ipk
opkg install /tmp/dropbear.ipk
/etc/init.d/dropbear enable
/etc/init.d/dropbear start
passwd
これで root での接続が可能になります。古い標準である ssh-rsa を許可するのをお忘れなく。
同様に BBS 上のチュートリアルを参考に、純正 FW をバックアップします:
# 查看分区表
cat /proc/mtd
# 备份原厂固件
dd if=/dev/mtd5 of=/tmp/mtd5_ubi
# (先把最大这个 ubi 拷出来)
# 单独备份(小)分区
rm -rf /tmp/mtd5_ubi
dd if=/dev/mtd1 of=/tmp/mtd1_BL2
dd if=/dev/mtd3 of=/tmp/mtd3_Factory
dd if=/dev/mtd4 of=/tmp/mtd4_FIP
途中で mtd5_ubi を削除している点に注意してください。2つに分けて実行すればOKです(メモリ足りないから一気にすると OOM になります)。ファイルのコピーには WinSCP を使ってもよいですが、コマンドラインで直接やる方が明らかに簡単です。ここでは Windows 上で実行しているため NUL を使用していますが、Linux の場合はもちろん /dev/null を使用します。
> scp -O -o StrictHostKeyChecking=no -o UserKnownHostsFile=NUL -o PubkeyAcceptedAlgorithms=+ssh-rsa -o HostKeyAlgorithms=+ssh-rsa -P 22 root@192.168.124.1:/tmp/mtd5_ubi .
Warning: Permanently added '192.168.124.1' (RSA) to the list of known hosts.
root@192.168.124.1's password:
mtd5_ubi 100% 64MB 8.7MB/s 00:07
> scp -O -o StrictHostKeyChecking=no -o UserKnownHostsFile=NUL -o PubkeyAcceptedAlgorithms=+ssh-rsa -o HostKeyAlgorithms=+ssh-rsa -P 22 root@192.168.124.1:/tmp/mtd1_BL2 .
Warning: Permanently added '192.168.124.1' (RSA) to the list of known hosts.
root@192.168.124.1's password:
mtd1_BL2 100% 1024KB 9.5MB/s 00:00
> scp -O -o StrictHostKeyChecking=no -o UserKnownHostsFile=NUL -o PubkeyAcceptedAlgorithms=+ssh-rsa -o HostKeyAlgorithms=+ssh-rsa -P 22 root@192.168.124.1:/tmp/mtd3_Factory .
Warning: Permanently added '192.168.124.1' (RSA) to the list of known hosts.
root@192.168.124.1's password:
mtd3_Factory 100% 2048KB 9.4MB/s 00:00
> scp -O -o StrictHostKeyChecking=no -o UserKnownHostsFile=NUL -o PubkeyAcceptedAlgorithms=+ssh-rsa -o HostKeyAlgorithms=+ssh-rsa -P 22 root@192.168.124.1:/tmp/mtd4_FIP .
Warning: Permanently added '192.168.124.1' (RSA) to the list of known hosts.
root@192.168.124.1's password:
mtd4_FIP 100% 2048KB 10.4MB/s 00:00
バックアップが完了したら、引き続き https://blog.qust.me/nx30pro のチュートリアルに従って uboot を焼けば完了です。uboot.bin を scp で機器側の /tmp にコピーした後、以下を実行します:
md5sum uboot.bin
mtd write /tmp/uboot.bin FIP
しかし、どの uboot を焼くかについては、実は少しこだわる点があります。少なくとも筆者は最終的に、前述の uboot は使いませんでした。
OpenWRT と uboot のバージョン選択
分解なしの場合でも分解して焼く場合でも、筆者は uboot のバージョン選択にはこだわりのポイントがあると述べましたが、これは OpenWRT イメージで採用されるフォーマットとパーティションレイアウトに関係しています。以前一部の機種を焼く時は、システムを読込み専用にする前に factory を焼く必要があったと思いますが、この機器ではすでになくなったようです。なぜなら、他人がコンパイルした uboot がすでに sysupgrade フォーマットを対応しているからです。パーティションレイアウトには純正、大容量パーティション、OpenWRT uboot など様々なレイアウトがあります。
NX30 Pro というこの機器は、H 氏の話によれば、純正のいくつかのデータが YAFFS ファイルシステムを使用しているため、非純正のパーティションレイアウトを焼くと、何らかの不可解な問題により NMBM がフラッシュメモリ全体を Bad Block としてマークしてしまい、文鎮化する恐れがあるそうです。そのため純正のレイアウトを維持することが推奨されています。同時に純正レイアウトの中には無線キャリブレーションや MAC アドレス等のデータも含まれており、これを保持しておく方が良いでしょう。筆者はいずれにせよ GFW 回避等のルーター上で大量のタスクを走らせるようなニーズはないので、純正レイアウトで十分でした。Bad Block 管理について、どう処理されているか筆者も分かりません(笑)。純正と同じく UBIFS の上に squashfs なのかもしれません。とにかく NMBM は有効になっていません。
イメージのフォーマットについては、天霊(Tianling)氏が短い紹介記事を書いています。重要なのは -sysupgrade.bin と -sysupgrade.itb の2つのフォーマットです。前者は直接焼くことができますが、後者は新しい Flattened uImage Tree (FIT) フォーマットであり、uboot のサポートが必要です。公式の uboot 以外にも、以下のような uboot がよく使われます:
- OpenWRT の uboot(実際には das uboot)。OpenWRT をコンパイルすると uboot の bin ファイルが得られます。機能は非常に強力ですが、使用は少し面倒で、主に Web インターフェースから直接システムを焼けないという点があります。Web 画面に入れないため文鎮化したと勘違いする人もいます。
- H 氏の uboot。MTK が提供した uboot をベースに改造されているため、様々なコマンドラインツールがあり、同時に直接焼ける Web UI も追加されています。23や24など複数のバージョンがあり、最新の(コンパイル済みのバイナリがリリースされていない)バージョン25では FIT のサポートが追加されたようです。
- 天霊氏の uboot。H 大の旧バージョンの uboot をベースに改変されており、バイナリが提供されるとともに FIT にもサポートしています。
システムに関しては、いくつかよく使われるシステムがあります:
- 公式の純正システム。OpenWRT 21.02 ベースで、Hardware Acceleration と Closed Source Driver が含まれています。
- OpenWRT メインラインシステム。現在の最新安定版は 24.10、カーネルバージョンは 6.6 であり、ある程度成熟した mt76 ドライバ などの Open Source Driver のみが含まれます。
- H 氏の ImmortalWRT-mt798x。OpenWRT + ImmortalWRT 21.02 ベースでHardware Acceleration と Closed Source Driver が含まれています。
- P 氏の ImmortalWRT-mt798x-6.6。OpenWRT + ImmortalWRT 24.10 ベースで、カーネルバージョンが 6.6、同様に hw accel と Closed Source Driver が含まれています。
- 意地でもという方は、mtk-openwrt-feeds をメインラインの OpenWRT に統合し、最新メインライン + クローズドソースドライバを試してみるのも良いでしょう。筆者はコンパイルに失敗し、サポート書きやデバッグに割く時間もありませんでした。
筆者は ImmortalWRT の豊富なパッケージリポジトリなどは不要だったため、自分でメインラインの OpenWRT 24.10 システムをコンパイルすることを選びました。メインラインシステムは FIT .tib フォーマットを使用するため、uboot には天霊氏の FIT をサポートするバージョンを使用しました。コンパイル自体に特筆すべき点はなく、OpenWRT Wiki のチュートリアル に従って一歩ずつ進めるだけです。最終的に以下のファイルが得られます:
この中で焼く必要があるのは -squashfs-sysupgrade.itb と -proloader.bin の2つのファイルです。まず上で焼いた uboot のバージョンは違っているので、有線 LAN カードに手動で 192.168.1.2 を指定します(上記のいくつかの uboot には DHCP 機能がないため)。RESET を押しながら電源を入れ、http://192.168.1.1/uboot.html を開いて天霊氏の uboot を焼き込みます。
焼き終わったら、再度 RESET を押しながら電源を入れ、http://192.168.1.1/bl2.html を開いて preloader を焼き込みます。下の footer にある 《ヨスガノソラ》 のサブタイトル「In solitude, where we are least alone」の行に注目してください。
最後に FIT .itb フォーマットのシステムを焼き込みます:
焼き終わったら再起動し、IP を自動取得に変更すれば、http://192.168.1.1 を開いて OpenWRT にアクセスできるようになります。

