← Blog 一覧

Holux M-241 の GPS 週数ロールオーバーを Mac から 2 バイトで直した

逆解析 ファームウェア GPS MTK MT3318 Holux WNRO

パッチ済ファームウェア書き換え後の Holux M-241 GPS ロガー。Setting メニューにバージョン「V1.14」が表示されており、純正の V1.13 からアップデートされたことが分かる

要旨

手元の Holux M-241(GPS ロガー、製造元は 2019 年に GPS 事業から撤退済)が、GPS 週数ロールオーバー(WNRO)で日付を 19.6 年遅れて記録するようになっていた。これを Mac だけで完結する 2 バイトのファーム書き換え で修復した。

  • リポジトリ: github.com/NekobaTechnology/holux-m241-wnro-fix (MIT、proprietary バイナリは含まず)
  • 検証: 実機で $GPRMC の date が現在 UTC 日付を返すことを確認
  • 副産物: ファームの未文書 PMTK aiding コマンド(715/716/717)

背景:GPS Week Number Rollover

GPS の週数フィールドは 10 bit。1024 週(約 19.6 年)で必ずオーバーフローする。

第何回日時
1st1999-08-22
2nd2019-04-06
3rd2038-11-21

M-241 のメインファーム(2006 年ビルド)は 1st rollover までしか想定されていない。今は 2nd rollover を超えているので、現在の日付が 1024 週ぶん遅れた 2006 年 として記録される。NMEA 出力、内蔵 OLED、ログファイルの全部に影響する。

既存策と「もう一歩」の領域

既存の対策は大きく 2 つ:

  1. GPSBabel 等で「+7168 日」する後処理 — ログだけ補正、リアルタイム不可。
  2. NMEA プロキシ — ホスト側で $GPRMC の date を書き換えて中継。実用的だが、端末単体での時計は狂ったまま。

これで足りない領域 — 「端末を持ち歩いてその場で時計を見たい」 — はファーム本体を直すしかない。Holux 公式は出してこないので、自分でやる。

ハードルとブレークスルー

Mac から MTK BootROM を喋る

公式書込ツール PowerFlash.exe は Windows 専用。MTK MT3318 BootROM 自体はシリアル経由のシンプルなブートローダで、a0 / 0a / 50 / 05 の 4 バイト同期 → コマンド受信、という構造。GlobalTop 純正の brom.dll を逆解析して(IDA + capstone)、Python から喋る mtk_proto.py を書いた。

vtable 構造が分かれば、Read16 / Write16 / Jump 等の基本コマンドは素直に実装できた。読み出しはこの時点で完全動作

Download Agent (DA) のロード先

書込のためには MTK 製の Download Agent (DA) という小さなブートローダを RAM に転送して実行させる必要がある。問題は 「DA を RAM のどこに置くか」が公式ドキュメントにない こと。

最初に素直に 0x100000 に置いたが、4 バイト書き込んだ瞬間にチップが死んだ。色々試して詰まった。

突破口は、Holux のもう 1 つの GUI ツール HoluxLoggerUtility.exe を逆解析したこと。brom.dllDA_SetStartAddr を ordinal 経由で呼んでいる箇所が見つかり、引数が 0x100800 だった:

push 0x100800           ; ← 正解
push ecx                  ; DA handle
call DA_SetStartAddr

0x100000-0x1007FF は BootROM 自身のスタック/スクラッチパッド領域だった。書き込めば BootROM が即死するのは当然だった。

DA ファイル(DA_v3.8.4.bin)もこの前提で構築されていて、prelude の bx r3 → 0x101718 が DA file offset 0xf18(関数のプロローグ)に着地する設計になっていた。

WRITE16 のエンディアン

0x100800 に DA を書いてもまだ動かなかった。チップから意味不明な 1c 00 e0 00 パターンが返ってくる。

調べると、MTK の WRITE16 プロトコルは 16-bit ワード単位で BE16 を期待してチップ側で LE16 に並び替えて格納する仕様だった。送信時に byte swap を入れたら DA が起動。SYNC バイト 0xc0 と Flash 情報(512KB MXIC 0x22ba)を正しく報告した。

