2013-01-09 178 views
20

我目前正在研究如何實現一個沙箱(類似於Google's NaCl project),我可以在其中運行不受信任的x86代碼(受限指令集),使其無法損害其餘我的過程。在Windows 64位上使用自定義堆棧實現沙盒

與NaCl不同,不可信代碼不會在單獨的進程中運行,而會與主機應用程序執行相同的進程。因此,一個關鍵的步驟就是讓Windows的結構化異常處理正確,以便在Windows殺死我的主機應用程序之前捕獲錯誤(如無效內存訪問或div除以0)並正常終止沙箱。 (NaCl不會面臨這些問題,沙箱是一個獨立的進程,只是在出現錯誤時被殺死)。

此外,沙箱代碼不應該使用主機應用程序堆棧,而應該在某些單獨的「堆棧」這是我自己分配的。

確切地說,這種組合(存在自定義分配堆棧時的異常處理)正在扭曲我的想法。我已經檢查了GoFactor的語言實現,這些實現類似的東西,並且這個幫助有一些東西在運行。

但仍然有一些開放性問題和不確定性。所以我想我會使用堆棧溢出的奇妙知識,得到一些意見:-)

下面是一個工作代碼段砍倒在覈心問題:

code.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); 
} 

code.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分配的堆棧。但它沒有幫助。

另一個問題是,你是否知道我可以遇到的其他陷阱。例如,我注意到如果RSP不均勻,Windows不喜歡它(我想是因爲通過在16字節對齊的堆棧指針上執行2/4/8字節的PUSH和POP,永遠不會得到不均勻的RSP)。

感謝, 喬納斯

+0

你玩過[Sandboxie](http://www.sandboxie.com)嗎?如果您只需要一個便捷的沙盒實用程序,可能會有所幫助。 –

+1

不,不幸的是這並沒有幫助。我沒有談論在我的操作系統中運行不受信任的應用程序,而是在我的應用程序中運行不受信任的x86代碼。例如。視頻播放器中新視頻編解碼器的插件。我不想讓一個錯誤的插件崩潰我的應用程序。 – m3tikn0b

+0

[依賴於異常可能不夠好 - IsBadXxxPtr應該真的叫做CrashProgramRandomly](http://blogs.msdn.com/b/oldnewthing/archive/2006/09/27/773741.aspx)。奇怪的是,您可以在64位模式下推送和彈出16位值,而32位則不能。如果你不想讓一個錯誤的插件崩潰你的進程,你可能想要在一個單獨的/子進程中運行該插件。 –

回答

0

要執行的代碼,並檢查它的有效性。你可以用你自己的沙箱做。請參閱x86處理器的虛擬框實現。它可以幫助。但所有的虛擬機都會付出代價:仿真處理器運行底層代碼的速度比您的應用慢5-10倍。

如果您只需要在覈心cpu上進行錯誤檢查並運行應用程序,那麼您需要在線程中運行它。當線程掛起應用程序未觸及。你可以注入一些代碼給線程來查找它的執行。 但這種情況不太安全,因爲惡意代碼可能會破壞您的檢查程序並將其用於後門程序/生根您的系統。

所以,我的答案:爲了安全 - 使用您自己的虛擬機,以便在另一個線程中執行。

最好的問候,祝你好運 弗拉基米爾

1

運行在同一個進程不可信,第三方代碼是自找麻煩。該代碼可以通過各種方式殺死你的進程。例如。它可以在故障時調用exit(),請求大量內存或寫入由線程分配的內存。

更安全但並不那麼困難的解決方案是在不同的過程中運行此代碼,類似於Chrome所做的。每個Chrome擴展程序都在不同的進程中運行,並且數據在進程之間傳遞。

應用程序可以啓動一個獨立的過程,並用它雖然管道,視窗消息,共享存儲器(存儲器映射文件)用於共享大型數據通信等

一個插件(通常)實現了一個接口,以便你需要編寫一個代理對象來抽象出插件駐留在另一個進程中的事實,並隱藏了多進程應用程序帶來的IPC複雜性。 gSoap就是這樣一個框架,其他的存在。