0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ORANGE picoAdvent Calendar 2023

Day 13

ORANGE pico のRAMをダンプする

Posted at

ORANGE pico で使用されているマイコンの PIC32MX170F256B には、RAMが64KiB搭載されている。
前回の記事
ORANGE pico の配列の負の添字の先に何があるのか #配列 - Qiita
では、配列の負の添字へのアクセスを利用し、RAMの先頭から配列のデータがある部分までの領域のダンプに成功した。
今回は、別の方法を使い、RAM全体のダンプを行う。

配列を用いてリセットせずにRAMをダンプする

前回の記事では「リセットがかかるまでメモリの内容を取得する処理を続ける」というアプローチを行ったが、今回は処理を繋げるため、リセットがかからない形でメモリの内容を取得したい。
そこで、前回の調査結果に基づいてメモリー配列の先頭アドレスを 0x03c8 (KSEG1 上では 0xa00003c8) と決め打ちし、メモリからメモリー配列の先頭を探すことで、取得中のアドレスを特定し、リセットがかからない範囲のみを取得できるようにすることにした。

具体的には、以下のプログラムを作成した。

10 memarrayaddr = &H03C8
20 dim array(3)
30 for i=0 to 3
40 v=rnd(&H10000):array(i)=v
50 mpoke i*2+0,v & &HFF:mpoke i*2+1,(v>>8) & &HFF
60 next
70 i=-1
80 if array(i)<>array(0) then i=i-1:goto 80
90 if array(i+1)<>array(1) then i=i-1:goto 80
100 if array(i+2)<>array(2) || array(i+3)<>array(3) then i=i-1:goto 80
110 print i
120 for i=i-memarrayaddr/2 to 3
130 uartput 1,format$("%02x%02x",array(i) & &HFF,(array(i)>>8) & &HFF)
140 next

まず、4要素の配列を用意し、合計8バイトの乱数を書き込む。
そして、その乱数の内容をメモリー配列の先頭に書き込み、添字を遡って乱数の内容が書き込まれている部分を探す。
該当部分が見つかったら、そこがメモリー配列の先頭であると仮定し、ダンプを実行する。
今回は、データを反転せずに From Hex だけで取り出せるような形で出力を行う。
さらに、画面に出力する print のかわりに、UARTに直接出力する uartput を用いるようにした。

実行すると、5秒ほどでメモリー配列の先頭を特定することができ、そこから10秒ほどでダンプを終えることができた。

gput を用いたメモリ内容の取得

手法

gput コマンドは、メモリー配列の内容をグラフィック画面に描画するコマンドである。
以前書いた記事
【リ】ORANGE pico をリセットするコード|みけCAT
では、gput コマンドを幅712で実行するとリセットはかからず、幅720で実行するとリセットがかかった、という報告をした。
このときのデータ量は、幅712のとき $712 \times 712 \div 8 = 63368$ バイト、幅720のとき $720 \times 720 \div 8 = 64800$ バイトである。
このデータ量にメモリー配列の先頭アドレス 0x03c8 を加えると、幅712のときは 0xfb3c、幅720のときは 0x100e8 となり、RAMの最終アドレス 0xffff を超えるか否かの境界があることがわかる。
ということは、gput コマンドをうまく用いることで、RAM上の指定した位置のデータをグラフィック画面に出力させ、取得することが可能そうである。

これを、以下のプログラムで実装した。

10 memarrayaddr = &H03C8:endaddr=&H10000
20 dim array(3)
30 for i=0 to 3
40 v=rnd(&H10000):array(i)=v
50 mpoke i*2+0,v & &HFF:mpoke i*2+1,(v>>8) & &HFF
60 next
70 s=-1
80 if array(s)<>array(0) then s=s-1:goto 80
90 if array(s+1)<>array(1) then s=s-1:goto 80
100 if array(s+2)<>array(2) || array(s+3)<>array(3) then s=s-1:goto 80
110 print s
120 for i=s-memarrayaddr/2 to s+3
130 uartput 1,format$("%02x%02x",array(i) & &HFF,(array(i)>>8) & &HFF)
140 next
150 addr=memarrayaddr+8:width=8
160 datasize=width*width/8
170 readidx=addr-(datasize-1)-memarrayaddr
180 if readidx>=8000 then width=width+8:goto 160
190 line 0,0,7,0,0:gput -width+8,-width+1,width,readidx,&HFFFF
200 gget 0,0,8,0
210 uartput 1,format$("%02x",mpeek(0))
220 addr=addr+1
230 if addr<endaddr then goto 170

