ごちゃごちゃしたIT勉強記録

自分が勉強したものと実際にやってみたものの記録です。メインはセキュリティ(多分)こんなものでも誰かの参考になれば嬉しいです。

30日OS自作入門-5日目(GDTとIDTの前まで)-

だいたい投稿の間隔は1週間という感じでしょうか。
もしかしたらもうちょっとかかるかもしれませんけれども。
ということで5日目のラスト以外の内容について。

今回のまとめ

主には、4日目に表示させた画面上に文字だったり、マウスカーソルを表示させていきます。
どん詰まりすると思われるのはやはりフォントを増やす部分(harib02e)と変数の値を画面に表示させる(harib02g)部分かと思います。そこについては参考となるサイトをガン見してやっていけば問題ないかと思います。

harib02a

nasmhead.nasで記録されている画面モードを取得して、それを使って処理をおこなっていきます。
まずは、bootpack.cを変更していきます。
上記の処理と、4日目の最後harib01hで表示したboxfill8が大量に並んでいるところを関数としてまとめて定義(init_screen関数)します。

/* proto type declaration */ 
extern void io_hlt(void); 
extern void io_cli(void);
extern void io_out8(int port, int data);
extern int io_load_eflags(void);
extern void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen(char *vram, int x, int y);

#define COL8_000000	0
#define COL8_FF0000	1
#define COL8_00FF00	2
#define COL8_FFFF00	3
#define COL8_0000FF	4
#define COL8_FF00FF	5
#define COL8_00FFFF	6
#define COL8_FFFFFF	7
#define COL8_C6C6C6	8
#define COL8_840000	9
#define COL8_008400	10
#define COL8_848400	11
#define COL8_000084	12
#define COL8_840084	13
#define COL8_008484	14
#define COL8_848484	15

