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.