0

事前知識

スタックとデータ構造について少し読んで理解しましたが、この特定の質問に対する答えを見つけることができませんでした。私は、何か価値のあるプログラマーなら誰でも、プログラムのデフォルトを超えてセキュリティ例外処理を実装することを知っています。

状況

任意のケースSの例外処理を使用して、比較的堅牢なスタックを確立するプログラムをCで作成する方法を理解したいと思います。私の目標は、この非常に具体的な情報から、なぜ(私が理解していることから)常に可能であるかを識別することです。プログラムでSEHを悪用し、任意のコードを実行します。

問題は、バッファオーバーフローの概念を理解していないということではありません-スタック(カナリアなど)に実装されているセキュリティがこれらの問題(ヒープなど)に十分に対処できない理由(非常に具体的には、CPUアーキテクチャ関連の理由)がわかりませんオーバーフローを止めることはできませんか?)。

参考情報

「構造内のデータのレイアウトを変更する正しい方法はありません。構造はモジュール間で同じであることが期待されます。特に共有ライブラリの場合はそうです。バッファ後の構造内のデータはカナリアで保護できません。したがって、プログラマーは変数の編成方法と構造体の使用方法には十分注意してください。CおよびC++では、バッファーを含む構造体をmalloc()するか、newで取得する必要があります。」-http://en.wikipedia.org/wiki/Buffer_overflow_protection#Implementations経由

また:

http://blogs.msdn.com/b/michael_howard/archive/2006/08/16/702707.aspx

この資料を理解するための優れたリソースを知っている人、またはコードスニペットを提供できる人がいれば、非常にありがたいです。

4

3 に答える 3

1

スタックのセキュリティは、スタック オーバーフローに対処するのに十分なほど安全にすることができます。

http://msdn.microsoft.com/en-us/library/9a89h429(VS.80).aspx

登録された例外ハンドラ ( safe -SEH)が必要な理由と、通常の例外ハンドラがそれをカットしない理由についての質問は、非常に大きなスタック オーバーフローが発生する場合のためです。

始まる関数があるとしましょう

try {
   char buffer[N];
   strcpy(&buffer, &attacker);
} __except(...) { }

これは、アセンブリ コードに変換される可能性があります。

push ebp
mov ebp, esp
; GS if you want to here

; install the exception handler:
push lbl_Exceptionhandler
push dword ptr [fs:0]
mov dword ptr[fs:0], esp

; setup the locals inside the stack
sub esp, LOCALS
; GS if you want to here

; call strcpy
lea ecx [ebp + offset_to_buffer];
push ecx
lea edx, [ebp + offset_to_attacker]
push edx
call _strcpy
add esp, 8

; uninstall the locals
mov esp, ebp

; uninstall the exception handler
pop dword ptr [fs:0]

; return
pop ebp

; optionally check GS cookies that we might have also inserted at any point in this function.
call _checksecuritycookie
ret 

つまり、スタックは次のようになります。

RET PTR 
/GS1
SAVED EBP
/GS2
SAVED FS:0
/GS3
LOCAL char buffer[N]

GS1、GS2、および GS3 は、スタック カナリアがスタック Cookie の書き込みを選択する場所です。Cookie は関数の最後にのみチェックされることに注意してください (これはコンピューターのセキュリティにとって重要です。チェックを導入するときは、チェックがオーバーフローを検出するかどうかだけでなく、オーバーフローが発生する前に検出するかどうかも考慮する必要があります。スタック Cookie の場合、通常、スタック Cookie は戻りアドレスを保護するためだけに存在し、ローカル変数を保護するためではないため、Cookie は関数の終了時にのみ検査されます)。

通常の例外ハンドラーの問題は、攻撃者のバッファーが非常に大きい場合に発生することです。それが非常に巨大で、スタック全体を破壊し、スレッドのガード ページに書き込み、障害をトリガーするとしましょう。

さて、カーネルは ntdll にコールバックし、プロセスを整理するように指示します。ntdll の最初の呼び出しポートは、登録された例外ハンドラーがあるかどうかを確認することです。では、呼び出す例外ハンドラをどのように見つけるのでしょうか。スタック上の例外ハンドラを指す fs:0 を調べて、例外ハンドラ ポインタを呼び出します。攻撃者が破壊したばかりのスタック上の例外ハンドラーを除いて。

おっとっと。これで、攻撃者が EIP を制御できるようになり、あなたは負けます。

Safe-SEH は、呼び出す可能性のある例外ハンドラーのリストが、実際にはコンパイル時に完全に決定される有限リストであることを指摘することで、この問題を解決します。このリストを PE ファイル自体に書き込むことにより、ntdll は、ジャンプ先の例外ハンドラーが実際には実際の例外ハンドラーであり、悪意のある攻撃者が EIP を乗っ取ろうとする計画の原因ではないことを再確認する機会があります。

Safe-SEH にはコストがかかります (オプトインする理由) が、そのコストは、例外ハンドラーが引き継ぐ前に ntdll がより多くの作業を行うようになるため、例外をキャッチするコストが高くなることです。

それにもかかわらず、私のアドバイスは、SafeSEH を常にオンにすることです。アプリのパフォーマンスは例外をスローする速度に大きく依存しているため、顧客のクレジット カードの詳細を簡単に紛失してしまうということは、開発者の精神がひどく壊れていることを示唆しており、開発者はすぐに大砲に入れられて太陽に向かって発砲されるべきです。社会に損害を与える恐ろしいコード。

于 2013-02-07T05:58:52.467 に答える
0

Cでスタックを実装していないが、(任意の言語で)Cコンパイラを実装している場合は、..

プログラミングエラーを検出して安全なコードを生成するCコンパイラを作成することは可能です。たとえば、コンパイラは読み取りまたは書き込みごとにチェックを挿入して、読み取りまたは書き込みが1つのストレージ領域内に含まれていることを確認できます(たとえば、charなどの配列の最後に4バイトを書き込もうとしていない場合)。これらのチェックのいずれかが失敗した場合にシグナルが発生する場所(「SIGSEGV」など)。

Cの性質上、これらのチェックにはヒープなどのスキャンが含まれ、スタック上のもののサイズを追跡するために、より多くのコードを挿入する必要があります。

これが実装されていない主な理由は、パフォーマンスに大きな問題が発生するため、そもそもCを使用する目的が損なわれるためです。

ただし、仮想マシン内でアプリケーションを実行することによってこのタイプのチェックを行うデバッグツール(valgrindなど)があります(仮想マシンはストレージ領域のサイズを追跡し、実行前に読み取り/書き込みをチェックできます)。

于 2013-02-07T04:52:55.503 に答える
0

C でスタックを実装する通常の方法は、連結リストを使用することです。例えば:

struct stack_entry {
    struct stack_entry *previous;
    /* other fields for the actual data */
}

struct stack_entry *stack_top = NULL;

void push(struct stack_entry *entry) {
    entry->previous = stack_top;
    stack_top = entry;
}

struct stack_entry *pop(void) {
    struct stack_entry *entry;

    entry = stack_top;
    if(entry != NULL) stack_top = entry->previous;
    return entry;
}

これは、他の通常のコードと同じくらい堅牢で悪用が困難です。

于 2013-02-07T04:21:11.040 に答える