スポンサーサイト

一定期間更新がないため広告を表示しています

スポンサードリンク | - | | - | - |

コンパイラによる最適化

 コンパイラによる最適化で思ったようにクラック出来なかった原因を探るため、

次のようなコードを書いてみた。

#include <stdio.h>
#include <string.h>

int main()
{
    int i = 256;
    int j = 1024;
   
    return 0;
}

VC++でコンパイルし、出力されたアセンブリコードを見てみる。

最初は最適化オプションオフ。



教科書どおりだ。

次に最適化オン。



i=256などとしても以後使用されないので領域確保しても無駄である。

なので変数の代入はおろか、領域確保さえ行われない。

昨日書いたコードが最適化によってうまく動かなかったのはこういう事情があったようだ。

コンパイル時点で変数の要不要が判断できないようなプログラムを書けばいいかな、と思ったがそれだけではだめなようだ。

ローカル変数の格納場所が教科書どおりではないかも。

ゆっくり読み解かないと。
ジャジャガッチ | アセンブリ言語 | 21:43 | comments(0) | trackbacks(0) |

VC++でバッファオーバーフロー実験

VC++でコンパイルしたプログラムに対してバッファオーバーフローを利用したクラックができるか試してみた。

出来ない。

最適化オプションを切ってみた。

出来た。

最適化はそういうところにも影響するのか。

ついでに他のあやしげなオプションも全部オフにしてみた。

VC++で出力したアセンブリ言語はわけわからんと思ってたが、これなら読めそう。

暇なときに解読しよう。
ジャジャガッチ | アセンブリ言語 | 21:59 | comments(0) | trackbacks(0) |

アセンブリ言語:リターンアドレスの確認

 久々のアセンブリ言語。

C言語をディスアセンブルして勉強。

main関数から他の関数を呼び出すと、復帰するためにリターンアドレスを記憶しておく必要がある。

通常、これはベースポインタ(ebp)の指すアドレスの次のアドレスに入っている。

これを確認した。

fサブルーチンをcallして制御が移ったところで止めている。

この状態でebpの指すアドレスの次を確認すると・・・



ちゃんと戻り先が記憶してある。
ジャジャガッチ | アセンブリ言語 | 20:19 | comments(0) | trackbacks(0) |

自作プログラムをクラックしてみた

 「HACKING:美しき策謀」に載っていた方法を真似て自分で作ったプログラムをクラックしてみた。

やってることはこの本に書いてあることほぼそのままだし(プログラムはオリジナルだけど)、
具体的には書かない。

簡単に言うと、バッファをあふれさせて本来書き換えられてはならない領域を侵す
ことによってクラックする。

侵された領域が条件分岐に使う変数なら分岐が狂うし、サブルーチンからの戻りアドレスが
記述された領域ならばおかしなところに制御が飛ぶ。

両方やってみた。

次はシェルコードというものを使う方法だ。

最初シェルスクリプトと勘違いしたけど違うものだ。

機械語で書くっぽい。

メモリを悪意ある機械語命令で侵すということかな?
ジャジャガッチ | アセンブリ言語 | 21:59 | comments(0) | trackbacks(0) |

バッファオーバーフローを利用したクラック

 「HACKING:美しき策謀」を150ページくらいまで読んだ。
最初の方はC言語入門みたいなことが書いてあった。
アセンブリコードと対応づけて書いてあって面白かった。
ただ、これでC言語に入門するのは無理だと思う。
あらかじめそれなりの知識が必要だろう。

で、ようやくハックの例に入った。バッファオーバーフローを利用したハックで、
興味深かった。で、ちょっと怖かった。具体的な内容はここには書かない。

理解の助けに簡単なCコードを読み解いてみた。特にスタックポインタらへん。

続きを読む >>
ジャジャガッチ | アセンブリ言語 | 21:56 | comments(0) | trackbacks(0) |

main関数内でローカル変数を使う

今回はmain関数内で整数型変数を定義してアセンブリコードを見てみた。
 (gdb) list
