Não tente isto em casa! Ele pode travar seu sistema e, se você for realmente azarado, pode danificar um periférico ou tornar seu computador impossível de inicializar.
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 sem privilégios. Com um usuário sem privilégios, o comando é perfeitamente inofensivo porque você não pode abrir arquivos /dev/mem.
Quando você executa um comando como root, você deve saber o que está fazendo. O kernel às vezes o impede de fazer algo perigoso, mas nem sempre. /dev/memé uma daquelas coisas potencialmente perigosas em que você realmente deveria saber o que está fazendo.
Vou explicar como uma gravação /dev/memfunciona no Linux. O princípio geral seria o mesmo em outros Unices, mas coisas como opções de 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 trata esse arquivo de dispositivo. Por exemplo, escrever para /dev/meminvoca a função write_mememdrivers/char/mem.c . Esta 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 até aqui 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 de crw-r-----propriedade de root:kmem, então se você tentar abri-lo para escrita sem ser root, você receberá apenas “permissão negada” (EACCESS). Mas se você for root (ou se root tiver alterado as permissões deste arquivo), a abertura passa e você pode tentar uma gravação.
O código na write_memfunção faz algumas verificações de sanidade, 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 deslocamentos de arquivo 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 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 sobre os dados em blocos que podem caber em uma página 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, então o código precisa se organizar para mapear a 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 de forma permanente ou deve ser feito em tempo real; esse é o trabalho de xlate_dev_mem_ptr(e unxlate_dev_mem_ptrdesfaz tudo o que xlate_dev_mem_ptrfaz). Em seguida, a função copy_from_userlê do buffer que foi passado para owritechamada de 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 acontece antes dessa gravação. Dentro do loop, os page_is_allowedblocos funcionais acessam determinados endereços se a opção de configuração do kernel CONFIG_STRICT_DEVMEMestiver habilitada (que é o caso por padrão): somente endereços permitidos por devmem_is_allowedpodem ser acessados através /dev/memde , para outros a escrita falha com EPERM (operação não permitida). A descrição desta opção afirma:
Se esta opção estiver ativada e IO_STRICT_DEVMEM=n, o arquivo /dev/mem permitirá apenas acesso ao espaço do usuário ao espaço PCI e ao código do BIOS e às regiões de dados. Isso é suficiente para DOSEMU e X e todos os usuários comuns de /dev/mem.
Esta é uma descrição muito centrada em x86. Na verdade, mais genericamente, CONFIG_STRICT_DEVMEMbloqueia o acesso a endereços de memória física que mapeiam 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 eles excluem a RAM onde os dados do kernel e dos processos de usuário da terra são armazenados. A opção adicional CONFIG_IO_STRICT_DEVMEM(desativada a partir do Ubuntu 18.04) bloqueia acessos a endereços físicos que são reivindicados por um driver.
Endereços de memória física que mapeiam para RAM . Então, 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 para qual periférico enviar a loja. (Quando digo “o processador”, englobo controladores de periféricos que podem não ser do mesmo fabricante.) A RAM é apenas um desses periféricos. A forma como o despacho é 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 procura 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 armazenamento em cache e buffer podem estar envolvidos, mas em poucas palavras, após essa decomposição,barramento 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 neste endereço, caso em que 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 para um endereço que mapeia para a RAM não “faz” nada além de sobrescrever o valor que foi armazenado anteriormente nesse endereço, com a promessa de que um carregamento posterior no mesmo endereço devolverá o último valor armazenado. Mas até a RAM tem alguns endereços que não se comportam assim: tem alguns registradores que podem controlar coisas como taxa de atualização e voltagem.
Em geral, uma leitura ou gravação em um registrador de hardware faz o que o hardware está programado para fazer. A maioria dos acessos ao hardware funciona assim: o software (normalmente código do kernel) acessa um determinado endereço físico, este chega ao 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 que são distintos do carregamento e armazenamento da 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 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 pode desligar o computador (provavelmente há um endereço que faz isso de fato), substituir alguma configuração do BIOS que impossibilita a inicialização ou até mesmo atingir alguns buggy periférico de uma forma que o danifica.
alias Russian_roulette='dd if=/dev/urandom of=/dev/mem seek=$((4096*RANDOM+4096*32768*RANDOM))'
/dev/mem é um arquivo de dispositivo de caractere que é uma imagem da memória principal do computador. Ele pode ser usado, por exemplo, para examinar (e até mesmo corrigir) o sistema.
Portanto, em teoria, 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 efetivamente travar o sistema. Na prática, há limite. Da mesma página man:
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 através deste 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-----. Da Wiki do Ubuntu :
proteção /dev/mem
Alguns aplicativos (Xorg) precisam de acesso direto à memória física 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 de kernel CONFIG_STRICT_DEVMEM foi introduzida para bloquear o acesso à memória não-dispositivo (originalmente denominada 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 seria executado se essa opção estivesse ativada. De acordo com a duplicata 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 houver).
Existe um método sugerido na duplicata vinculada anteriormente busybox devmem, portanto, se você está determinado a mexer com a RAM, pode haver uma maneira, afinal.
Não tente isto em casa! Ele pode travar seu sistema e, se você for realmente azarado, pode danificar um periférico ou tornar seu computador impossível de inicializar.
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 sem privilégios. Com um usuário sem privilégios, o comando é perfeitamente inofensivo porque você não pode abrir arquivos
/dev/mem
.Quando você executa um comando como root, você deve saber o que está fazendo. O kernel às vezes o impede de fazer algo perigoso, mas nem sempre.
/dev/mem
é uma daquelas coisas potencialmente perigosas em que você realmente deveria saber o que está fazendo.Vou explicar como uma gravação
/dev/mem
funciona no Linux. O princípio geral seria o mesmo em outros Unices, mas coisas como opções de 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 trata esse arquivo de dispositivo. Por exemplo, escrever para
/dev/mem
invoca a funçãowrite_mem
emdrivers/char/mem.c
. Esta 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 até aqui 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ão decrw-r-----
propriedade deroot:kmem
, então se você tentar abri-lo para escrita sem ser root, você receberá apenas “permissão negada” (EACCESS). Mas se você for root (ou se root tiver alterado as permissões deste arquivo), a abertura passa e você pode tentar uma gravação.O código na
write_mem
função faz algumas verificações de sanidade, 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 deslocamentos de arquivo 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 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 sobre os dados em blocos que podem caber em uma página 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, então o código precisa se organizar para mapear a 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 de forma permanente ou deve ser feito em tempo real; esse é o trabalho dexlate_dev_mem_ptr
(eunxlate_dev_mem_ptr
desfaz tudo o quexlate_dev_mem_ptr
faz). Em seguida, a funçãocopy_from_user
lê do buffer que foi passado para owrite
chamada de 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 acontece antes dessa gravação. Dentro do loop, os
page_is_allowed
blocos funcionais acessam determinados endereços se a opção de configuração do kernelCONFIG_STRICT_DEVMEM
estiver habilitada (que é o caso por padrão): somente endereços permitidos pordevmem_is_allowed
podem ser acessados através/dev/mem
de , para outros a escrita falha com EPERM (operação não permitida). A descrição desta opção afirma:Esta é uma descrição muito centrada em x86. Na verdade, mais genericamente,
CONFIG_STRICT_DEVMEM
bloqueia o acesso a endereços de memória física que mapeiam 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 eles excluem a RAM onde os dados do kernel e dos processos de usuário da terra são armazenados. A opção adicionalCONFIG_IO_STRICT_DEVMEM
(desativada a partir do Ubuntu 18.04) bloqueia acessos a endereços físicos que são reivindicados por um driver.Endereços de memória física que mapeiam para RAM . Então, 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 para qual periférico enviar a loja. (Quando digo “o processador”, englobo controladores de periféricos que podem não ser do mesmo fabricante.) A RAM é apenas um desses periféricos. A forma como o despacho é 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 procura 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 armazenamento em cache e buffer podem estar envolvidos, mas em poucas palavras, após essa decomposição,barramento 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 neste endereço, caso em que 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 para um endereço que mapeia para a RAM não “faz” nada além de sobrescrever o valor que foi armazenado anteriormente nesse endereço, com a promessa de que um carregamento posterior no mesmo endereço devolverá o último valor armazenado. Mas até a RAM tem alguns endereços que não se comportam assim: tem alguns registradores que podem controlar coisas como taxa de atualização e voltagem.
Em geral, uma leitura ou gravação em um registrador de hardware faz o que o hardware está programado para fazer. A maioria dos acessos ao hardware funciona assim: o software (normalmente código do kernel) acessa um determinado endereço físico, este chega ao 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 que são distintos do carregamento e armazenamento da 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 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 pode desligar o computador (provavelmente há um endereço que faz isso de fato), substituir alguma configuração do BIOS que impossibilita a inicialização ou até mesmo atingir alguns buggy periférico de uma forma que o danifica.É seguro, se você configurou o kernel corretamente (seguro porque não funcionará)
Por página de manual mem(4) :
Portanto, em teoria,
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 efetivamente travar o sistema. Na prática, há limite. Da mesma página man: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-----
. Da Wiki do Ubuntu :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 seria executado se essa opção estivesse ativada. De acordo com a duplicata 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 houver).Existe um método sugerido na duplicata vinculada anteriormente
busybox devmem
, portanto, se você está determinado a mexer com a RAM, pode haver uma maneira, afinal.