最初は、前のプログラムと同様に、配列を用いてメモリー配列の先頭の位置を特定し、RAMの先頭からメモリー配列の先頭までのダンプを行う。
その後、gput で用いるデータ量に基づいて参照される最後のバイトが取得したいバイトになるように gput で指定するアドレスを調整する。
このアドレスが8000以上になった時は、gput で取得する幅を増やし、アドレスを再計算する。
幅720以下において、幅を8増やしたときに増えるデータ量は8000を下回るので、この方法でうまくいくはずである。
そして、VRAMの使用が最小限になるよう、画面の左上8ピクセルを黒く塗り、そこに gput で描画する右下の隅 (すなわち、最後のバイト) が来るように座標を調整して描画を行う。
さらに、ここで描画したデータを gget コマンドで取得し、UARTで送信する。

実行結果 (VRAMの位置の特定)

VRAMの位置を調べるため、DE AD BE EF のパターンをグラフィック画面に描画した後、このプログラムをEEPROMから読み込んで実行した。

gput を用いたメモリ内容の取得

実行には約1時間かかった。

以下は、得られたメモリ内容の最初の部分である。

メモリ内容 1

メモリー配列の最初の部分である。
最後に取得して書き込んだ乱数の2バイトが、メモリ内容の先頭2バイトと一致している。
これは偶然なのか、それとも何か関係があるのだろうか?

メモリ内容 2

0x4014 から、テキスト画面に描画されている内容が出ている。
したがって、ここがテキスト画面用のVRAMであると考えられる。
ここは前回の記事では 0x33 の連続が観測されていたが、これは 0x33 はASCII文字の 3 であるため、データ「0x33」を表現する文字列「33」を画面に出力するとデータは「0x33」のままとなり、収束するためである可能性がある。

メモリ内容 3

一定間隔おきに DE AD BE EF が格納されたエリアが現れた。
ここがグラフィック画面用のVRAMであると推測できる。
パターンを書き込んだのはy座標1からであるので、DE AD BE EF が格納されている間隔を考えると先頭アドレスは 0x44c4 であると推測できる。

メモリ内容 4

文字列がまばらに格納されている。
gget コマンドを幅448で実行するとリセットがかかったと報告しているが、このときこのあたりまで書き込みが行われると考えられる。
よく見ると、RAMのアドレスと思われるデータも格納されており、このアドレスの付近に次のアドレスと思われるデータが格納されている場所もみられた。
ということは、この領域はスタックとして使われており、このアドレスと思われるデータは呼び出し元のベースポインタである可能性が考えられる。

メモリ内容 5

前回の記事では 00 の連続だった 0x99b0 付近に、「pico」や「ramdump」というデータが見られる。
これは、プログラムをEEPROMから読み込んだため、ヘッダが格納されていると考えられる。

メモリ内容 6

配列の各要素のデータが連続して格納されている。
その後、配列の変数名 array が4個格納されている。
よく見ると、それぞれの変数名の20バイト前に、配列のデータへのポインタや要素数の情報が格納されているようである。

メモリ内容 7

変数が格納された領域を過ぎると、謎のデータがあった。
CyberChef で Entropy を計算すると、このあたりのエントロピーは7を超えており、乱数または暗号に近いデータであると考えられる。

メモリ内容 8
エントロピー

一旦 00 が多い部分があり、また乱数のようなデータが少しあったあと、再び 00 が多くなった。
意味はよくわからない。

メモリ内容 9

RAMの最後の部分である。
意味はよくわからない。

メモリ内容 10

gput を用いたメモリ内容の取得 (改良)

手法

前の章では、配列を用いて取得する部分を最小限にし、RAMの大部分を gput を用いて取得した。
しかし、この手法は長時間かかり、特に gput で指定する幅が大きくなるにつれてかかる時間も伸びていくようであった。
ただ、これで gput を用いても配列を用いるのと同様のデータが取得できることが確認できたので、配列のデータの直前までを配列を用いて取得し、配列では取得できない残りを gput を用いて取得するようにした。

配列と gput の担当範囲を変えただけであり、プログラムの変更はほとんど無い。

10 memarrayaddr = &H03C8:endaddr=&H10000
20 dim array(3)
30 for i=0 to 3
40 v=rnd(&H10000):array(i)=v
50 mpoke i*2+0,v & &HFF:mpoke i*2+1,(v>>8) & &HFF
60 next
70 s=-1
80 if array(s)<>array(0) then s=s-1:goto 80
90 if array(s+1)<>array(1) then s=s-1:goto 80
100 if array(s+2)<>array(2) || array(s+3)<>array(3) then s=s-1:goto 80
110 print s
120 for i=s-memarrayaddr/2 to -1
130 uartput 1,format$("%02x%02x",array(i) & &HFF,(array(i)>>8) & &HFF)
140 next
150 addr=memarrayaddr+2*(-s):width=8
160 datasize=width*width/8
170 readidx=addr-(datasize-1)-memarrayaddr
180 if readidx>=8000 then width=width+8:goto 160
190 line 0,0,7,0,0:gput -width+8,-width+1,width,readidx,&HFFFF
200 gget 0,0,8,0
210 uartput 1,format$("%02x",mpeek(0))
220 addr=addr+1
230 if addr<endaddr then goto 170

