Análise do Bootkit

Olá pessoal! Em conexão com o lançamento do curso “Engenharia Reversa”, realizamos uma lição aberta planejada. Desmontou o algoritmo de operação do kit de inicialização em diferentes estágios de carregamento.



Professor - Arthur Pakulov , analista de vírus da Kaspersky Lab.

O artigo a seguir é introdutório e é uma versão em texto de apenas parte da lição, dedicada ao instalador do bootkit. Uma análise detalhada do próprio kit de inicialização, veja o vídeo.

Um kit de inicialização é um programa malicioso que modifica o Master Boot Record, o primeiro setor do primeiro disco físico ou setor de inicialização, o VBR. Programas desse tipo, basicamente, têm uma funcionalidade Trojan e são usados ​​para executar qualquer ação oculta no sistema. Em nosso exemplo, o kit de inicialização executa a escalação de privilégios para o nível do processo, cujo nome começa com uma sequência de letras: “inte”. De fato, um kit de inicialização é um rootkit que começa a funcionar no anel de proteção 0, que é iniciado antes mesmo do sistema operacional começar a carregar. É por isso que ele é de grande interesse para a pesquisa.

Para desenvolver esse programa, as habilidades usuais de engenharia reversa não são suficientes. Não basta apenas ler a listagem, você também precisa entender coisas como arquitetura do processador, endereçamento de memória etc. Examinamos os principais locais do kit de inicialização em uma lição aberta.

Para o trabalho, foi preparado um exemplo especial de bootkit-xp.exe, funcionando no Windows XP. Portanto, além de estudar o kit de inicialização, éramos um pouco nostálgicos para esse sistema operacional. Mas, em geral, o OS XP foi escolhido para facilitar a reversão, pois o XP é uma boa visualização e a ausência de complicações desnecessárias. Bem, a amostra foi escrita especificamente para este SO, a julgar pelo seu código.

Aqui está a aparência do instalador do bootkit :



Apenas olhando para ele, você pode tirar certas conclusões. Por exemplo, é imediatamente evidente que esse arquivo tem um peso pequeno. Se você observar o ponto de entrada, poderá ver que o código está recebendo um descritor de arquivo com o nome de um link simbólico para o primeiro disco físico - "PhysicalDrive0":



Além disso, para maior conveniência da percepção do código, mudamos para a IDA . Torna-se claro que, para um cavalo de Troia típico, a funcionalidade disponível é bastante pequena. Até a tabela de importação é suspeitamente pequena:



Essa imagem geralmente surge ao analisar amostras empacotadas. Mas, no nosso caso, o arquivo não parece compactado. Acontece que a amostra é coberta por algum protetor / criptor e, no processo de seu trabalho, recebe os endereços das funções de forma dinâmica, após o que chama o funcional desejado, ou está tudo bem, e a amostra está como está.

Continuamos a explorar o código.



Como vimos no HIEW, a função CreateFileA é chamada com um argumento interessante como o primeiro parâmetro.O que exatamente está sendo feito aqui? Aqui é apropriado relembrar objetos do kernel . Eles não podem ser manipulados diretamente do modo de usuário, são controlados pelo kernel do sistema operacional. No modo de usuário, um programa só pode fazer uma solicitação para receber / alterar o estado de qualquer objeto do kernel. Para indicar ao sistema com qual objeto de kernel específico o programa funcionará, é necessário obter o identificador do objeto de kernel necessário. Mediante solicitação de recebimento, se todas as verificações forem aprovadas, o sistema operacional retornará o identificador do OA solicitado. E já usando o handle, podemos trabalhar com o OA associado.

Portanto, na imagem acima, usando CreateFile, um link simbólico é acessado no primeiro disco rígido físico conectado. Se o acesso for concedido, você poderá trabalhar com um "arquivo" como em qualquer outro arquivo simples. Ou seja, todo o disco rígido será apresentado como um único arquivo grande.

Então, vamos continuar. Handle está de volta, e nos encontramos aqui:



O que acontece depois? E a função ReadFile lê os primeiros 0x200 bytes. E nós temos lá o primeiro setor do primeiro disco físico.



Como você deve ter adivinhado, esse é o MBR ( Master Boot Record ). O MBR consiste em 3 partes: parte do código, tabela de partição e assinatura. Em uma situação normal, o BIOS lê o MBR na memória no endereço 0: 0x7c00h, passando o controle para ele. Portanto, a parte do código do MBR começa a ser executada. Durante a execução, ele analisa a tabela de partição, localiza o setor de inicialização e o carrega. No caso de um kit de inicialização, se o MBR for substituído, seu código agora receberá controle.

Ok, o MBR é lido e o que vem a seguir ? E o bootkit abre o PhysicalDrive0 novamente, mas com o modo de acesso de gravação.



Em seguida, um ponteiro é definido no 600º deslocamento. Ou seja, o setor original é lido e copiado para o terceiro setor .

Por que fazer backup de um setor? Obviamente, isso é necessário para o fato de que será necessário no futuro.

Então o ciclo começa. Naturalmente, olhando o código, não se pode deixar de prestar atenção a constantes como var_1C, 1BEh e outras. E, ao mesmo tempo, a estrutura MBR localizada acima deve ser atualizada na memória. Em particular, estamos interessados ​​na coluna "Deslocamento".



Veja, o buffer de leitura do primeiro setor está no lpBuffer . Em seguida, é adicionado 1BEh e, de fato, o ponteiro vai para o início da tabela de partição. Todos os dados da tabela até o final do setor são inseridos em _marked_bytes , iniciando no mesmo deslocamento - 1BE.



Ou seja, a segunda e a terceira parte do MBR original são inseridas em _marked_bytes .

O que acontece depois? E então SetFilePointer define o ponteiro para o início do nosso "arquivo", ou seja, para o MBR.



Depois, há uma gravação (WriteFile) formada _marked_bytes e liberando memória. Com isso, a funcionalidade do instalador do kit de inicialização termina.

Mas seria bom ver o que exatamente está na primeira parte de _marked_bytes . Para fazer isso, despeje-o em disco e analise-o. A primeira coisa que chama sua atenção é uma diminuição de 2 do conteúdo da variável no endereço 0x413.



Se você examinar a documentação técnica, poderá descobrir que a variável em 0X413 contém a quantidade de memória física instalada em kilobytes. Assim, o código do kit de inicialização "corta" dois kilobytes de memória:



Agora, para não ser feito mais, será considerado que há 2 kilobytes de memória a menos do que era. Por que - ainda não está claro.

Em seguida, o endereço físico é calculado para a parte da memória "bit off" usando um deslocamento à esquerda de 6 bits, bit a bit:



Um deslocamento de 6 executa duas ações ao mesmo tempo - converte kilobytes em bytes (multiplicando o valor da variável por 2 ^ 10), obtendo assim o endereço físico da parte da memória removida e extrai o número do segmento, dividindo o resultado por 0x10 (2 ^ 4).

Depois disso, seu corpo será copiado sobre esse pedaço de memória e, como o código do kit de inicialização está no pedaço "pouco", ninguém irá perturbá-lo ainda mais . Além disso, nenhuma interrupção não mudará o que está escrito nesta área de memória. Podemos dizer que o código se torna praticamente invisível para o sistema , como se não houvesse memória lá.

Este é apenas o começo do kit de inicialização. Haverá interceptação de interrupção, rastreamento de assinatura ntldr, modificação do módulo do kernel do SO, etc.

Portanto, não vamos estragar tudo, é melhor assistir ao webinar até o fim para não perder nada.

Source: https://habr.com/ru/post/pt482086/


All Articles