2013-01-09 5 views
20

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

+0

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. –

+1

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

+0

[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. –

risposta

0

si vuole eseguire il codice e controllare la validità. Puoi farlo con sandbox il tuo. Vedi l'implementazione della virtual box del processore x86. Può aiutare. Ma tutte le macchine virtuali hanno un prezzo: il processore di emulazione esegue il codice sottostante 5-10 volte più lento della tua app.

Se è necessario solo il controllo degli errori ed eseguire l'app su core cpu, quindi è necessario eseguirlo in thread. Quando il thread si blocca l'applicazione non è stata toccata.E puoi inserire del codice nel thread per cercarlo nell'esecuzione. Ma questo caso è meno sicuro, perché il codice dannoso può interrompere le routine di controllo e sfruttarlo per backdooring/root del sistema.

Quindi, la mia risposta: per sicurezza - usa la tua macchina virtuale, per velocità - esegui in un altro thread.

Cordiali saluti, e buona fortuna Vladimir

1

esecuzione non attendibile, il codice 3rd party nello stesso processo è in cerca di guai. Quel codice può uccidere il tuo processo in vari modi. Per esempio. può chiamare exit() in caso di errori, richiedere molta memoria o scrivere nella memoria allocata dai thread.

La soluzione più sicura ma non così difficile è eseguire questo codice in un processo diverso, simile a quello che sta facendo Chrome. Ogni estensione di Chrome viene eseguita in un processo diverso e i dati vengono passati tra i processi.

L'applicazione può avviare un processo separato e comunicare con esso, però un tubo, finestre messaggio, la memoria condivisa (file mappato memoria) per la condivisione di dati di grandi dimensioni, ecc

Un plugin (di solito) implementa un'interfaccia in modo da Avremo bisogno di scrivere un oggetto proxy per astrarre il fatto che il plugin risiede in un altro processo e nascondere la complessità IPC che viene fornita con le app multi processo. gSoap è un tale quadro, altri esistono.