実行結果 (文字列変数の格納場所の特定)

今回は、文字列変数のデータがどこに格納されるのかを調べるため、プログラムをEEPROMから読み込んだ後、文字列変数にデータを格納する処理を追加し、実行を行った。

文字列変数の処理を追加して実行

実行には約20分かかった。

今回取得したRAMの内容の先頭である。0x0020 付近には前回の取得との違いが見られるが、最初の32バイトは前回取得したデータと同じである。

メモリ内容 2-1

メモリー配列の先頭部分である。
書き込まれた乱数が、前回と全く同じである。

メモリ内容 2-2

最初の文字列の内容と思われるデータが、0x2AF8 から見つかった。
その後も、256バイトごとに文字列のデータが並んでるようである。

メモリ内容 2-3

0x34f8 から、別のデータが格納されているようである。
文字列を格納する場所は10個分しか無い…?

メモリ内容 2-4

前の観察で、グラフィック画面のVRAMは 0x44c4 から始まるらしいことがわかった。
これに320×200ピクセル分のデータサイズ $320 \div 8 \times 200 = 8000$ バイトを足すと、0x6404 となる。
このアドレスを観察すると、再び文字列のデータが格納されていた。
同じデータが複数格納されており、作業領域の可能性がある。

メモリ内容 2-5

先ほどスタックであると推測した領域にも、文字列のデータが格納されていた。
スタックにしては、VRAMに隣接しすぎており、余裕が無い。
それでも、作業用のデータが格納され、下手な書き込みを行うとリセットがかかるほど重要な領域であるようである。
となると、ヒープの可能性が考えられる。
ヒープであれば、前から使われる可能性が高いのでVRAMに隣接していてもよく、空き領域の管理用に隣接する領域のアドレスを書き込む可能性もある。

メモリ内容 2-6

変数が格納された領域である。
変数 memarrayaddr を見ると、変数名の20バイト前に変数の値 0x03c8 がリトルエンディアンで格納されているのがわかる。
同様に文字列変数の変数名の20バイト前を見ると、先ほどの領域の何番目のデータを使用するかの値が格納されているようである。

メモリ内容 2-7

変数が格納された領域を過ぎた部分で 00 が多い部分をよくみると、4で割って3余るアドレスに 0xa00x9d がよくみられる。
PIC32MX系のマイコンにおいて、0xa0 で始まるアドレスは KSEG1 の RAM、0x9d で始まるアドレスは KSEG0 のProgram Flash である。
したがって、これらが呼び出し元のベースポインタやリターンアドレスを表している可能性があり、このあたりがスタックとして用いられている可能性が考えられる。

メモリ内容 2-8

追加実験 (文字列変数の最大数)

文字列変数のデータが格納された領域を観察した結果、格納する場所が10個しかないようであったので、「文字列変数は10個までしか管理できない」という仮説を立てた。
検証のため、先ほどのプログラムの実行後、文字列変数を追加していった。
すると、プログラム内の変数と合わせて11個目を追加しようとした際、「Out of string space」が出た。
実際に、文字列変数は10個までしか管理できないようである。

文字列変数の数の限界

まとめ

gput コマンドを用いてメモリの内容を一旦VRAMに読み出すことにより、配列のデータの場所を超える部分のRAMのデータを取得することができた。

前回と今回の実験結果から、ORANGE pico (Ver 1.06) のRAMには、以下の位置に以下のデータが格納されていると推測できた。
なお、「位置」はRAMの先頭からのオフセットを表す。

位置 内容
0x0000 乱数の状態?
0x03c8 メモリー配列
0x2af8 文字列変数のデータ
0x3780 キャラクターパターン
0x4014 テキスト画面 VRAM
0x44c4 グラフィック画面 VRAM
0x6404 ヒープ?
0x8968 UART受信バッファ
0x8d68 UART受信バッファ上のポインタ
0x99b0 プログラム (ヘッダを含む)
0xb9b0 構文解析用の情報?
0xd990 変数・スタック?

文字列変数のデータは1個が256バイトの固定長で、10個分の領域が用意されている。
文字列変数は10個までしか同時に管理することができない。

キャラクターパターンは、まずそれぞれの文字の1行目が1バイトずつ、合計256バイト並んだ後、それぞれの文字の2行目が1バイトずつ…というように、文字ごとではなく行ごとにまとまって格納されている。

変数が格納された領域では、変数名の20バイト前にその変数の値が4バイトリトルエンディアンで格納されている。
整数変数では、値がそのまま格納されている。
浮動小数点数変数では、値がfloat32型で格納されている。
文字列変数では、データ領域の何番目を用いるかの番号が格納されている。
配列変数では、同じ変数名が4個格納され、最初の変数名で配列のデータへのポインタが、残りの変数名で配列の要素数が格納されるようである。
(dim a(1,2,3) は実行できたが、dim a(1,2,3,4) は Syntax error となり、配列は3次元までしか使用できないようである)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?