現時点の情報です。最新情報は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つのファイルを転送しています。
転送物確認
0x0e000 | tools/partitions/boot_app0.bin |
0x01000 | tools/sdk/bin/bootloader_dio_80m.bin |
0x10000 | sketch_test.ino.bin |
0x08000 | sketch_test.ino.partitions.bin |
Arduino IDEの環境設定から、より詳細な情報を表示するで、書き込みをしているコマンドがわかり、そこから書き込んでいるファイル名を特定することができました。
上の2つはブートローダーで、固定ファイル。下2つがスケッチ用にコンパイルしたファイルのようです。
Arduinoのパーティションテーブル
Name | Type | SubType | Offset | Size | Flags |
nvs | data | nvs | 0x009000 | 0x005000 | – |
otadata | data | ota | 0x00e000 | 0x002000 | – |
app0 | app | ota_0 | 0x010000 | 0x140000 | – |
app1 | app | ota_1 | 0x150000 | 0x140000 | – |
spiffs | data | spiffs | 0x290000 | 0x170000 | – |
デフォルト設定ですので、上記設定になっています。
フラッシュ領域の分析
アドレス | 内容 | ファイル |
0x001000 | セカンドストレージブートローダー | bootloader_dio_80m.bin |
0x008000 | パーティションテーブル | sketch_test.ino.partitions.bin |
0x009000 | NVSデータ領域 | |
0x00e000 | otadata領域 | boot_app0.bin |
0x010000 | app0領域 | 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を設定すると、リンクしたファイルパスも保存されていますので、ビルドした人のユーザー名などが埋め込まれる可能性があります。
まとめ
メモリマップやパーティションテーブルの理解がすすみました。暗号化もどこかでチャレンジしたいと思いますが、なかなか中身が見えるのも悩ましい問題です。
コメント