
Un kit de herramientas de IDA para analizar kernelcaches de iOS.
Este fork se actualizó para funcionar con IDA 9.x / Python 3 / iOS 27.0 beta 1
ida_kernelcache es un módulo de IDAPython para IDA Pro que facilita el trabajo con kernelcaches de iOS. El módulo proporciona funciones para:
__PRELINK_INFO del kernel y convertirlo en un diccionario de Python.__got y las funciones stub de las secciones __stubs.La función de procesamiento principal está diseñada para ejecutarse antes de cualquier análisis manual o ingeniería inversa. Con la configuración predeterminada, IDA tiende a pasar por alto mucha información útil en el kernelcache. Estos scripts ayudan a IDA aprovechando la estructura conocida del kernelcache para propagar automáticamente información útil.
Además de la funcionalidad estándar del módulo, ida_kernelcache contiene varios scripts para facilitar el análisis del kernelcache de iOS. Por ejemplo, puedes usar los scripts para autogenerar estructuras de C utilizadas por una función.
Muchas de las técnicas utilizadas en ida_kernelcache fueron desarrolladas para y tomadas directamente de memctl.
ida_kernelcache ha sido probado con IDA Pro 6.95 en kernelcaches de las versiones de iOS 10.1.1, 11.0, 11.2, 11.3.1 y 12.0 beta. Actualmente solo se admiten kernelcaches Arm64 de iOS 10 y posteriores.
Ya debes tener un archivo de kernelcache descomprimido cargado en IDA. Puedes encontrar la URL para descargar un IPSW concreto de Apple en línea, y hay una serie de herramientas públicas (incluido memctl) capaces de descomprimir el kernelcache.
En IDA, selecciona "File" -> "Script file..." en la barra de menú y luego elige el script ida_kernelcache.py
del directorio principal. Esto cargará el módulo ida_kernelcache en el intérprete de IDAPython
con los nombres ida_kernelcache y kc. En el prompt de IDAPython, escribe
kc.kernelcache_process() y pulsa Enter para comenzar a analizar el kernelcache. Esta función realiza
todos los análisis principales compatibles con ida_kernelcache. La función se ejecutará durante varios minutos mientras
IDA identifica y analiza funciones nuevas.
ida_kernelcache intentará no sobrescribir los nombres de usuario de las direcciones. Esto significa que si el
kernelcache ha sido analizado manualmente antes de la inicialización con kernelcache_process, los
resultados pueden no ser tan exhaustivos porque los nombres especificados por el usuario pueden bloquear la propagación
automática de nombres. Sin embargo, tampoco hay garantía de que ida_kernelcache no altere análisis previos, así que si
decides ejecutar kernelcache_process en un archivo de kernelcache que ya hayas analizado, haz una
copia de seguridad primero.
ida_kernelcache está pensado para cargarse mediante ida_kernelcache.py; los submódulos del
directorio ida_kernelcache no están pensados para cargarse directamente. Sin embargo, ida_kernelcache expone
la funcionalidad de muchos de estos submódulos. Esto es lo que hace cada uno:
ida_utilities:
Este módulo envuelve algunas de las funciones de IDA para ofrecer una API más fácil de usar. Particularmente útiles son
is_mapped, read_word, read_struct, force_function y ReadWords. is_mapped comprueba
si una dirección está mapeada y, opcionalmente, si contiene un valor conocido. read_word lee una
palabra de tamaño variable desde una dirección. read_struct lee un tipo de estructura en un diccionario de Python
o en un objeto de acceso de Python, lo que facilita mucho el análisis de estructuras de datos. force_function intenta
varios trucos para convertir una dirección en el inicio de una función en IDA. ReadWords es un
generador para iterar sobre palabras de datos y sus direcciones en un rango.
build_struct: Este módulo interno contiene utilidades para completar automáticamente una estructura de IDA basándose en una secuencia de accesos a la estructura.
class_struct: Este módulo proporciona funciones para generar estructuras de IDA que representan tablas de métodos virtuales de C++ y clases. escanea las tablas de métodos virtuales (simbolizadas) y crea estructuras de IDA para contener punteros a métodos virtuales. realiza un análisis de flujo de datos sobre los métodos virtuales para identificar accesos a los campos de cada clase y luego construye estructuras de IDA para representar las clases. Las instrucciones que parecen hacer referencia a un campo también se convierten en referencias de offset de estructura. Consulta la docstring del módulo para más detalles.
El script ida_kernelcache_reload.py es idéntico a ida_kernelcache.py, excepto que fuerza la
recarga del módulo ida_kernelcache y de todos los submódulos. Es útil sobre todo para el desarrollo.
El directorio scripts contiene scripts que usan ida_kernelcache para realizar algún tipo de análisis.
Estos scripts son demasiado específicos para formar parte del módulo principal de ida_kernelcache, pero son útiles
a la hora de aplicar ingeniería inversa al kernelcache. Incluyen:
find_virtual_method_overrides.py: Un script para encontrar descendientes de una clase que sobrescriban un método virtual que contenga la cadena especificada. Las sobrescrituras coincidentes se imprimen en la consola.
populate_struct.py: Rellena los campos de una clase de C++ o de una estructura de C realizando un análisis de flujo de datos desde la dirección actual.
process_external_methods.py:
Procesa un array de IOExternalMethod o IOExternalMethodDispatch a un formato estándar para su uso por
herramientas de fuzzing.
Si estás utilizando el descompilador Hex-Rays, una de las características más interesantes de ida_kernelcache es la reconstrucción automática de clases de C++, que utiliza la información de OSMetaClass y el análisis de flujo de datos para crear estructuras de IDA que representen las clases encontradas en el kernelcache. Estas representaciones pueden mejorar drásticamente la legibilidad de la representación en pseudocódigo. Para saber más, consulta el artículo Reconstruyendo clases de C++ en el kernelcache de iOS con IDA Pro.
Con iOS 12, Apple introdujo un nuevo formato de kernelcache en algunos dispositivos. Entre los cambios, los punteros del kernel de este nuevo
kernelcache están etiquetados para enlazarlos en una lista, presumiblemente para permitir que iBoot deslice
el kernel sin los datos _PrelinkLinkKASLROffsets del diccionario prelink. Intentar analizar
un kernelcache estándar con este formato en IDA es difícil debido a las referencias cruzadas faltantes. Consulta
el artículo Analizando los punteros etiquetados del kernelcache de iOS 12 para más detalles.
Si solo quieres quitar la etiqueta de los punteros del kernelcache sin realizar ningún otro
procesamiento, ejecuta kc.tagged_pointers.untag_pointers().
Parte de esta funcionalidad probablemente se aplica de forma más amplia que solo a los kernelcaches de Apple (por
ejemplo, el análisis de vtable y la propagación de símbolos, o la mayoría de las funciones de ida_utilities.py).
Aun así, he limitado el alcance de la importación únicamente al módulo ida_kernelcache porque no he
probado nada de esto en otros tipos de binarios.
ida_kernelcache se publica bajo la licencia MIT.
Gran parte de la funcionalidad de ida_kernelcache está tomada de memctl, que también se publica bajo la licencia MIT. Otras fuentes se indican en los comentarios de los archivos correspondientes.
Brandon Azad
initialize_vtable_structsinitialize_class_structsclasses:
Este módulo define el tipo ClassInfo que contiene información sobre las clases de C++ del
kernelcache y proporciona la función collect_class_info para escanear el kernelcache en busca de clases y
poblar el diccionario global class_info con un mapa de nombres de clase a objetos ClassInfo. El
tipo ClassInfo registra el nombre de la clase, la instancia de OSMetaClass, la tabla de métodos virtuales y
el nombre de la superclase para cada clase de C++. Además, cada objeto ClassInfo almacena referencias al
ClassInfo de la superclase y al ClassInfo de todas las subclases directas, lo que facilita
examinar y recorrer la jerarquía de clases. collect_class_info también almacena el conjunto de todas las tablas
de métodos virtuales en el conjunto global vtables.
data_flow: Este módulo interno contiene operaciones de flujo de datos utilizadas por el resto de ida_kernelcache.
kernel:
Este módulo proporciona las variables globales base y prelink_info. base es la dirección base de
la imagen del kernel (el inicio del encabezado Mach-O del kernel). prelink_info es el diccionario
__PRELINK_INFO analizado.
kplist:
Este módulo proporciona la función kplist_parse para analizar plists de estilo kernel.
metaclass:
Este módulo proporciona la función initialize_metaclass_symbols, que añade un símbolo para cada
instancia de OSMetaClass conocida.
offset:
Este módulo proporciona las funciones initialize_data_offsets e initialize_offset_symbols. La
primera recorre los segmentos buscando punteros que puedan convertirse en offsets. La
segunda simboliza los offsets de la sección __got de cada kext si el destino del offset tiene un
símbolo.
segment:
Este módulo proporciona la función initialize_segments para renombrar los segmentos de IDA de forma más útil.
Por defecto, IDA parece crear los nombres de los segmentos combinando una suposición del identificador de paquete
con la sección Mach-O que describe la región. initialize_segments extrae el identificador de paquete
real del diccionario __PRELINK_INFO y renombra cada segmento para incluir el identificador de
paquete, el segmento Mach-O y la sección Mach-O. Esto permite, por ejemplo, distinguir
entre __TEXT.__const y __DATA_CONST.__const. Este módulo también proporciona la función
kernelcache_kext (reexportada en el nivel superior) para determinar el kext que contiene la dirección
especificada (solo en el antiguo formato de kernelcache de kexts divididos de iOS 11).
stub:
Muchos kexts del kernelcache contienen funciones stub en una sección __stubs que saltan a funciones
del kernel propiamente dicho. Desafortunadamente, estos stubs suponen una barrera para propagar referencias cruzadas
e información de tipos. Este módulo no resuelve estos problemas, pero sí facilita la visualización de los stubs
renombrando automáticamente las funciones stub para que el nombre de la función de destino sea visible.
Los stubs y sus destinos se convierten forzosamente en funciones en IDA, lo que ayuda a que las
funciones en IDA coincidan con las funciones del código fuente original.
tagged_pointers: El nuevo formato de kernelcache fusionado de iOS 12 tiene los 2 bytes superiores de cada puntero etiquetados con un offset para encadenar los punteros en una lista. Este módulo contiene funciones para procesar y restaurar esos punteros etiquetados.
vtable:
Este módulo proporciona muchas funciones útiles para trabajar con tablas de métodos virtuales, incluyendo
vtable_length, convert_vtable_to_offsets, vtable_overrides, initialize_vtable_symbols y
initialize_vtable_method_symbols. vtable_length comprueba si la dirección especificada podría ser una
vtable y devuelve la longitud de la vtable. El generador vtable_overrides enumera los métodos
virtuales de una clase que sobrescriben métodos virtuales utilizados por la superclase. La función
initialize_vtable_symbols añade un símbolo para el inicio de cada vtable identificada.
initialize_vtable_method_symbols itera sobre los métodos sobrescritos en cada vtable y
propaga símbolos de la superclase a la subclase. Esto es posible porque la mayoría de las clases
base de IOKit están definidas en XNU con información de símbolos relativamente completa. Cada método
sobrescrito en la vtable de una subclase debe cumplir la misma interfaz que el método de la
superclase, lo que significa que podemos generar un símbolo para la sobrescritura sustituyendo el nombre de la
subclase por el nombre de la superclase en el símbolo de método virtual de la superclase. Por ejemplo, si no
tenemos nombre para el método virtual en el índice 7 de la clase AppleKeyStore, pero sabemos que el
método virtual en el índice 7 de su superclase IOService se llama
__ZNK9IOService12getMetaClassEv, entonces podemos deducir que el índice 7 debería llamarse
__ZNK13AppleKeyStore12getMetaClassEv en la subclase. Esta técnica puede utilizarse para simbolizar
la mayoría de los métodos virtuales de la mayoría de las clases.