Pergunta

Quero criar um alocador que forneça memória com os seguintes atributos:

  • não pode ser paginado para o disco.
  • é incrivelmente difícil de acessar através de um depurador anexado

A ideia é que contenha informações confidenciais (como informações de licença) que devem ser inacessíveis ao usuário.Fiz a pesquisa usual on-line e perguntei a algumas outras pessoas sobre isso, mas não consigo encontrar um bom ponto de partida para esse problema.

Atualizações

Josh menciona usando VirtualAlloc para definir a proteção no espaço de memória.Eu criei um alocador personalizado (mostrado abaixo). Encontrei o usando o VirtualLock função limita a quantidade de memória que posso alocar.No entanto, isso parece ser intencional.Como estou usando para objetos pequenos, isso não é um 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 é usado

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

Ted Percival menciona mlock, mas ainda não tenho implementação disso.

eu encontrei Criptografia Prática por Neil Furguson e Bruce Schneier bastante útil também.

Foi útil?

Solução

Você realmente não pode proteger contra acesso à memória.Provavelmente, você pode impedir a paginação se estiver executando como administrador ou como sistema, mas não pode impedir que o administrador ou o sistema leia sua memória.Mesmo que você pudesse de alguma forma bloquear completamente a leitura de sua memória por outros processos (o que você não pode), outro processo ainda poderia injetar um novo thread em seu processo e ler a memória dessa maneira.

Mesmo se você pudesse de alguma forma bloquear completamente o seu processo e garantir que o sistema operacional nunca permitir que qualquer outra pessoa acesse seu processo, você ainda não terá proteção total.Todo o sistema operacional poderia estar rodando em uma máquina virtual, que poderia ser pausada e inspecionada a qualquer momento.

Você não pode proteger o conteúdo da memória do proprietário do sistema.Hollywood e a indústria musical estão sofrendo por isso há anos.Se fosse possível, eles já estariam fazendo isso.

Outras dicas

Em sistemas Unix você pode usar mllock(2) para bloquear páginas de memória na RAM, evitando que sejam paginadas.

mlock () e mlockall (), respectivamente, bloqueiam a parte ou toda a parte do espaço de endereço virtual do processo de chamada na RAM, impedindo que essa memória seja paginada na área de troca.

Existe um limite de quantidade de memória que cada processo pode bloquear, isso pode ser mostrado com ulimit -l e é medido em quilobytes.No meu sistema, o limite padrão é 32 KiB por processo.

Se você estiver desenvolvendo para Windows, existem maneiras de restringir o acesso à memória, mas bloquear totalmente outras pessoas não é possível.Se você deseja manter um segredo secreto, leia Escrevendo código seguro - que aborda esse problema com certa profundidade, mas esteja ciente de que você não tem como saber se o seu código está sendo executado em uma máquina real ou virtual.Há um monte de coisas da API Win32 para lidar com criptografia que lida com esse tipo de coisa, incluindo armazenamento seguro de segredos - o livro fala sobre isso.Você pode olhar on-line CyproAPI da Microsoft para detalhes;os designers do sistema operacional reconhecem esse mesmo problema e a necessidade de manter o texto não criptografado seguro (novamente, leia Escrevendo código seguro).

A função da API Win32 VirtualAlloc é o alocador de memória no nível do sistema operacional.Permite definir proteção de acesso;o que você pode fazer é definir o acesso a PAGE_GUARD ou PAGE_NOACCESS, e mude o acesso para algo mais amigável enquanto seu programa lê e redefina-o depois, mas isso é apenas um obstáculo se alguém estiver realmente se esforçando para espiar seu segredo.

Em resumo, observe as APIs criptográficas em sua plataforma, elas resolverão o problema melhor do que algo que você mesmo hackeia.

Vamos analisar isso um pouco de cada vez:

Quero criar um alocador que fornece memória com os seguintes atributos:

Isso é justo.

* cannot be paged to disk.

Isso vai ser difícil.Pelo que sei, você não pode desativar a paginação virtual, pois ela é controlada pelo sistema operacional.Se houver uma maneira, você estará explorando as entranhas do sistema operacional.

* is incredibly hard to access through an attached debugger

Você pode executá-lo através do PGP e armazená-lo criptografado na memória e descriptografá-lo conforme necessário.Grande impacto no desempenho.

A idéia é que isso conterá informações confidenciais (como informações de licença) que devem ser inacessíveis para o usuário.Fiz a pesquisa usual on -line e perguntei a algumas outras pessoas sobre isso, mas não consigo encontrar um bom lugar para começar esse problema.

Mantenha todas as informações confidenciais fora da máquina.Seriamente.Não armazene informações confidenciais na memória.Escreva uma rotina de exclusão personalizada que removerá automaticamente todos os dados de qualquer alocação que você realizar.Nunca permita o acesso geral a uma máquina que contenha material sensível.Se você realizar acesso ao banco de dados, certifique-se de que todo o acesso esteja limpo antes de disparar.Somente pessoas com logins específicos têm permissão para acessar.Sem acesso geral ao grupo.

Em uma nota lateral, que outros métodos existem de acessar a memória de um processo além de anexar um depurador?

Fazendo um despejo na memória.

instale o Libsodium, use mecanismos de alocação #incluindo <sodium.h>

Alocações de heap protegidas

Mais lentos que malloc() e amigos, eles requerem 3 ou 4 páginas extras de memória virtual.

void *sodium_malloc(size_t size);

Aloque memória para armazenar dados confidenciais usando sodium_malloc() e sodium_allocarray().Você precisará ligar primeiro sodium_init() antes de usar esses protetores de heap.

void *sodium_allocarray(size_t count, size_t size);