1       #include <stdio.h>
2
3       int main(void)
4       {
5       int i = 256;
6       return 0;
7       }
(gdb) disassemble main
Dump of assembler code for function main:
0x08048344 <main+0>:    push   ebp
0x08048345 <main+1>:    mov    ebp,esp
0x08048347 <main+3>:    sub    esp,0x8
0x0804834a <main+6>:    and    esp,0xfffffff0
0x0804834d <main+9>:    mov    eax,0x0
0x08048352 <main+14>:   sub    esp,eax
0x08048354 <main+16>:   mov    DWORD PTR [ebp-4],0x100
0x0804835b <main+23>:   mov    eax,0x0
0x08048360 <main+28>:   leave  
0x08048361 <main+29>:   ret    
End of assembler dump.

予想通りになっている。

0x08048347 <main+3>:    sub    esp,0x8

でローカル変数用の領域を確保して、そこに

0x08048354 <main+16>:   mov    DWORD PTR [ebp-4],0x100

で変数を定義している。じゃあ、変数を増やしてみよう。

(gdb) list
1       #include <stdio.h>
2
3       int main(void)
4       {
5       int i = 256,j = 128,k = 64,l = 32,m = 16,n = 8,o = 4,p = 2,q = 1;
6       return 0;
7       }
(gdb) disassemble main
Dump of assembler code for function main:
0x08048344 <main+0>:    push   ebp
0x08048345 <main+1>:    mov    ebp,esp
0x08048347 <main+3>:    sub    esp,0x28
0x0804834a <main+6>:    and    esp,0xfffffff0
0x0804834d <main+9>:    mov    eax,0x0
0x08048352 <main+14>:   sub    esp,eax
0x08048354 <main+16>:   mov    DWORD PTR [ebp-4],0x100
0x0804835b <main+23>:   mov    DWORD PTR [ebp-8],0x80
0x08048362 <main+30>:   mov    DWORD PTR [ebp-12],0x40
0x08048369 <main+37>:   mov    DWORD PTR [ebp-16],0x20
0x08048370 <main+44>:   mov    DWORD PTR [ebp-20],0x10
0x08048377 <main+51>:   mov    DWORD PTR [ebp-24],0x8
0x0804837e <main+58>:   mov    DWORD PTR [ebp-28],0x4
0x08048385 <main+65>:   mov    DWORD PTR [ebp-32],0x2
0x0804838c <main+72>:   mov    DWORD PTR [ebp-36],0x1
0x08048393 <main+79>:   mov    eax,0x0
0x08048398 <main+84>:   leave  
0x08048399 <main+85>:   ret    
End of assembler dump.

あたりまえだけど、

0x08048347 <main+3>:    sub    esp,0x28

さっきと比べると確保する領域が大きくなっている。
ジャジャガッチ | アセンブリ言語 | 07:34 | comments(0) | trackbacks(0) |

GDB

 「HACKING:美しき策謀」ではGDBというデバッガを使っている。

使ってみるとこれが便利。手始めにHello worldプログラムを解析した。

まず、

gcc -g test.c

でデバッグ情報を付加してコンパイルする。gdbを起動する。

gdb -q ./a.out

list

と入力すればソースコードが表示される。

(gdb) list
1	#include <stdio.h>
2	
3	int main(void)
4	{
5	printf("Hello world¥n");
6	return 0;
7	}

逆アセンブルする。

(gdb) dissasemble main Dump of assembler code for function main: 0x08048374 <main+0>: push ebp 0x08048375 <main+1>: mov ebp,esp 0x08048377 <main+3>: sub esp,0x8 0x0804837a <main+6>: and esp,0xfffffff0 0x0804837d <main+9>: mov eax,0x0 0x08048382 <main+14>: sub esp,eax 0x08048384 <main+16>: mov DWORD PTR [esp],0x8048474 0x0804838b <main+23>: call 0x80482a0 <printf@plt> 0x08048390 <main+28>: mov eax,0x0 0x08048395 <main+33>: leave 0x08048396 <main+34>: ret End of assembler dump.
main+14あたりまでがたぶん関数のプロローグと呼ばれる部分

