O que é esse "seguro" de que você fala? Seguro em relação a quê?
27519 waltinator
O que você deseja alcançar com este comando?
jochen
Respostas:
23
Não tente isto em casa! Ele pode travar o sistema e, se você tiver realmente azar, pode danificar um periférico ou tornar seu computador não inicializável.
Na verdade, na maioria das plataformas, apenas falha com um erro, mas isso depende da arquitetura do hardware. Definitivamente, não há garantia de que isso seja inofensivo, a menos que você execute o comando como um usuário não privilegiado. Com um usuário sem privilégios, o comando é perfeitamente inofensivo porque você não pode abrir /dev/mem.
Quando você executa um comando como root, deve saber o que está fazendo. Às vezes , o kernel impede que você faça algo perigoso, mas nem sempre. /dev/memé uma daquelas coisas potencialmente perigosas em que você realmente deve saber o que está fazendo.
Vou mostrar como funciona uma gravação /dev/memno Linux. O princípio geral seria o mesmo em outros Unices, mas coisas como as opções do kernel são completamente diferentes.
O que acontece quando um processo lê ou grava em um arquivo de dispositivo depende do kernel. Um acesso a um arquivo de dispositivo executa algum código no driver que lida com esse arquivo de dispositivo. Por exemplo, escrever para /dev/meminvoca a função write_memindrivers/char/mem.c . Essa função recebe 4 argumentos: uma estrutura de dados que representa o arquivo aberto, um ponteiro para os dados a serem gravados, o número de bytes a serem gravados e a posição atual no arquivo.
Observe que você só chega tão longe se o chamador tiver permissão para abrir o arquivo em primeiro lugar. Os arquivos do dispositivo obedecem às permissões de arquivo normalmente. As permissões normais de /dev/memsão crw-r-----propriedade de root:kmem, por isso, se você tentar abri-lo para a escrita sem ser raiz, você só vai obter “permissão negada” (eAccess). Mas se você é root (ou se o root alterou as permissões deste arquivo), a abertura continua e você pode tentar escrever.
O código na write_memfunção faz algumas verificações de integridade, mas essas verificações não são suficientes para proteger contra tudo de ruim. A primeira coisa que faz é converter a posição atual do arquivo *pposem um endereço físico. Se isso falhar (na prática, porque você está em uma plataforma com endereços físicos de 32 bits, mas compensa os arquivos de 64 bits e o deslocamento do arquivo é maior que 2 ^ 32), a gravação falha com EFBIG (arquivo muito grande). A próxima verificação é se o intervalo de endereços físicos a serem gravados é válido nessa arquitetura de processador específica e se uma falha resulta em EFAULT (endereço incorreto).
Em seguida, no Sparc e no m68k, qualquer parte da gravação na primeira página física é ignorada silenciosamente.
Chegamos agora ao loop principal que itera os dados em blocos que podem caber em uma página da MMU .
/dev/memacessa a memória física, não a memória virtual, mas as instruções do processador para carregar e armazenar dados na memória usam endereços virtuais; portanto, o código precisa organizar o mapeamento da memória física em algum endereço virtual. No Linux, dependendo da arquitetura do processador e da configuração do kernel, esse mapeamento existe permanentemente ou deve ser feito em tempo real; esse é o trabalho de xlate_dev_mem_ptr(e unxlate_dev_mem_ptrdesfaz o que xlate_dev_mem_ptrfaz). Em seguida, a função copy_from_userlê do buffer que foi passado para owritechamada do sistema e apenas grava no endereço virtual onde a memória física está atualmente mapeada. O código emite instruções normais de armazenamento de memória e o que isso significa depende do hardware.
Antes de discutir o que uma gravação em um endereço físico faz, discutirei uma verificação que ocorre antes dessa gravação. Dentro do loop, os page_is_allowedblocos de funções acessam determinados endereços se a opção de configuração do kernel CONFIG_STRICT_DEVMEMestiver ativada (que é o caso por padrão): somente endereços permitidos devmem_is_allowedpodem ser alcançados /dev/mem; para outros, a gravação falha com EPERM (operação não permitida). A descrição desta opção indica:
Se essa opção estiver ativada e IO_STRICT_DEVMEM = n, o arquivo / dev / mem permitirá apenas o espaço do usuário acessar o espaço PCI e o código do BIOS e as regiões de dados. Isso é suficiente para o dosemu e o X e todos os usuários comuns de / dev / mem.
Esta é uma descrição muito centrada em x86. De fato, mais genericamente, CONFIG_STRICT_DEVMEMbloqueia o acesso a endereços de memória física que são mapeados para a RAM, mas permite o acesso a endereços que não são mapeados para a RAM. Os detalhes de quais intervalos de endereços físicos são permitidos dependem da arquitetura do processador, mas todos excluem a RAM na qual os dados do kernel e dos processos de terra do usuário são armazenados. A opção adicional CONFIG_IO_STRICT_DEVMEM(desativada no Ubuntu 18.04) bloqueia o acesso a endereços físicos reivindicados por um driver.
Endereços de memória física que são mapeados para a RAM . Portanto, existem endereços de memória física que não são mapeados para a RAM? Sim. Essa é a discussão que prometi acima sobre o que significa escrever para um endereço.
Uma instrução de armazenamento de memória não necessariamente grava na RAM. O processador decompõe o endereço e decide em qual periférico enviar o armazenamento. (Quando digo "o processador", abro controladores periféricos que podem não ser do mesmo fabricante.) A RAM é apenas um desses periféricos. O modo como o envio é feito depende muito da arquitetura do processador, mas os fundamentos são mais ou menos os mesmos em todas as arquiteturas. O processador basicamente decompõe os bits mais altos do endereço e os pesquisa em algumas tabelas que são preenchidas com base em informações codificadas, informações obtidas pela sondagem de alguns barramentos e informações configuradas pelo software. Muito cache e buffer podem estar envolvidos, mas, em poucas palavras, após essa decomposição,ônibus e então cabe ao periférico lidar com isso. (Ou o resultado da pesquisa de tabela pode ser que não há periférico nesse endereço; nesse caso, o processador entra em um estado de trap onde executa algum código no kernel que normalmente resulta em um SIGBUS para o processo de chamada.)
Um armazenamento em um endereço que mapeia para a RAM não "substitui" o valor que foi anteriormente armazenado nesse endereço, com a promessa de que uma carga posterior no mesmo endereço retornará o último valor armazenado. Mas mesmo a RAM possui alguns endereços que não se comportam dessa maneira: possui alguns registros que podem controlar coisas como taxa de atualização e voltagem.
Em geral, uma leitura ou gravação em um registro de hardware faz o que o hardware está programado para fazer. A maioria dos acessos ao hardware funciona da seguinte maneira: o software (normalmente código do kernel) acessa um determinado endereço físico, atinge o barramento que conecta o processador ao periférico, e o periférico faz o seu trabalho. Alguns processadores (em particular o x86) também possuem instruções de CPU separadas que causam leituras / gravações em periféricos distintos do carregamento e armazenamento de memória, mas mesmo no x86, muitos periféricos são alcançados através do carregamento / armazenamento.
O comando dd if=/dev/urandom of=/dev/memgrava dados aleatórios em qualquer periférico mapeado no endereço 0 (e nos endereços subsequentes, desde que as gravações sejam bem-sucedidas). Na prática, espero que em muitas arquiteturas, o endereço físico 0 não tenha nenhum periférico mapeado para ele ou tenha RAM e, portanto, a primeira tentativa de gravação falhe. Mas se houver um periférico mapeado no endereço 0, ou se você alterar o comando para gravar em um endereço diferente, acionará algo imprevisível no periférico. Com dados aleatórios em endereços crescentes, é improvável que faça algo interessante, mas, em princípio, ele pode desligar o computador (provavelmente há um endereço que faça isso de fato), substituir algumas configurações da BIOS que impossibilitem a inicialização ou até atingir algumas periférico de buggy de uma maneira que o danifique.
alias Russian_roulette='dd if=/dev/urandom of=/dev/mem seek=$((4096*RANDOM+4096*32768*RANDOM))'
Obrigado! Era isso que eu estava procurando! Fiquei confuso se o / dev / mem permite acesso à memória de coisas como periféricos e coisas relacionadas a hardware!
Coder14
Um periférico não pode ser mapeado no endereço físico 0 no x86 pc; essa configuração nunca inicializaria.
27719 Joshua
Isso não é verdade
Yvain
1
Claro que você pode danificar o kernel, mas não o BIOS
Yvain
1
@Yvain O que não é verdade? E, na verdade, você não pode danificar o kernel se CONFIG_STRICT_DEVMEMestiver ativado.
Gilles 'SO- stop be evil'
12
É seguro, se você tiver configurado o kernel corretamente (seguro porque não funcionará)
/ dev / mem é um arquivo de dispositivo de caractere que é uma imagem da memória principal do computador. Pode ser usado, por exemplo, para examinar (e até corrigir) o sistema.
Portanto, em teoria, você dd if=/dev/urandom of=/dev/memdeve substituir todo o espaço de endereço da memória física que você instalou e, como o kernel e outros programas são executados a partir da memória, isso deve travar o sistema. Na prática, há limite. Na mesma página do manual:
Desde o Linux 2.6.26, e dependendo da arquitetura, a opção de configuração do kernel CONFIG_STRICT_DEVMEM limita as áreas que podem ser acessadas por esse arquivo.
Tentando isso na máquina virtual Ubuntu 18.04, ele retorna um erro dd: writing to '/dev/mem': Operation not permittedmesmo com sudoe apesar das permissões para root crw-r-----. Do Ubuntu Wiki :
/ dev / proteção de mem
Alguns aplicativos (Xorg) precisam de acesso direto à memória física a partir do espaço do usuário. O arquivo especial / dev / mem existe para fornecer esse acesso. No passado, era possível visualizar e alterar a memória do kernel desse arquivo se um invasor tivesse acesso root. A opção do kernel CONFIG_STRICT_DEVMEM foi introduzida para bloquear o acesso à memória que não é do dispositivo (originalmente chamado CONFIG_NONPROMISC_DEVMEM).
Então, tecnicamente, não, não é seguro (já que travaria o sistema) e se a opção do kernel CONFIG_STRICT_DEVMEMestiver desativada, isso é uma falha de segurança, mas pelo que vejo até agora, o comando não funcionaria se essa opção estiver ativada. De acordo com a duplicação entre sites , uma reinicialização corrigirá quaisquer problemas com ela, mas é claro que os dados na RAM naquele momento seriam perdidos e não liberados para o disco (se necessário).
Há um método sugerido no duplicado vinculado anteriormente, busybox devmemportanto, se você estiver determinado a mexer com a RAM, pode haver uma maneira, afinal.
"É seguro" Não, certamente não é. Mesmo com CONFIG_STRICT_DEVMEM, você pode acessar regiões de memória onde um periférico é mapeado, que é o ponto principal de se ter /dev/mem. Se você escreve coisas aleatórias em periféricos, tudo pode acontecer. Você obtém "operação não permitida" se tentar acessar um endereço que não está mapeado e o comando inicia no endereço 0. Se o endereço 0 é mapeado para algo ruim, depende da arquitetura do hardware. Pelo que sei, ele pode nunca ser mapeado para qualquer coisa em um PC, mas não é seguro em geral.
Gilles 'SO- stop be evil'
1
@Gilles No x86 (não tenho certeza sobre o x86-64), o primeiro 1 KiB de RAM (endereços 0x0 a 0x3ff) mantém os vetores de interrupção; endereço de quatro bytes por vetor. Se você conseguir substituir aqueles com lixo aleatório, todos os tipos de coisas interessantes provavelmente acontecerão muito em breve. Provavelmente, você vai acabar com uma falha dobro ou o triplo eo sistema irá falhar, mas não há garantias ...
um CVn
@aCVn Certamente há algo mapeado ( head -c 1024 </dev/mem | od -tx1), mas não sei se eles são usados quando o processador não está no modo real (modo 8088). Eu não acho que eles possam ser usados no modo de 64 bits: afinal, os vetores de interrupção 8088 têm apenas 32 bits para o endereço. E pela maneira como isso é acessível com o CONFIG_STRICT_DEVMEMset, acho que o Linux não o usa.
Gilles 'SO- stop be evil'
@Gilles: A página 0 no x86 é reservada para v86, gerenciador de inicialização, etc; essa é a tabela de vetores de interrupção no modo real lá. No modo protegido, o IVT está em outro lugar (um registro da máquina diz onde).
Respostas:
Não tente isto em casa! Ele pode travar o sistema e, se você tiver realmente azar, pode danificar um periférico ou tornar seu computador não inicializável.
Na verdade, na maioria das plataformas, apenas falha com um erro, mas isso depende da arquitetura do hardware. Definitivamente, não há garantia de que isso seja inofensivo, a menos que você execute o comando como um usuário não privilegiado. Com um usuário sem privilégios, o comando é perfeitamente inofensivo porque você não pode abrir
/dev/mem
.Quando você executa um comando como root, deve saber o que está fazendo. Às vezes , o kernel impede que você faça algo perigoso, mas nem sempre.
/dev/mem
é uma daquelas coisas potencialmente perigosas em que você realmente deve saber o que está fazendo.Vou mostrar como funciona uma gravação
/dev/mem
no Linux. O princípio geral seria o mesmo em outros Unices, mas coisas como as opções do kernel são completamente diferentes.O que acontece quando um processo lê ou grava em um arquivo de dispositivo depende do kernel. Um acesso a um arquivo de dispositivo executa algum código no driver que lida com esse arquivo de dispositivo. Por exemplo, escrever para
/dev/mem
invoca a funçãowrite_mem
indrivers/char/mem.c
. Essa função recebe 4 argumentos: uma estrutura de dados que representa o arquivo aberto, um ponteiro para os dados a serem gravados, o número de bytes a serem gravados e a posição atual no arquivo.Observe que você só chega tão longe se o chamador tiver permissão para abrir o arquivo em primeiro lugar. Os arquivos do dispositivo obedecem às permissões de arquivo normalmente. As permissões normais de
/dev/mem
sãocrw-r-----
propriedade deroot:kmem
, por isso, se você tentar abri-lo para a escrita sem ser raiz, você só vai obter “permissão negada” (eAccess). Mas se você é root (ou se o root alterou as permissões deste arquivo), a abertura continua e você pode tentar escrever.O código na
write_mem
função faz algumas verificações de integridade, mas essas verificações não são suficientes para proteger contra tudo de ruim. A primeira coisa que faz é converter a posição atual do arquivo*ppos
em um endereço físico. Se isso falhar (na prática, porque você está em uma plataforma com endereços físicos de 32 bits, mas compensa os arquivos de 64 bits e o deslocamento do arquivo é maior que 2 ^ 32), a gravação falha com EFBIG (arquivo muito grande). A próxima verificação é se o intervalo de endereços físicos a serem gravados é válido nessa arquitetura de processador específica e se uma falha resulta em EFAULT (endereço incorreto).Em seguida, no Sparc e no m68k, qualquer parte da gravação na primeira página física é ignorada silenciosamente.
Chegamos agora ao loop principal que itera os dados em blocos que podem caber em uma página da MMU .
/dev/mem
acessa a memória física, não a memória virtual, mas as instruções do processador para carregar e armazenar dados na memória usam endereços virtuais; portanto, o código precisa organizar o mapeamento da memória física em algum endereço virtual. No Linux, dependendo da arquitetura do processador e da configuração do kernel, esse mapeamento existe permanentemente ou deve ser feito em tempo real; esse é o trabalho dexlate_dev_mem_ptr
(eunxlate_dev_mem_ptr
desfaz o quexlate_dev_mem_ptr
faz). Em seguida, a funçãocopy_from_user
lê do buffer que foi passado para owrite
chamada do sistema e apenas grava no endereço virtual onde a memória física está atualmente mapeada. O código emite instruções normais de armazenamento de memória e o que isso significa depende do hardware.Antes de discutir o que uma gravação em um endereço físico faz, discutirei uma verificação que ocorre antes dessa gravação. Dentro do loop, os
page_is_allowed
blocos de funções acessam determinados endereços se a opção de configuração do kernelCONFIG_STRICT_DEVMEM
estiver ativada (que é o caso por padrão): somente endereços permitidosdevmem_is_allowed
podem ser alcançados/dev/mem
; para outros, a gravação falha com EPERM (operação não permitida). A descrição desta opção indica:Esta é uma descrição muito centrada em x86. De fato, mais genericamente,
CONFIG_STRICT_DEVMEM
bloqueia o acesso a endereços de memória física que são mapeados para a RAM, mas permite o acesso a endereços que não são mapeados para a RAM. Os detalhes de quais intervalos de endereços físicos são permitidos dependem da arquitetura do processador, mas todos excluem a RAM na qual os dados do kernel e dos processos de terra do usuário são armazenados. A opção adicionalCONFIG_IO_STRICT_DEVMEM
(desativada no Ubuntu 18.04) bloqueia o acesso a endereços físicos reivindicados por um driver.Endereços de memória física que são mapeados para a RAM . Portanto, existem endereços de memória física que não são mapeados para a RAM? Sim. Essa é a discussão que prometi acima sobre o que significa escrever para um endereço.
Uma instrução de armazenamento de memória não necessariamente grava na RAM. O processador decompõe o endereço e decide em qual periférico enviar o armazenamento. (Quando digo "o processador", abro controladores periféricos que podem não ser do mesmo fabricante.) A RAM é apenas um desses periféricos. O modo como o envio é feito depende muito da arquitetura do processador, mas os fundamentos são mais ou menos os mesmos em todas as arquiteturas. O processador basicamente decompõe os bits mais altos do endereço e os pesquisa em algumas tabelas que são preenchidas com base em informações codificadas, informações obtidas pela sondagem de alguns barramentos e informações configuradas pelo software. Muito cache e buffer podem estar envolvidos, mas, em poucas palavras, após essa decomposição,ônibus e então cabe ao periférico lidar com isso. (Ou o resultado da pesquisa de tabela pode ser que não há periférico nesse endereço; nesse caso, o processador entra em um estado de trap onde executa algum código no kernel que normalmente resulta em um SIGBUS para o processo de chamada.)
Um armazenamento em um endereço que mapeia para a RAM não "substitui" o valor que foi anteriormente armazenado nesse endereço, com a promessa de que uma carga posterior no mesmo endereço retornará o último valor armazenado. Mas mesmo a RAM possui alguns endereços que não se comportam dessa maneira: possui alguns registros que podem controlar coisas como taxa de atualização e voltagem.
Em geral, uma leitura ou gravação em um registro de hardware faz o que o hardware está programado para fazer. A maioria dos acessos ao hardware funciona da seguinte maneira: o software (normalmente código do kernel) acessa um determinado endereço físico, atinge o barramento que conecta o processador ao periférico, e o periférico faz o seu trabalho. Alguns processadores (em particular o x86) também possuem instruções de CPU separadas que causam leituras / gravações em periféricos distintos do carregamento e armazenamento de memória, mas mesmo no x86, muitos periféricos são alcançados através do carregamento / armazenamento.
O comando
dd if=/dev/urandom of=/dev/mem
grava dados aleatórios em qualquer periférico mapeado no endereço 0 (e nos endereços subsequentes, desde que as gravações sejam bem-sucedidas). Na prática, espero que em muitas arquiteturas, o endereço físico 0 não tenha nenhum periférico mapeado para ele ou tenha RAM e, portanto, a primeira tentativa de gravação falhe. Mas se houver um periférico mapeado no endereço 0, ou se você alterar o comando para gravar em um endereço diferente, acionará algo imprevisível no periférico. Com dados aleatórios em endereços crescentes, é improvável que faça algo interessante, mas, em princípio, ele pode desligar o computador (provavelmente há um endereço que faça isso de fato), substituir algumas configurações da BIOS que impossibilitem a inicialização ou até atingir algumas periférico de buggy de uma maneira que o danifique.fonte
CONFIG_STRICT_DEVMEM
estiver ativado.É seguro, se você tiver configurado o kernel corretamente (seguro porque não funcionará)
Por página de manual mem (4) :
Portanto, em teoria, você
dd if=/dev/urandom of=/dev/mem
deve substituir todo o espaço de endereço da memória física que você instalou e, como o kernel e outros programas são executados a partir da memória, isso deve travar o sistema. Na prática, há limite. Na mesma página do manual:Tentando isso na máquina virtual Ubuntu 18.04, ele retorna um erro
dd: writing to '/dev/mem': Operation not permitted
mesmo comsudo
e apesar das permissões para rootcrw-r-----
. Do Ubuntu Wiki :Então, tecnicamente, não, não é seguro (já que travaria o sistema) e se a opção do kernel
CONFIG_STRICT_DEVMEM
estiver desativada, isso é uma falha de segurança, mas pelo que vejo até agora, o comando não funcionaria se essa opção estiver ativada. De acordo com a duplicação entre sites , uma reinicialização corrigirá quaisquer problemas com ela, mas é claro que os dados na RAM naquele momento seriam perdidos e não liberados para o disco (se necessário).Há um método sugerido no duplicado vinculado anteriormente,
busybox devmem
portanto, se você estiver determinado a mexer com a RAM, pode haver uma maneira, afinal.fonte
CONFIG_STRICT_DEVMEM
, você pode acessar regiões de memória onde um periférico é mapeado, que é o ponto principal de se ter/dev/mem
. Se você escreve coisas aleatórias em periféricos, tudo pode acontecer. Você obtém "operação não permitida" se tentar acessar um endereço que não está mapeado e o comando inicia no endereço 0. Se o endereço 0 é mapeado para algo ruim, depende da arquitetura do hardware. Pelo que sei, ele pode nunca ser mapeado para qualquer coisa em um PC, mas não é seguro em geral.head -c 1024 </dev/mem | od -tx1
), mas não sei se eles são usados quando o processador não está no modo real (modo 8088). Eu não acho que eles possam ser usados no modo de 64 bits: afinal, os vetores de interrupção 8088 têm apenas 32 bits para o endereço. E pela maneira como isso é acessível com oCONFIG_STRICT_DEVMEM
set, acho que o Linux não o usa.