O sodium_allocarray() A função retorna um ponteiro a partir do qual objetos de contagem com tamanho de bytes de memória cada um podem ser acessados.Oferece as mesmas garantias que sodium_malloc() mas também protege contra estouros aritméticos quando count * size excede SIZE_MAX.

Essas funções adicionam páginas de proteção em torno dos dados protegidos para torná-los menos propensos a serem acessíveis em um cenário semelhante ao heartbleed.

Além disso, a proteção para regiões de memória alocadas dessa forma pode ser alterada usando as operações de bloqueio de memória: sodium_mprotect_noaccess(), sodium_mprotect_readonly() e sodium_mprotect_readwrite().

Depois sodium_malloc você pode usar sodium_free() para desbloquear e desalocar memória.Neste ponto da sua implementação, considere zerar a memória após o uso.

zerar a memória após o uso

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

Após o uso, os dados confidenciais devem ser substituídos, mas memset() e o código escrito à mão podem ser eliminados silenciosamente por um compilador otimizador ou pelo vinculador.

A função sódio_memzero() tenta efetivamente zerar len bytes começando em pnt, mesmo se otimizações estiverem sendo aplicadas ao código.

bloqueando a alocação de memória

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

O sodium_mlock() A função bloqueia pelo menos len bytes de memória começando em addr.Isso pode ajudar a evitar a troca de dados confidenciais para o disco.

int sodium_mprotect_noaccess(void *ptr);

A função sódio_mprotect_noaccess() torna uma região alocada usando sódio_malloc() ou sódio_allocarray() inacessível.Não pode ser lido ou escrito, mas os dados são preservados.Esta função pode ser usada para tornar inacessíveis dados confidenciais, exceto quando realmente necessários para uma operação específica.

int sodium_mprotect_readonly(void *ptr);

A função sódio_mprotect_readonly() marca uma região alocada usando sódio_malloc() ou sódio_allocarray() como somente leitura.A tentativa de modificar os dados fará com que o processo seja encerrado.

int sodium_mprotect_readwrite(void *ptr);

O sodium_mprotect_readwrite() função marca uma região alocada usando sodium_malloc() ou sodium_allocarray() como legível e gravável, depois de ter sido protegido usando sodium_mprotect_readonly() ou sodium_mprotect_noaccess().

O que você está pedindo é tratado no nível do sistema operacional.Assim que os dados estiverem em seu programa, eles poderão ser paginados.

Para acessar a memória, um indivíduo motivado pode anexar um depurador de hardware.

@graham

Você pode executá-lo através do PGP e armazená-lo criptografado na memória e descriptografá-lo conforme necessário.Grande impacto no desempenho.

Então você teria que manter a chave na memória.Isso tornaria tudo um pouco mais difícil, mas definitivamente não impossível.Qualquer pessoa motivada ainda conseguirá obter os dados da memória.

Sua melhor aposta é implementar algo semelhante à classe SecureString do .NET e ter muito cuidado para zerar quaisquer cópias de texto simples de seus dados assim que terminar (não se esqueça de limpar mesmo quando exceções forem lançadas).Uma boa maneira de fazer isso com std::string e outros é usar um alocador personalizado.

No Windows, se você usar CryptProtectMemory (ou RtlEncryptMemory para sistemas mais antigos), a senha de criptografia será armazenada em memória não paginável (kernel?).Nos meus testes, essas funções são muito rápidas, especialmente.levando em conta a proteção que eles estão lhe dando.

Em outros sistemas, gosto de usar Blowfish porque é uma boa mistura entre velocidade e força.Neste último caso, você terá que gerar aleatoriamente sua própria senha (mais de 16 bytes de entropia para Blowfish) na inicialização do programa.Infelizmente, não há muito que você possa fazer para proteger essa senha sem o suporte do sistema operacional, embora você possa usar técnicas gerais de ofuscação para incorporar um valor salt codificado em seu executável que você pode combinar com a senha (cada pouquinho ajuda).

No geral, esta estratégia é apenas uma parte de uma abordagem mais ampla de defesa em profundidade.Lembre-se também de que bugs simples, como buffer overflows e não higienização da entrada do programa, continuam sendo, de longe, os vetores de ataque mais comuns.

Você não pode proteger o conteúdo da memória do proprietário do sistema.Hollywood e a indústria musical estão sofrendo por isso há anos.Se fosse possível, eles já estariam fazendo isso.

Você já deu uma olhada no Vista (e acima) Processos Protegidos (direto baixar .doc).Acredito que a proteção imposta pelo sistema operacional é cortesia da indústria do entretenimento.

@Derek:Ah, mas com computação confiável, você pode usar cortina de memória!:-P</devils-advocate>

@roo

Eu realmente esperava que isso fosse possível e que ainda não tivesse encontrado.Seu exemplo me fez perceber que é exatamente isso que estamos tentando fazer - permitir apenas o acesso aos arquivos no contexto do nosso programa e assim preservar o IP.

Acho que tenho que aceitar que não existe uma maneira verdadeiramente segura de armazenar os arquivos de alguém em outro computador, especialmente se em algum momento o proprietário permitir o acesso a esse arquivo.

Esse é definitivamente o problema.Você pode armazenar algo com segurança, desde que nunca conceda acesso, mas assim que você concede acesso, seu controle desaparece.Você pode tornar isso um pouco mais difícil, mas isso é tudo.

@Chris

Ah, mas com uma computação confiável, você pode usar a proteção de memória!:-P

Mas então você precisa estar realmente disposto a pagar por um computador de propriedade de outra pessoa.:p

@Derek Parque

Ele apenas disse mais difícil, não impossível.O PGP tornaria tudo mais difícil, não impossível.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top