Configuração do Ambiente de Desenvolvimento
A instalação do angr foi realizada em um ambiente Kali Linux, utilizando um ambiente virtual Python isolado para garantir a pureza das dependências. O comando source venv/bin/activate inicia o ambiente virtual, enquanto deactivate o finaliza. O uso do IPython é recomendado para uma experiência interativa aprimorada.
Explorando Mecanismos Básicos de Simulação
Exemplo Inicial: Localização por Endereço
O primeiro caso demonstra a localização de um estado específico utilizando um endereço de memória como alvo.
import angr
proj = angr.Project('./exemplo_00')
estado_inicial = proj.factory.entry_state()
gerenciador = proj.factory.simulation_manager(estado_inicial)
gerenciador.explore(find=0x8048678)
if gerenciador.found:
estado_encontrado = gerenciador.found[0]
print(estado_encontrado.posix.dumps(0))
Exemplo com Condições Personalizadas
Em cenários mais complexos, é possível definir condições personalizadas para busca e evitar caminhos indesejados, otimizendo a execução.
import angr
def estado_valido(estado):
return b'Sucesso' in estado.posix.dumps(1)
def estado_invalido(estado):
return b'Falha' in estado.posix.dumps(1)
proj = angr.Project('./exemplo_02')
estado_inicial = proj.factory.entry_state()
gerenciador = proj.factory.simulation_manager(estado_inicial)
gerenciador.explore(find=estado_valido, avoid=estado_invalido)
if gerenciador.found:
solucao = gerenciador.found[0].posix.dumps(0)
print(solucao)
Trabalhando com Variáveis Simbólicas e Estados
Variáveis em Registradores
Para problemas que envolvem múltiplas entradas simbólicas armazenadas em registradores, pode-se utilizar estados em branco para inicialização personalizada.
import angr
import claripy
proj = angr.Project('./exemplo_03')
ponto_inicio = 0x08048980
estado_inicial = proj.factory.blank_state(addr=ponto_inicio)
entrada_a = claripy.BVS('entrada_a', 32)
entrada_b = claripy.BVS('entrada_b', 32)
entrada_c = claripy.BVS('entrada_c', 32)
estado_inicial.regs.eax = entrada_a
estado_inicial.regs.ebx = entrada_b
estado_inicial.regs.edx = entrada_c
gerenciador = proj.factory.simulation_manager(estado_inicial)
gerenciador.explore(find=estado_valido, avoid=estado_invalido)
if gerenciador.found:
resultado = gerenciador.found[0]
sol_a = resultado.solver.eval(entrada_a)
sol_b = resultado.solver.eval(entrada_b)
sol_c = resultado.solver.eval(entrada_c)
print(f"Resultado: {sol_a:x} {sol_b:x} {sol_c:x}")
Variáveis na Pilha
Quando as entradas são passadas através da pilha, é necessário simular seu layout manualmente.
import angr
proj = angr.Project('./exemplo_04')
estado_inicial = proj.factory.blank_state(addr=0x08048697)
estado_inicial.stack_push(estado_inicial.regs.ebp)
estado_inicial.regs.ebp = estado_inicial.regs.esp
estado_inicial.regs.esp -= 8
var_a = estado_inicial.solver.BVS('var_a', 32)
var_b = estado_inicial.solver.BVS('var_b', 32)
estado_inicial.stack_push(var_a)
estado_inicial.stack_push(var_b)
gerenciador = proj.factory.simgr(estado_inicial)
gerenciador.explore(find=estado_valido, avoid=estado_invalido)
if gerenciador.found:
estado_final = gerenciador.found[0]
print(f"Solução: {estado_final.solver.eval(var_a)} {estado_final.solver.eval(var_b)}")
Manipulação de Memória e Arquivos
Armazenamento Simbólico em Memória
Para variáveis armazenadas em regiões específicas da memória, utiliza-se o método memory.store.
import angr
proj = angr.Project('./exemplo_05')
estado_inicial = proj.factory.blank_state(addr=0x08048601)
dado_1 = estado_inicial.solver.BVS('dado_1', 64)
dado_2 = estado_inicial.solver.BVS('dado_2', 64)
endereco_1 = 0x0A1BA1C0
endereco_2 = 0x0A1BA1C8
estado_inicial.memory.store(endereco_1, dado_1)
estado_inicial.memory.store(endereco_2, dado_2)
gerenciador = proj.factory.simgr(estado_inicial)
gerenciador.explore(find=estado_valido, avoid=estado_invalido)
if gerenciador.found:
resultado = gerenciador.found[0]
valor_1 = resultado.solver.eval(dado_1, cast_to=bytes).decode('utf-8')
valor_2 = resultado.solver.eval(dado_2, cast_to=bytes).decode('utf-8')
print(f"Solução: {valor_1} {valor_2}")
Memória Dinâmica e Manipulação de Arquivos
Cenários que envolvem alocação dinâmica ou leitura de arquivos exigem configuração adicional de bufffers e sistemas de arquivos simbólicos.
import angr
proj = angr.Project('./exemplo_07')
estado_inicial = proj.factory.blank_state(addr=0x080488D6)
conteudo_arquivo = estado_inicial.solver.BVS('conteudo', 0x40 * 8)
arquivo_sim = angr.storage.SimFile("dados.txt", content=conteudo_arquivo, size=0x40)
estado_inicial.fs.insert("dados.txt", arquivo_sim)
gerenciador = proj.factory.simgr(estado_inicial)
gerenciador.explore(find=estado_valido, avoid=estado_invalido)
if gerenciador.found:
solucao_bytes = gerenciador.found[0].solver.eval(conteudo_arquivo, cast_to=bytes)
print(f"Solução: {solucao_bytes}")
Otimizações e Técnicas Avançadas
Mitigação de Explosão de Caminhos com Restrições
Para evitar a explosão combinatória de caminhos, podem-se adicionar restrições diretamente aos estados encontrados.
import angr
proj = angr.Project('./exemplo_08')
estado_inicial = proj.factory.blank_state(addr=0x08048625)
buffer = 0x0804A050
entrada_sim = estado_inicial.solver.BVS('entrada', 16 * 8)
estado_inicial.memory.store(buffer, entrada_sim)
gerenciador = proj.factory.simgr(estado_inicial)
ponto_verificacao = 0x08048673
gerenciador.explore(find=ponto_verificacao)
if gerenciador.found:
estado_verif = gerenciador.found[0]
string_esperada = "AUPDNNPROEZRJWKB"
dados_carregados = estado_verif.memory.load(buffer, 16)
estado_verif.add_constraints(string_esperada == dados_carregados)
solucao_final = estado_verif.solver.eval(entrada_sim, cast_to=bytes)
print(f"Solução: {solucao_final}")
Utilização de Hooks para Substituição de Comportamento
Hooks permitem interceptar e substituir a execução de funções ou instruções específicas, útil para simplificar a análise.
import angr
import claripy
proj = angr.Project('./exemplo_09')
estado_inicial = proj.factory.entry_state()
endereco_hook = 0x080486B3
@proj.hook(endereco_hook, length=5)
def substituir_verificacao(estado):
endereco_input = 0x0804A054
dados = estado.memory.load(endereco_input, 16)
string_alvo = "XYMKBKUHNIQYNQXE"
estado.regs.eax = claripy.If(
string_alvo == dados,
claripy.BVV(1, 32),
claripy.BVV(0, 32)
)
gerenciador = proj.factory.simgr(estado_inicial)
gerenciador.explore(find=estado_valido, avoid=estado_invalido)
if gerenciador.found:
print(f"Entrada bem-sucedida: {gerenciador.found[0].posix.dumps(0)}")