Domanda

Voglio creare un allocatore che fornisca memoria con i seguenti attributi:

  • non può essere paginato su disco.
  • è incredibilmente difficile accedervi tramite un debugger collegato

L'idea è che conterrà informazioni sensibili (come le informazioni sulla licenza) che dovrebbero essere inaccessibili all'utente.Ho fatto la solita ricerca online e ho chiesto informazioni ad alcune altre persone, ma non riesco a trovare un buon punto di partenza per questo problema.

Aggiornamenti

Josh menziona l'uso VirtualAlloc per impostare la protezione sullo spazio di memoria.Ho creato un allocatore personalizzato (mostrato di seguito) e ho trovato utilizzando il file VirtualLock funzione limita la quantità di memoria che posso allocare.Questo sembra essere previsto però.Dato che lo sto usando per piccoli oggetti questo non è un problema.

//
template<class _Ty>
class LockedVirtualMemAllocator : public std::allocator<_Ty>
{
public:
    template<class _Other>
    LockedVirtualMemAllocator<_Ty>& operator=(const LockedVirtualMemAllocator<_Other>&)
    {   // assign from a related LockedVirtualMemAllocator (do nothing)
        return (*this);
    }

    template<class Other>
    struct rebind {
        typedef LockedVirtualMemAllocator<Other> other;
    };

    pointer allocate( size_type _n )
    {
        SIZE_T  allocLen = (_n * sizeof(_Ty));
        DWORD   allocType = MEM_COMMIT;
        DWORD   allocProtect = PAGE_READWRITE;
        LPVOID pMem = ::VirtualAlloc( NULL, allocLen, allocType, allocProtect );
        if ( pMem != NULL ) {
            ::VirtualLock( pMem, allocLen );
        }
        return reinterpret_cast<pointer>( pMem );
    }
    pointer allocate( size_type _n, const void* )
    {
        return allocate( _n );
    }

    void deallocate(void* _pPtr, size_type _n )
    {
        if ( _pPtr != NULL ) {
            SIZE_T  allocLen = (_n * sizeof(_Ty));
            ::SecureZeroMemory( _pPtr, allocLen );
            ::VirtualUnlock( _pPtr, allocLen );
            ::VirtualFree( _pPtr, 0, MEM_RELEASE );
        }
    }
};

e viene utilizzato

 //a memory safe std::string
 typedef std::basic_string<char, std::char_traits<char>, 
                           LockedVirtualMemAllocato<char> > modulestring_t;

Ted Percival menziona mlock, ma non ne ho ancora implementato.

ho trovato Crittografia pratica di Neil Furguson e Bruce Schneier anche abbastanza utile.

È stato utile?

Soluzione

Non puoi davvero proteggerti dall'accesso alla memoria.Probabilmente puoi impedire il paging se stai lavorando come amministratore o come sistema, ma non puoi impedire all'amministratore o al sistema di leggere la tua memoria.Anche se potessi in qualche modo impedire completamente ad altri processi di leggere la tua memoria (cosa che non puoi), un altro processo potrebbe comunque effettivamente inserire un nuovo thread nel tuo processo e leggere la memoria in quel modo.

Anche se potessi in qualche modo bloccare completamente il tuo processo e garantire che il sistema operativo lo faccia Mai consenti a chiunque altro di accedere al tuo processo, non avrai comunque una protezione completa.L'intero sistema operativo potrebbe essere eseguito in una macchina virtuale, che potrebbe essere messa in pausa e ispezionata in qualsiasi momento.

Voi non può proteggere il contenuto della memoria dal proprietario del sistema.Hollywood e l’industria musicale soffrono per questo da anni.Se fosse possibile lo starebbero già facendo.

Altri suggerimenti

Sui sistemi Unix è possibile utilizzare blocco(2) per bloccare le pagine di memoria nella RAM, impedendo che vengano paginate.

mlock () e mlockall () blocca rispettivamente la parte o tutto lo spazio degli indirizzi virtuali del processo di chiamata nella RAM, impedendo a tale memoria di essere pagata nell'area di swap.

Esiste un limite alla quantità di memoria che ciascun processo può bloccare e con cui può essere visualizzato ulimit -l ed è misurato in kilobyte.Sul mio sistema, il limite predefinito è 32 kiB per processo.

