我目前正在研究如何實現一個沙箱(類似於Google's NaCl project),我可以在其中運行不受信任的x86代碼(受限指令集),使其無法損害其餘我的過程。在Windows 64位上使用自定義堆棧實現沙盒
與NaCl不同,不可信代碼不會在單獨的進程中運行,而會與主機應用程序執行相同的進程。因此,一個關鍵的步驟就是讓Windows的結構化異常處理正確,以便在Windows殺死我的主機應用程序之前捕獲錯誤(如無效內存訪問或div除以0)並正常終止沙箱。 (NaCl不會面臨這些問題,沙箱是一個獨立的進程,只是在出現錯誤時被殺死)。
此外,沙箱代碼不應該使用主機應用程序堆棧,而應該在某些單獨的「堆棧」這是我自己分配的。
確切地說,這種組合(存在自定義分配堆棧時的異常處理)正在扭曲我的想法。我已經檢查了Go和Factor的語言實現,這些實現類似的東西,並且這個幫助有一些東西在運行。
但仍然有一些開放性問題和不確定性。所以我想我會使用堆棧溢出的奇妙知識,得到一些意見:-)
下面是一個工作代碼段砍倒在覈心問題:
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)。
感謝, 喬納斯
你玩過[Sandboxie](http://www.sandboxie.com)嗎?如果您只需要一個便捷的沙盒實用程序,可能會有所幫助。 –
不,不幸的是這並沒有幫助。我沒有談論在我的操作系統中運行不受信任的應用程序,而是在我的應用程序中運行不受信任的x86代碼。例如。視頻播放器中新視頻編解碼器的插件。我不想讓一個錯誤的插件崩潰我的應用程序。 – m3tikn0b
[依賴於異常可能不夠好 - IsBadXxxPtr應該真的叫做CrashProgramRandomly](http://blogs.msdn.com/b/oldnewthing/archive/2006/09/27/773741.aspx)。奇怪的是,您可以在64位模式下推送和彈出16位值,而32位則不能。如果你不想讓一個錯誤的插件崩潰你的進程,你可能想要在一個單獨的/子進程中運行該插件。 –