2008年9月23日火曜日

なんとなくWindowsでC言語プログラミング_(2)

「なんとなくPシリーズ」タグを作って続行。タイトルは 続 → 新 → … と続けたかったがやめておく。
一応コンセプトとしてはC言語を勉強してLinuxカーネルソースに迫ろうとしているのだが、果たして。
 
 

前回のなんとなくWindowsでC言語プログラミングで思い出したものがある。
こんにちはマイコン」、1982年!の本。次は折角なのでそれっぽいことをやってみよう。
変数のお勉強でたしかこんなコード、N-88 Basic だっけ?
10 INPUT "ナマエ ヲ ニュウリョク";A$
20 PRINT A$;"ノ アホー"
(30 goto 20)
※3行目gotoもしたかったけど、今回大分話がそれていくのでパス。
 
キーボードから文字列の入力を促し、変数に代入。それを標準出力から表示する単純なプログラムだ。
これの下りは面白かった記憶がある。
 

ついでなのでbatで再現


[sourcecode language='bash']
@echo off
set /P name="ナマエ ヲ ニュウリョク"
:10
echo %name% ノ アホー
goto 10
[/sourcecode]
簡単だが、やはりこれでは展開がない。
 
 

それではCで再現


main(void)とコメントを覚えた。
[sourcecode language='c']
#include

int main(void) {
char name[8]; /*メモリ8バイト確保!*/
printf("ナマエ ヲ ニュウリョク");
scanf("%s",name); /*入力をnameのポインタのとこに並べる*/
printf("%sノ アホー\n",name); /*nameの中身を出力*/
return 0;
}

[/sourcecode]

収穫は変数の宣言とscanfのおかげでポインタがなんとなく分かった事。

実行してみる。
>inputter.exe
ナマエ ヲ ニュウリョクarashi
arashiノ アホー

上々。
 
char宣言で8バイトメモリを確保、アドレスの情報をポインタとしてnameに格納しているというわけかいな。
 

実験タイム1


ほなここで実験してみよう、変数2つ宣言したら並ぶの?
[sourcecode language='c']
#include

int main(void) {
char name[8]; /*メモリ8バイト確保!*/
char name1[8]; /*同様にname1を宣言する*/
printf("ナマエ ヲ ニュウリョク");
scanf("%s",name);
printf("%sサンハ テンサイ\n",name);
printf("%x\n",&name); /*nameのアドレス*/
printf("%x\n",&name1); /*name1のアドレス*/
return 0;
}

[/sourcecode]


>inputter.exe
ナマエ ヲ ニュウリョクsatoru
satoruサンハ テンサイ
12ff64
12ff6c


えーと、8バイトだからー。 12ff64 から 12ff6b までで8バイト、次は 12ff6c から、、合ってる。並んでるね。
 

実験タイム2


入出力で実験、入力してない変数を出力するとか。
[sourcecode language='c']
#include

int main(void) {
char name[8];
char name1[8];
printf("ナマエ ヲ ニュウリョク");
scanf("%s",name);
printf("%sサンハ テンサイ\n",name);
printf("%sノ アホー\n",name1); /*入力してないname1を出力する*/
return 0;
}
[/sourcecode]

実行する、まずは普通の入力
>inputter.exe
ナマエ ヲ ニュウリョクippata
ippataサンハ テンサイ
3n@ノ アホー

3n@ てなんじゃらほい。確保したメモリに元からあった情報と言うことらしいけど、まあ0で埋めるなりの初期化すれば問題ないことが分かった。
 

でも次の入力。20文字入れてみる。
>inputter.exe
ナマエ ヲ ニュウリョク123456789a123456789b
123456789a123456789bサンハ テンサイ
9a123456789bノ アホー

ああああぁ、は、はみ出したーー
scanf()に脆弱性が、っていうのはテクニカルエンジニアで習ったけどこういうことか、ものすごく納得。
とはいえ、狙ったコードの実行は至難の技にも見えるんだけど、どうなんだろう。なんか手法が確立されてるんだろうか。
 
 

そして脱線へ


これはどうやって動いているんだろうと思ったのでちょっと追う。
WindowsのCコンパイラは "/Fa" というオプションをつけたらアセンブリリストのファイルを作ってくれる。
で、作ってみた。ちょっと省略してこんな感じ。
_main PROC
; File c:\cprog\inputter.c
; Line 3
push ebp
mov ebp, esp
sub esp, 20 ; 00000014H
mov eax, DWORD PTR ___security_cookie
xor eax, ebp
mov DWORD PTR __$ArrayPad$[ebp], eax
; Line 6
push OFFSET $SG2471
call _printf
add esp, 4
; Line 7
lea eax, DWORD PTR _name$[ebp]
push eax
push OFFSET $SG2472
call _scanf
add esp, 8
; Line 8
lea ecx, DWORD PTR _name$[ebp]
push ecx
push OFFSET $SG2473
call _printf
add esp, 8
; Line 9
lea edx, DWORD PTR _name1$[ebp]
push edx
push OFFSET $SG2474
call _printf
add esp, 8
; Line 10
xor eax, eax
; Line 11
mov ecx, DWORD PTR __$ArrayPad$[ebp]
xor ecx, ebp
call @__security_check_cookie@4
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END

 

うーん。。まさにこれは全部書いてあるんだろうけど、次回だな。
 
 

しかし カーネル への道のはずなんだが。。。やっぱり遠回りなのでは。
そうだコンテキストスイッチというのはこれを切り替えているんだろうなと言うことでまとめよう。