Unicorn Engine

Unicorn engine é uma framework que permite a emulação de CPU para executar instruções de diferentes arquiteturas, sendo utilizado em desafios CTFs, engenharia reversa, análise de malware e fuzzing.

Instalação e documentação

Para instalar o Unicorn como uma biblioteca python, basta apenas usar o seguinte comando no terminal

pip install unicorn

A documentação do Unicorn pode ser encontrada em https://www.unicorn-engine.org/docs/

Exemplos

Exemplo 1

A seguir, um exemplo de um uso simples do Unicorn, apenas para demonstrar principais funcionalidades:

from unicorn import *
from unicorn.x86_const import *

# Cada instrução em assembly é representada em opcodes
# Código em Assembly x86: mov eax, 2; add eax, 3; nop
# Em hexadecimal: B8 02 00 00 00 83 C0 03 90
CODE = b"\xB8\x02\x00\x00\x00\x83\xC0\x03\x90"

# Endereço base onde o código será carregado
ADDRESS = 0x1000

# Inicializa o emulador para x86 (32 bits)
mu = Uc(UC_ARCH_X86, UC_MODE_32)

# Mapeia 4KB de memória a partir do endereço escolhido
mu.mem_map(ADDRESS, 4 * 1024)

# Escreve o código na memória emulada
mu.mem_write(ADDRESS, CODE)

# Define o registrador EAX com 0
mu.reg_write(UC_X86_REG_EAX, 0)

# Inicia a emulação do código (do início até o fim)
mu.emu_start(ADDRESS, ADDRESS + len(CODE))

# Lê o valor final do registrador EAX
eax_value = mu.reg_read(UC_X86_REG_EAX)
print(f"EAX = {eax_value}")

Exemplo 2

A seguir, há um exemplo de uma resolução de um desafio de autoria própria. Primeiramente, precisamos analisar o código obtido através do ghidra com o binário disponibilizado:

Basicamente, esse programa utiliza a função nmap para escrever bytes em uma região de memória, utilizando as variáveis local_a8 até local_50, e então chama essa função para cada caractere que o usuário digitou e com local_1c como segundo parâmetro. Após isso, o valor retornado de cada valor é comparado com cada valor do vetor local_188 e, se todos os valores forem iguais, imprime que a flag está correta.

Uma solução que poderíamos utilizar seria reconstruir o código em assembly da região de memória que foi alocado e então fazer a engenharia reversa da função. Porém, podemos utilizar para executar essa função em assembly e então com brute-force obter a flag.

Essa função, pegas os valores definidos na função e transforma em uma sequência de bytes separados por espaço.

Agora, podemos realizar a emulação com o Unicorn.

Referências

Site oficial: https://www.unicorn-engine.org/

Unicorn notes: https://github.com/alexander-hanel/unicorn-engine-notes

Atualizado