
Um Kit de Ferramentas IDA para analisar kernelcaches do iOS.
Este fork foi atualizado para funcionar no IDA 9.x / Python 3 / iOS 27.0 beta 1
ida_kernelcache é um módulo IDAPython para o IDA Pro que facilita o trabalho com kernelcaches do iOS. O módulo fornece funções para:
__PRELINK_INFO do kernel em um dicionário Python.__got e funções stub em seções __stubs.A função de processamento principal foi projetada para ser executada antes de qualquer análise manual ou engenharia reversa. Com as configurações padrão, o IDA tende a perder muitas informações úteis no kernelcache. Esses scripts ajudam o IDA, aproveitando a estrutura conhecida do kernelcache para propagar automaticamente informações úteis.
Além da funcionalidade padrão do módulo, o ida_kernelcache contém vários scripts para facilitar a análise do kernelcache do iOS. Por exemplo, você pode usar os scripts para gerar automaticamente estruturas C usadas por uma função.
Muitas das técnicas usadas no ida_kernelcache foram desenvolvidas para e emprestadas diretamente do memctl.
O ida_kernelcache foi testado com o IDA Pro 6.95 em kernelcaches para as versões do iOS 10.1.1, 11.0, 11.2, 11.3.1 e 12.0 beta. Atualmente, apenas kernelcaches Arm64 do iOS 10 em diante são suportados.
Você já precisa ter um arquivo kernelcache descomprimido carregado no IDA. Você pode encontrar a URL para baixar um IPSW específico da Apple online, e existem várias ferramentas públicas (incluindo o memctl) capazes de descomprimir o kernelcache.
No IDA, selecione "File" -> "Script file..." na barra de menu, depois escolha o script
ida_kernelcache.py no diretório principal. Isso carregará o módulo ida_kernelcache no interpretador
IDAPython sob os nomes ida_kernelcache e kc. No prompt IDAPython, digite
kc.kernelcache_process() e pressione Enter para começar a analisar o kernelcache. Esta função
realiza todas as análises principais suportadas pelo ida_kernelcache. A função será executada por
vários minutos enquanto o IDA identifica e analisa novas funções.
O ida_kernelcache tentará não sobrescrever nomes de usuário para endereços. Isso significa que se o
kernelcache foi analisado manualmente antes da inicialização com kernelcache_process, os resultados
podem não ser tão completos, pois nomes especificados pelo usuário podem bloquear a propagação
automática de nomes. No entanto, também não há garantia de que o ida_kernelcache não estrague análises
anteriores; portanto, se você decidir executar kernelcache_process em um arquivo kernelcache já
analisado, faça um backup primeiro.
O ida_kernelcache deve ser carregado via ida_kernelcache.py; os submódulos no diretório
ida_kernelcache não devem ser carregados diretamente. No entanto, o ida_kernelcache expõe a
funcionalidade de muitos desses submódulos. Aqui está o que cada um deles faz:
ida_utilities:
Este módulo encapsula algumas funções do IDA para fornecer uma API mais fácil de usar.
Particularmente úteis são is_mapped, read_word, read_struct, force_function e ReadWords.
is_mapped verifica se um endereço está mapeado e, opcionalmente, se contém um valor conhecido.
read_word lê uma palavra de tamanho variável de um endereço. read_struct lê um tipo de estrutura
em um dicionário Python ou objeto acessor Python, o que facilita muito a análise de estruturas de
dados. force_function tenta vários truques para converter um endereço no início de uma função no
IDA. ReadWords é um gerador para iterar sobre palavras de dados e seus endereços em um intervalo.
build_struct: Este módulo interno contém utilitários para preencher automaticamente uma estrutura IDA com base em uma sequência de acessos à estrutura.
class_struct:
Este módulo fornece funções para gerar estruturas IDA que representam tabelas de métodos virtuais C++
e classes. initialize_vtable_structs escaneia as tabelas de métodos virtuais (simbolizadas) e cria
estruturas IDA para armazenar ponteiros de métodos virtuais. realiza uma
análise de fluxo de dados nos métodos virtuais para identificar acessos aos campos de cada classe e,
em seguida, constrói estruturas IDA para representar as classes. Instruções que parecem referenciar
um campo também são convertidas em referências de offset de estrutura. Consulte a docstring do módulo
para mais detalhes.
O script ida_kernelcache_reload.py é idêntico ao ida_kernelcache.py, exceto que força o
recarregamento do módulo ida_kernelcache e de todos os submódulos. É útil principalmente para
desenvolvimento.
O diretório scripts contém scripts que usam o ida_kernelcache para realizar algum tipo de análise.
Esses scripts são muito específicos para fazer parte do módulo principal ida_kernelcache, mas são
úteis ao fazer engenharia reversa do kernelcache. Eles incluem:
find_virtual_method_overrides.py: Um script para encontrar descendentes de uma classe que substituem um método virtual contendo a string especificada. As substituições correspondentes são exibidas no console.
populate_struct.py: Preenche campos para uma classe C++ ou struct C realizando análise de fluxo de dados a partir do endereço atual.
process_external_methods.py:
Processa um array IOExternalMethod ou IOExternalMethodDispatch em um formato padrão para uso por
ferramentas de fuzzing.
Se você estiver usando o descompilador Hex-Rays, um dos recursos mais interessantes do ida_kernelcache é a reconstrução automática de classes C++, que usará as informações do OSMetaClass e a análise de fluxo de dados para criar estruturas IDA que representam as classes encontradas no kernelcache. Essas representações podem melhorar drasticamente a legibilidade da representação em pseudocódigo. Para saber mais, veja o post Reconstructing C++ classes in the iOS kernelcache using IDA Pro.
Com o iOS 12, a Apple introduziu um novo formato de kernelcache em alguns dispositivos. Entre as
mudanças, os ponteiros do kernel desse novo kernelcache são marcados para vinculá-los em uma lista,
presumivelmente para permitir que o iBoot deslize o kernel sem os dados _PrelinkLinkKASLROffsets no
dicionário prelink. Tentar analisar um kernelcache padrão usando esse formato no IDA é difícil devido
às referências cruzadas ausentes. Veja o artigo Analyzing the iOS 12 kernelcache's tagged pointers
para detalhes.
Se você quiser apenas remover as marcações dos ponteiros no kernelcache sem realizar nenhum
processamento adicional, execute kc.tagged_pointers.untag_pointers().
Parte dessa funcionalidade provavelmente se aplica de forma mais ampla do que apenas a kernelcaches
da Apple (por exemplo, análise de vtable e propagação de símbolos, ou a maioria das funções em
ida_utilities.py). No entanto, limitei o escopo de importação apenas ao módulo ida_kernelcache
porque não testei nada disso em outros tipos de binários.
O ida_kernelcache é distribuído sob a licença MIT.
Grande parte da funcionalidade do ida_kernelcache é emprestada do memctl, que também é distribuído sob a licença MIT. Outras fontes são indicadas nos comentários nos arquivos correspondentes.
Brandon Azad
initialize_class_structsclasses:
Este módulo define o tipo ClassInfo que armazena informações sobre classes C++ no kernelcache e
fornece a função collect_class_info para escanear o kernelcache em busca de classes e preencher o
dicionário global class_info com um mapa de nomes de classes para objetos ClassInfo. O tipo
ClassInfo registra o nome da classe, a instância OSMetaClass, a tabela de métodos virtuais e o nome
da superclasse para cada classe C++. Além disso, cada objeto ClassInfo armazena referências ao
ClassInfo da superclasse e ao ClassInfo de todas as subclasses diretas, facilitando a análise e a
travessia da hierarquia de classes. collect_class_info também armazena o conjunto de todas as
tabelas de métodos virtuais no conjunto global vtables.
data_flow: Este módulo interno contém operações de fluxo de dados usadas pelo resto do ida_kernelcache.
kernel:
Este módulo fornece as variáveis globais base e prelink_info. base é o endereço base da imagem
do kernel (o início do cabeçalho Mach-O do kernel). prelink_info é o dicionário __PRELINK_INFO
analisado.
kplist:
Este módulo fornece a função kplist_parse para analisar plists no estilo do kernel.
metaclass:
Este módulo fornece a função initialize_metaclass_symbols que adiciona um símbolo para cada
instância OSMetaClass conhecida.
offset:
Este módulo fornece as funções initialize_data_offsets e initialize_offset_symbols. A primeira
escaneia os segmentos em busca de ponteiros que possam ser convertidos em offsets. A última simboliza
offsets na seção __got de cada kext se o destino do offset tiver um símbolo.
segment:
Este módulo fornece a função initialize_segments para renomear os segmentos do IDA para serem mais
úteis. Por padrão, o IDA parece criar os nomes dos segmentos combinando um palpite do identificador
do pacote com a seção Mach-O que descreve a região. initialize_segments extrai o identificador real
do pacote do dicionário __PRELINK_INFO e renomeia cada segmento para incluir o identificador do
pacote, o segmento Mach-O e a seção Mach-O. Isso torna possível, por exemplo, distinguir entre
__TEXT.__const e __DATA_CONST.__const. Este módulo também fornece a função kernelcache_kext
(reexportada no nível superior) para determinar o kext que contém o endereço especificado (apenas no
formato antigo de kernelcache split-kext do iOS 11).
stub:
Muitos kexts no kernelcache contêm funções stub em uma seção __stubs que saltam para funções no
kernel propriamente dito. Infelizmente, esses stubs fornecem uma barreira para a propagação de
referências cruzadas e informações de tipo. Este módulo não resolve esses problemas, mas facilita a
visualização de stubs ao renomear automaticamente as funções stub para que o nome da função de
destino fique visível. Stubs e seus destinos são forçadamente convertidos em funções no IDA, o que
ajuda a alinhar as funções no IDA com as funções no código-fonte original.
tagged_pointers: O novo formato de kernelcache mesclado do iOS 12 tem os 2 bytes superiores de cada ponteiro marcados com um offset para encadear os ponteiros em uma lista. Este módulo contém funções para processar e restaurar esses ponteiros marcados.
vtable:
Este módulo fornece muitas funções úteis para trabalhar com tabelas de métodos virtuais, incluindo
vtable_length, convert_vtable_to_offsets, vtable_overrides, initialize_vtable_symbols e
initialize_vtable_method_symbols. vtable_length verifica se o endereço especificado pode ser uma
vtable e retorna o comprimento da vtable. O gerador vtable_overrides enumera os métodos virtuais em
uma classe que substituem métodos virtuais usados pela superclasse. A função
initialize_vtable_symbols adiciona um símbolo para o início de cada vtable identificada.
initialize_vtable_method_symbols itera pelos métodos substituídos em cada vtable e propaga símbolos
da superclasse para a subclasse. Isso é possível porque a maioria das classes base no IOKit são
definidas no XNU com informações de símbolo relativamente completas. Cada substituição de método na
vtable de uma subclasse deve estar em conformidade com a mesma interface do método na superclasse, o
que significa que podemos gerar um símbolo para a substituição substituindo o nome da subclasse pelo
nome da superclasse no símbolo do método virtual na superclasse. Por exemplo, se não temos nome para
o método virtual no índice 7 na classe AppleKeyStore, mas sabemos que o método virtual no índice 7
em sua superclasse IOService é chamado __ZNK9IOService12getMetaClassEv, então podemos inferir
que o índice 7 deve ser chamado __ZNK13AppleKeyStore12getMetaClassEv na subclasse. Esta técnica
pode ser usada para simbolizar a maioria dos métodos virtuais na maioria das classes.