2010-06-01 9 views
5

Qualcuno mi dice come bloccare alcune chiamate di sistema specifiche all'interno di un programma, per favore? Sto costruendo un sistema che prende un pezzo di codice sorgente C, lo compila con gcc e lo esegue. Per motivi di sicurezza, ho bisogno di impedire al programma compilato di chiamare alcune chiamate di sistema. C'è un modo per farlo, dal livello del codice sorgente (ad esempio, eliminando i file di intestazione di gcc, rilevando chiamate esterne dannose, ...) al livello eseguibile?GCC come bloccare le chiamate di sistema all'interno di un programma?

Modificato n. 1: Aggiungere dettagli sulle chiamate dannose.

Modificato # 2: Il mio sistema è GNU/Linux.

A cura # 3:

ho provato alcuni metodi nel giro di pochi giorni e qui ci sono le conclusioni che ho ottenuto finora:

  1. la scansione del codice sorgente di non risolve il principale problema dal momento che si può sempre nascondere il suo file sorgente C piuttosto bene.
  2. "Sostituire il simbolo C" funziona bene per le librerie, ma per le chiamate di sistema non ho ottenuto ciò che volevo. Questa idea non è morta, tuttavia, fare questo mi causerebbe sicuramente molto tempo di hacking (gcc e/o ld).
  3. La deiscalizzazione dei permessi funziona come un incantesimo. Potrei usare fakeroot o un utente "ospite" per farlo. Questo metodo è anche il più semplice da implementare.

L'altro è native client che non ho ancora provato ma sicuramente lo farei nel prossimo futuro a causa del comune tra il progetto e il mio lavoro.

+1

Sembra una vera sfida. Dopotutto, quasi ogni programma C concepibile, in modo del tutto legittimo, effettua chiamate di sistema come: allocare memoria, leggere e scrivere file, usando stdin e stdout e così via. –

+0

Che tipo di programma pensi che non faccia mai una chiamata di sistema? Per favore pubblica un esempio. –

+0

@ High Performance Mark: Beh, voglio solo bloccare alcune chiamate di sistema specifiche dal momento che sono limitate, chiamate fork(), open(), ... @Neil Butterworth: Intendevo non tutte le chiamate di sistema ma malevoli chiamate di sistema, ad es execv() potrebbe essere usato per eseguire uno script BASH che cancella i miei dati sul disco. –

risposta

8

Come altri utenti hanno notato, è impossibile che un programma eviti di effettuare chiamate di sistema, poiché consentono la libreria C ovunque.

Tuttavia si potrebbe essere in grado di fare qualche progresso con l'uso attento del meccanismo LD_PRELOAD, se la piattaforma lo supporta (ad es. Linux): si scrive una libreria condivisa con gli stessi nomi di simboli della libreria C, che sono chiamato invece di le funzioni di libc previste. (Per esempio, recinto elettrico è costruito come una libreria condivisa su sistemi basati su Debian e intercetta le chiamate a malloc, free et al.)

Ho il sospetto che si potrebbe usare questo meccanismo per intrappolare o argomento-controllare le chiamate a qualsiasi funzione libc non ti piace e forse prendere nota di quelli che consideri incondizionatamente sicuri. Potrebbe quindi essere ragionevole scansionare l'eseguibile compilato per il codice corrispondente a INT 80 per bloccare qualsiasi tentativo di creare syscalls non elaborate (0xcd 0x80 - ma attenzione ai falsi positivi). Comunque ho solo dare questo un paio di momenti di riflessione, ho potuto facilmente perso qualcosa o questo potrebbe rivelarsi impraticabile ...

+0

Brillante idea del tipo di override del simbolo C! In questo modo ho potuto controllare e rilevare totalmente le chiamate con cattive intenzioni. Sicuramente proveremo questo e gli altri due modi (scansionando il file sorgente e la deiscalation dei permessi) e vedremo quale è il più appropriato. –

+1

Ho appena pensato ad un esempio più dettagliato della potenza di questo meccanismo: controlla il pacchetto 'fakeroot'. – crazyscot

+3

È possibile eseguire un syscall senza utilizzare alcuna libreria, quindi se si prevede di creare un sistema sicuro contro utenti malintenzionati, l'uso di LD_PRELOAD non è sufficiente. –

1

solo per illustrare che questo non è possibile, il seguente programma:

int main() { 
    return 0; 
} 

rende più di 20 chiamate di sistema come riportato utilizzando strace. Le chiamate includono open (due volte), che è una delle chiamate che sembra voler bloccare.

