Python é uma linguagem muito versátil que ganhou bastante destaque nos últimos anos. Acessível para desenvolvedores menos experientes, com centenas de bibliotecas disponíveis ao público e apoiado por uma grande comunidade, o número de desenvolvedores que o utilizam cresceu consideravelmente.

Neste contexto, embora o Python não seja uma das linguagens mais tradicionais para o desenvolvimento de malwares, ele tem sido adotado por muitos cibercriminosos para este fim. Um exemplo disso é o Machete, um backdoor utilizado para fins de espionagem, que visa principalmente órgãos governamentais na América Latina.

Python é uma linguagem interpretada, o que significa que seu código pode ser executado em qualquer plataforma para a qual exista um intérprete. À primeira vista, isto pode parecer benéfico para os cibercriminosos, mas na verdade é um empecilho. Isto porque, para que o malware seja executado no PC da vítima, é necessário que a vítima conte com um intérprete instalado. Alguns sistemas operacionais Linux vêm com uma instalação Python por padrão; entretanto, este não é o caso dos sistemas operacionais Windows, que são os mais visados por cibercriminosos.

Portanto, os desenvolvedores de malware precisam de uma alternativa para poder executar seu código malicioso escrito em Python nos sistemas operacionais Windows, e a solução escolhida para este problema é o uso de software de empacotamento; capaz de pegar um script .py e um .exe executável.

Ao ser independente, este executável conterá toda a funcionalidade maliciosa do script e pode ser executado sem a necessidade de contar com o intérprete Python instalado. Os softwares mais utilizados pelos cibercriminosos para este fim são:

  • Py2exe
  • PyInstaller

Deve-se notar que ambos os programas são legítimos e são usados principalmente pela comunidade de desenvolvedores Python que tentam distribuir facilmente seus desenvolvimentos. Em outras palavras, há muito software benigno que também usa esses programas.

Como os compiladores funcionam?

O objetivo destes programas é pegar um arquivo .py e gerar um .exe executável capaz de rodar no Windows sem nenhum problema. Para conseguir isso, eles compilam o script Python .py para Python .pyc bytecode. Este código .pyc pode ser executado por intérpretes Python. Então, o software também empacota dentro do executável .exe o código correspondente a um intérprete rodando no Windows. Finalmente, o script .py pode usar dependências e bibliotecas específicas, que também devem ser compiladas e incluídas no .exe resultante.

Processo de compilação e empacotamento realizado por ferramentas como Py2Exe e PyInstaller.

Entretanto, a fim de analisar os malwares que passaram por este processo, será necessário realizar o processo inverso. Comece extraindo o script .pyc compilado contido no executável malicioso com software como unpy2exe ou pyinsxtractor. Em seguida, realize a descompilação do arquivo .pyc extraído usando um descompilador como o Uncompyle6. Desta forma, será possível obter o script malicioso (.py) em texto simples, podendo assim analisá-lo com mais conforto e eficiência.

O processo de extração e descompilação e as ferramentas que podem ser usadas em cada etapa.

Software necessário para descompilar

unpy2exe: este é um script Python que pode ser baixado de seu repositório github ou instalado usando o comando "pip install unpy2exe". Como consideração adicional, se você quiser usar este script em um .exe gerado a partir do código Python 2.x, será necessário ter que executá-lo com o Python 2.x. Por outro lado, se você quiser usá-lo em um .exe gerado a partir do código Python 3.x, é necessário executá-lo com o Python 3.x.

pyinstractor: este é um script Python e pode ser baixado de seu repositório github. Requer, para ser executado, uma versão de Python maior ou igual a 2.7 e, como para unpy2exe, é aconselhável executá-lo com a mesma versão de Python que a usada pelo executável a ser analisado.

Uncompyle6: desenvolvido em Python, este script é capaz de descompilar arquivos .pyc para a maioria das versões. Ele pode ser baixado de seu site oficial ou usando o comando "pip install uncompyle6". Caso você tenha algum problema para descompilar as versões mais recentes do Python +3.7, há outro projeto derivado deste chamado Decompile3, que está focado nestas versões.

