GNU/Linux >> Linux Esercitazione >  >> Linux

Linux:come garantire che una libreria condivisa abbia le sue pagine di memoria condivise da diversi processi?

Lascia che ti descriva prima cosa voglio fare, poi cosa riesco a fare e infine il mio problema.

Obiettivo:implementare un attacco flush+flush cache in C

Sto cercando di implementare in C l'attacco flush+flush cache (https://gruss.cc/files/flushflush.pdf). Fondamentalmente, sfrutta il fatto che due diversi processi potrebbero condividere le stesse pagine di memoria quando si utilizzano librerie condivise. Ciò si traduce in un "utilizzo condiviso" della cache.

Supponiamo di avere un processo vittima, che esegue continuamente e talvolta esegue una funzione func importato da una libreria condivisa.

Parallelamente, assumiamo di avere un processo di spionaggio, in esecuzione sullo stesso computer della vittima, il cui obiettivo è spiare quando la vittima chiama func . La spia ha anche accesso alla stessa libreria condivisa. Lo pseudo-codice del processo spia è il seguente:

i=0;
for (i = 0; i < trace_length ; i++)
{
    trace[i] = flush_time( address of function "func");
    i++;
}

dove flush_time( <address> ) è una funzione che restituisce il tempo impiegato dalla CPU per svuotare la memoria indicata da address da tutti i livelli di cache. Sui processori Intel, questo può essere ottenuto tramite l'istruzione di montaggio clflush . Si può osservare che l'esecuzione di clflush è più veloce quando l'indirizzo non è presente nella cache. Di conseguenza, i tempi necessari per svuotare un indirizzo di memoria possono essere tradotti direttamente in sua presenza (o meno) all'interno della cache.

Il processo spia restituisce un vettore di traccia che contiene i risultati flush_time nel tempo. Dall'osservazione precedente, questa traccia mostrerà tempi più elevati quando la vittima chiama anche la funzione func . La spia quindi detrarrà quando la vittima chiama func .

Cosa riesco a fare:far funzionare l'attacco contro la libreria condivisa GSL

Ho implementato il suddetto attacco, dove la libreria condivisa è la GSL. Arbitrariamente, ho scelto gsl_stats_mean (definito in gsl_statistics_double ) per fungere da funzione func Sono disposto a spiare.

In tal caso, lo spionaggio funziona perfettamente poiché posso vedere chiaramente una differenza di tempo quando il programma vittima effettua chiamate a gsl_stats_mean

Il mio problema:l'attacco non funziona su una libreria condivisa fatta in casa

Ora voglio creare la mia libreria condivisa e usarla per il test spia/vittima. Assumiamo . denota la cartella in cui il mio spy.c e victim.c i file sono. Ho creato due file myl.c e myl.h in una cartella ./src/myl , che contengono rispettivamente la descrizione di func ed è dichiarazione. Come in precedenza, l'obiettivo della mia spia è rilevare l'utilizzo di func dalla vittima.

Entrambi spy.c e victim.c contengono la riga di inclusione:

 #include "src/myl/myl.h"

La creazione della libreria condivisa avviene tramite i seguenti comandi:

gcc -c -fPIC src/myl/myl.c -o bin/shared/myl.o  #creation of object in ./bin/shared
gcc -shared bin/shared/myl.o -o bin/shared/libmyl.so #creation of the shared library in ./bin/shared
gcc -c spy.c -o spy.o #creation of spy's process object file
gcc -c victim.c -o victim.o #creation of victim's process object file
gcc spy.o -Lbin/shared -lmyl -o spy #creation of spy's executable
gcc victim.o -Lbin/shared -lmyl -o victim #creation of victim's executable

Quindi lancio la mia vittima e spio usando le seguenti righe:

LD_LIBRARY_PATH=$(pwd)/bin/shared ./victim
LD_LIBRARY_PATH=$(pwd)/bin/shared ./spy

Tuttavia, a differenza del caso in cui stavo usando la funzione GSL, non riesco più a vedere alcuna attività nella cache. Immagino che questo significhi che i miei processi spia e vittima non condividono la stessa pagina di memoria per la mia libreria condivisa (mentre, tuttavia, era il caso quando si utilizzava il GSL). Nota che quando si compila in questo modo, lo spionaggio funziona ancora quando si prende di mira una funzione GSL.