self._brom.write16(BASE_ADDR_DA, da_trim,
                   byte_swap=True,
                   verify_echo=True,
                   skip_cksum=True)

WNRO パッチ箇所の特定

DA が動けば flash 書込は可能。残るは 「どの 1 ヶ所を書き換えれば WNRO が直るか」

ファーム内で +1024(= movs r1, #1; lsls r1, r1, #10)パターンを grep すると 27 箇所ヒットした。WNRO 関連と思われる候補 4 ヶ所を順に試したが、どれも NMEA RMC date には影響しなかった(flash の該当バイトはちゃんと書き換わっているのに、出力日付は元のまま)。

最終的に効いたのは、GPS 時刻 → Unix 時刻変換に使われる Unix epoch 定数 を直接シフトする方針だった。

ファーム内に 315964800(= 1980-01-06 の Unix 時刻)というハードコード定数が 2 ヶ所ある。両方を +619315200(= 7168 日 = 1024 週)シフトすると 二重にずれて 2045 年に飛んだ(実証)ので、片方の 0x402F4 だけ書き換える。これが効いた。

PATCHES = [
    {
        "offset": 0x402F4,
        "size": 4,
        "old": 315964800,
        "new": 935280000,    # = 315964800 + 619315200
        "description": "WNRO fix: shift Unix epoch base forward by +1024 weeks",
    },
    {
        "offset": 0x59C2C,
        "size": 1,
        "old": 0x33,           # ASCII '3'
        "new": 0x34,           # ASCII '4'
        "description": "OLED version string: V1.13 -> V1.14",
    },
]

V1.13 → V1.14 のおまけパッチも入れた(改造済ファームの判別用)。

結果

書き戻して衛星捕捉 → NMEA 出力:

$GPRMC,162247.000,V,3546.5988,N,13953.0069,E,0.67,269.95,030526,,,N*7A
                                                       ^^^^^^
                                                       030526 = 2026-05-03 ✓

OLED にも V1.14 が表示され、設定 → タイムゾーンを +9 にすれば JST 表示も正しい。

ボーナス:未文書 PMTK aiding コマンド

ファーム内の文字列 grep で、外部公開されていない PMTK コマンドが見つかった。実機に投げて応答(PMTK001 status=3 = 受理)を確認:

PMTK引数動作
PMTK715Y,M,D,h,m,s (UTC)時刻 aiding
PMTK716lat,lon,alt位置 aiding
PMTK717lat,lon,alt,Y,M,D,h,m,s両方

cold-start 後に投げると衛星探索範囲が絞られる。ただし ephemeris 取得自体は衛星 1 機あたり 30 秒(50 bps の broadcast 仕様上)かかるので、瞬時 fix にはならない。

公開と再現性

リポジトリ: github.com/NekobaTechnology/holux-m241-wnro-fix

  • ライセンス: MIT(コードのみ)
  • proprietary バイナリ(brom.dll、DA、ファーム本体)は同梱せず、ユーザー自身が用意する設計
  • 解析過程の全ログ・BootROM プロトコル詳細・DA 起動フローを docs/ に収録
  • スクリプト構成: dump → patch → flash の 3 段階、Mac/Linux で完結
  • Fresh clone で patch_firmware の出力 SHA256 が実機検証済バイナリと一致することを確認

文鎮化リスクの注意は DISCLAIMER.md に明記してある(JTAG なしのデバイスへの破壊的書込なので)。

制作プロセスについて

逆解析、brom.dll / PowerFlash.exe / HoluxLoggerUtility.exe の disasm、ファーム解析、パッチ箇所特定、Python 実装の全行程を、Anthropic の Claude Code と対話しながら進めた。これがなければ MT3318 + DA プロトコルの解析に何倍もかかっていたはず。実機で動かす判断とリスク評価は人間側で行った。

教訓

  • 公式ツールがあれば必ず逆解析する。 DA_SetStartAddr(handle, 0x100800) の 1 行が分かるかどうかで作業時間が劇的に変わる。
  • 「+1024 を書き換える」は素直すぎて罠。 同じパターンが 27 ヶ所あると、どれが date 出力経路に効くかは試して確認するしかない。Unix epoch 定数を直接いじる方が決定論的だった。
  • JTAG なしでもファーム書換は十分実用的。 ブートローダがシリアル経由でアクセス可能なら、それが事実上の JTAG 代わりになる。
  • AI 補助の逆解析ワークフロー は、IDA Pro や Ghidra に張り付くより遥かに反復が速い。disasm 結果を会話で投げて解釈・仮説立て・検証 → 次手、のサイクルが分単位で回る。

よくある質問

Holux M-241 の日付がズレるのはなぜ?

GPS の週数フィールドが 10 bit しかなく、1024 週(約 19.6 年)で必ずオーバーフローするため。これを GPS 週数ロールオーバー(GPS Week Number Rollover, WNRO)と呼ぶ。M-241 のメインファームは 2006 年ビルドで、1999 年の第1ロールオーバーまでしか考慮されていない。第2ロールオーバー(2019-04-06)を越えた今、現在日付が 1024 週(7168 日)ぶん戻った値で記録される。2026 年なら 2006 年として保存されてしまう。

Holux のメーカーサポートを受ければ直る?

直らない。Holux は 2019 年に GPS 事業から撤退済み。M-241 向けのファームアップデートはもう出ない。やるなら自分で直すしかない というのがこの記事の出発点。

Mac だけで本当に直せる? Windows 機は必要?

Mac だけで完結する。公式書込ツール PowerFlash.exe は Windows 専用だが、本プロジェクトは MTK BootROM プロトコルを Pure Python で再実装しているので、pyserial が動くホスト(macOS / Linux / Windows いずれも)で書込まで完了する。著者は Apple Silicon Mac + CP210x USB-UART 経由で実機検証済。

JTAG なしで書き換えて文鎮化リスクはないの?

リスクはある。M-241 は JTAG ヘッダが基板に出ていないため、ハードブリック(BootROM まで死ぬ状態)になるとチップ単体での復旧が必要になり、事実上の廃棄になる場合がある。ただし MTK BootROM 自体は ROM 上にあるため通常の書込ミスでは消えず、Flash 領域だけが壊れる soft-brick で済むのが普通。それでも書込前のダンプは必ず取り、SHA256 を 2 回検証してから書込を実行している。詳細は DISCLAIMER.md 参照。

ファーム書き換えではなく、ログ補正やプロキシで済ませる方法は?

ある。代表的には以下の 2 つ:

  1. GPSBabel 等で「+7168 日」する後処理 — 記録済ログの日付を後から補正する。リアルタイムで端末の表示は直らない。
  2. NMEA プロキシ — ホスト側で $GPRMC の date フィールドを書き換えながら中継する。ホストアプリ上は正しく見えるが、M-241 本体の OLED 時計は狂ったまま。

「端末を持ち歩いて、その場で正しい日付を確認したい」ニーズだけはファーム改造でしか満たせないので、本プロジェクトはそこを埋めている。

同じ手法で他の MTK GPS チップ(MT3329 など)も直せる?

部分的に流用可能。MTK BootROM / Download Agent (DA) プロトコルは MT333x シリーズで概ね共通。ただし Unix epoch 定数のアドレス・DA のロードアドレス・Flash チップ種別はモデルごとに異なるので、それぞれ実機ダンプ + 解析が必要になる。本リポジトリの mtk_proto.pydocs/01-protocol.md がそのままスタート地点として使える。

改造済の M-241 を中古として売っていい?

修理・自家用としては問題ないが、改造済機を「未改造・純正」と称して転売するのは詐欺。ファームバージョンを V1.13 → V1.14 に書き換えるおまけパッチを入れているのは、改造済機が一目で判別できるようにするため。譲渡時は改造済である旨を明示すること(リポジトリの DISCLAIMER でも同じ立場を明記している)。

2026 年問題と関係ある?

直接の関係はないが、構造的に同類。2026 年問題と呼ばれるのは Galileo 衛星航法システムの週数ロールオーバー(2038-11-21 の GPS 第3 ロールオーバーとは別系統)で、これも 10 bit / 12 bit 等の有限ビット幅が原因。古い GPS デバイスを使い続けるなら、自分のチップの WNRO 周期を一度確認しておくと良い。

この記事をシェア