+0

Grazie, ma intendevo alcuni specifici, non tutti. –

3

Non è possibile.

Anche questo programma:

#include <stdio.h> 

int main() 
{ 
    printf("Hello, World\n"); 
    return 0; 
} 

fa almeno una chiamata di sistema (per inviare la stringa "Ciao, mondo \ n" sullo standard output). Le chiamate di sistema sono l'unico modo per un programma di interagire con il mondo esterno. Utilizzare il modello di sicurezza del sistema operativo per sicurezza.

cura per questo commento:

non significava tutte le chiamate di sistema, ma le chiamate di sistema dannosi, per esempio execv() potrebbe essere usato per eseguire uno script BASH che cancella i miei dati sul disco.

Il sistema operativo include già meccanismi per impedire che succeda questo genere di cose. Ad esempio, affinché uno script bash cancelli i dati, il processo deve già avere accesso in scrittura a tali dati. Ciò significa che deve essere stato avviato da te o da root. La tua unica vera opzione è di non installare software inaffidabile.

A proposito, a seconda della piattaforma, execv non è necessariamente una chiamata di sistema. Su Linux, è un wrapper della libreria C per la vera chiamata di sistema (execve).

+0

Ora che si parla dell'autorizzazione del file system, trovo una soluzione per questo eseguendola come utente personalizzato che non ha i permessi di scrittura in quella directory. L'ho quasi dimenticato. Grazie MrGreen –

1

Bene, se si desidera bloccare solo chiamate specifiche, perché non eseguire semplicemente un grep tramite il codice sorgente prima di tentare di compilarlo? E rifiuta i programmi che utilizzano le chiamate di sistema non sicure.

+0

Posso farlo automaticamente, utilizzando un programma che esegue la scansione del codice sorgente? Ad esempio, ho un file sorgente che include "unistd".h "che potrebbe causare situazioni dannose e un altro file sorgente che ha una struttura denominata" unistd "che ha un campo denominato" .h ", come faccio a differenziare le circostanze? –

+0

Puoi certamente farlo automaticamente. con grep o sed, ma se quella non è la tua tazza di tè, qualsiasi linguaggio di programmazione comune, specialmente uno con buone capacità di espressione regolare, lo farebbe. –

+0

Sì, probabilmente funzionerebbe, anche se devo progettare attentamente l'algoritmo per scansionare usando grep o Perl inline o limitando la convenzione di denominazione del codice sorgente: Grazie :-) –

4

È possibile eseguire il programma compilato estraendolo da un wrapper e utilizzare la funzione Linux ptrace (2) per intercettare e ispezionare tutte le chiamate di sistema invocate dal programma.

Il seguente codice di esempio mostra un wrapper che esegue il comando/usr/bin/w, stampa ogni chiamata di sistema invocata dal comando e termina il comando se tenta di richiamare la chiamata di sistema write (2).

 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <sys/ptrace.h> 
#include <sys/wait.h> 
#include <sys/syscall.h> 
#include <sys/reg.h> 

#define BAD_SYSCALL __NR_write 

int main(int argc, char *argv) 
{ 
    pid_t child; 
    int status, syscall_nr; 

    child = fork(); 
    if (child == 0) { 
     /* In child. */ 
     ptrace(PTRACE_TRACEME, 0, NULL, NULL); 
     execl("/usr/bin/w", NULL, NULL); 
     // not reached 
    } 

    /* In parent. */ 
    while (1) { 
     wait(&status); 

     /* Abort loop if child has exited. */ 
     if (WIFEXITED(status) || WIFSIGNALED(status)) 
      break; 

     /* Obtain syscall number from the child's process context. */ 
     syscall_nr = ptrace(PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL); 
     printf("Child wants to execute system call %d: ", syscall_nr); 

     if (syscall_nr != BAD_SYSCALL) { 
      /* Allow system call. */ 
      printf("allowed.\n"); 
      ptrace(PTRACE_SYSCALL, child, NULL, NULL); 
     } else { 
      /* Terminate child. */ 
      printf("not allowed. Terminating child.\n"); 
      ptrace(PTRACE_KILL, child, NULL, NULL); 
     } 
    } 

    exit(EXIT_SUCCESS); 
} 

Si possono fare cose molto più potenti che utilizzano ptrace, come ispezionare e cambiare uno spazio di indirizzi del processo (ad esempio, per ottenere e modificare i parametri passati a una chiamata di sistema).

Una buona introduzione può essere trovata in questo Linux Journal Article e nel suo follow-up.

Problemi correlati