19

私は現在、信頼されていない x86 コード (制限された命令セット) を実行できるサンドボックス ( Google の NaCl プロジェクトに似ています) を実装する方法を調査しています。

NaCl とは異なり、信頼できないコードは別のプロセスではなく、ホスト アプリケーションと同じプロセスで実行されます。したがって、重要なステップの 1 つは、エラー (無効なメモリ アクセスや 0 による div など) をキャッチし、Windows がホスト アプリケーションを強制終了する前にサンドボックスを適切に終了するために、Windows の構造化例外処理を正しく行うことです。(NaCl はこれらの問題に直面していません。サンドボックスは別のプロセスであり、エラーが発生した場合は単に強制終了されます。)

さらに、サンドボックス化されたコードは、ホスト アプリケーション スタックを使用するのではなく、自分で割り当てた別の「スタック」で実行する必要があります。

まさにこの組み合わせ (カスタム割り当てスタックが存在する場合の例外処理) は、私の心をねじ曲げています。同様のことを行うGoFactorの言語実装を確認しましたが、この助けを借りて何かが実行されました。

しかし、未解決の問題や不確実性がまだいくつかあります。そこで、Stack Overflow の素晴らしい知識を使って意見を集めようと思いました :-)

以下は、コアの問題に切り詰められた実用的なコード スニペットです。

コード.cpp

#include <Windows.h>
extern "C" void Sandbox();

// just a low level helper to print "msg"
extern "C" void Write(const char* msg)
{
    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
              msg, (DWORD)strlen(msg), NULL, NULL);
}

// should be called first on error and continue exception handling
LONG __stdcall GlobalExceptionHandler(_EXCEPTION_POINTERS*)
{
    Write("GEH ");
    return EXCEPTION_CONTINUE_SEARCH;
}

// should be called afterwards on error and terminate the process
// of course this is just a stub to simplify the issue
// in real world it would just terminate the sandbox
extern "C" EXCEPTION_DISPOSITION __stdcall FrameExceptionHandler(
        PEXCEPTION_RECORD, ULONG64, PCONTEXT, PVOID)
{
    Write("FEH ");
    ExitProcess(42);
}

void main()
{
    AddVectoredExceptionHandler(1, GlobalExceptionHandler);
    Sandbox();
    // never reach this...
    ExitProcess(23);
}

コード.asm

EXTERN FrameExceptionHandler:PROC
EXTERN malloc:PROC

.code

Handler:
    jmp FrameExceptionHandler

Sandbox PROC FRAME : Handler
    ; function prologue compliant with Windows x86_64 calling conventions
    ; saves rsp to the "frame-pointer" r15
    push r15
    .PUSHREG r15
    sub rsp, 20h
    .ALLOCSTACK(20h)
    mov r15, rsp
    .SETFRAME r15, 0h
    .ENDPROLOG

    ; set rsp to the top of a "heap allocated stack" of size 0x10000 bytes
    mov rcx, 10000h
    call malloc
    lea rsp, [rax+10000h]

    ; got this from implementation of the Go language runtime:
    ; while unwinding the stack, Windows sanity checks the values of
    ; RSP to be within stack-bounds. Of course RSP is set to our
    ; "heap allocated stack" and not within the bounds of what Windows
    ; thinks should be the stack.
    ; Fix this by adjusting StackBase and StackEnd in the TIB (thread
    ; information block), so that basically the stack is unbounded:
    ; StackBase = 0xffffffffffffffff, StackEnd = 0x0000000000000000
    mov rcx, 0FFFFFFFFFFFFFFFFh
    mov gs:[008h], rcx
    mov rcx, 0
    mov gs:[010h], rcx


    ; trigger an access error by reading invalid memory
    mov rax, 0DEADBEEFh
    mov rax, [rax]

    ; function epilogue - will never get here
    mov rax, 0
    add rsp, 28h
    ret
Sandbox ENDP

end

これを実行すると、「GEH FEH」が出力され、コード 42 で正常に終了します。

StackBase & StackEndこのセットの「ハック」についてもっと洞察を持っている人はいますか? スタック制限を次のように狭めようとしました:

    mov gs:[008h], rsp
    mov gs:[010h], rax    ; rax is the address returned by malloc

しかし、うまくいきません。「GEH」を出力し、未処理の例外が原因でクラッシュします。 FrameExceptionHandler()実行されることはありません。

また、「ヒープ割り当てスタック」と Windows によって割り当てられたスタックを含む、より緩和された境界も試しました。しかし、それは役に立ちません。

もう 1 つの質問は、私が遭遇できる他のトラップを知っているかどうかです。たとえば、RSP が不均一な場合、Windows はそれを好まないことに気付きました (16 バイトでアラインされたスタック ポインターで 2/4/8 バイトの PUSH と POP を実行しても、RSP が不均一になることは決してないためだと思います)。

ありがとう、ジョナス

4

2 に答える 2

1

同じプロセスで信頼できないサードパーティのコードを実行すると、問題が発生します。そのコードは、さまざまな方法でプロセスを強制終了する可能性があります。たとえばexit()、失敗時に呼び出したり、大量のメモリを要求したり、スレッドによって割り当てられたメモリに書き込んだりできます。

Chrome が行っているのと同様に、別のプロセスでこのコードを実行することで、より安全でありながらそれほど難しい解決策はありません。すべての Chrome 拡張機能は異なるプロセスで実行され、データはプロセス間で渡されます。

アプリケーションは別のプロセスを起動し、大きなデータを共有するために、パイプ、Windows メッセージ、共有メモリ (メモリ マップ ファイル) などを介して通信できます。

プラグイン (通常) はインターフェイスを実装するため、プラグインが別のプロセスに存在するという事実を抽象化し、マルチプロセス アプリに伴う IPC の複雑さを隠すために、プロキシ オブジェクトを作成する必要があります。gSoap はそのようなフレームワークですが、他にも存在します。

于 2014-06-28T21:32:52.623 に答える
0

コードを実行して有効性を確認したい。独自のサンドボックスでそれを行うことができます。x86 プロセッサの仮想ボックス実装を参照してください。それは助けることができます。ただし、すべての仮想マシンには価格が伴います。プロセッサをエミュレートすると、アプリよりも 5 倍から 10 倍遅く、基盤となるコードが実行されます。

エラーチェックのみが必要でコアCPUでアプリを実行する場合は、スレッドで実行する必要があります。スレッドがハングアップすると、アプリケーションは変更されません。また、スレッドにコードを挿入して、その実行を探すことができます。ただし、悪意のあるコードがチェック ルーチンを破り、システムのバックドア/ルート化に悪用する可能性があるため、このケースはあまり安全ではありません。

だから、私の答え:安全のために-自分の仮想マシンを使用し、速度のために-別のスレッドで実行してください。

よろしく、そして幸運を祈ります ウラジミール

于 2014-06-03T04:02:10.670 に答える