こちらのスライドでご存知の方も多いと思いますが、ARMの実行バイナリをバイナリエディタのビットマップビューで見るとゴマ粒のようなものが縦に並んでいるのが見えます。
これはほとんどの命令の先頭4bitが0xEになるというARM命令の特徴によるものであり、CTFなどでそのバイナリがどのアーキテクチャ向けのものかを特定する手がかりになります。
ここまでは私も知っていたのですが、ではなぜ命令の頭が0xEになるかまでは調べたことが無かったので、調べてみました。
条件実行
先頭が0xEとなる理由を探るため、ARM7の資料を参照してARM命令のフォーマットを確認してみます。
この資料より、ARM命令のフォーマットは大まかに以下のような構成になっていることがわかります。
| cond(4bit) | opcode | Rd | Rn | Operand2 |
多くが0xEとなっている先頭4bitの部分には、コンディション(Cond)フィールドがあります。 この部分は、ARM命令の特徴としてもよくあげられる、条件実行命令のために利用されます。
この条件実行命令がどういうものかというと、例えば「R0とR1の値が同じ値ならR0にR2を加算する」といったようなことをしようとしたとき、通常は以下のようなプログラムを書きます。
cmp R0, R1 beq addC b noaddC addC: add R0, R0, R2 noaddC: nop
一方、ここで条件実行命令を使うと、以下のように書くことができます。
cmp R0, R1 addeq R0, R0, R2 nop
このように、命令の後ろに条件を付けることで、特定の条件を満たす場合のみ、その命令を実行するといったことができます。 この条件実行命令の条件を指定する部分が、コンディションフィールドになります。
命令を常に実行したい場合は、条件によらず常に実行するというALコンディションを使いますが、このALコンディションは命令から省略することが可能です。
つまり、add
命令と addal
の命令は、コンパイルすると同じ命令が生成されることになります。
[tnishinaga@tx201 add]$ cat add.S add r0, r0, #0 addal r0, r0, #0 [tnishinaga@tx201 add]$ arm-none-eabi-gcc -c -o add.o add.S [tnishinaga@tx201 add]$ arm-none-eabi-objdump -D add.o add.o: file format elf32-littlearm Disassembly of section .text: 00000000 <.text>: 0: e2800000 add r0, r0, #0 4: e2800000 add r0, r0, #0
このALを示すコードが0b1110であり、16進数では0xEとなります。 これが、ARM命令の先頭によく出てくる0xEの正体です。
ARMの命令は32bit固定長かつリトルエンディアンです。 なので、バイナリエディタで見ると0x*****Eのような値が規則正しく並び、ビットマップビューではこのEの部分が黒塗りとなるため、ゴマ粒が縦に綺麗に並んだバイナリを見ることができるのです。
まとめ
- ARMバイナリの規則正しい黒ごまが見えるのは命令の最初が0xEから始まるため
- 多くの命令の先頭が0xEなのは、コンディションフィールドの値がAL(0xE)だから
以上です。
間違い等あればコメントで指摘のほどよろしくお願いします。