La mia domanda principale è la seguente:come garantire che una libreria condivisa compilata in casa abbia il paging della memoria condivisa quando viene eseguita da più processi contemporaneamente? Sembra essere il caso delle librerie "corrette" che ho installato, come GSL, gmp, librerie native ecc…. Ma non per quello che ho fatto io.

Grazie in anticipo e mi scuso se la risposta è semplice.

EDIT:output di LD_DEBUG=libs e files sia per la spia che per la vittima.
NOTA:la vittima si chiama pg2 e spia si chiama pg1 (mi dispiace per quello)

Innanzitutto, le librerie per la vittima, seguite dai file per la vittima (pg2 ). Quindi, le librerie per la spia, seguite dai file per la spia (pg1 ):

LD_DEBUG=libs LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg2
 31714: find library=libmyl.so [0]; searching
 31714:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared/tls:/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared      (LD_LIBRARY_PATH)
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64/libmyl.so
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/libmyl.so
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64/libmyl.so
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31714: 
 31714: find library=libc.so.6 [0]; searching
 31714:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libc.so.6
 31714:  search cache=/etc/ld.so.cache
 31714:   trying file=/lib/x86_64-linux-gnu/libc.so.6
 31714: 
 31714: 
 31714: calling init: /lib/x86_64-linux-gnu/libc.so.6
 31714: 
 31714: 
 31714: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31714: 
 31714: 
 31714: initialize program: ./pg2
 31714: 
 31714: 
 31714: transferring control: ./pg2
 31714: 



LD_DEBUG=files LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg2
 31901: 
 31901: file=libmyl.so [0];  needed by ./pg2 [0]
 31901: file=libmyl.so [0];  generating link map
 31901:   dynamic: 0x00007f5a3b34be48  base: 0x00007f5a3b14b000   size: 0x0000000000201028
 31901:     entry: 0x00007f5a3b14b580  phdr: 0x00007f5a3b14b040  phnum:                  7
 31901: 
 31901: 
 31901: file=libc.so.6 [0];  needed by ./pg2 [0]
 31901: file=libc.so.6 [0];  generating link map
 31901:   dynamic: 0x00007f5a3b144ba0  base: 0x00007f5a3ad81000   size: 0x00000000003c99a0
 31901:     entry: 0x00007f5a3ada1950  phdr: 0x00007f5a3ad81040  phnum:                 10
 31901: 
 31901: 
 31901: calling init: /lib/x86_64-linux-gnu/libc.so.6
 31901: 
 31901: 
 31901: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31901: 
 31901: 
 31901: initialize program: ./pg2
 31901: 
 31901: 
 31901: transferring control: ./pg2
 31901: 


