86バイナリでも記号プログラミング!

2010-12-07

こんにちは、はせがわです。

「記号プログラミングなんてLLだけの世界なので、バイナリアンな俺様には関係ない」なんて思っていませんか?
今日はそんな硬派なあなたに贈る、8086記号バイナリプログラムの話です。

早速ですが、まずは以下のコードを test.com という名前でファイルに保存してください。

%@"%"@,~,%,!`_^[^_^]-;>`_^[^_^]%"!,^,:`_^[^_^]-@{-`{-?:`_[^_^]_-``-``-@@`_^[^_^]-`~-``-@$`_^[^_^]-``-``-@@`_^[^_^]-`~-``-@#`_^[^_^]-+~-/~-?;`_^[^_^]%!~-;-,;`_^[^_^]-"$-@~-@``_^[^_^]-{[-);-@:`_^[^_^]-/*,%`_^[^_^]`_^[^_^]`_^[^_^]`_^[^_^]%@$-@;-?;`_^[^_^]-/~-`&,#`_^[^_^]-`~-`{,*`_^[^_^]-@@-$!`_^[^_^]-:$,[,<`_^[^_^]-!|-.),!`_^[^_^]-@{-@`-/(`_^[^_^]`_^[^_^]`_^[^_^]`_^[^_^]-{!-{.,.`_^[^_^]-~/-/``_^[^_^]%""-}@$"`_^[^_^]%@@-!/,!`_^[^_^]-:*-=%`[[[[[[[[`^^^^^-%+)@@^^^!;@@_!,((,.((-$+)@*+@!!@-,!"(+@@,$-,!"($%&,&,&_&,"@"'%_&"',&$&-@*@$"

そして、コマンドプロンプトから実行してみましょう。

C:\>test.com
Hello, World

はい、さっぱり意味がわかりませんが、顔文字っぽい記号だけのコードをふつうにバイナリとして実行できましたね。

というわけで詳しく説明していきます。

実はこのバイナリは、JavaScript で書かれたアセンブラ Symbolic Assembler で生成されたものです。
このアセンブラでは、任意のアセンブラプログラム(といってもサポートしている命令はほとんどありませんが…)をアセンブルし、以下のような手順で記号だけの MS-DOS COM 形式のバイナリを生成します。

  1. 入力されたプログラムを通常通りアセンブル
  2. アセンブルしたバイナリを記号だけにエンコード
  3. デコーダ展開コード(記号だけで構成されている)を CS:0100 番地に配置
  4. デコーダ展開コードの後にエンコードされたバイナリ(ペイロード)を配置

アーキテクチャとして 8086 の MS-DOS COM 形式を選んでいるのは、この形式はヘッダ等を持たず、実行可能ファイルとしてもっとも簡単なフォーマットだからです。

実行時には、以下のように動作します。

  1. デコーダ展開コードは、デコーダとして動作するバイナリコードをスタック上に展開
  2. 展開が完了するとスタック上のデコーダのコードにジャンプ
  3. スタック上のデコーダコードは、ペイロードを CS:0x0100 番地以降にデコードして配置
  4. CS:0x0100 番地へジャンプ

キモとなるのは、「デコーダ展開コード」になります。これさえ記号で書くことができれば、あとは簡単なはずです。
記号プログラミングで使用可能なバイナリ値は、0x21-0x2F、0x3A-0x3F、0x5B-0x60、0x7B-0x7Eまでの32種類です。
さて、この32種類の記号では、どのような命令が使用できるのでしょうか。各バイト値を逆アセンブルしてみると、以下の311命令が得られます。

0x21 0x21 : AND  [ BX + DI ], SP
0x21 0x22 : AND  [ BP + SI ], SP
0x21 0x23 : AND  [ BP + DI ], SP
0x21 0x24 : AND  [ SI ], SP
0x21 0x25 : AND  [ DI ], SP
0x21 0x26 : AND  [ addr16 ], SP
0x21 0x27 : AND  [ BX ], SP
0x21 0x28 : AND  [ BX + SI ], BP
0x21 0x29 : AND  [ BX + DI ], BP
0x21 0x2A : AND  [ BP + SI ], BP
0x21 0x2B : AND  [ BP + DI ], BP
0x21 0x2C : AND  [ SI ], BP
0x21 0x2D : AND  [ DI ], BP
0x21 0x2E : AND  [ addr16 ], BP
0x21 0x2F : AND  [ BX ], BP
0x21 0x3A : AND  [ BP + SI ], DI
0x21 0x3B : AND  [ BP + DI ], DI
0x21 0x3C : AND  [ SI ], DI
0x21 0x3D : AND  [ DI ], DI
0x21 0x3E : AND  [ addr16 ], DI
0x21 0x3F : AND  [ BX ], DI
0x21 0x40 : AND  [ BX + SI + addr8 ], AX
0x21 0x5B : AND  [ BP + DI + addr8 ], BX
0x21 0x5C : AND  [ SI + addr8 ], BX
0x21 0x5D : AND  [ DI + addr8 ], BX
0x21 0x5E : AND  [ BP + addr8 ], BX
0x21 0x5F : AND  [ BX + addr8 ], BX
0x21 0x60 : AND  [ BX + SI + addr8 ], SP
0x21 0x7B : AND  [ BP + DI + addr8 ], DI
0x21 0x7C : AND  [ SI + addr8 ], DI
0x21 0x7D : AND  [ DI + addr8 ], DI
0x21 0x7E : AND  [ BP + addr8 ], DI
0x22 0x21 : AND  AH, [ BX + DI ]
0x22 0x22 : AND  AH, [ BP + SI ]
0x22 0x23 : AND  AH, [ BP + DI ]
0x22 0x24 : AND  AH, [ SI ]
0x22 0x25 : AND  AH, [ DI ]
0x22 0x26 : AND  AH, [ addr16 ]
0x22 0x27 : AND  AH, [ BX ]
0x22 0x28 : AND  CH, [ BX + SI ]
0x22 0x29 : AND  CH, [ BX + DI ]
0x22 0x2A : AND  CH, [ BP + SI ]
0x22 0x2B : AND  CH, [ BP + DI ]
0x22 0x2C : AND  CH, [ SI ]
0x22 0x2D : AND  CH, [ DI ]
0x22 0x2E : AND  CH, [ addr16 ]
0x22 0x2F : AND  CH, [ BX ]
0x22 0x3A : AND  BH, [ BP + SI ]
0x22 0x3B : AND  BH, [ BP + DI ]
0x22 0x3C : AND  BH, [ SI ]
0x22 0x3D : AND  BH, [ DI ]
0x22 0x3E : AND  BH, [ addr16 ]
0x22 0x3F : AND  BH, [ BX ]
0x22 0x40 : AND  AL, [ BX + SI + addr8 ]
0x22 0x5B : AND  BL, [ BP + DI + addr8 ]
0x22 0x5C : AND  BL, [ SI + addr8 ]
0x22 0x5D : AND  BL, [ DI + addr8 ]
0x22 0x5E : AND  BL, [ BP + addr8 ]
0x22 0x5F : AND  BL, [ BX + addr8 ]
0x22 0x60 : AND  AH, [ BX + SI + addr8 ]
0x22 0x7B : AND  BH, [ BP + DI + addr8 ]
0x22 0x7C : AND  BH, [ SI + addr8 ]
0x22 0x7D : AND  BH, [ DI + addr8 ]
0x22 0x7E : AND  BH, [ BP + addr8 ]
0x23 0x21 : AND  SP, [ BX + DI ]
0x23 0x22 : AND  SP, [ BP + SI ]
0x23 0x23 : AND  SP, [ BP + DI ]
0x23 0x24 : AND  SP, [ SI ]
0x23 0x25 : AND  SP, [ DI ]
0x23 0x26 : AND  SP, [ addr16 ]
0x23 0x27 : AND  SP, [ BX ]
0x23 0x28 : AND  BP, [ BX + SI ]
0x23 0x29 : AND  BP, [ BX + DI ]
0x23 0x2A : AND  BP, [ BP + SI ]
0x23 0x2B : AND  BP, [ BP + DI ]
0x23 0x2C : AND  BP, [ SI ]
0x23 0x2D : AND  BP, [ DI ]
0x23 0x2E : AND  BP, [ addr16 ]
0x23 0x2F : AND  BP, [ BX ]
0x23 0x3A : AND  DI, [ BP + SI ]
0x23 0x3B : AND  DI, [ BP + DI ]
0x23 0x3C : AND  DI, [ SI ]
0x23 0x3D : AND  DI, [ DI ]
0x23 0x3E : AND  DI, [ addr16 ]
0x23 0x3F : AND  DI, [ BX ]
0x23 0x40 : AND  AX, [ BX + SI + addr8 ]
0x23 0x5B : AND  BX, [ BP + DI + addr8 ]
0x23 0x5C : AND  BX, [ SI + addr8 ]
0x23 0x5D : AND  BX, [ DI + addr8 ]
0x23 0x5E : AND  BX, [ BP + addr8 ]
0x23 0x5F : AND  BX, [ BX + addr8 ]
0x23 0x60 : AND  SP, [ BX + SI + addr8 ]
0x23 0x7B : AND  DI, [ BP + DI + addr8 ]
0x23 0x7C : AND  DI, [ SI + addr8 ]
0x23 0x7D : AND  DI, [ DI + addr8 ]
0x23 0x7E : AND  DI, [ BP + addr8 ]
0x24      : AND    AL, %u8
0x25      : AND    AX, addr16
0x26      : ES:
0x27      : DAA
0x28 0x21 : SUB  [ BX + DI ], AH
0x28 0x22 : SUB  [ BP + SI ], AH
0x28 0x23 : SUB  [ BP + DI ], AH
0x28 0x24 : SUB  [ SI ], AH
0x28 0x25 : SUB  [ DI ], AH
0x28 0x26 : SUB  [ addr16 ], AH
0x28 0x27 : SUB  [ BX ], AH
0x28 0x28 : SUB  [ BX + SI ], CH
0x28 0x29 : SUB  [ BX + DI ], CH
0x28 0x2A : SUB  [ BP + SI ], CH
0x28 0x2B : SUB  [ BP + DI ], CH
0x28 0x2C : SUB  [ SI ], CH
0x28 0x2D : SUB  [ DI ], CH
0x28 0x2E : SUB  [ addr16 ], CH
0x28 0x2F : SUB  [ BX ], CH
0x28 0x3A : SUB  [ BP + SI ], BH
0x28 0x3B : SUB  [ BP + DI ], BH
0x28 0x3C : SUB  [ SI ], BH
0x28 0x3D : SUB  [ DI ], BH
0x28 0x3E : SUB  [ addr16 ], BH
0x28 0x3F : SUB  [ BX ], BH
0x28 0x40 : SUB  [ BX + SI + addr8 ], AL
0x28 0x5B : SUB  [ BP + DI + addr8 ], BL
0x28 0x5C : SUB  [ SI + addr8 ], BL
0x28 0x5D : SUB  [ DI + addr8 ], BL
0x28 0x5E : SUB  [ BP + addr8 ], BL
0x28 0x5F : SUB  [ BX + addr8 ], BL
0x28 0x60 : SUB  [ BX + SI + addr8 ], AH
0x28 0x7B : SUB  [ BP + DI + addr8 ], BH
0x28 0x7C : SUB  [ SI + addr8 ], BH
0x28 0x7D : SUB  [ DI + addr8 ], BH
0x28 0x7E : SUB  [ BP + addr8 ], BH
0x29 0x21 : SUB  [ BX + DI ], SP
0x29 0x22 : SUB  [ BP + SI ], SP
0x29 0x23 : SUB  [ BP + DI ], SP
0x29 0x24 : SUB  [ SI ], SP
0x29 0x25 : SUB  [ DI ], SP
0x29 0x26 : SUB  [ addr16 ], SP
0x29 0x27 : SUB  [ BX ], SP
0x29 0x28 : SUB  [ BX + SI ], BP
0x29 0x29 : SUB  [ BX + DI ], BP
0x29 0x2A : SUB  [ BP + SI ], BP
0x29 0x2B : SUB  [ BP + DI ], BP
0x29 0x2C : SUB  [ SI ], BP
0x29 0x2D : SUB  [ DI ], BP
0x29 0x2E : SUB  [ addr16 ], BP
0x29 0x2F : SUB  [ BX ], BP
0x29 0x3A : SUB  [ BP + SI ], DI
0x29 0x3B : SUB  [ BP + DI ], DI
0x29 0x3C : SUB  [ SI ], DI
0x29 0x3D : SUB  [ DI ], DI
0x29 0x3E : SUB  [ addr16 ], DI
0x29 0x3F : SUB  [ BX ], DI
0x29 0x40 : SUB  [ BX + SI + addr8 ], AX
0x29 0x5B : SUB  [ BP + DI + addr8 ], BX
0x29 0x5C : SUB  [ SI + addr8 ], BX
0x29 0x5D : SUB  [ DI + addr8 ], BX
0x29 0x5E : SUB  [ BP + addr8 ], BX
0x29 0x5F : SUB  [ BX + addr8 ], BX
0x29 0x60 : SUB  [ BX + SI + addr8 ], SP
0x29 0x7B : SUB  [ BP + DI + addr8 ], DI
0x29 0x7C : SUB  [ SI + addr8 ], DI
0x29 0x7D : SUB  [ DI + addr8 ], DI
0x29 0x7E : SUB  [ BP + addr8 ], DI
0x2A 0x21 : SUB  AH, [ BX + DI ]
0x2A 0x22 : SUB  AH, [ BP + SI ]
0x2A 0x23 : SUB  AH, [ BP + DI ]
0x2A 0x24 : SUB  AH, [ SI ]
0x2A 0x25 : SUB  AH, [ DI ]
0x2A 0x26 : SUB  AH, [ addr16 ]
0x2A 0x27 : SUB  AH, [ BX ]
0x2A 0x28 : SUB  CH, [ BX + SI ]
0x2A 0x29 : SUB  CH, [ BX + DI ]
0x2A 0x2A : SUB  CH, [ BP + SI ]
0x2A 0x2B : SUB  CH, [ BP + DI ]
0x2A 0x2C : SUB  CH, [ SI ]
0x2A 0x2D : SUB  CH, [ DI ]
0x2A 0x2E : SUB  CH, [ addr16 ]
0x2A 0x2F : SUB  CH, [ BX ]
0x2A 0x3A : SUB  BH, [ BP + SI ]
0x2A 0x3B : SUB  BH, [ BP + DI ]
0x2A 0x3C : SUB  BH, [ SI ]
0x2A 0x3D : SUB  BH, [ DI ]
0x2A 0x3E : SUB  BH, [ addr16 ]
0x2A 0x3F : SUB  BH, [ BX ]
0x2A 0x40 : SUB  AL, [ BX + SI + addr8 ]
0x2A 0x5B : SUB  BL, [ BP + DI + addr8 ]
0x2A 0x5C : SUB  BL, [ SI + addr8 ]
0x2A 0x5D : SUB  BL, [ DI + addr8 ]
0x2A 0x5E : SUB  BL, [ BP + addr8 ]
0x2A 0x5F : SUB  BL, [ BX + addr8 ]
0x2A 0x60 : SUB  AH, [ BX + SI + addr8 ]
0x2A 0x7B : SUB  BH, [ BP + DI + addr8 ]
0x2A 0x7C : SUB  BH, [ SI + addr8 ]
0x2A 0x7D : SUB  BH, [ DI + addr8 ]
0x2A 0x7E : SUB  BH, [ BP + addr8 ]
0x2B 0x21 : SUB  SP, [ BX + DI ]
0x2B 0x22 : SUB  SP, [ BP + SI ]
0x2B 0x23 : SUB  SP, [ BP + DI ]
0x2B 0x24 : SUB  SP, [ SI ]
0x2B 0x25 : SUB  SP, [ DI ]
0x2B 0x26 : SUB  SP, [ addr16 ]
0x2B 0x27 : SUB  SP, [ BX ]
0x2B 0x28 : SUB  BP, [ BX + SI ]
0x2B 0x29 : SUB  BP, [ BX + DI ]
0x2B 0x2A : SUB  BP, [ BP + SI ]
0x2B 0x2B : SUB  BP, [ BP + DI ]
0x2B 0x2C : SUB  BP, [ SI ]
0x2B 0x2D : SUB  BP, [ DI ]
0x2B 0x2E : SUB  BP, [ addr16 ]
0x2B 0x2F : SUB  BP, [ BX ]
0x2B 0x3A : SUB  DI, [ BP + SI ]
0x2B 0x3B : SUB  DI, [ BP + DI ]
0x2B 0x3C : SUB  DI, [ SI ]
0x2B 0x3D : SUB  DI, [ DI ]
0x2B 0x3E : SUB  DI, [ addr16 ]
0x2B 0x3F : SUB  DI, [ BX ]
0x2B 0x40 : SUB  AX, [ BX + SI + addr8 ]
0x2B 0x5B : SUB  BX, [ BP + DI + addr8 ]
0x2B 0x5C : SUB  BX, [ SI + addr8 ]
0x2B 0x5D : SUB  BX, [ DI + addr8 ]
0x2B 0x5E : SUB  BX, [ BP + addr8 ]
0x2B 0x5F : SUB  BX, [ BX + addr8 ]
0x2B 0x60 : SUB  SP, [ BX + SI + addr8 ]
0x2B 0x7B : SUB  DI, [ BP + DI + addr8 ]
0x2B 0x7C : SUB  DI, [ SI + addr8 ]
0x2B 0x7D : SUB  DI, [ DI + addr8 ]
0x2B 0x7E : SUB  DI, [ BP + addr8 ]
0x2C      : SUB    AL, %u8
0x2D      : SUB    AX, addr16
0x2E      : CS:
0x2F      : DAS
0x3A 0x21 : CMP  AH, [ BX + DI ]
0x3A 0x22 : CMP  AH, [ BP + SI ]
0x3A 0x23 : CMP  AH, [ BP + DI ]
0x3A 0x24 : CMP  AH, [ SI ]
0x3A 0x25 : CMP  AH, [ DI ]
0x3A 0x26 : CMP  AH, [ addr16 ]
0x3A 0x27 : CMP  AH, [ BX ]
0x3A 0x28 : CMP  CH, [ BX + SI ]
0x3A 0x29 : CMP  CH, [ BX + DI ]
0x3A 0x2A : CMP  CH, [ BP + SI ]
0x3A 0x2B : CMP  CH, [ BP + DI ]
0x3A 0x2C : CMP  CH, [ SI ]
0x3A 0x2D : CMP  CH, [ DI ]
0x3A 0x2E : CMP  CH, [ addr16 ]
0x3A 0x2F : CMP  CH, [ BX ]
0x3A 0x3A : CMP  BH, [ BP + SI ]
0x3A 0x3B : CMP  BH, [ BP + DI ]
0x3A 0x3C : CMP  BH, [ SI ]
0x3A 0x3D : CMP  BH, [ DI ]
0x3A 0x3E : CMP  BH, [ addr16 ]
0x3A 0x3F : CMP  BH, [ BX ]
0x3A 0x40 : CMP  AL, [ BX + SI + addr8 ]
0x3A 0x5B : CMP  BL, [ BP + DI + addr8 ]
0x3A 0x5C : CMP  BL, [ SI + addr8 ]
0x3A 0x5D : CMP  BL, [ DI + addr8 ]
0x3A 0x5E : CMP  BL, [ BP + addr8 ]
0x3A 0x5F : CMP  BL, [ BX + addr8 ]
0x3A 0x60 : CMP  AH, [ BX + SI + addr8 ]
0x3A 0x7B : CMP  BH, [ BP + DI + addr8 ]
0x3A 0x7C : CMP  BH, [ SI + addr8 ]
0x3A 0x7D : CMP  BH, [ DI + addr8 ]
0x3A 0x7E : CMP  BH, [ BP + addr8 ]
0x3B 0x21 : CMP  SP, [ BX + DI ]
0x3B 0x22 : CMP  SP, [ BP + SI ]
0x3B 0x23 : CMP  SP, [ BP + DI ]
0x3B 0x24 : CMP  SP, [ SI ]
0x3B 0x25 : CMP  SP, [ DI ]
0x3B 0x26 : CMP  SP, [ addr16 ]
0x3B 0x27 : CMP  SP, [ BX ]
0x3B 0x28 : CMP  BP, [ BX + SI ]
0x3B 0x29 : CMP  BP, [ BX + DI ]
0x3B 0x2A : CMP  BP, [ BP + SI ]
0x3B 0x2B : CMP  BP, [ BP + DI ]
0x3B 0x2C : CMP  BP, [ SI ]
0x3B 0x2D : CMP  BP, [ DI ]
0x3B 0x2E : CMP  BP, [ addr16 ]
0x3B 0x2F : CMP  BP, [ BX ]
0x3B 0x3A : CMP  DI, [ BP + SI ]
0x3B 0x3B : CMP  DI, [ BP + DI ]
0x3B 0x3C : CMP  DI, [ SI ]
0x3B 0x3D : CMP  DI, [ DI ]
0x3B 0x3E : CMP  DI, [ addr16 ]
0x3B 0x3F : CMP  DI, [ BX ]
0x3B 0x40 : CMP  AX, [ BX + SI + addr8 ]
0x3B 0x5B : CMP  BX, [ BP + DI + addr8 ]
0x3B 0x5C : CMP  BX, [ SI + addr8 ]
0x3B 0x5D : CMP  BX, [ DI + addr8 ]
0x3B 0x5E : CMP  BX, [ BP + addr8 ]
0x3B 0x5F : CMP  BX, [ BX + addr8 ]
0x3B 0x60 : CMP  SP, [ BX + SI + addr8 ]
0x3B 0x7B : CMP  DI, [ BP + DI + addr8 ]
0x3B 0x7C : CMP  DI, [ SI + addr8 ]
0x3B 0x7D : CMP  DI, [ DI + addr8 ]
0x3B 0x7E : CMP  DI, [ BP + addr8 ]
0x3C      : CMP    AL, %u8
0x3D      : CMP    AX, addr16
0x3E      : DS:
0x3F      : AAS
0x40      : INC    AX
0x5B      : POP    BX
0x5C      : POP    SP
0x5D      : POP    BP
0x5E      : POP    SI
0x5F      : POP    DI
0x60      : PUSHA
0x7B      : JPO    $ + 2 + addr8
0x7C      : JL $ + 2 + addr8
0x7D      : JGE    $ + 2 + addr8
0x7E      : JLE    $ + 2 + addr8


この311命令が使用可能な命令ですが、よく見てみると、MOV は使えませんし、演算命令も AND と SUB の一部だけです。
これらの命令を使って、任意のコードを書いてみましょう。
まずは AX を 0 にする、XOR AX, AX の代替コードです。

0x25 0x40 0x22 : XOR  AX, 2240   ; %@"
0x25 0x40 0x22 : XOR  AX, 4022   ; %"@

0x2240 と 0x4022、それぞれと AND をとることで、AX レジスタの初期値に依存することなく AX レジスタを 0 にできました。
次に、SUB 命令を使って AX レジスタの値を適切に設定します。

0x2C 0x7E      : SUB  AL, 7E     ; ,~
0x2C 0x25      : SUB  AL, 25     ; ,%
0x2C 0x21      : SUB  AL, 21     ; ,!

これで AX レジスタは 0x003C となりました。唯一利用可能なデータ転送命令である PUSHA を使用して、AX を含む全レジスタをスタック上に置きます。

0x60           : PUSHA           ; `

これで、AX,CX,DX,BX,SP,BP,SI,DIの順に各レジスタがスタック上にpushされました。引き続き POP 命令を利用して、AX以外の値をスタック上から削除します。

0x5F           : POP    DI       ; _
0x5E           : POP    SI       ; ^
0x5B           : POP    BX       ; [
0x5E           : POP    SI       ; ^
0x5F           : POP    DI       ; _
0x5E           : POP    SI       ; ^
0x5D           : POP    BP       ; ]

このように、7回POP命令を呼ぶことで、スタック上にはAXレジスタの値である 0x3C 0x00 という値のみが残りました(エンディアンに注意)。
(余談ですが、POP命令は、上記に挙げた命令であればどのレジスタを対象にしてもよいのですが、生成されるバイナリが顔文字っぽくなるということから、上のようなレジスタを選定しています。)

このように、SUB命令でAXレジスタに任意の値を生成、PUSHA でスタックに全レジスタの値を配置、POP×7でAX以外の値をスタックから削除、という手順を繰り返し、スタック上にデコーダのコードを生成し、デコーダのコードがスタック上に配置できればあとはスタックにジャンプし、無事コードが実行される、という仕組みになっています。

より複雑な記号バイナリプログラムについても、後日 id:TAKESAKO さんからこのトラックで説明があると思いますのでお楽しみに!