お決まりの、ベースポインタを退避させて、スタックポインタの値を入れて、スタックポインタをデクリメントして・・・、という部分。

そのあと、main+16でespレジスタの値を0x8048474にしている。このメモリアドレスの

内容を表示すると、

(gdb) x/20b 0x8048474
0x8048474:      0x48    0x65    0x6c    0x6c    0x6f    0x20    0x77    0x6f
0x804847c:      0x72    0x6c    0x64    0x0a    0x00    Cannot access memory at address 0x8048481

x/20bは指定アドレス以降を1バイト単位で20個表示するコマンド。x/iでアセンブリ表示。x $eipとかでレジスタに格納されたアドレスの内容表示。

asciiコードと照らし合わせるとこれがHello world。

レジスタを使って引数を渡している。エピローグ(ベースポインタ、スタックポインタの復元)がないぞ、

と思ったらleave命令がエピローグの実行をする命令らしい。

ここからgdbのコマンド覚書。

・ブレイクポイントの設定
b 1
b main

・レジスタに格納された値をみる
i r  eip

・ステップ実行
step
next
stepi
nexti
・run
run or r
・ブレイクポイント削除
delete

ジャジャガッチ | アセンブリ言語 | 07:12 | comments(0) | trackbacks(0) |

関数への引数渡し

 VC++で確認した。

#include <stdio.h>

int main(void)
{
    printf("Hello world");
    return 0;
}

をコンパイルしてアセンブリコードを出力すると、

    push    OFFSET ??_C@_0M@KIBDPGDE@Hello?5world?$AA@
    call    DWORD PTR __imp__printf

こういう行がある。引数をpushで渡しているのが確認できる。

上のほうには

??_C@_0M@KIBDPGDE@Hello?5world?$AA@ DB 'Hello world', 00H ; `string'

とあって文字列がメモリに格納されているのがわかる。
ジャジャガッチ | アセンブリ言語 | 17:49 | comments(0) | trackbacks(0) |

ベースポインタ、スタックポインタ

 久々のアセンブリ言語いじり。

関数への引数の引渡しについて復習していてベースポインタ、スタックポインタについて
勘違いしていたことに気がついた。

まず、ベースポインタはスタック領域の底を表していて、基本的にconst。

スタックポインタは次にpushされる領域をあらわすアドレスを指していて、
pushされるとデクリメントされる(スタックはアドレスの小さい側へ積まれていく)。

たぶん最初はスタックポインタとベースポインタの指す値は一致しているんだと思う。

ここらへんのことがちゃんとわかっていなかった。

関数へ引数を渡すにはまず呼び出し元で渡したい値をpushする。

次に関数側でbpを保持しておくためpushする。

bpをspの指すアドレスに一致させる。これでbpからの相対アドレスで引数を使えるようになった。

最後にspをローカル変数用に必要な分だけデクリメントする。

これで絶対安全な領域が出来上がったのでspとbpの間のメモリをローカル変数で利用する。
ジャジャガッチ | アセンブリ言語 | 17:05 | comments(0) | trackbacks(0) |

ローカル変数とスタックオーバーフロー

 アセンブリ言語においてローカル変数を実現する方法を勉強した。

スタック領域を一時的にローカル変数用の領域として確保する方法だ。

ローカル変数用の領域はスタック領域に作られるので大きすぎる配列などを用意すると

領域をオーバーしてしまう。

また、再帰が長く続きすぎるとその度にローカル変数用の領域が大きくなっていくので

結局領域をオーバーする。

これをスタックオーバーフローという。

自作プログラムで時々みたエラーが具体的にどう発生しているのかわかって有意義だった。
ジャジャガッチ | アセンブリ言語 | 15:59 | comments(0) | trackbacks(0) |
1/2PAGES | >> |

06
--
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
--
>>
<<
--
PR
RECOMMEND
RECENT COMMENT
MOBILE
qrcode
OTHERS
Since 2013/09/17
LATEST ENTRY
CATEGORY
ARCHIVE
LINKS
PROFILE
SEARCH