Se stai sviluppando per Windows, ci sono modi per limitare l'accesso alla memoria, ma non è possibile bloccarne assolutamente altri.Se speri di mantenere un segreto segreto, leggi Scrittura di codice sicuro - che risolve questo problema in modo approfondito, ma tieni presente che non hai modo di sapere se il tuo codice è in esecuzione su una macchina reale o virtuale.C'è un sacco di materiale API Win32 per gestire la crittografia che gestisce questo genere di cose, inclusa l'archiviazione sicura dei segreti: il libro ne parla.Puoi guardare online Microsoft CyproAPI per dettagli;i progettisti del sistema operativo riconoscono proprio questo problema e la necessità di mantenere sicuro il testo in chiaro (di nuovo, leggi Scrittura di codice sicuro).

La funzione API Win32 VirtualAlloc è l'allocatore di memoria a livello di sistema operativo.Permette di impostare la protezione dell'accesso;quello che potresti fare è impostare l'accesso a PAGE_GUARD O PAGE_NOACCESS, e inverti l'accesso a qualcosa di più amichevole mentre il tuo programma legge e lo ripristina in seguito, ma è semplicemente un dosso se qualcuno si sta sforzando di sbirciare il tuo segreto.

In sintesi, guarda le API crittografiche sulla tua piattaforma, risolveranno il problema meglio di qualcosa che hai hackerato tu stesso.

Prendiamolo un po' alla volta:

Voglio creare un allocatore che fornisca la memoria i seguenti attributi:

Questo è abbastanza giusto.

* cannot be paged to disk.

Sarà difficile.Per quanto ne so, non è possibile disabilitare il paging virtuale poiché è gestito dal sistema operativo.Se c'è un modo, allora ti troverai a esplorare le viscere del sistema operativo.

* is incredibly hard to access through an attached debugger

Potresti eseguirlo tramite PGP e archiviarlo crittografato in memoria e decrittografarlo secondo necessità.Un enorme successo in termini di prestazioni.

L'idea è che ciò conterrà informazioni sensibili (come le informazioni sulla licenza) che dovrebbero essere inaccessibili all'utente.Ho fatto le solite ricerche online e ho chiesto ad alcune altre persone, ma non riesco a trovare un buon posto su questo problema.

Mantenere tutte le informazioni sensibili lontane dalla macchina.Sul serio.Non archiviare informazioni sensibili in memoria.Scrivi una routine di eliminazione personalizzata che rimuoverà automaticamente tutti i dati da qualsiasi allocazione eseguita.Non consentire mai l'accesso generale a una macchina con materiale sensibile su di essa.Se esegui l'accesso DB, assicurati che tutti gli accessi siano disinfettati prima di attivare.L'accesso è consentito solo alle persone con login specifici.Nessun accesso generale al gruppo.

In una nota a margine, quali altri metodi ci sono di accedere alla memoria di un processo diverso dall'allegamento di un debugger?

Fare un dump della memoria.

installa Libsodium, usa i meccanismi di allocazione #incluso <sodium.h>

Allocazioni heap protette

Più lenti di malloc() e simili, richiedono 3 o 4 pagine extra di memoria virtuale.

void *sodium_malloc(size_t size);

Allocare memoria per archiviare dati sensibili utilizzando sodium_malloc() E sodium_allocarray().Dovrai prima chiamare sodium_init() prima di utilizzare queste protezioni dell'heap.

void *sodium_allocarray(size_t count, size_t size);

IL sodium_allocarray() La funzione restituisce un puntatore da cui è possibile accedere agli oggetti count che hanno dimensioni pari a byte di memoria.Fornisce le stesse garanzie di sodium_malloc() ma protegge anche dagli overflow aritmetici quando count * size supera SIZE_MAX.

Queste funzioni aggiungono pagine di guardia attorno ai dati protetti per renderli meno accessibili in uno scenario simile al cuore.

Inoltre, la protezione per le regioni di memoria allocate in questo modo può essere modificata utilizzando le operazioni di blocco della memoria: sodium_mprotect_noaccess(), sodium_mprotect_readonly() E sodium_mprotect_readwrite().

Dopo sodium_malloc Puoi usare sodium_free() per sbloccare e deallocare la memoria.A questo punto dell'implementazione considera l'azzeramento della memoria dopo l'uso.

azzerare la memoria dopo l'uso

void sodium_memzero(void * const pnt, const size_t len);

Dopo l'uso, i dati sensibili dovrebbero essere sovrascritti, ma memset() e il codice scritto a mano possono essere rimossi silenziosamente da un compilatore di ottimizzazione o dal linker.

La funzione sodio_memzero() tenta effettivamente di azzerare i byte len a partire da pnt, anche se al codice vengono applicate delle ottimizzazioni.

bloccando l'allocazione della memoria

int sodium_mlock(void * const addr, const size_t len);

IL sodium_mlock() la funzione blocca almeno len byte di memoria a partire dall'indirizzo.Ciò può aiutare a evitare lo scambio di dati sensibili su disco.

