Desmistificando técnicas de ransomware usando Assemblies.NET: conjuntos EXE versus DLL
Em parte um desta série, examinamos algumas técnicas usadas por malware, ransomware especificamente. Como vimos, essas técnicas individuais, como baixadores, droppers e carregadores, bem como codificação e criptografia, são todas capacidades legítimas e programáveis oferecidas pelo .Net estrutura de software (dot net) e muitas outras estruturas de programação e linguagens de código. Abaixo está uma colagem de algumas das técnicas discutidas no artigo anterior.

Neste segundo artigo, examinaremos os fundamentos das montagens por meio da estrutura da Microsoft .Net. Vamos nos aprofundar nas diferenças entre assemblies (EXE versus DLL) e seus relacionamentos, o que permite que esses recursos sejam eventualmente executados a partir de um código inicial de alto nível, como o código de programação C#. Usaremos o código apresentado no artigo anterior para explorar essas diferenças e relacionamentos.
O que é o Microsoft.Net?
Microsoft .Net é uma estrutura de desenvolvimento de software projetada para suportar várias linguagens de programação e ter como alvo diferentes sistemas operacionais. Linguagens de programação suportadas, como C# (pronunciado C sharp), são compiladas e executadas como o que é conhecido como código gerenciado (em oposição ao código nativo ou não gerenciado). Para conseguir isso, .Net executa seu código em uma máquina virtual dedicada em vez de diretamente na plataforma de destino. Essa máquina virtual é conhecida como .Net Common Language Runtime (CLR). Ele pode ser considerado o intermediário comum que eventualmente executa o código compilado ou montado de todas as diferentes linguagens de programação, como C#, VB.Net e F#, que .Net suportes. O exemplo abaixo mostra o código da linguagem de programação C# do artigo anterior.

Código gerenciado significa que o código de alto nível da linguagem de programação C# acima e outros, como F# e VB.Net, são compilados primeiro em uma linguagem intermediária (IL). O código de alto nível do C# mostrado acima é compilado de acordo com as instruções da linguagem intermediária mostradas na imagem abaixo. Esse código se assemelha à sintaxe de programação de montagem de baixo nível.

Essa linguagem intermediária (IL) é então compilada em código nativo ou de máquina visando a plataforma de máquina relevante. Esta compilação é feita por outro .Net componente chamado compilador Just-in-Time (JIT).
O código nativo ou de máquina é o conjunto de instruções (zeros e uns) que o processador (CPU) de um determinado computador entende. Essa última etapa é gerenciada pelo Common Language Runtime (CLR), que também contém o JIT. O CLR é o .Net ambiente de execução ou máquina virtual. Java é outra estrutura de software que usa o conceito de tempos de execução intermediários. Semelhante à Java Virtual Machine, é uma parte principal do que faz com que o .Net independente da plataforma. .Net o código é chamado de código gerenciado porque o código de programação é gerenciado pelo CLR intermediário e não executado diretamente pela CPU do computador.
Uma vantagem do código gerenciado em .Net é gerenciamento automático de memória e coleta de lixo. Isso significa que o desenvolvedor não precisa se preocupar em alocar e desalocar a memória do computador em seu código para economizar recursos do sistema, como no caso do código C ou C ++. Em .Net, existe o coletor de lixo que é executado periodicamente para lidar com a memória desalocada. Ele também pode ser chamado pelo programador quando necessário. O diagrama abaixo mostra a arquitetura de um .Net aplicativo.

Em contraste, não-.Net compiladores como VB6, C e C++ compilam seu código de alto nível diretamente no código de máquina da plataforma de destino (SO e CPU). O executável ou conjunto de código resultante está, portanto, vinculado à plataforma da máquina de destino do compilador. Isso também é conhecido como código não gerenciado ou nativo. Embora arquitetonicamente diferente, é possível usar código de assemblies, especialmente DLLs desenvolvidas em código nativo em um .Net-aplicativo gerenciado por meio de um recurso conhecido como Interop Marshalling (Platform Invoke). Exemplos disso serão o uso de DLLs nativas do sistema operacional Windows ou bibliotecas externas, como código escrito em C++, sendo referenciado em um gerenciado .Net aplicativo para habilitar algumas funcionalidades de baixo nível do sistema operacional. Nesse caso, .Net em si pode ser considerado um invólucro seguro para as DLLs nativas das quais o sistema operacional Windows depende e muitas das quais são, na verdade, escritas em C ++.
O que é uma montagem do.Net?
A Microsoft descreve .Net montagens como uma única unidade de implantação. O que isso significa é que uma montagem é uma coleção de vários tipos de código e arquivos associados que foram compilados (montados) em alguma forma que pode ser executada em qualquer compatível .Net plataforma de destino. A execução é feita por .Net tempo de execução de linguagem comum. Exemplos de assemblies no sistema operacional Windows são arquivos executáveis (.exe) e arquivos de biblioteca de classes ou biblioteca de vínculo dinâmico (.dll).
Um aprofundamento na imagem de código de exemplo abaixo mostra a montagem executável em C# à esquerda e outro código de montagem de DLL em C# (também conhecido como biblioteca de classes) à direita. O código executável faz referência ao arquivo DLL e, em seguida, chama um método (função) específico do código DLL durante a execução. Essas referências e chamadas foram destacadas na imagem abaixo. Explicaremos os detalhes de ambas as partes do código posteriormente neste artigo. Também mostraremos como essa combinação pode ser usada para fins maliciosos nesta série.

