【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

MINIX 2事始め

16bitのx86コードに触れたくなりました。そこでMINIX 2に目を付けました。

MINIX 2は16bitと32bitの両方に対応しています。16bitの方は80286のプロテクトモードを使用しているためリアルモードではありません。カーネルではなくユーザープログラムの動きを追いたいので、ユーザーだけを見ている限りはリアルモードとの違いは少ないのではないかと予想しています。詳しいことは触りながら調べてみる予定です。

入手

ダウンロードからリンクされているPrevious Versionsから入手できます。

展開すると、中には更にアーカイブが入っています。そこからバイナリやソースが入手できます。16bitに関連するファイルを挙げます。

i86/USR.TAZ /usr以下に展開されるバイナリ
src/SYS.TAZ カーネルやlibcなどのソース
src/CMD.TAZ コマンドのソース
src/FIX.TAZ カーネルのNICに関する差し替え(→パッチ化したもの

今回はOSのインストールではなくバイナリ調査が目的ですが、USR.TAZを展開するだけで簡単に入手できました。

MINIX 1

もしかするとMINIX 1の方が単純で調べやすいのかもしれません。

1.7.5と2.0.4のファイルサイズを比較すると、バイナリとソースのファイルサイズは大差ありません。配布物のサイズが倍近く違うのはドキュメント量の差のようです。

ファイル1.7.52.0.4
Intel-*.tar.bz214,677,07929,407,996
USR.TAZ3,691,4244,041,076
SYS.TAZ2,191,2172,397,026
CMD.TAZ3,287,0493,414,449

とりあえず2で始めて、複雑過ぎるようであれば1を検討しようかと思います。

シェルスクリプト

usr/binにはシェルスクリプトが混ざっています。

$ file * | grep script
DESCRIBE:      POSIX shell script text executable
M:             POSIX shell script text executable
MAKEDEV:       POSIX shell script text executable
(略)
srccrc:        POSIX shell script text executable
svclog:        POSIX shell script text executable
whatis:        POSIX shell script text executable

これらを除外して小さい順に並べます。

$ ls -lrS | cut -d ' ' -f 5-

  332 Aug 24  2003 sync
  462 Aug 24  2003 yes
 1472 Aug 24  2003 sleep
 1518 Aug 24  2003 update
 1926 Aug 24  2003 tr
 1982 Aug 24  2003 rmdir
 2318 Aug 24  2003 tee
 2538 Nov 10  2003 uname
 2538 Nov 10  2003 arch
 2546 Aug 24  2003 loadkeys
(略)

yesが取っ付きやすそうです。

fileコマンドでは以下のように識別されます。ヘッダにOSを識別する情報が入っていないようです。

$ file yes
yes: Linux-8086 executable

アセンブラ

コマンドを眺めると、dis88という逆アセンブラを見付けました。ソースから再コンパイルすれば手元の環境(MSYS)でも動きました。

Makefileを修正します。

--- Makefile.orig
+++ Makefile
@@ -24,14 +24,14 @@
 # the initialization  data in the lookup tables.  It should not
 # be necessary to alter the formats of the tables.

-CFLAGS =-O -wo
+CFLAGS =-O -I. #-wo
 OBJ = disrel.o dismain.o distabs.o dishand.o disfp.o

 all:   dis88

 dis88: $(OBJ)
-       cc -i -o dis88 $(OBJ)
-       install -S 5kw dis88
+       cc -o dis88 $(OBJ)
+       #install -S 5kw dis88

 install:       /usr/bin/dis88

usr/includeからa.out.hとansi.hをコピーします。

POSIXに準拠したソースらしく、修正なしですんなりmakeが通りました。

yesの逆アセンブル
$ dis88 yes
(略)
T00000: xor     bp,bp
T00002: mov     bx,sp
T00004: mov     ax,(bx)
T00006: lea     dx,*2(bx)
T00009: lea     cx,*4(bx)
T0000c: add     cx,ax
T0000e: add     cx,ax
T00010: mov     bx,#10
T00013: cmp     bx,#14
(略)

オペランドの並びはIntel形式ですが、*や#などの記号が独特です。バイナリが併記されないため、オペコードの調査には一手間掛かりそうです。

.textの抽出

先ほどコピーしたa.out.hを見ると、ヘッダ情報が分かります。

struct	exec {			/* a.out header */
  unsigned char	a_magic[2];	/* magic number */
  unsigned char	a_flags;	/* flags, see below */
  unsigned char	a_cpu;		/* cpu id */
  unsigned char	a_hdrlen;	/* length of header */
  unsigned char	a_unused;	/* reserved for future use */
  unsigned short a_version;	/* version stamp (not used at present) */
  long		a_text;		/* size of text segement in bytes */
  long		a_data;		/* size of data segment in bytes */
  long		a_bss;		/* size of bss segment in bytes */
  long		a_entry;	/* entry point */
  long		a_total;	/* total memory allocated */
  long		a_syms;		/* size of symbol table */

  /* SHORT FORM ENDS HERE */
  long		a_trsize;	/* text relocation size */
  long		a_drsize;	/* data relocation size */
  long		a_tbase;	/* text relocation base */
  long		a_dbase;	/* data relocation base */
};

a_hdrlenとa_textを見れば.textだけを抽出できそうです。Pythonで抽出スクリプトを書きました。

extract-text.py
import sys, struct

if len(sys.argv) != 3:
    print "usage: %s input output" % sys.argv[0]
    sys.exit(1)

with open(sys.argv[1], "rb") as f:
    assert f.read(2) == "\x01\x03", "not a.out"
    f.seek(4)
    a_hdrlen = ord(f.read(1))
    f.seek(8)
    a_text = struct.unpack("<L", f.read(4))[0]
    f.seek(a_hdrlen)
    text = f.read(a_text)

with open(sys.argv[2], "wb") as f:
    f.write(text)

yesから.textを抽出して、objdumpで逆アセンブルします。-bで生バイナリ、-mで16bit、-MでIntel形式、-Dでファイル全体の逆アセンブルを指定します。

$ python extract-text.py yes yes-text
$ objdump -b binary -m i8086 -M intel -D yes-text
(略)
   0:   31 ed                   xor    bp,bp
   2:   89 e3                   mov    bx,sp
   4:   8b 07                   mov    ax,WORD PTR [bx]
   6:   8d 57 02                lea    dx,[bx+0x2]
   9:   8d 4f 04                lea    cx,[bx+0x4]
   c:   01 c1                   add    cx,ax
   e:   01 c1                   add    cx,ax
  10:   bb 0a 00                mov    bx,0xa
  13:   81 fb 0e 00             cmp    bx,0xe
(略)

NASMに含まれるndisasmでも逆アセンブルしてみます。オプションが必要ないため簡単です。

$ ndisasm yes-text
00000000  31ED              xor bp,bp
00000002  89E3              mov bx,sp
00000004  8B07              mov ax,[bx]
00000006  8D5702            lea dx,[bx+0x2]
00000009  8D4F04            lea cx,[bx+0x4]
0000000C  01C1              add cx,ax
0000000E  01C1              add cx,ax
00000010  BB0A00            mov bx,0xa
00000013  81FB0E00          cmp bx,0xe
(略)

先ほどのdis88とフォーマットがやや異なりますが、内容は一致しています。バイナリが併記されているので便利です。objdumpを使うかndisasmを使うかは好みです。

以上でMINIX 2のバイナリを入手して逆アセンブルする所までできました。