void HariMain(void)
{
	char *vram;
	int xsize, ysize;	
	short *binfo_scrnx, *binfo_scrny;
	int *binfo_vram;

	init_palette();		/* setting palette */
	binfo_scrnx = (short *) 0x0ff4;
	binfo_scrny = (short *) 0x0ff6;
	binfo_vram = (int *) 0x0ff8;
	xsize = *binfo_scrnx;
	ysize = *binfo_scrny;
	vram = (char *) *binfo_vram;

	init_screen(vram, xsize, ysize);

	for(;;){
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16*3] = {
		0x00, 0x00, 0x00,	/* 0:black */
		0xff, 0x00, 0x00,	/* 1:light red */
		0x00, 0xff, 0x00,	/* 2:light green */
		0xff, 0xff, 0x00,	/* 3:light yellow */
		0x00, 0x00, 0xff,	/* 4:light blue */
		0xff, 0x00, 0xff,	/* 5:light purple */
		0x00, 0xff, 0xff,	/* 6:light water blue */
		0xff, 0xff, 0xff,	/* 7:white */
		0xc6, 0xc6, 0xc6,	/* 8:light gray */
		0x84, 0x00, 0x00,	/* 9:dark red */
		0x00, 0x84, 0x00,	/*10:dark green */
		0x84, 0x84, 0x00,	/*11:dark yellow */
		0x00, 0x00, 0x84,	/*12:dark blue */
		0x84, 0x00, 0x84,	/*13:dark purple */
		0x00, 0x84, 0x84,	/*14:dark water blue */
		0x84, 0x84, 0x84	/*15:dark gray */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char operation is equals to DB operation */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();		/* record the allowing interrupt flags value */
	io_cli();				/* set allowing flag 0 for prohibitting interrupt */
	io_out8(0x03c8, start);

	for(i = start; i <= end; i++){
		io_out8(0x03c9, rgb[0]/4);
		io_out8(0x03c9, rgb[1]/4);
		io_out8(0x03c9, rgb[2]/4);
		rgb += 3;			/* gain rgb pointer index 3 */
	}

	io_store_eflags(eflags);		/* restore interrupt allowing flags */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x,y;
	for(y = y0; y <=y1; y++){
		for(x = x0; x <= x1; x++){
			vram[y * xsize + x] = c;
		}
	}
	return;
}

void init_screen(char *vram, int x, int y)
{
	boxfill8(vram, x, COL8_008484,  0,	0,	x - 1, 	y - 29);
	boxfill8(vram, x, COL8_C6C6C6,  0,	y - 28,	x - 1, 	y - 28);
	boxfill8(vram, x, COL8_FFFFFF,  0,	y - 27,	x - 1, 	y - 27);
	boxfill8(vram, x, COL8_C6C6C6,  0,	y - 26,	x - 1, 	y - 1);

	boxfill8(vram, x, COL8_FFFFFF,  3,	y - 24,	59,	y - 24);
	boxfill8(vram, x, COL8_FFFFFF,  2,	y - 24,	2, 	y - 4);
	boxfill8(vram, x, COL8_848484,  3,	y - 4,	59, 	y - 4);
	boxfill8(vram, x, COL8_848484,  59,	y - 4,	59, 	y - 5);
	boxfill8(vram, x, COL8_000000,  2,	y - 3,	59, 	y - 3);
	boxfill8(vram, x, COL8_000000,  60,	y - 24,	60, 	y - 3);

	boxfill8(vram, x, COL8_848484,  x - 47,	y - 24,	x - 4, 	y - 24);
	boxfill8(vram, x, COL8_848484,  x - 47,	y - 23,	x - 47, y - 4);
	boxfill8(vram, x, COL8_FFFFFF,  x - 47,	y - 3,	x - 4, 	y - 3);
	boxfill8(vram, x, COL8_FFFFFF,  x - 3,	y - 24,	x - 3, 	y - 3);
	
	return;
}

今回は、コードをまとめたりしただけなので、実際にmakeして実行させてもharib01hのときのものと結果は変わりません。
HariMainのところで追加で変数宣言したり、値を代入を以下のようにしています。

void HariMain(void)
{
	char *vram;
	int xsize, ysize;	
	short *binfo_scrnx, *binfo_scrny;
	int *binfo_vram;

	init_palette();		/* setting palette */
	binfo_scrnx = (short *) 0x0ff4;
	binfo_scrny = (short *) 0x0ff6;
	binfo_vram = (int *) 0x0ff8;
	xsize = *binfo_scrnx;
	ysize = *binfo_scrny;
	vram = (char *) *binfo_vram;

で、binfo_scrnxやbinfo_scrny, binfo_vramにアドレスを格納していますが、これらはnasmhead.nasで画面解像度の値が格納されているアドレスになります。
nasmhead.nasで該当する部分がこちら。

; related to BOOT_INFO
CYLS	EQU	0x0ff0		; setting boot sector
LEDS	EQU	0x0ff1
VMODE	EQU	0x0ff2		; how many bit color ?
SCRNX	EQU	0x0ff4		; X display resolution
SCRNY	EQU	0x0ff6		; Y display resolution
VRAM	EQU	0x0ff8		; start address fo graphic buffer

	ORG	0xc200		; start address this program is loaded

; display mode settings
	
	MOV	AL,0x13		; VGA graphics, 320x200x8bit color
	MOV	AH,0x00		; fixed value
	INT	0x10
	MOV	BYTE [VMODE],8	; store display mode in the following
	MOV	WORD [SCRNX],320
	MOV	WORD [SCRNY],200
	MOV	DWORD [VRAM],0x000a0000

SCRNX, SCRNY, VRAMのアドレスに値をそれぞれ320, 200, 0xa0000と入れています。この値をbinfo_ふんちゃかでは参照しています。

また、harib01hの時にHariMain関数にあった大量のboxfill8関数をまとめてinit_screen関数に置いたことでHariMainの中身がすっきりしました。

この後のharib02bとharib02cは構造体とアロー演算子の話なのですっとばします。
アロー演算子は、構造体のポインタ変数を宣言した際に、そのメンバのポインタを指定する際に使用するものです。
通常の構造体変数を宣言した場合はメンバへのアクセスはドットを使い、構造体ポインタを宣言した場合にはメンバへのアクセスはアロー演算子を使う、といった具合。

harib02d

今まで何もなかった画面上にAという文字を表示させます。
変更するのはbootpack.cになります。コードは以下の通り。

/* proto type declaration */ 
extern void io_hlt(void); 
extern void io_cli(void);
extern void io_out8(int port, int data);
extern int io_load_eflags(void);
extern void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen(char *vram, int x, int y);
void putfont8(char *vram, int xsize, int x, int y, char c, char *font);
 
#define COL8_000000	0
#define COL8_FF0000	1
#define COL8_00FF00	2
#define COL8_FFFF00	3
#define COL8_0000FF	4
#define COL8_FF00FF	5
#define COL8_00FFFF	6
#define COL8_FFFFFF	7
#define COL8_C6C6C6	8
#define COL8_840000	9
#define COL8_008400	10
#define COL8_848400	11
#define COL8_000084	12
#define COL8_840084	13
#define COL8_008484	14
#define COL8_848484	15

struct BOOTINFO{
	char cyls, leds, vmode, reserve;
	short scrnx, scrny;
	char *vram;
};

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
	static char font_A[16] = {
		0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
		0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
	};

	init_palette();		/* setting palette */
	init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
	putfont8(binfo->vram, binfo->scrnx, 10, 10, COL8_FFFFFF, font_A);

	for(;;){
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16*3] = {
		0x00, 0x00, 0x00,	/* 0:black */
		0xff, 0x00, 0x00,	/* 1:light red */
		0x00, 0xff, 0x00,	/* 2:light green */
		0xff, 0xff, 0x00,	/* 3:light yellow */
		0x00, 0x00, 0xff,	/* 4:light blue */
		0xff, 0x00, 0xff,	/* 5:light purple */
		0x00, 0xff, 0xff,	/* 6:light water blue */
		0xff, 0xff, 0xff,	/* 7:white */
		0xc6, 0xc6, 0xc6,	/* 8:light gray */
		0x84, 0x00, 0x00,	/* 9:dark red */
		0x00, 0x84, 0x00,	/*10:dark green */
		0x84, 0x84, 0x00,	/*11:dark yellow */
		0x00, 0x00, 0x84,	/*12:dark blue */
		0x84, 0x00, 0x84,	/*13:dark purple */
		0x00, 0x84, 0x84,	/*14:dark water blue */
		0x84, 0x84, 0x84	/*15:dark gray */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char operation is equals to DB operation */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();		/* record the allowing interrupt flags value */
	io_cli();				/* set allowing flag 0 for prohibitting interrupt */
	io_out8(0x03c8, start);

	for(i = start; i <= end; i++){
		io_out8(0x03c9, rgb[0]/4);
		io_out8(0x03c9, rgb[1]/4);
		io_out8(0x03c9, rgb[2]/4);
		rgb += 3;			/* gain rgb pointer index 3 */
	}

	io_store_eflags(eflags);		/* restore interrupt allowing flags */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x,y;
	for(y = y0; y <=y1; y++){
		for(x = x0; x <= x1; x++){
			vram[y * xsize + x] = c;
		}
	}
	return;
}

void init_screen(char *vram, int x, int y)
{
	boxfill8(vram, x, COL8_008484,  0,	0,	x - 1, 	y - 29);
	boxfill8(vram, x, COL8_C6C6C6,  0,	y - 28,	x - 1, 	y - 28);
	boxfill8(vram, x, COL8_FFFFFF,  0,	y - 27,	x - 1, 	y - 27);
	boxfill8(vram, x, COL8_C6C6C6,  0,	y - 26,	x - 1, 	y - 1);

	boxfill8(vram, x, COL8_FFFFFF,  3,	y - 24,	59,	y - 24);
	boxfill8(vram, x, COL8_FFFFFF,  2,	y - 24,	2, 	y - 4);
	boxfill8(vram, x, COL8_848484,  3,	y - 4,	59, 	y - 4);
	boxfill8(vram, x, COL8_848484,  59,	y - 4,	59, 	y - 5);
	boxfill8(vram, x, COL8_000000,  2,	y - 3,	59, 	y - 3);
	boxfill8(vram, x, COL8_000000,  60,	y - 24,	60, 	y - 3);

	boxfill8(vram, x, COL8_848484,  x - 47,	y - 24,	x - 4, 	y - 24);
	boxfill8(vram, x, COL8_848484,  x - 47,	y - 23,	x - 47, y - 4);
	boxfill8(vram, x, COL8_FFFFFF,  x - 47,	y - 3,	x - 4, 	y - 3);
	boxfill8(vram, x, COL8_FFFFFF,  x - 3,	y - 24,	x - 3, 	y - 3);
	
	return;
}

void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
	int i;
	char *p, d;	// d == data
	for(i = 0; i < 16; i++){
		p = vram + (y + i) * xsize + x;
		d = font[i];
		if((d & 0x80) != 0){ p[0] = c; }
		if((d & 0x40) != 0){ p[1] = c; }
		if((d & 0x20) != 0){ p[2] = c; }
		if((d & 0x10) != 0){ p[3] = c; }
		if((d & 0x08) != 0){ p[4] = c; }
		if((d & 0x04) != 0){ p[5] = c; }
		if((d & 0x02) != 0){ p[6] = c; }
		if((d & 0x01) != 0){ p[7] = c; }
	}
	return;
}

まず、構造体ポインタbinfoはアドレス0x0ff00をさしています。構造体自体は配列と同じように、メモリの連続したアドレス上にデータを格納します。配列と異なる点は、構造体の場合メンバ変数ごとに型が異なることです(メンバ変数がすべて同じ型であれば配列と同じようになると思います)。
それを踏まえると、構造体のメンバのアドレスはそれぞれ以下の通りになります。

0x0ff0 : cyls, 0x0ff1 : leds, 0x0ff2 : vmode, 0x0ff3 : reserve, 
0x0ff4 : scrnx, 0x0ff6 : scrny,
0x0ff8 : vram
char型は1バイトを使用、short型は2バイトを使用

それぞれのアドレスがnasmhead.nasで定義したBOOT_INFOの部分に一致するのが確認できるかと思います(上に記載したnasmhead.nasの該当部分を参照してください)。

次にfont_A配列について。
これは本に書いてある通り、「A」という文字を8(横)×16(縦)のドットで表現した場合に「塗りつぶしたドット部分」をhexで表した値を配列として定義したものになります。
下に表したものだとわかりやすいかな。

0 0 0 0 0 0 0 0    = 0x00
0 0 0 1 1 0 0 0    = 0x18
0 0 0 1 1 0 0 0    = 0x18
0 0 0 1 1 0 0 0    = 0x18
0 0 0 1 1 0 0 0    = 0x18
0 0 1 0 0 1 0 0    = 0x24
0 0 1 0 0 1 0 0    = 0x24
0 0 1 0 0 1 0 0    = 0x24
0 0 1 0 0 1 0 0    = 0x24
0 1 1 1 1 1 1 0    = 0x7e
0 1 0 0 0 0 1 0    = 0x42
0 1 0 0 0 0 1 0    = 0x42
0 1 0 0 0 0 1 0    = 0x42
1 1 1 0 0 1 1 1    = 0xe7
0 0 0 0 0 0 0 0    = 0x00
0 0 0 0 0 0 0 0    = 0x00

2進数表現でドットを塗りつぶした部分を1とした場合、各行の2進数をhexで表したものがfont_A配列に格納されています。
あとは、これをvramに書き込んであげることで「A」という文字を表示します。その処理を行うのがputfont8関数です。

putfont8関数では、上で定義したfont_A配列を使ってAを表示します。
for文内にあるif文は各ループにおいて全て実施されます。記述されているif文は、font_Aの各配列要素を2進数で考えた際に「1になっている部分を識別して、その部分に色をセットする」ことをやっています。どの色をセットするかはputfont8のarg4で指定したもの(今回の場合はCOL8_FFFFFF)で決定しています。

ということで、実行した結果は以下のようになります。
f:id:motojiroxx:20180619010703p:plain
図形だけでなく文字もちゃんと表示することができました。

harib02e

フォントを増やして色々な文字を表示する、という話なのですがここでも独自ツール。。。はぁ。
フォントの元となるhankaku.txtファイルはあるので、それを読み取って先ほどのAと同じようなものを作るコードが必要になります。
処理の方針としては、hankaku.txtに書かれているドットとアスタリスクで描かれたものを読み取り、ドットだったら0、アスタリスクだったら1に変換して、それをfont_X[16]といった形でhexの値を入れていくという形です。
で、自分にはコードを自力で書く能力はないので、いつも通り参考となるサイトに書いてあるものを見よう見まねでやってみます。
参考とするのは、以下のサイトです。いつも参考にさせていただいている「サラリーマンがハッカーを真剣に目指す」のOS自作5日目です
GDT(グローバルディスクリプタテーブル) | OS自作入門 5日目-1 【Linux】 | サラリーマンがハッカーを真剣に目指す
ということで、上記に書いてあるコードを参考に(丸パクリ)します。
converter.cとして保存し、まずはこいつをコンパイルして実行します
(このコードについては、下で自分なりの解説などを入れたいと思います)

gcc converter.c -o converter.o
./converter.o

実行がうまくいくと、ディレクトリにhankaku.cというコードが出てきますので、これをつかってOS上でA以外のフォントを表示させます。
bootpack.cでの変更点は、まずfont_Aの配列をなくしてhankaku.cで定義している配列を取り込むコードをかきます。次に取り込んだ配列を使って複数の文字を画面上に描画する処理をかきます。
今回は変更点だけ以下に記します(他の部分は変更ありません)。

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
	extern char hankaku[4096];

	init_palette();		/* setting palette */
	init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
	putfont8(binfo->vram, binfo->scrnx,  8, 8, COL8_FFFFFF, hankaku + 'A' * 16);
	putfont8(binfo->vram, binfo->scrnx, 16, 8, COL8_FFFFFF, hankaku + 'B' * 16);
	putfont8(binfo->vram, binfo->scrnx, 24, 8, COL8_FFFFFF, hankaku + 'C' * 16);
	putfont8(binfo->vram, binfo->scrnx, 40, 8, COL8_FFFFFF, hankaku + '1' * 16);
	putfont8(binfo->vram, binfo->scrnx, 48, 8, COL8_FFFFFF, hankaku + '2' * 16);
	putfont8(binfo->vram, binfo->scrnx, 56, 8, COL8_FFFFFF, hankaku + '3' * 16);

また、hariboteos.img作成時にもhankaku.cをリンクさせないといけないので、Makefileを少し変更します。
とはいってもbootpack.hrbの部分にhankaku.cを追加するだけです。

ootpack.hrb: hankaku.c bootpack.c nasmfunc.o os.lds
	gcc -march=i486 -m32 -nostdlib -T os.lds nasmfunc.o hankaku.c bootpack.c -o bootpack.hrb

実行した結果は以下の通りです。
f:id:motojiroxx:20180619032809p:plain

いいかんじです!

harib02f

文字列をかきますが、やることは先ほどのコードのうち、文字を設定する関数をまとめるだけです。で、文字列の終端はヌルバイトになるため、ヌルバイトに到達したら設定を終了するという処理をかけばいいです。bootpack.cに関数を追加しますが、その関数がこちら。

void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
        extern char hankaku[4096];
        for (; *s != 0x00; s++) {
                putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
                x += 8;
        }
        return;
}