No exemplo subsequente, o arquivo DLL é referenciado manualmente no código executável. Isso significa que a DLL e as informações relacionadas sobre seus metadados, bem como o código (composto por módulos, classes e métodos), são referenciadas durante o tempo de compilação do código executável.

Como uma biblioteca compartilhada, o código DLL não pode ser executado sozinho diretamente. Do ponto de vista do código, isso ocorre porque as DLLs não têm uma função de ponto de entrada principal para serem executadas e, portanto, não podem ser executadas como código autônomo da mesma forma que um código executável (.exe) está configurado para fazer. Como exemplo, a mensagem de erro abaixo mostra as consequências de tentar executar uma biblioteca de classes ou um arquivo DLL diretamente de um compilador.

O código executável, por outro lado, terá uma função ou método de ponto de entrada principal onde a execução começa, mas uma DLL realmente não precisa de uma função de ponto de entrada principal, pois é basicamente uma biblioteca de blocos de código referenciados por outros assemblies.
Uma vez referenciado, o código específico no arquivo DLL que é de interesse pode ser chamado para execução. Conforme mostrado no artigo anterior, os exemplos de código (EXE e DLL) abaixo reiteram esse ponto.

O aplicativo executável é executado e chama o código da DLL referenciada para produzir a saída mostrada na imagem a seguir.

Este programa simples mostra como .Net assemblies como EXEs e DLLs podem ser usados juntos.
O código DLL mencionado acima tem um método (função) que usa dois parâmetros por entrada — nome e idade — e exibe uma mensagem de saudação usando essas informações. O código executável, por outro lado, executa um código que aceita detalhes de entrada do usuário sobre nome e idade na linha de comando e, em seguida, passa essas informações para o método DLL como argumentos ou entradas. A mensagem do código DLL é então exibida de volta na tela do console usando as informações que o aplicativo EXE coletou do usuário.
Analisando assemblies .Net
A execução de uma análise estática no executável mostra as várias referências de DLLs e outros componentes importados para execução. Além de nossa própria DLL personalizada, o conjunto executável também importa DLLs adicionais associadas a .Net em si, como mscorlib que é uma DLL que contém código base (classes, tipos etc.) e é algo que nosso programa precisa para funcionar sem problemas.

Em nosso ambiente de desenvolvimento de código Visual Studio, podemos confirmar o uso de mscorlib rastreando suas origens em um dos tipos de dados (nesse caso, fio desde System.String em .Net). Isso revela o embutido .Net montagem de onde esse tipo se origina, que é mscorlib conforme mostrado abaixo.

String é um tipo de dados em termos de programação em que o texto que o usuário insere e depois é exibido novamente é armazenado. Também podemos ver em nossa análise estática a DLL chamada”Montagem DLL_DontNet.” Esta é nossa DLL personalizada que contém o”Método DisplayMsg” método que mostra ao usuário uma mensagem depois de inserir seus detalhes.
Em nosso exemplo, referenciamos e carregamos nossa DLL personalizada manualmente durante a compilação de todo o nosso código antes de o programa começar a ser executado. Também é possível referenciar uma DLL durante a execução de um executável. Isso pode ser especialmente útil nos casos em que talvez não tenhamos acesso à DLL desejada durante a compilação do nosso código. Esse processo é conhecido como reflexão e permite a capacidade de examinar um .Net montagem (metadados e atributos) e também para usar o código (módulos, classes, métodos e propriedades) contido nele durante o tempo de execução do nosso programa. Essa técnica também pode ser ajustada para fins maliciosos no que é conhecido como ataques de injeção reflexiva de DLL.
.Net assemblies (executáveis e bibliotecas de classes) também consistem em um arquivo manifesto que contém metadados sobre a montagem e o código da linguagem intermediária (IL) que, juntos, permitem que o Common Language Runtime execute a montagem em qualquer plataforma compatível que possa ser executada .Net. A imagem abaixo mostra as instruções de montagem IL e a estrutura de manifesto das duas montagens — EXE e DLL. O arquivo de manifesto contém os metadados sobre o .Net montagem como número da versão, descrição, etc.

Devemos agora ter uma compreensão fundamental do .Net estrutura de software, seus conjuntos associados e como eles podem interagir entre si.
No próximo artigo, colocaremos as técnicas e os recursos que discutimos e aprendemos até agora em um único executável de ransomware malicioso.
Saiba mais sobre como a segmentação Illumio Zero Trust pode ajudar você a conter violações de ransomware.