Um plugin BinaryNinja para representar graficamente uma árvore de instruções BNIL e correspondências de instruções python de metaprograma.
A instalação é suportada de duas maneiras, a primeira usando o novo gerenciador de plugins e a segunda sendo uma instalação manual.
Use o novo gerenciador de plugins selecionando "Gerenciar Plugins" no menu "Editar". Pesquise na lista de plugins por "BNIL Instruction Graph", clique com o botão direito sobre ele e clique em "Instalar", depois clique com o botão direito novamente e selecione "Ativar".
$ git clone https://github.com/withzombies/bnil-graph.git
$ cd ~/Library/Application Support/Binary Ninja/plugins
$ ln -s ~/git/bnil-graph .
Para usar o gráfico bnil, clique com o botão direito em uma instrução e selecione "Gráfico de instruções BNIL". Isso representa graficamente as instruções BNIL associadas a esse endereço e as exibe como um formulário HTML.
Binary Ninja adiciona acessadores de operandos dinamicamente, devido a isso os acessores convenientes não aparecem nas chamadas dir()
ou na documentação da API. O gráfico bnil mostra a estrutura da instrução IL, incluindo seus bons nomes de acessadores (como insn.src
para o registro de origem ou memória)
Gráfico de exemplo:
Além do plugin gráfico, o bnil-graph também irá gerar uma função matcher que corresponderá exatamente às instruções selecionadas. Este recurso permitirá que novos desenvolvedores de plugins correspondam rapidamente às instruções. O uso pretendido é encontrar uma instrução semelhante àquela que você deseja corresponder, gerar uma função correspondente e, em seguida, modificar a função gerada para melhor atender às suas necessidades.
Um exemplo seria tentar encontrar todas as instruções MediumLevelILSSA MLIL_CALL_SSA que utilizam 3 parâmetros. Gerei um matcher contra uma função não relacionada com 0 parâmetros:
def match_MediumLevelILSSA_140001194_0 ( insn ):
# mem#1 = 0x14000d49c() @ mem#0
if insn . operation != MediumLevelILOperation . MLIL_CALL_SSA :
return False
# invalid
if insn . output . operation != MediumLevelILOperation . MLIL_CALL_OUTPUT_SSA :
return False
if insn . output . dest_memory != 0x1 :
return False
if len ( insn . output . dest ) != 0 :
return False
# 0x14000d49c
if insn . dest . operation != MediumLevelILOperation . MLIL_CONST_PTR :
return False
if insn . dest . constant != 0x14000d49c :
return False
if len ( insn . params ) != 0 :
return False
if insn . src_memory != 0x0 :
return False
return True
Podemos modificar isso para remover algumas restrições específicas:
def match_MediumLevelILSSA_140001194_0 ( insn ):
# mem#1 = 0x14000d49c() @ mem#0
if insn . operation != MediumLevelILOperation . MLIL_CALL_SSA :
return False
# invalid
if insn . output . operation != MediumLevelILOperation . MLIL_CALL_OUTPUT_SSA :
return False
# 0x14000d49c
if insn . dest . operation != MediumLevelILOperation . MLIL_CONST_PTR :
return False
if len ( insn . params ) != 0 :
return False
return True
Removemos o destino da chamada e as restrições de versão de memória. Em seguida, atualize a verificação de parâmetros para verificar 3 parâmetros:
def match_3_param_MLIL_CALL_SSA ( insn ):
if insn . operation != MediumLevelILOperation . MLIL_CALL_SSA :
return False
if insn . output . operation != MediumLevelILOperation . MLIL_CALL_OUTPUT_SSA :
return False
if insn . dest . operation != MediumLevelILOperation . MLIL_CONST_PTR :
return False
if len ( insn . params ) != 3 :
return False
return True
Agora temos um matcher que identificará instruções MLIL_CALL_SSA com 3 parâmetros! Agora itere as instruções MLIL SSA e chame o matcher e pronto:
if __name__ == '__main__' :
bv = binaryninja . BinaryViewType . get_view_of_file ( sys . argv [ 1 ])
bv . update_analysis_and_wait ()
for func in bv . functions :
mlil = func . medium_level_il
for block in mlil . ssa_form :
for insn in block :
if match_3_param_MLIL_CALL_SSA ( insn ):
print "Match: {}" . format ( insn )
Exemplo de correspondência:
Este projeto é protegido por Ryan Stortz (@withzombies) e está disponível sob a LICENÇA Apache 2.0.