LD_DEBUG=libs LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg1
 31938: find library=libmyl.so [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared/tls:/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared      (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64/libmyl.so
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/libmyl.so
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64/libmyl.so
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31938: 
 31938: find library=libgsl.so.23 [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libgsl.so.23
 31938:  search cache=/etc/ld.so.cache
 31938:   trying file=/usr/local/lib/libgsl.so.23
 31938: 
 31938: find library=libgslcblas.so.0 [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libgslcblas.so.0
 31938:  search cache=/etc/ld.so.cache
 31938:   trying file=/usr/local/lib/libgslcblas.so.0
 31938: 
 31938: find library=libc.so.6 [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libc.so.6
 31938:  search cache=/etc/ld.so.cache
 31938:   trying file=/lib/x86_64-linux-gnu/libc.so.6
 31938: 
 31938: find library=libm.so.6 [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libm.so.6
 31938:  search cache=/etc/ld.so.cache
 31938:   trying file=/lib/x86_64-linux-gnu/libm.so.6
 31938: 
 31938: 
 31938: calling init: /lib/x86_64-linux-gnu/libc.so.6
 31938: 
 31938: 
 31938: calling init: /lib/x86_64-linux-gnu/libm.so.6
 31938: 
 31938: 
 31938: calling init: /usr/local/lib/libgslcblas.so.0
 31938: 
 31938: 
 31938: calling init: /usr/local/lib/libgsl.so.23
 31938: 
 31938: 
 31938: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31938: 
 31938: 
 31938: initialize program: ./pg1
 31938: 
 31938: 
 31938: transferring control: ./pg1
 31938: 
0: 322 # just some output of my spying program
1: 323 # just some output of my spying program
 31938: 
 31938: calling fini: ./pg1 [0]
 31938: 
 31938: 
 31938: calling fini: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so [0]
 31938: 
 31938: 
 31938: calling fini: /usr/local/lib/libgsl.so.23 [0]
 31938: 
 31938: 
 31938: calling fini: /usr/local/lib/libgslcblas.so.0 [0]
 31938: 
 31938: 
 31938: calling fini: /lib/x86_64-linux-gnu/libm.so.6 [0]
 31938: 




LD_DEBUG=files LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg1
 31940: 
 31940: file=libmyl.so [0];  needed by ./pg1 [0]
 31940: file=libmyl.so [0];  generating link map
 31940:   dynamic: 0x00007fb3d8794e48  base: 0x00007fb3d8594000   size: 0x0000000000201028
 31940:     entry: 0x00007fb3d8594580  phdr: 0x00007fb3d8594040  phnum:                  7
 31940: 
 31940: 
 31940: file=libgsl.so.23 [0];  needed by ./pg1 [0]
 31940: file=libgsl.so.23 [0];  generating link map
 31940:   dynamic: 0x00007fb3d8582ac8  base: 0x00007fb3d8126000   size: 0x000000000046da60
 31940:     entry: 0x00007fb3d8180e30  phdr: 0x00007fb3d8126040  phnum:                  7
 31940: 
 31940: 
 31940: file=libgslcblas.so.0 [0];  needed by ./pg1 [0]
 31940: file=libgslcblas.so.0 [0];  generating link map
 31940:   dynamic: 0x00007fb3d8124df0  base: 0x00007fb3d7ee8000   size: 0x000000000023d050
 31940:     entry: 0x00007fb3d7eea120  phdr: 0x00007fb3d7ee8040  phnum:                  7
 31940: 
 31940: 
 31940: file=libc.so.6 [0];  needed by ./pg1 [0]
 31940: file=libc.so.6 [0];  generating link map
 31940:   dynamic: 0x00007fb3d7ee1ba0  base: 0x00007fb3d7b1e000   size: 0x00000000003c99a0
 31940:     entry: 0x00007fb3d7b3e950  phdr: 0x00007fb3d7b1e040  phnum:                 10
 31940: 
 31940: 
 31940: file=libm.so.6 [0];  needed by /usr/local/lib/libgsl.so.23 [0]
 31940: file=libm.so.6 [0];  generating link map
 31940:   dynamic: 0x00007fb3d7b1cd88  base: 0x00007fb3d7815000   size: 0x00000000003080f8
 31940:     entry: 0x00007fb3d781a600  phdr: 0x00007fb3d7815040  phnum:                  7
 31940: 
 31940: 
 31940: calling init: /lib/x86_64-linux-gnu/libc.so.6
 31940: 
 31940: 
 31940: calling init: /lib/x86_64-linux-gnu/libm.so.6
 31940: 
 31940: 
 31940: calling init: /usr/local/lib/libgslcblas.so.0
 31940: 
 31940: 
 31940: calling init: /usr/local/lib/libgsl.so.23
 31940: 
 31940: 
 31940: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31940: 
 31940: 
 31940: initialize program: ./pg1
 31940: 
 31940: 
 31940: transferring control: ./pg1
 31940: 
0: 325 # just some output of my spying program
1: 327 # just some output of my spying program
 31940: 
 31940: calling fini: ./pg1 [0]
 31940: 
 31940: 
 31940: calling fini: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so [0]
 31940: 
 31940: 
 31940: calling fini: /usr/local/lib/libgsl.so.23 [0]
 31940: 
 31940: 
 31940: calling fini: /usr/local/lib/libgslcblas.so.0 [0]
 31940: 
 31940: 
 31940: calling fini: /lib/x86_64-linux-gnu/libm.so.6 [0]
 31940: 

Risposta accettata:

Dal momento che l'output di debug da ld il linker/caricatore dinamico conferma che sia la victim e spy i programmi caricano il file di input corretto, il passaggio successivo sarebbe verificare se il kernel ha effettivamente impostato le pagine fisiche in cui libmyl.so viene caricato in memoria per essere condiviso tra la victim e spy .

In Linux questo è possibile verificare dalla versione del kernel 2.6.25 tramite la pagemap interfaccia nel kernel che consente ai programmi dello spazio utente di esaminare le tabelle delle pagine e le relative informazioni leggendo i file in /proc .

La procedura generale per l'utilizzo di pagemap per scoprire se due processi condividono la memoria è la seguente:

  1. Leggi/proc/<pid>/maps per entrambi i processi per determinare quali parti dello spazio di memoria sono mappate a quali oggetti.
  2. Seleziona le mappe che ti interessano, in questo caso le pagine a cui libmyl.so è mappato.
  3. Apri /proc/<pid>/pagemap . La pagemap è costituito da descrittori di pagemap a 64 bit, uno per pagina. La mappatura tra l'indirizzo della pagina e l'indirizzo dei descrittori nella pagemap è indirizzo della pagina/dimensione della pagina * dimensione del descrittore . Cerca i descrittori delle pagine che vorresti esaminare.
  4. Leggi un descrittore a 64 bit come un intero senza segno per ogni pagina dalla pagemap .
  5. Confronta il numero di frame della pagina (PFN) nei bit 0-54 del descrittore di pagina tra libmyl.so pagine per victim e spy . Se le PFN corrispondono, i due processi condividono le stesse pagine fisiche.
Correlati:Windows – In che modo Windows NON può corrompere il file system Linux quando incasina gli schemi di partizione?

Il codice di esempio seguente illustra come la pagemap è possibile accedere e stampare dall'interno del processo. Usa dl_iterate_phdr() per determinare l'indirizzo virtuale di ciascuna libreria condivisa caricata nello spazio di memoria dei processi, quindi cerca e stampa la corrispondente pagemap da /proc/<pid>/pagemap .

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <link.h>
#include <errno.h>
#include <error.h>

#define E_CANNOT_OPEN_PAGEMAP 1
#define E_CANNOT_READ_PAGEMAP 2

typedef struct __attribute__ ((__packed__)) {
    union {
        uint64_t pmd;
        uint64_t page_frame_number : 55;
        struct {
            uint64_t swap_type: 5;
            uint64_t swap_offset: 50;
            uint64_t soft_dirty: 1;
            uint64_t exclusive: 1;
            uint64_t zero: 4;
            uint64_t file_page: 1;
            uint64_t swapped: 1;
            uint64_t present: 1;
        };
    };
} pmd_t;


static int print_pagemap_for_phdr(struct dl_phdr_info *info,
                                  size_t size, void *data)
{
    struct stat statbuf;

    size_t pagesize = sysconf(_SC_PAGESIZE);

    char pagemap_path[BUFSIZ];
    int pagemap;

    uint64_t start_addr, end_addr;

    if (!strcmp(info->dlpi_name, "")) {
        return 0;
    }

    stat(info->dlpi_name, &statbuf);

    start_addr = info->dlpi_addr;
    end_addr = (info->dlpi_addr + statbuf.st_size + pagesize) & ~(pagesize-1);

     printf("n%10p-%10p %snn",
            (void *)start_addr,
            (void *)end_addr,
            info->dlpi_name);

     snprintf(pagemap_path, sizeof pagemap_path, "/proc/%d/pagemap", getpid());

     if ((pagemap = open(pagemap_path, O_RDONLY)) < 0) {
         error(E_CANNOT_OPEN_PAGEMAP, errno, 
               "cannot open pagemap: %s", pagemap_path);
     }

     printf("%10s %8s %7s %5s %8s %7s %7sn",
            "", "", "soft-", "", "file /", "", "");
     printf("%10s %8s %7s %5s %11s %7s %7sn",
            "address", "pfn", "dirty", "excl.",
            "shared anon", "swapped", "present");

     for (unsigned long i = start_addr; i < end_addr; i += pagesize) {
          pmd_t pmd;

          if (pread(pagemap, &pmd.pmd, sizeof pmd.pmd, (i / pagesize) * sizeof pmd) != sizeof pmd) {
              error(E_CANNOT_READ_PAGEMAP, errno,
                    "cannot read pagemap: %s", pagemap_path);
          }

          if (pmd.pmd != 0) {
              printf("0x%10" PRIx64 " %06" PRIx64 " %3d %5d %8d %9d %7dn", i,
                     (unsigned long)pmd.page_frame_number,
                     pmd.soft_dirty,
                     pmd.exclusive,
                     pmd.file_page,
                     pmd.swapped,
                     pmd.present);
          }
    }

    close(pagemap);

    return 0;
}

int main()
{
    dl_iterate_phdr(print_pagemap_for_phdr, NULL);

    exit(EXIT_SUCCESS);
}

L'output del programma dovrebbe essere simile al seguente:

$ sudo ./a.out

0x7f935408d000-0x7f9354256000 /lib/x86_64-linux-gnu/libc.so.6

                      soft-         file /                
   address      pfn   dirty excl. shared anon swapped present
0x7f935408d000 424416   1     0        1         0       1
0x7f935408e000 424417   1     0        1         0       1
0x7f935408f000 422878   1     0        1         0       1
0x7f9354090000 422879   1     0        1         0       1
0x7f9354091000 43e879   1     0        1         0       1
0x7f9354092000 43e87a   1     0        1         0       1
0x7f9354093000 424790   1     0        1         0       1
...

dove:

  • address è l'indirizzo virtuale della pagina
  • pfn è il numero del frame della pagina delle pagine
  • soft-dirty indica se il bit soft-dirty è impostato nelle pagine Page Table Entry (PTE).
  • excl. indica se la pagina è mappata esclusivamente (cioè la pagina è mappata solo per questo processo).
  • file / shared anon indica se la pagina è un file pagine o una pagina anonima condivisa.
  • swapped indica se la pagina è attualmente scambiata (implica present è zero).
  • present indica se la pagina è attualmente presente nel set residente dei processi (implica swapped è zero).
Correlati:linux di base (1) Cheat Sheet

(Nota:eseguo il programma di esempio con sudo come da Linux 4.0 solo utenti con il CAP_SYS_ADMIN capacità può ottenere PFN da /proc/<pid>/pagemap . A partire da Linux 4.2 il campo PFN viene azzerato se l'utente non possiede CAP_SYS_ADMIN . Il motivo di questa modifica è rendere più difficile lo sfruttamento di un'altra vulnerabilità correlata alla memoria, l'attacco Rowhammer, utilizzando le informazioni sulla mappatura da virtuale a fisica esposte dalle PFN.)

Se esegui più volte il programma di esempio, dovresti notare che l'indirizzo virtuale della pagina dovrebbe cambiare (a causa di ASLR), ma il PFN per le librerie condivise che sono utilizzate da altri processi dovrebbe rimanere lo stesso.

Se i PFN per libmyl.so corrispondenza tra la victim e spy programma, inizierei a cercare un motivo per cui l'attacco non riesce nel codice di attacco stesso. Se i PFN non corrispondono, i bit aggiuntivi potrebbero fornire qualche suggerimento sul motivo per cui le pagine non sono impostate per essere condivise. La pagemap i bit indicano quanto segue:

present file exclusive state:
   0      0     0      non-present
   1      1     0      file page mapped somewhere else
   1      1     1      file page mapped only here
   1      0     0      anonymous non-copy-on-write page (shared with parent/child)
   1      0     1      anonymous copy-on-write page (or never forked)

Copia in scrittura delle pagine in (MAP_FILE | MAP_PRIVATE) le aree sono anonime in questo contesto.

Bonus: Per ottenere il numero di volte una pagina è stata mappata, la PFN può essere utilizzata per cercare la pagina in /proc/kpagecount . Questo file contiene un conteggio a 64 bit del numero di volte in cui ciascuna pagina è stata mappata, indicizzata da PFN.


Linux
  1. Come cancellare la cache di memoria in Linux

  2. Come inizializzare una libreria condivisa su Linux

  3. Come elencare i processi collegati a un segmento di memoria condivisa in Linux?

  4. Come utilizzare la memoria condivisa con Linux in C

  5. Come eseguire il controllo delle versioni di una libreria condivisa in Linux?

Come controllare la memoria condivisa di Linux usando il comando ipcs

Come installare la libreria Ncurses in Linux

Come cancellare la memoria di scambio in Linux

Come trovare i migliori processi in esecuzione in base all'utilizzo di memoria e CPU in Linux

In che modo l'errore di memoria a bit singolo influirà su Linux?

Linux inizierà a uccidere i miei processi senza chiedermi se la memoria si sta esaurendo?