Attualmente sto studiando come implementare una sandbox (simile a Google's NaCl project) in cui posso eseguire codice x86 non protetto (set di istruzioni con restrizioni) in modo tale da non danneggiare il resto del mio processo.Implementazione di una sandbox con stack personalizzato su Windows 64 bit
A differenza di NaCl, il codice non attendibile non verrà eseguito in un processo separato ma nello stesso processo dell'applicazione host. Quindi, un passaggio cruciale è ottenere la gestione strutturata delle eccezioni di Windows in modo corretto per catturare gli errori (come l'accesso non valido alla memoria o il div da 0) e terminare con grazia la sandbox prima che Windows uccida la mia applicazione host. (NaCl non affronta questi problemi.La sandbox è un processo separato e viene semplicemente uccisa in caso di errore.)
Inoltre, il codice sandboxed non deve utilizzare lo stack di applicazioni host ma funzionare su uno "stack" separato che è assegnato da me stesso.
Esattamente questa combinazione (gestione delle eccezioni in presenza di uno stack assegnato personalizzato) mi tormenta. Ho controllato le implementazioni linguistiche di Go e Factor che fanno cose simili e con questo aiuto ho ottenuto qualcosa in esecuzione.
Ma ci sono ancora alcune domande aperte e incertezze. Così ho pensato io uso la fantastica conoscenza della Stack Overflow per ottenere alcune opinioni :-)
Il seguente è un codice di lavoro snippet ridotto a questioni fondamentali:
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
l'esecuzione di questo stamperà "GEH FEH "e quindi uscire con grazia con il codice 42.
Qualcuno ha più informazioni in questo set StackBase & StackEnd
" hack "? Ho cercato di restringere i limiti dello stack a qualcosa di simile:
mov gs:[008h], rsp
mov gs:[010h], rax ; rax is the address returned by malloc
Ma non funziona. Stampa "GEH" e poi si blocca a causa di eccezioni non gestite. FrameExceptionHandler()
non verrà mai eseguito.
Ho anche provato dei limiti più rilassati che includono lo "stack allocato heap" e lo stack assegnato da Windows. Ma non aiuta.
Un'altra domanda è, se si conoscono altre trappole che posso incontrare. Ad esempio ho notato che a Windows non piace se RSP non è uniforme (suppongo perché non si può mai arrivare a un RSP non uniforme facendo PUSH e POP su 2/4/8 byte su un puntatore dello stack allineato a 16 byte).
Grazie, Jonas
Hai mai giocato con [Sandboxie] (http://www.sandboxie.com)? Potrebbe essere utile se hai solo bisogno di avere un'utilità sandbox a portata di mano. –
No, sfortunatamente questo non aiuta. Non sto parlando di eseguire un'applicazione non affidabile nel mio sistema operativo, ma di eseguire codice x86 non affidabile all'interno della mia applicazione. Per esempio. un plugin per il nuovo codec video in un lettore video. Non voglio un plug-in con errori per bloccare la mia applicazione. – m3tikn0b
[Affidarsi alle eccezioni potrebbe non essere abbastanza buono: IsBadXxxPtr dovrebbe davvero essere chiamato CrashProgramRandomly] (http://blogs.msdn.com/b/oldnewthing/archive/2006/09/27/773741.aspx). Stranamente, puoi spingere e inserire valori a 16 bit in modalità 64 bit, mentre non puoi fare lo stesso con quelli a 32 bit. Se non si desidera che un plug-in con errori blocchi il processo, è possibile che si desideri eseguire il plug-in in un processo separato/secondario. –