Descompilação de malware empacotado com Py2exe

O processo para descompilar pacotes de malware com Py2exe é muito simples. Começamos executando unpy2exe com o executável malicioso que queremos descompilar como parâmetro e ele irá extrair o código compilado.

Execução do unpy2exe para extrair o arquivo .pyc do example.exe.

Uma vez executado o comando, podemos ver que um novo arquivo com extensão .pyc foi criado. Este arquivo é o bytecode correspondente ao código Python compilado.

Arquivo .pyc extraído do executável pela ferramenta unpy2exe.

Neste ponto, resta descompilar o arquivo .pyc para obter o código fonte do malware em texto simples. Para isso, precisaremos executar o uncompyle6 passando como parâmetro o arquivo .pyc extraído na etapa anterior.

Código obtido pela descompilação do arquivo .pyc extraído na etapa anterior com o Uncompyle6.

Como podemos ver, o código Python do executável malicioso foi extraído e descompilado com sucesso.

Descompilação de malware empacotados com PyInstaller

O processo de descompilação de malware empacotado com o PyInstaller é semelhante ao dos executáveis empacotados pelo Py2exe, mas requer algumas etapas adicionais.

Começamos executando o pyinstractor.py com o executável malicioso como parâmetro.

Execução do pyinstractor para extrair o arquivo .pyc do example.exe. Você pode ver o "Possible entry point", entre outras informações sobre o executável.

Aqui podemos ver que o pyinstractor extraiu com sucesso os arquivos .pyc do executável malicioso. Entre as informações que nos dá há um item que diz “Possible entry point”  e o valor deste será útil na próxima etapa.

Vamos para a pasta "*nomeexecutável*.exe_extraído", que foi criada pelo pyinstractor, e lá poderemos ver todos os arquivos extraídos.

Arquivos extraídos do executável pelo pyinstractor. Você pode ver um arquivo sem extensão cujo nome corresponde ao "Possible entry point".

Entre todos os arquivos extraídos podemos observar um arquivo sem extensão cujo nome é o mesmo que o mencionado no item “Possible entry point” pelo pyinstractor. Geralmente, este arquivo contém o bytecode correspondente ao código principal do executável malicioso.

Entretanto, teremos que modificar o cabeçalho deste arquivo para adicionar o magic number correspondente ao formato .pyc. Para isso, vamos abri-lo com o software HxD.

Arquivo aberto com HxD para poder observar seu conteúdo em formato binário. Aqui você pode ver que o magic number correspondente ao cabeçalho do arquivo .pyc não aparece.

Aqui podemos ver o conteúdo do arquivo e que o magic number está faltando no início do arquivo. Portanto, este valor deve ser adicionado à primeira posição do arquivo para completar o cabeçalho. O magic number pode variar dependendo da versão do Python usada por aquele bytecode e pode ser copiado e colado de um dos outros arquivos .pyc extraídos pelo pyinstractor (para ter certeza de que estamos adicionando o correto).

Arquivo .pyc com o cabeçalho já corrigido e completo.

Uma vez feito isso, salve o arquivo e mude sua extensão para .pyc para proceder à descompilação usando o Uncompyle6. Basta executá-lo passando o arquivo .pyc recém editado como parâmetro.

Código obtido ao descompilar com o Uncompyle6 o arquivo .pyc reparado na etapa anterior.

Como podemos ver, o código Python do executável malicioso foi extraído e descompilado com sucesso.

Pós-descompilação

Cibercriminosos experientes estão frequentemente cientes de que geralmente é possível obter o código-fonte das ameaças desenvolvidas em linguagens de interpretação ou de script (JS, PS, VBS, Python, etc.). Por esta razão, a fim de tornar as tarefas dos analistas mais difíceis, muitos deles escolhem ofuscar o código antes de compilá-lo. Então, se ao final de todas estas etapas encontrarmos um código incompreensível, será necessário analisá-lo mais cuidadosamente em busca de padrões que correspondam à sintaxe do idioma para revelar a lógica oculta do idioma.

Veja mais: Estruturas de controle de fluxo em código Assembly