実行した結果は以下の通りです。
f:id:motojiroxx:20180622001637p:plain

harib02g

変数の値を表示させます。
が、他のサイトでもあるとおり、sprintf関数をそのまま使おうとするとエラーが出て先に進めなくなります。
私の場合、以下のようなエラーが出ました。

forensic@forensic-virtual-machine:~/tegaki_os/day5/harib02g$ make hariboteos.img
nasm ipl.nas -o ipl.bin
nasm nasmhead.nas -o nasmhead.bin -l nasmhead.lst
nasm -f elf nasmfunc.nas -o nasmfunc.o -l nasmfunc.lst
gcc -march=i486 -m32 -nostdlib -T os.lds nasmfunc.o hankaku.c bootpack.c -o bootpack.hrb
/tmp/ccfzvo0m.o: In function `HariMain':
bootpack.c:(.text+0xc3): undefined reference to `sprintf'
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'bootpack.hrb' failed
make: *** [bootpack.hrb] Error 1

リンクで問題がおこっているっぽいので、その辺どうにか対処すればいきそうなのですがちょっと今回はさくっといきたいのでharib02eの時と同様に参考サイトのsprintf関数のコードをお借りします。
sprintfを実装する | OS自作入門 5日目-2 【Linux】 | サラリーマンがハッカーを真剣に目指す
上記のリンクにあるコードをmysprintf.cとして保存します。
あとは、bootpack.cでmysprintf.cで定義されている関数を使うので、Makefileを編集してちゃんとリンクされるように設定します。
変更したのはbootpack.hrbを生成する部分で、加えてmysprintf.oを生成する項目を新たに記述いたしました。

bootpack.hrb: hankaku.c mysprintf.o bootpack.c nasmfunc.o os.lds
        gcc -march=i486 -m32 -nostdlib -T os.lds nasmfunc.o hankaku.c mysprintf.o bootpack.c -o bootpack.hrb
・・・
mysprintf.o: mysprintf.c
        gcc -c -m32 -march=i486 -nostdlib mysprintf.c -o mysprintf.o

bootpack.cのコードについては本の内容で十分かと思いますので、改めてここで説明する必要はないかと。
ということで、実行した結果は以下の通りです。
f:id:motojiroxx:20180624182706p:plain

harib02h

マウスカーソルを描いてみます。ようやくOSっぽさが出てきた感じです。
4日目の最後にやったタスクバーの表示などと似たような感じで、マウスカーソルを出していきます。
今回もbootpack.cにコードを追加していきます。これも本を見ておけば問題ないかなと。
ということで、実行した結果は以下の通り。
f:id:motojiroxx:20180625010956p:plain

まだカーソルは動きませんが、だんだんOSっぽくなってきました!


ということで、今回はこの辺で。
ソースコードについては、ここで載せるよりかはgithubにあげた方がいいかと思いますので、後日その辺は載せようかと。

30日OS自作入門-4日目-

長くかかった3日目が終了し、これでやっとC言語を使った作業が行えます。
ということで早速はじめていきます。

今日のまとめ

4日目は画面表示がメインの項目になります。
画面表示をさせたい場合にはVRAMの領域に表示させたいものを記述させることで、OS起動時にVRAMを読み取って画面出力を行う、と言う流れです。
なので、今回やる内容としては

  • OS起動時に画面表示を行う関数を作成
  • 関数内で、「どういったものを画面表示させるか」を設定

ということになります。特にここではメモリのアドレスを意識してやっていく必要があります。

harib01a

3日目の最後に作成したharib00jのファイルに追記していく形になります。
まずは、nasmfunc.nasに画面表示用の関数と処理を定義します。nasmfunc.nasの中身は以下の通り

; File name : nasmfunc.nas
; TAB=4

[BITS 32]			; 32 bit mode machine code

; information for object file

	GLOBAL	io_hlt		; function name that contains this program
	GLOBAL	write_mem8


[SECTION .text]			; write this description at first
io_hlt:				; void io_hlt(void)
	HLT
	RET

write_mem8:			; void write_mem8(int addr, int data)
	MOV	ECX,[ESP+4]
	MOV	AL,[ESP+8]
	MOV	[ECX],AL
	RET			

write_mem8という関数を追加しています。
これ自体は、呼び出し元で設定された変数を読み出して各レジスタに格納している処理になります。

続いて、nasmfunc.nasで定義した関数を呼び出す側の方をみていきます。write_mem8を呼び出しているのはbookpack.cになります。

/* File name : bootpack.c*/
/* proto type declaration */
extern void io_hlt(void);
extern void write_mem8(int addr, int data);

void HariMain(void)
{
	int i;			/* 32bit integer */
	for(i = 0xa0000; i <= 0xaffff; i++){
		write_mem8(i,15);	/* MOV BYTE [i],15*/
	}

	for(;;){
		io_hlt();
	}
}

write_mem8で設定されている引数はarg1=i, arg2=15となります。
arg1で設定されているiは、繰り返しによって値が0xa0000から0xaffffまで変化します。
arg2では固定値として15が指定されています。

結論から言うと、動作させた結果は以下の通り真っ白な画面が出てきます。
f:id:motojiroxx:20180611023807p:plain
arg1の0xa0000から0xaffffの範囲は「画面表示させるものが記録されているメモリアドレス」でarg2の15が「実際に表示させるもの」になります。
で、15が画面の色で白を意味するので画面全体が真っ白になる、ということです。

ベースはわかったので、あとはちょいちょい値を変更していって画面の変化を観察していきます。

harib01b

今度はしましま模様を出します。変更する部分はbootpack.cのwrite_mem8に渡す引数部分です。
arg2で今度はiと0x0fのANDを取っています。

