Arduino CoreのESP32へのフラッシュ書き込み調査

現時点の情報です。最新情報はM5StickC非公式日本語リファレンスを確認してみてください。

概要

Arduino CoreでESP32にプログラムを書き込み、どのようにフラッシュに保存されているのかを調査しました。

フラッシュのクリア

esptool.py --port COM3 -b 1500000 erase_flash

最初にフラッシュをクリアして、未使用領域を0xFFで初期化しました。これで昔のプログラムの残骸が残っていることはなくなります。

Arduino Coreのサンプルスケッチ

#include <Preferences.h>

Preferences preferences;

char password[] = "password";
const char password2[] = "PASSWORD";

void setup() {
  Serial.begin(115200);
  Serial.println(password);
  Serial.println(password2);

  preferences.begin("Wi-Fi:Setting", false);
  preferences.putString("ssid", "Wi-Fi:SSID");
  preferences.putString("key", "Wi-Fi:KEY");
  preferences.end();
}

void loop() {
}

上記のプログラムを書き込みました。書き込み直後に実行され、NVS領域にWi-Fi設定が書き込まれているはずです。

ちなみに文字列は利用されていないと、コンパイラの最適化により消されていましたので、無駄にシリアル出力しています。

Arduinoの転送ログ抜粋

Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.0 seconds (effective 4096.0 kbit/s)...
Wrote 15856 bytes (10276 compressed) at 0x00001000 in 0.1 seconds (effective 991.0 kbit/s)...
Wrote 219584 bytes (112197 compressed) at 0x00010000 in 1.6 seconds (effective 1109.7 kbit/s)...
Wrote 3072 bytes (128 compressed) at 0x00008000 in 0.0 seconds (effective 1536.0 kbit/s)...

0x0000e000、0x00001000、0x00010000、0x00008000からのアドレスに4つのファイルを転送しています。

転送物確認

0x0e000tools/partitions/boot_app0.bin
0x01000tools/sdk/bin/bootloader_dio_80m.bin
0x10000sketch_test.ino.bin
0x08000sketch_test.ino.partitions.bin

Arduino IDEの環境設定から、より詳細な情報を表示するで、書き込みをしているコマンドがわかり、そこから書き込んでいるファイル名を特定することができました。

上の2つはブートローダーで、固定ファイル。下2つがスケッチ用にコンパイルしたファイルのようです。

Arduinoのパーティションテーブル

NameTypeSubTypeOffsetSizeFlags
nvsdatanvs0x0090000x005000
otadatadataota0x00e0000x002000
app0appota_00x0100000x140000
app1appota_10x1500000x140000
spiffsdataspiffs0x2900000x170000

デフォルト設定ですので、上記設定になっています。

フラッシュ領域の分析

アドレス内容ファイル
0x001000セカンドストレージブートローダーbootloader_dio_80m.bin
0x008000パーティションテーブルsketch_test.ino.partitions.bin
0x009000NVSデータ領域
0x00e000otadata領域boot_app0.bin
0x010000app0領域sketch_test.ino.bin

パーティションテーブルの情報も合わせると上記の表になりました。

ブートローダーに80MHzで動作するDIOモードのフラッシュ用のファイルを転送。パーティションテーブルはArduino IDEで指定したパーティション情報が書き込まれ、NVS領域は領域だけ確保だけされ、プログラムの中で初期化などをするので何も転送しません。

otadata領域はOTAではなく、Arduino IDEからの書き込みなのでapp0から起動するファイルを転送。app0にスケッチをコンパイルした内容を転送していました。

フラッシュ読み出し

C:\Temp>esptool.py --port COM3 -b 1500000 read_flash 0x00000 0x400000 image4M.bin
esptool.py v2.7
Serial port COM3
Connecting.....
Detecting chip type... ESP32
Chip is ESP32-PICO-D4 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, Embedded Flash, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: ??:??:??:??:??:??
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 1500000
Changed.
4194304 (100 %)
4194304 (100 %)
Read 4194304 bytes at 0x0 in 49.1 seconds (683.7 kbit/s)...
Hard resetting via RTS pin...

フラッシュの内容をファイルに保存します。

内容検証

0x001000 セカンドストレージブートローダー

バイナリエディタで読み込んだ内容です。bootloader_dio_80m.binと同じ内容の物が書き込まれています。/home/runnerでビルドしたブートローダーってことが見てわかります。

0x008000 パーティションテーブル

フラッシュのパーティションテーブルが定義されています。04から4バイトぐらいで開始アドレス、08から2バイトでサイズが入っているのかな?

0x009000 NVSデータ領域

  preferences.begin("Wi-Fi:Setting", false);
  preferences.putString("ssid", "Wi-Fi:SSID");
  preferences.putString("key", "Wi-Fi:KEY");

上記3行のデータが保存されているのが見えます。

0x00e000 otadata領域

boot_app0.binの内容がそのまま書き込まれています。

0x010000 app0領域

プログラム本体です。上記だけだとわかりませんが、文字列検索をするとpasswordとPASSWORDの文字列を発見することができました。

ESP32の場合constがついた変数は変更されませんので、RAM領域ではなくフラッシュ領域に置かれます。constがついていない変数は変更される可能性があるのでRAM領域に置かれます。そのため2つの文字列が保存されている場所は異なります。

constのフラッシュ領域には、他にも文字列が保存されており、プログラム中で利用している”Wi-Fi:Setting”などもありました。また、Core Debug Levelを設定すると、リンクしたファイルパスも保存されていますので、ビルドした人のユーザー名などが埋め込まれる可能性があります。

まとめ

メモリマップやパーティションテーブルの理解がすすみました。暗号化もどこかでチャレンジしたいと思いますが、なかなか中身が見えるのも悩ましい問題です。

コメントする

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)