int sodium_mprotect_noaccess(void *ptr);

La funzione sodio_mproteggi_noaccess() rende inaccessibile una regione allocata utilizzando sodio_malloc() o sodio_allocarray().Non può essere letto o scritto, ma i dati vengono conservati.Questa funzione può essere utilizzata per rendere inaccessibili i dati riservati tranne quando effettivamente necessari per un'operazione specifica.

int sodium_mprotect_readonly(void *ptr);

La funzione sodio_mprotect_readonly() contrassegna una regione allocata utilizzando sodio_malloc() o sodio_allocarray() come di sola lettura.Il tentativo di modificare i dati causerà l'interruzione del processo.

int sodium_mprotect_readwrite(void *ptr);

IL sodium_mprotect_readwrite() la funzione contrassegna una regione allocata utilizzando sodium_malloc() O sodium_allocarray() come leggibili e scrivibili, dopo essere stati protetti utilizzando sodium_mprotect_readonly() O sodium_mprotect_noaccess().

Ciò che stai chiedendo viene gestito a livello di sistema operativo.Una volta che i dati sono nel programma, è probabile che vengano paginati.

Per accedere alla memoria, un individuo motivato può allegare un debugger hardware.

@graham

Potresti eseguirlo tramite PGP e archiviarlo crittografato in memoria e decrittografarlo secondo necessità.Un enorme successo in termini di prestazioni.

Quindi dovresti tenere la chiave in memoria.Ciò renderebbe il tutto un po’ più difficile, ma sicuramente non impossibile.Chiunque sia motivato riuscirà comunque a ottenere i dati dalla memoria.

La soluzione migliore è implementare qualcosa di simile alla classe SecureString di .NET e fare molta attenzione a azzerare eventuali copie in chiaro dei dati non appena hai finito (non dimenticare di pulire anche quando vengono lanciate eccezioni).Un buon modo per farlo con std::string e simili è usare a allocatore personalizzato.

Su Windows, se usi CryptProtectMemory (o RtlEncryptMemory per i sistemi più vecchi), la password di crittografia viene archiviata nella memoria non paginabile (kernel?).Nei miei test, queste funzioni sono dannatamente veloci, specialmente.tenendo conto della protezione che ti stanno dando.

Su altri sistemi, mi piace usare Blowfish poiché è un buon mix tra velocità e forza.In quest'ultimo caso, dovrai generare casualmente la tua password (16+ byte di entropia per Blowfish) all'avvio del programma.Sfortunatamente, non c'è molto che puoi fare per proteggere quella password senza il supporto del sistema operativo, anche se potresti utilizzare tecniche generali di offuscamento per incorporare un valore salt codificato nel tuo eseguibile che puoi combinare con la password (ogni piccolo aiuto).

Nel complesso, questa strategia è solo una parte di un approccio più ampio di difesa in profondità.Tieni inoltre presente che bug semplici come buffer overflow e mancata sanificazione dell'input del programma rimangono di gran lunga i vettori di attacco più comuni.

Non è possibile proteggere il contenuto della memoria dal proprietario del sistema.Hollywood e l’industria musicale soffrono per questo da anni.Se fosse possibile lo starebbero già facendo.

Hai dato un'occhiata a Vista (e versioni successive) Processi protetti (diretto Scarica il file .doc).Credo che la protezione applicata dal sistema operativo sia una gentile concessione dell'industria dell'intrattenimento.

@Derek:Oh, ma con il Trusted Computing puoi usare cortina di memoria!:-P</devils-advocate>

@roo

Speravo davvero che fosse possibile e che non l'avessi ancora trovato.Il tuo esempio mi ha fatto capire che è esattamente ciò che stiamo cercando di fare: consentire l'accesso ai file solo nel contesto del nostro programma e quindi preservare l'IP.

Immagino di dover accettare che non esiste un modo veramente sicuro per archiviare i file di qualcuno su un altro computer, soprattutto se a un certo punto l'accesso a quel file viene consentito dal proprietario.

Questo è sicuramente il problema.Puoi archiviare qualcosa in modo sicuro finché non concedi mai l'accesso, ma non appena concedi l'accesso, il tuo controllo scompare.Puoi renderlo un po' più difficile, ma questo è tutto.

@Chris

Oh, ma con il Trusted Computing puoi usare il memory Curtaining!:-P

Ma poi devi essere effettivamente disposto a pagare per un computer che possiede qualcun altro.:P

@Derek Park

Ha solo detto più difficile, non impossibile.PGP lo renderebbe più difficile, non impossibile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top