/* File name : bootpack.c
/* proto type declaration */
extern void io_hlt(void);
extern void write_mem8(int addr, int data);

void HariMain(void)
{
	int i;			/* 32bit integer */
	for(i = 0xa0000; i <= 0xaffff; i++){
		write_mem8(i,i & 0x0f);		/* MOV BYTE [i],15*/
	}

	for(;;){
		io_hlt();
	}
}

実行結果はこちら。
f:id:motojiroxx:20180611025504p:plain

んで、このあとのharib01cからharib01eまではポインタの話がつらつら書いているだけなので飛ばします。

harib01f

harib01bとはちょっと色違いのしましまを出します。その際、harib01bとは異なり色番号を自分で設定します。

harib01bでコードを追加したbootpack.cに、さらに結構コードを追加します。
追加するコードは、自分で設定する色番号とそれに関連する関数になります。

/* File name : bootpack.c */
/* proto type declaration */

extern void io_hlt(void);
extern void io_cli(void);
extern void io_out8(int port, int data);
extern int io_load_eflags(void);
extern void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);

void HariMain(void)
{
	int i;			/* 32bit integer */
	char *p;		/* pointer */

	init_palette();		/* setting palette */

	p = (char *)0xa0000;	/* set address of VRAM area */

	for(i = 0; i <= 0xffff; i++){
		p[i] = i & 0x0f;	/* MOV BYTE [i],15*/
	}

	for(;;){
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16*3] = {
		0x00, 0x00, 0x00,	/* 0:black */
		0xff, 0x00, 0x00,	/* 1:light red */
		0x00, 0xff, 0x00,	/* 2:light green */
		0xff, 0xff, 0x00,	/* 3:light yellow */
		0x00, 0x00, 0xff,	/* 4:light blue */
		0xff, 0x00, 0xff,	/* 5:light purple */
		0x00, 0xff, 0xff,	/* 6:light water blue */
		0xff, 0xff, 0xff,	/* 7:white */
		0xc6, 0xc6, 0xc6,	/* 8:light gray */
		0x84, 0x00, 0x00,	/* 9:dark red */
		0x00, 0x84, 0x00,	/*10:dark green */
		0x84, 0x84, 0x00,	/*11:dark yellow */
		0x00, 0x00, 0x84,	/*12:dark blue */
		0x84, 0x00, 0x84,	/*13:dark purple */
		0x00, 0x84, 0x84,	/*14:dark water blue */
		0x84, 0x84, 0x84	/*15:dark gray */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char operation is equals to DB operation */
}

void set_palette(int start, int end, unsigned char *rgb)               /*パレットのアクセスの手順にしたがって処理を行う*/
{
	int i, eflags;
	eflags = io_load_eflags();		/* record the allowing interrupt flags value */
	io_cli();				/* set allowing flag 0 for prohibitting interrupt */
	io_out8(0x03c8, start);

	for(i = start; i <= end; i++){
		io_out8(0x03c9, rgb[0]/4);
		io_out8(0x03c9, rgb[1]/4);
		io_out8(0x03c9, rgb[2]/4);
		rgb += 3;			/* gain rgb pointer index 3 */
	}

	io_store_eflags(eflags);		/* restore interrupt allowing flags */
	return;
}

色番号の設定は、以下の2つの関数で行なっています。

init_palette
set_palette

2つの関数の処理の流れとしては、

  1. init_palette関数を呼び出し、関数内部で色番号の配列を定義しset_palette関数を呼び出す
  2. set_palette関数内で、eflagの値を退避させたり割込み禁止にしたりしたあと、for文のなかで自分が設定しようとしている色番号を登録し、割込み禁止解除とeflagsの値を復元

というものになります。
特に、「set_palette関数って何やってるの?」というところについては、以下のサイトに詳しく書いてあります。
http://oswiki.osask.jp/?VGA
上記のリンクの「ビデオDAコンバータ」の項目の「パレットのアクセスの手順」を確認しておくといいかもです。
あとは、HariMainのルーチンに戻ったあと、登録した色番号を使用してVRAMのアドレスに色を書き込んでいきます。
これが今回書いたbootpack.cの処理フローです。

続いて。上記で追加した関数の処理についてはnasmfunc.nasにて定義します。なのでnasmfunc.nasもコードを追加します。

; nasmfunc
; TAB=4

; [FORMAT "WCOFF"]		; the mode creating object file
[BITS 32]			; 32 bit mode machine code

; information for object file

; [FILE "nasmfunc.nas"]		; source file information

	GLOBAL	io_hlt		; function name that contains this program
	GLOBAL	io_cli
	GLOBAL	io_sti
	GLOBAL	io_stihlt
	GLOBAL	io_in8
	GLOBAL	io_in16
	GLOBAL	io_in32
	GLOBAL	io_out8
	GLOBAL	io_out16
	GLOBAL	io_out32
	GLOBAL	io_load_eflags
	GLOBAL	io_store_eflags

section .text			; write this description at first

io_hlt:				; void io_hlt(void)
	HLT
	RET

io_cli:				; void io_cli(void)
	CLI
	RET

io_sti:				; void io_sti(void)
	STI
	RET

io_stihlt:			; void io_stihlt(void)
	STI
	HLT
	RET

io_in8:				; int io_in8(int port)
	MOV	EDX,[ESP+4]	; [ESP+4] is arg1
	MOV	EAX,0
	IN	AL,DX
	RET


io_in16:			; int io_in16(int port)
	MOV	EDX,[ESP+4]
	MOV	EAX,0
	IN	AX,DX
	RET

io_in32:			; int io_in32(int port)
	MOV	EDX,[ESP+4]
	IN	EAX,DX
	RET

io_out8:			; void io_out8(int port, int data)
	MOV	EDX,[ESP+4]
	MOV	AL,[ESP+8]
	OUT	DX,AL
	RET

io_out16:			; void io_out16(int port, int data)
	MOV	EDX,[ESP+4]
	MOV	EAX,[ESP+8]
	OUT	DX,AX
	RET

io_out32:			; void io_out32(int port, int data)
	MOV	EDX,[ESP+4]
	MOV	EAX,[ESP+8]
	OUT	DX,EAX
	RET

io_load_eflags:			; int io_load_eflags(void);
	PUSHFD			; it is equals to PUSH EFLAGS
	POP	EAX
	RET

io_store_eflags:		; void io_store_eflags(int eflags);
	MOV	EAX,[ESP+4]
	PUSH	EAX
	POPFD			; it is equals to POP EFLAGS
	RET

一応、いくつか出てくるアセンブリ命令についてまとめておきます。

IN	EAX,DX                     ; 装置から電気信号を受け取る命令
OUT	DX,EAX                     ; 装置へ電気信号を送る命令
※扱うサイズによってEAXの部分はALAXになる

それぞれ、DXには「装置番号」、EAXには「受け取るor送る値」が設定されます
で、2つの命令では指定するレジスタはDXとEAXで、INとOUTでsourceとdestinationが入れ替わります。
わりと覚えやすいかも。

CLI                    ;  割り込みフラグを0にする命令(CLear Interrupt flag)-> つまり割り込み禁止にする
STI                    ;  割り込みフラグを1にする命令(SeT Interrupt flag)-> つまり割り込みを有効にする

何か重要な処理をしたいので「割り込みは勘弁!」といった際にはCLIを使い、その処理が終了後にSTIで元に戻せばいいんでしょうか。
割り込みに関しては後々やるとのことなので、こんな推測に留めておきます。

PUSHFD                  ; eflags(フラグレジスタの値)をDWORDサイズでスタックに積む
POPFD                    ; eflags(フラグレジスタの値)をDWORDサイズでスタックから取り出す

フラグレジスタの操作に関連したpushとpop命令です。


んで、最終的に実行結果はこちら。harib01bとはちょっと色が違うことが確認できるかと思います。
f:id:motojiroxx:20180613020616p:plain

harib01g

ここでは3つほど色の違う四角形を表示させます。
先ほど作成したbootpack.cの内容を色々変更しますが、変更点については以下の通り

  • 先ほど配列で定義していた色番号をdefineで定義しなおす
  • 四角形を描画する関数boxfill8を定義して、指定した色で塗りつぶして表示する処理を記述

というわけで変更後のbootpack.cのコードは以下の通りです。

/* proto type declaration */ 
extern void io_hlt(void); 
extern void io_cli(void);
extern void io_out8(int port, int data);
extern int io_load_eflags(void);
extern void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);

#define COL8_000000	0
#define COL8_FF0000	1
#define COL8_00FF00	2
#define COL8_FFFF00	3
#define COL8_0000FF	4
#define COL8_FF00FF	5
#define COL8_00FFFF	6
#define COL8_FFFFFF	7
#define COL8_C6C6C6	8
#define COL8_840000	9
#define COL8_008400	10
#define COL8_848400	11
#define COL8_000084	12
#define COL8_840084	13
#define COL8_008484	14
#define COL8_848484	15

void HariMain(void)
{
	int i;			/* 32bit integer */
	char *p;		/* pointer */

	init_palette();		/* setting palette */

	p = (char *)0xa0000;	/* set address of VRAM area */

/* 3つの四角形を、描画位置と色を指定して描画 */
	boxfill8(p, 320, COL8_FF0000, 20, 20, 120, 120);
	boxfill8(p, 320, COL8_00FF00, 70, 50, 170, 150);
	boxfill8(p, 320, COL8_0000FF, 120, 80, 220, 180);

	for(;;){
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16*3] = {
		0x00, 0x00, 0x00,	/* 0:black */
		0xff, 0x00, 0x00,	/* 1:light red */
		0x00, 0xff, 0x00,	/* 2:light green */
		0xff, 0xff, 0x00,	/* 3:light yellow */
		0x00, 0x00, 0xff,	/* 4:light blue */
		0xff, 0x00, 0xff,	/* 5:light purple */
		0x00, 0xff, 0xff,	/* 6:light water blue */
		0xff, 0xff, 0xff,	/* 7:white */
		0xc6, 0xc6, 0xc6,	/* 8:light gray */
		0x84, 0x00, 0x00,	/* 9:dark red */
		0x00, 0x84, 0x00,	/*10:dark green */
		0x84, 0x84, 0x00,	/*11:dark yellow */
		0x00, 0x00, 0x84,	/*12:dark blue */
		0x84, 0x00, 0x84,	/*13:dark purple */
		0x00, 0x84, 0x84,	/*14:dark water blue */
		0x84, 0x84, 0x84	/*15:dark gray */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char operation is equals to DB operation */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();		/* record the allowing interrupt flags value */
	io_cli();				/* set allowing flag 0 for prohibitting interrupt */
	io_out8(0x03c8, start);

	for(i = start; i <= end; i++){
		io_out8(0x03c9, rgb[0]/4);
		io_out8(0x03c9, rgb[1]/4);
		io_out8(0x03c9, rgb[2]/4);
		rgb += 3;			/* gain rgb pointer index 3 */
	}

	io_store_eflags(eflags);		/* restore interrupt allowing flags */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x,y;
	for(y = y0; y <=y1; y++){
		for(x = x0; x <= x1; x++){
			vram[y * xsize + x] = c;
		}
	}
	return;
}

表示される画面はこんな感じです。
f:id:motojiroxx:20180614025851p:plain

boxfill8で特定の座標に四角形を描画していますが、座標軸は通常数学でやるときの中心を原点とするものではないです。
PCの場合、画面左上を原点として座標の計算を行います(一般的な話かどうかは知りませんが)。
今回四角形の描画で使用しているboxfill8の引数と関連させてみると、以下の図のようになるかと。
f:id:motojiroxx:20180616142652p:plain

あとは、実際に描画させる際にはVRAMのアドレスを対応させればいいです。
なので、「画面上の」原点の位置は、「メモリ上では」0xa0000に対応するので、特定の座標の色に対応するメモリの位置は以下の計算式で求められます。

0xa0000 + x + y * 320

で、「なぜyに320をかけるのか?」と言う疑問がありますが、これについては以下のサイトにあるsksatさんの解説が非常にわかりやすいです(ページ上で「320」と検索をかければ見つかります」
http://hrb.osask.jp/wiki/?q_and_a

加えて、こちらのサイトの内容も確認しておくとこの疑問については解消されるのではないかと思います。
OS自作入門 4日目 【Linux】| VRAMとカラーパレット | サラリーマンがハッカーを真剣に目指す
具体的に、0xa0000から開始されるvramにおいて、(X座標, Y座標)がどのアドレスに該当するのかをわかりやすく示してくれています。


ここまでくると「おぉ〜」とちょっと感動しますね。

harib01h

4日目の最後です。ここでは、昔のウィンドウズっぽい画面を描画します。
ただ、とくに新しいことはなく、boxfill8関数を使ってそれっぽいものをじゃんじゃん描画していくのみになります。
なので、変更するのはbootpack.cのみ。

/* proto type declaration */ 
extern void io_hlt(void); 
extern void io_cli(void);
extern void io_out8(int port, int data);
extern int io_load_eflags(void);
extern void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);

#define COL8_000000	0
#define COL8_FF0000	1
#define COL8_00FF00	2
#define COL8_FFFF00	3
#define COL8_0000FF	4
#define COL8_FF00FF	5
#define COL8_00FFFF	6
#define COL8_FFFFFF	7
#define COL8_C6C6C6	8
#define COL8_840000	9
#define COL8_008400	10
#define COL8_848400	11
#define COL8_000084	12
#define COL8_840084	13
#define COL8_008484	14
#define COL8_848484	15

void HariMain(void)
{
	char *vram;
	int xsize, ysize;	

	init_palette();		/* setting palette */
	vram = (char *)0xa0000;	/* set address of VRAM area */
	xsize = 320;
	ysize = 200;

	boxfill8(vram, xsize, COL8_008484,  0,		0,		xsize - 1, 	ysize - 29);
	boxfill8(vram, xsize, COL8_C6C6C6,  0,		ysize - 28,	xsize - 1, 	ysize - 28);
	boxfill8(vram, xsize, COL8_FFFFFF,  0,		ysize - 27,	xsize - 1, 	ysize - 27);
	boxfill8(vram, xsize, COL8_C6C6C6,  0,		ysize - 26,	xsize - 1, 	ysize - 1);

	boxfill8(vram, xsize, COL8_FFFFFF,  3,		ysize - 24,	59,		ysize - 24);
	boxfill8(vram, xsize, COL8_FFFFFF,  2,		ysize - 24,	2, 		ysize - 4);
	boxfill8(vram, xsize, COL8_848484,  3,		ysize - 4,	59, 		ysize - 4);
	boxfill8(vram, xsize, COL8_848484,  59,		ysize - 4,	59, 		ysize - 5);
	boxfill8(vram, xsize, COL8_000000,  2,		ysize - 3,	59, 		ysize - 3);
	boxfill8(vram, xsize, COL8_000000,  60,		ysize - 24,	60, 		ysize - 3);

	boxfill8(vram, xsize, COL8_848484,  xsize - 47,	ysize - 24,	xsize - 4, 	ysize - 24);
	boxfill8(vram, xsize, COL8_848484,  xsize - 47,	ysize - 23,	xsize - 47, 	ysize - 4);
	boxfill8(vram, xsize, COL8_FFFFFF,  xsize - 47,	ysize - 3,	xsize - 4, 	ysize - 3);
	boxfill8(vram, xsize, COL8_FFFFFF,  xsize - 3,	ysize - 24,	xsize - 3, 	ysize - 3);

	for(;;){
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16*3] = {
		0x00, 0x00, 0x00,	/* 0:black */
		0xff, 0x00, 0x00,	/* 1:light red */
		0x00, 0xff, 0x00,	/* 2:light green */
		0xff, 0xff, 0x00,	/* 3:light yellow */
		0x00, 0x00, 0xff,	/* 4:light blue */
		0xff, 0x00, 0xff,	/* 5:light purple */
		0x00, 0xff, 0xff,	/* 6:light water blue */
		0xff, 0xff, 0xff,	/* 7:white */
		0xc6, 0xc6, 0xc6,	/* 8:light gray */
		0x84, 0x00, 0x00,	/* 9:dark red */
		0x00, 0x84, 0x00,	/*10:dark green */
		0x84, 0x84, 0x00,	/*11:dark yellow */
		0x00, 0x00, 0x84,	/*12:dark blue */
		0x84, 0x00, 0x84,	/*13:dark purple */
		0x00, 0x84, 0x84,	/*14:dark water blue */
		0x84, 0x84, 0x84	/*15:dark gray */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char operation is equals to DB operation */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();		/* record the allowing interrupt flags value */
	io_cli();				/* set allowing flag 0 for prohibitting interrupt */
	io_out8(0x03c8, start);

	for(i = start; i <= end; i++){
		io_out8(0x03c9, rgb[0]/4);
		io_out8(0x03c9, rgb[1]/4);
		io_out8(0x03c9, rgb[2]/4);
		rgb += 3;			/* gain rgb pointer index 3 */
	}

	io_store_eflags(eflags);		/* restore interrupt allowing flags */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x,y;
	for(y = y0; y <=y1; y++){
		for(x = x0; x <= x1; x++){
			vram[y * xsize + x] = c;
		}
	}
	return;
}

表示される画面はこんな感じ。
f:id:motojiroxx:20180616154402p:plain

なんかOSぽくなってきました!ちょっと今後が楽しみ

という感じで、今回は特に大きな失敗はありませんでした(逆に不安

30日OS自作入門-番外編その1 FTK Imagerを使ったイメージファイルの読み込み-

本編とは関係ないのですが、ちょっと自分が使っているツールの紹介をば。

 

まだ3日目ですが、作成したイメージファイルの中身の構造がどうなっているのかを知りたい際には、FTK Imagerというツールがおすすめです。

Windows上でのみ動くツールですが、イメージファイルの簡易的な解析には非常に使えます。まぁ、このツール自体がフォレンジック用途なので、ファイルシステムの解析などに強いと言うのもあるのですが。

 

 

私自身、フォレンジックをやっている身なので、よくイメージファイルの中身を簡易的に見るという作業をやっているのですが、その際に使用しています。

 

FTK Imger自体は、ダウンロードフォームに記入後、メールにてダウンロードリンクが飛んできますので、そこに記載されているダウンロードリンクから落とせます。

accessdata.com

 

このツールを使って作成したイメージファイルをマウントさせると、こんな感じで中身が見れます。

f:id:motojiroxx:20180609235025p:plain

 

ちょっとだけ、使い方を紹介いたします。

 

FTK Imagerの使い方

使うときのフローとしては以下の通り。

  1. 作成したイメージファイルor接続されているHDDを選択
  2. ツールに読み込まれたイメージファイルorHDDの中身をみる

です。非常に簡単です。

1. 作成したイメージファイルの選択

まずは、作成したイメージファイルの選択をします。以下の図のようにやっていただければいいです。

流れとしては

  • Evidenceの追加
  • Evidenceの種類を選択(今回はイメージファイルなので、Image Fileを選択)
  • イメージファイルを指定

f:id:motojiroxx:20180610001946p:plain

 

最後のFinishをクリックすると、下のような画面に切り替わります。

 

f:id:motojiroxx:20180610002253p:plain

それぞれの画面で、イメージファイルないのディレクトリツリー、ファイルのリスト、プロパティ情報、hexを確認することができます。

で、30日自作OSの3日目のharib00jで作成したイメージファイルであれば、イメージファイルの先頭から0x4200にbootpackのコードがあるはずです。

 

なので、Evidence Treeの部分でimgファイルを選択し、hexの画面上でCtrl+Gをすると任意のアドレス(オフセット?)に飛ぶことができます。実際に0x4200を指定してそのデータを見て見ると確かにそのコード部分が存在するのが確認できるかと思います。

f:id:motojiroxx:20180610003031p:plain

 

f:id:motojiroxx:20180610003047p:plain

 

hexを見るだけであればxxdコマンドなどを使えばいいのですが、こちらのツールを使うとファイルシステムを読み取って中の構成をちゃんと確認できるので、「作成したharibote.sysがファイルとして格納されているはずなんだけど、本当にあるのか?」など確認できます。

 

その他にも、イメージファイル内に存在するファイルを取ってきたりメモリダンプを取りことも可能です。フリーツールとは思えない完成度だとおもいます。。

 

興味があればぜひ使ってみてください。

30日OS自作入門-3日目(C言語導入)-

ちょっと気分を変えてブログの名前を変えてみました。

最近インフラ勉強会に参加しているのですが、セキュリティ以外の部分も色々やっていきたいなぁ、というところが芽生えまして。というところで、今後ともお付き合いいただければと思います。

さて、では始めていきます。
今回は、前回のつづきとしてC言語を導入し32ビットで動作するコードを書いていきます。とはいいつつ、まだまだアセンブリ多め。

 

今回のまとめ

3日目の後ろの方で、C言語を導入するところからになります。

で、本書とは違うツールを使用する場合には、作成するファイルであったり追加で必要となったりするファイルがあります。

そこらへんは以下のサイトを参考にして流れの確認やコマンドのオプション確認を行いました。

  

・流れの確認で参考にしたサイト

OS自作入門 3日目-1 【Linux】| 64bit環境での苦悩 | サラリーマンがハッカーを真剣に目指す

・コマンドの確認で使用したサイト

『30日でできる!OS自作入門』のメモ

 

んで、上のサイトはこの本を読み終えるまで非常に参考となります。私も逐一ここは確認しながらやっていこうと思います。

 

Haribote.img作成までの流れ

上で紹介したサイトにも流れは書いていますが、自分なりに図を起こしてみました。

どのコードがイメージファイルのどの部分に入ってくるのかイメージしやすいようにしてみました。特に、harib00iとharib00jの部分が本書で使用するツールを使わない場合になかなか難しいと思いますので、そこを中心に。

(作成する際のファイル名は、本書のものとはちょっと異なる(だいたい何のファイルかは想像がつくかと思います)ので、読み替えていただければ。

 

harib00i

まずは、harib00iで作成するイメージファイルの作成フローです

f:id:motojiroxx:20180610235430p:plain

作成したMakefileは以下の通り。上の図と比較しながら見ていただければ。

# Makefile for harib00i
bootpack.hrb: bootpack.c os.lds
    gcc -march=i486 -m32 -nostdlib -T os.lds bootpack.c -o bootpack.hrb

nasmhead.bin: nasmhead.nas
    nasm nasmhead.nas -o nasmhead.bin -l nasmhead.lst

ipl.bin: ipl.nas
    nasm ipl.nas -o ipl.bin

haribote.sys: nasmhead.bin bootpack.hrb
    cat nasmhead.bin bootpack.hrb > haribote.sys

.PHONY: run
run:
    qemu-system-i386 -fda hariboteos.img

.PHONY: remove
remove:
    rm *.bin *.lst *.sys *.hrb 

.PHONY: debug
debug:
    qemu-system-i386 -m 32 -localtime -vga std -fda hariboteos.img -gdb tcp::10000 -S &

hariboteos.img: ipl.bin haribote.sys
    mformat -f 1440 -C -B ipl.bin -i hariboteos.img ::
    mcopy -i hariboteos.img haribote.sys ::

 

 最後のhariboteos.imgを作成する際には、mformatとmcopyというコマンドを使っています(上記の参考サイトをみてやって見ました)。

一応、このコマンドが何をやっているのかをまとめておきます。

mformat -f 1440 -C -B ipl.bin -i hariboteos.img ::
    -f 1440 : サイズを指定、今回は1440byte
    -C : ディスクイメージを作成し、MS-DOSファイルシステムを構成する
    -B ipl.bin : ブートセクタを指定する。今回はipl.binがブートセクタ
    -i : よくわからない(image fileのiとか?と推測)

 ここでのmformatコマンドでは、1440byteのイメージファイルhariboteos.imgを作成し、ファイルシステムFAT12として、ブートセクタをipl.binに書かれている内容とする、ということをやっています。

FAT12の12の部分って「何ビットのクラスタ識別子を利用するか」という話だったんですね。。んでクラスタ識別子は「ファイルを何個まで格納できるようにするか」という話なので、2^12=4096個までファイルを格納できるファイルシステムFAT12ということでしょうね)

 

mcopy -i hariboteos.img haribote.sys ::
    -i : こちらもわからない(おそらくimage fileを指定したものだと思われる)

 mcopyコマンドでは、先ほどmformatで作成したイメージファイルhariboteos.imgのファイルシステム内にファイルを追加するということを行なっています。

追加するファイルはharibote.sysで、前回の記事で書いたFATの構成のRoot Directory部分にharibote.sysのファイル名を追加し、File Storage Spaceにharibote.sysのファイルの中身を記録しています。

ということで、xxdコマンドを使ってhariboteos.imgのファイル内をみてみます。

 

まずは、0x2600の部分にharibote.sysファイル名が書かれています。

f:id:motojiroxx:20180611003853p:plain

 

続いて、0x4200にharibote.sysの中身が書かれていることを確認します。

f:id:motojiroxx:20180611004008p:plain

 

個別に作成したharibote.sysの中身がこちら。

f:id:motojiroxx:20180611004157p:plain

多分同じになっているはずです。このような動きをmformatとmcopyのコマンドではやっています。

 

harib00j

次に、nasmfuncを加えたharib00jのイメージファイル作成フローです。

f:id:motojiroxx:20180610235455p:plain

作成したMakefileは以下の通り。こちらも同様に上の図を見ながら確認していただければ。

# Makefile for harib00j
bootpack.hrb: bootpack.c nasmfunc.o os.lds
    gcc -march=i486 -m32 -nostdlib -T os.lds nasmfunc.o bootpack.c -o bootpack.hrb

nasmhead.bin: nasmhead.nas
    nasm nasmhead.nas -o nasmhead.bin -l nasmhead.lst

nasmfunc.o: nasmfunc.nas
    nasm -f elf nasmfunc.nas -o nasmfunc.o -l nasmfunc.lst

ipl.bin: ipl.nas
    nasm ipl.nas -o ipl.bin

haribote.sys: nasmhead.bin bootpack.hrb
    cat nasmhead.bin bootpack.hrb > haribote.sys

.PHONY: run
run:
    qemu-system-i386 -fda hariboteos.img

.PHONY: remove
remove:
    rm *.bin *.lst *.sys *.hrb *.o 

.PHONY: debug
debug:
    qemu-system-i386 -m 32 -localtime -vga std -fda hariboteos.img -gdb tcp::10000 -S &

hariboteos.img: ipl.bin haribote.sys
    mformat -f 1440 -C -B ipl.bin -i hariboteos.img ::
    mcopy -i hariboteos.img haribote.sys ::

 

harib00iとharib00jの動作結果として、真っ黒の画面が出ていれば成功です(おそらく

 

bootpack.hrbを作成する際、gccを使用してbootpack.oとnasmfunc.oをos.ldsというリンカスクリプトを利用して生成しています。リンカスクリプトについては以下のサイトをみなさん参考にやっていたので、私も便乗しました。

https://vanya.jp.net/os/haribote.html#gcchrb

ただ、このリンカスクリプトの書き方も後でちゃんと確認しておこうと思います。

 

本当はここをldを使ってリンクをしたかったのですが、うまくいかなかったのでgccコマンドでまとめてやってしまっています。

これもちゃんとあとでldを使う場合にはどうしたらちゃんとうまくいくか確認してみようと思います。

 

ドツボにハマったところ

色々なサイトを見ていきながらコードを書いて、「いざ実行!」して見たのですが、なぜか画面が真っ黒にならず、一番最初の「Booting from floppy...」までの文字列が繰り返し表示される。。。

ひとまず、デバッグをして見たところ、なぜか0x7c00(ipl.binが置かれるメモリアドレス)と0xc200(nasmhead.binが置かれるメモリアドレス)を行ったり来たりしていました。

一応コードの間違いとかを確認しましたが、それを見つけられなかったので再び1からコードを書き直しました。書き直した方ではちゃんと動きました。

んで、何が間違っていたかというと、nasmheadの最後の部分がなんかおかしかったようです(hexみたらなんか違う。。。)。

なぜ繰り返したかはちょっとこの後デバッグしてみて確認しようと思います。

 

失敗したことと参考情報

特にharib00jの部分で色々とありましたので、そこをまとめておきます。

まず、nasmを使用している場合、nasmfunc.nasでは

  • [FORMAT "WCOFF"]と[FILE "nasmfunc.nas"]の部分は必要ない

実際にこれを書いた状態でアセンブルした場合、以下のようなエラーが出ます。

nasmfunc.nas:4: error: unrecognised directive [FORMAT]
nasmfunc.nas:9: error: unrecognised directive [FILE]

識別できないディレクティブがあるということですが、これらの行をコメントアウトすることで解決できます。

 

続いて、nasmfunc.nasのオブジェクトファイルを生成したのち、gccでbootpack.hrbを生成しようとしたところ、以下のようなエラーが吐かれました。

nasmfunc.o: file not recognized: File truncated

 

もともとMakefileに書いていたnasmのコマンドは以下の通りです。

nasm nasmfunc.nas -l nasmfunc.lst -o nasmfunc.o

 ということで、nasmfunc.oってどんなファイルになってるのかをfileコマンドで調べたところ、

nasmfunc.o: ISO-8859 text, with no line terminators

 となっていました。生成しなければいけないのはELF形式なので、こりゃだめだということでnasmfunc.nasアセンブルする際にコマンドのオプションを追加して解決できました(上記Makefileのnasmfunc.oの部分を参照のこと)

 

その後もエラーがでます。次はgccでbootpack.hrbを生成する際に、bootpack.cの部分で以下のようなエラーが出ました。

/tmp/ccjdiCmn.o: In function `HariMain':
bootpack.c:(.text+0x7): undefined reference to `io_hlt'

よくよく考えてみると、このエラーが出るのは当然かと。プロトタイプ宣言している関数が外部ファイルに定義されているものなのに、「外部ファイルから呼び出しますよー」という記述が一切ないですよね。

というわけで、bootpack.c内でプロトタイプ宣言されているio_hlt関数の先頭にexternを入れることで、「この関数は外部ファイルで定義されているますよー」という目印になります。これで無事解決。

んで、本書のダウンロードリンクから落としてきたソースにはexternがついていません。なので、これをそのまま写してgccコンパイルしたとしても通らない可能性が高いです。ご注意を。 

 

 

あと、harib00jで作成したイメージをgdbでリモートデバッグしてみたのですが、ちょっと嫌な予感がしています。。

まぁ、これについては詰まった時にまた考えることにします。

 

ちゃんと動くまでかなり時間を要しましたが、これでちゃんと先に進めそうです。

ただ、ここで3回ほどソースコードを全部書き直したりしたので、コードの流れがある程度把握できたと思います。

うまく動かない場合には一からコードを書き直すのもありだと思います。

 

T-Pot観測記録:2018/5/11〜5/17 vnc、またきたかおまいらw

最近、再びraspberry piarduinoを触り始めてはまってしまったのでちょっと遅れてしまいましたが、先週の分の記録をつけていきたいと思います。

...といいたいところなのですが、tpotにアクセスしたところ以下のような画面ががが。。

f:id:motojiroxx:20180521215929p:plain

 「なんでじゃー」ということで、Status Breakdownの部分を見てみると、elasticsearchの部分で"Elasticsearch is still initializing the kibana index"となっており、これが問題と思われる。

とりあえずこれをググってみる。....よくわからんがcurlをつかってREST APIクラスタヘルスをチェックできるらしい。が、みてもよくわからん!

ということで、私が参考にしているTKさんのブログに同じ現象の解決方法がありましたので、この手法を使ったところ無事復活しました(TKさん本当にありがたや)。

参考にした記事は以下の通りです。

tk-secu.hateblo.jp

 

色々ありましたが、はじめていきます。

 

全体の結果

f:id:motojiroxx:20180522022255p:plain

 

f:id:motojiroxx:20180522022807p:plain

 

私のところのハニーポットには、ずっとvncさんが定期的にきているという状況ですね。しかも、相変わらずロシアからです。ただ、ここまで時間が固定的だと自動化している可能性もありそう?

 

また、前回の記事で書いた内容に関連することですが、警察庁からRedisを標的とした探索行為の増加がみられる報告がありました。

www.npa.go.jp

4月ぐらいから急激に増えて、そのまま継続しているという状況でしょうか。

私のところでは先週はきていましたが、今週はきていないようでした。

 

ちょっと他も見てはみたものの面白いネタがないですw

 

ちょっと気になったこと

あと、私のところのSuricata CVEでは、今までCVE-20170143しか見たことないのですが、TKさんのところだと色々きていて羨ましい。

みなさんSuricataのチューニングとかやっているのでしょうか?

 

やってみたい!

これまたTKさんの記事にあるものですが、DionaeaとVTの連携は非常にやりたい!毎回コマンドうって確認するの疲れました。

なので、近々やろうと思います。

T-Pot観測記録:2018/5/4〜5/10

ちょっと遅れてしまいましたが、今週分の観測記録をつけようと思います。

全体の結果

f:id:motojiroxx:20180513030800p:plain
f:id:motojiroxx:20180513030812p:plain

先週まではvnclowpotに対するものが多かったのですが、この週は非常に変動がありました。継続してvnclowpotのイベントは多いのですが、それ以外のCowrie, Honeytrapのイベントが非常に多くなっています。なので、今回はこの3つを中心にイベントをみていきます。

vnclowpotのイベント

vnclowpotに対するものは依然としてある程間隔的にイベントが発生しています。ただ、その時間が少し変わっています。
 ・先週:毎日15:00近辺でスパイクしていた
 ・今回の週:6:00, 9:00などでスパイクしていることが多くなっている
攻撃元のIPは前回と変わらずロシアが大半を占めています。また、発信元はロシアのモスクワが圧倒的です。

ロシア(モスクワ)との時差は6時間(モスクワより日本の方が進んでいる)となるため、今回の週の時間をモスクワの時間で考えると
・今回の週(モスクワ時間):0:00, 3:00
となります。夜中にかけて攻撃を実施しているということなのでしょうか?たしかに、夜中にかけて攻撃した方が、日本の企業の人にとっては気づきにくい+対処しにくいでしょうね。

Honeytrapのイベント

こちらもロシアがほとんどです。すごいですね、ロシア。。
送信先ポートは以下の通り
 dest port : 6379, 2222, 2323, 5902, 3307

5902はvncらしいですね。というのも、vncではポート番号が基本的に「5900+ディスプレイ番号」になるとのこと。つまり、5902では2番のディスプレイ番号を使用していることを表します。
また、6379はRedisと呼ばれるインメモリ型のNoSQLデータベース(キー・バリュー型)らしいです。この、「インメモリ型のDB」という話ですが、最近話題になったmemcachedも同じようなものらしいです。分類としてもRedisと同じような分類(インメモリ型NoSQL(キー・バリュー型))らしいです。ここら辺の詳しい話はちょっとわかりませんが。。

Cowrieのイベント

こちらについては、この週において非常に多く発生するようになりました。
 ・攻撃元:メキシコ、ベトナム、アメリカ、ブラジルの順で多い(中国やロシアは少なかったです。
 ・ポート:2222(ssh), 2223(telnet)がほとんど。443と80はかなり少ない。
また、メキシコからの攻撃ではtelnetに対するものが多く、ベトナムからはsshという感じです。アメリカに関しては半々という結果でした。

では、Cowrieで使用されたアカウントとパスワードをみていきます。

アカウント名
f:id:motojiroxx:20180513052703p:plain
パスワード
f:id:motojiroxx:20180513052719p:plain

アカウント名は、前回の記事でも出たものがちらほらと見えます。新しいものであれば、以下のものでしょうか。

 e8ehome, telecomadmin,

両方とも、Miraiに似たネットワークスキャンで使用されるものっぽいですね。以下参考記事。
blog.trendmicro.co.jp

パスワードについても、上記の記事に書いているものが多いですね。
強いていうなら以下のものがちょっと特徴的でしょうか。

 nE7jA%5m

ただ、これも調べてみたら中国製のルータのデフォルトパスワードっぽいですね。

このようにみて見ると、デフォルトパスワードがいかに危険か実感できますね。なので、デフォルトパスワードは使用しないように気をつけなければ。


今はこんな感じで記録をつけていますが、他のハニーポッターの方はどんな風に(使っているツールとか)ログ解析しているのかは非常に気になりますね。

30日OS自作入門-1〜2日目-

前回までで環境構築を終わらせましたので、早速作業に入りたいと思います。

今回やっていく内容

  • 1日目の部分
  • 2日目の部分

1日目部分

やったことは以下の通り

  • おもむろにバイナリエディタを起動し、hexを写経(鬼w
  • テキストエディタを起動し、DB命令を使ってhexを記述(18万4314行の¥x00を打ち込む必要がある。これも鬼w)
  • アセンブリ命令を少しづつ加えながら、hex自体で書く量を減らしていく

主にhexを写経していくような作業。結構しんどいけど、「0x〜」を打鍵するスピードが上がった気がします

命令

DB(data byte)

  • ファイルに1byteのデータを直接書く命令
  • DB命令のオペランドはカンマで区切ると複数指定可能?(本では8バイト分)
  • ファイルに書くデータの大きさによって以下のような命令がある
    • DW(data word) : 2byteのデータを書き込む
    • DD(data double-word) : 4byteのデータを書き込む
  • DB命令については、オペランドに文字列を指定可能

   
RESB(reserve byte)

  • 指定したバイト数だけ0x00を書き込む

んで、RESBに関連する部分で$マークを使い余り部分を0書きするという部分がありました。

RESB  0x1fe - $

ここでの$は「先頭行から何バイト目か」を教えてくれる変数として機能します。なので、例として$マークの部分が先頭から0x1f0行目だったとしましょう。そうすると、

0x1fe - 0x1f0 = 0x00e

となるため、RESB命令部分は

RESB  0x00e

となり、ファイル内のアドレス0x1f0から14byte分0を書き込みます。

。。。という感じなのですが、私の環境(linux+nasm)では$のままだとアセンブルできないので(エラーが出る)、linux+nasmでやっている方であればこの部分のコードを以下のように変更します。

RESB  0x1fe - ($-$$)

これについては、以下の記事を参考にしました。
tsurugidake.hatenablog.jp

この2つの命令を使ってnasファイルにコードを打ち込みアセンブルしてimgファイルを作成し、それをqemuで実行します。


アセンブル

nasm helloos1.nas -o helloos1.img

qemuでの実行

qemu-system-i386 helloos1.img

そうすると、こんな感じで画面が出てきます。
f:id:motojiroxx:20180509030602p:plain
実際に起動して、ちゃんとhello worldの文字が見えるとやはり感動しますね。

まぁ、1日目については主に今後の作業の流れを掴むような作業でしょうか。

2日目部分

  • アセンブリ命令をじゃんじゃん使って、1日目にhexで書いた部分をアセンブリコードで書き直す
  • CPU内に存在する記憶領域であるレジスタや、CPUからみると外部記憶装置であるメモリに関するお話

2日目から少しずつアセンブリ命令とレジスタなどを使って処理を書いていきます。

命令

ORG(origin)

  • 記述した機械語をメモリ上のどのアドレスに読み込ませるか指定する命令
  • ORG命令を使ってメモリ上のアドレスを指定した場合、$マークの意味も「読み込まれるメモリ上のアドレス」になる(nasmを使う場合は注意)
  • コードの先頭部分でまずは以下のように指定を行う
ORG  0x7c00


INT(interrupt)

  • ソフトウェア割り込み命令(詳細は後日)


HLT(halt)

  • CPUを待機状態にする命令
  • 外部で変化があればCPUがプログラムの続きを実行

(節電したいのであれば、使うべき?)

レジスタ関連

レジスタについては、今の所16bitのものが出てきますが、そのうちAX, BX, CX, DXについてはそれぞれ8bitレジスタとしても使える(AXはAHとALとして指定することも可能)というところは覚えておく必要あり(あとで出てくるので)。

バイトオーダー

MOVなどの転送命令でメモリ上にデータを格納する場合には、データの下位バイトからメモリに格納される点に注意。このような、メモリ上にデータを格納する方式のことをバイトオーダーと言い、こん秋のようにデータの下位バイト(桁のくらいが小さい方)からメモリ上に格納していく方式をリトルエンディアン。
(ビッグエンディアンはデータの上位バイトから格納していく方式。直感的にはこっちの方がわかりやすいかも)

エラー、警告関連

記述したアセンブリコード(nasファイル)をnasmでアセンブルするたびに以下の警告が吐かれていました。

warning : uninitialized space declared in .text section: zeroing

ひとまず警告は出ていたとしてもimgは起動できたので置いておいたのですが、気になったので解決法を調べてみたところ、同じような環境でやっている方がブログに書いておりました。有難や。
d.hatena.ne.jp
tsurugidake.hatenablog.jp

基本的にはRESB命令をTIMES命令に書き換えて、格納する値を定義してやるという感じですね

TIMES  <繰り返す回数>  DB(データの大きさ)  値

書き換えることによって、上記の警告は出なくなりました。