macOS Dyld Process
Información Básica
El verdadero punto de entrada de un binario Mach-o es el enlazador dinámico, definido en LC_LOAD_DYLINKER
, que generalmente es /usr/lib/dyld
.
Este enlazador necesitará localizar todas las bibliotecas ejecutables, mapearlas en memoria y enlazar todas las bibliotecas no perezosas. Solo después de este proceso se ejecutará el punto de entrada del binario.
Por supuesto, dyld
no tiene dependencias (utiliza llamadas al sistema y fragmentos de libSystem).
Si este enlazador contiene alguna vulnerabilidad, al ejecutarse antes de ejecutar cualquier binario (incluso los altamente privilegiados), sería posible escalar privilegios.
Flujo
Dyld será cargado por dyldboostrap::start
, que también cargará cosas como el canario de pila. Esto se debe a que esta función recibirá en su argumento apple
este y otros valores sensibles.
dyls::_main()
es el punto de entrada de dyld y su primera tarea es ejecutar configureProcessRestrictions()
, que generalmente restringe las variables de entorno DYLD_*
explicadas en:
Luego, mapea la caché compartida de dyld que preenlaza todas las bibliotecas del sistema importantes y luego mapea las bibliotecas en las que depende el binario y continúa de forma recursiva hasta que se cargan todas las bibliotecas necesarias. Por lo tanto:
comienza a cargar bibliotecas insertadas con
DYLD_INSERT_LIBRARIES
(si se permite)Luego las compartidas en caché
Luego las importadas
Luego continúa importando bibliotecas de forma recursiva
Una vez que todas están cargadas, se ejecutan los inicializadores de estas bibliotecas. Estos están codificados usando __attribute__((constructor))
definidos en LC_ROUTINES[_64]
(ahora obsoletos) o mediante un puntero en una sección marcada con S_MOD_INIT_FUNC_POINTERS
(generalmente: __DATA.__MOD_INIT_FUNC
).
Los terminadores están codificados con __attribute__((destructor))
y se encuentran en una sección marcada con S_MOD_TERM_FUNC_POINTERS
(__DATA.__mod_term_func
).
Stubs
Todos los binarios en macOS están enlazados dinámicamente. Por lo tanto, contienen algunas secciones de stubs que ayudan al binario a saltar al código correcto en diferentes máquinas y contextos. Es dyld cuando se ejecuta el binario el cerebro que necesita resolver estas direcciones (al menos las no perezosas).
Algunas secciones de stubs en el binario:
__TEXT.__[auth_]stubs
: Punteros de secciones__DATA
__TEXT.__stub_helper
: Pequeño código que invoca el enlace dinámico con información sobre la función a llamar__DATA.__[auth_]got
: Tabla de desplazamiento global (direcciones a funciones importadas, cuando se resuelven, (vinculadas durante el tiempo de carga ya que está marcada con la banderaS_NON_LAZY_SYMBOL_POINTERS
)__DATA.__nl_symbol_ptr
: Punteros de símbolos no perezosos (vinculados durante el tiempo de carga ya que está marcada con la banderaS_NON_LAZY_SYMBOL_POINTERS
)__DATA.__la_symbol_ptr
: Punteros de símbolos perezosos (vinculados en el primer acceso)
Ten en cuenta que los punteros con el prefijo "auth_" utilizan una clave de cifrado en proceso para protegerlo (PAC). Además, es posible utilizar la instrucción arm64 BLRA[A/B]
para verificar el puntero antes de seguirlo. Y el RETA[A/B] se puede utilizar en lugar de una dirección RET.
De hecho, el código en __TEXT.__auth_stubs
utilizará braa
en lugar de bl
para llamar a la función solicitada para autenticar el puntero.
También ten en cuenta que las versiones actuales de dyld cargan todo como no perezoso.
Encontrar símbolos perezosos
Parte interesante del desensamblaje:
Es posible ver que el salto para llamar a printf va a __TEXT.__stubs
:
En el desensamblaje de la sección __stubs
:
Puedes ver que estamos saltando a la dirección de la GOT, que en este caso se resuelve de forma no perezosa y contendrá la dirección de la función printf.
En otras situaciones, en lugar de saltar directamente a la GOT, podría saltar a __DATA.__la_symbol_ptr
que cargará un valor que representa la función que está intentando cargar, luego saltar a __TEXT.__stub_helper
que salta al __DATA.__nl_symbol_ptr
que contiene la dirección de dyld_stub_binder
que toma como parámetros el número de la función y una dirección.
Esta última función, después de encontrar la dirección de la función buscada, la escribe en la ubicación correspondiente en __TEXT.__stub_helper
para evitar búsquedas en el futuro.
Sin embargo, ten en cuenta que las versiones actuales de dyld cargan todo como no perezoso.
Opcodes de Dyld
Finalmente, dyld_stub_binder
necesita encontrar la función indicada y escribirla en la dirección adecuada para no buscarla nuevamente. Para hacerlo, utiliza opcodes (una máquina de estados finitos) dentro de dyld.
Vector de argumentos de apple[]
En macOS, la función principal recibe en realidad 4 argumentos en lugar de 3. El cuarto se llama apple y cada entrada está en forma de clave=valor
. Por ejemplo:
El proceso de inyección de bibliotecas en macOS implica cargar una biblioteca dinámica en un proceso en ejecución. Esto se logra manipulando el cargador dinámico del sistema, dyld
, para forzar la carga de una biblioteca maliciosa. Una vez cargada la biblioteca, puede utilizarse para realizar diversas acciones, como el secuestro de funciones del sistema o la escalada de privilegios.
Para el momento en que estos valores llegan a la función principal, la información sensible ya ha sido eliminada de ellos o podría haber sido una fuga de datos.
es posible ver todos estos valores interesantes depurando antes de llegar a la función principal con:
dyld_all_image_infos
Esta es una estructura exportada por dyld con información sobre el estado de dyld que se puede encontrar en el código fuente con información como la versión, puntero a la matriz dyld_image_info, a dyld_image_notifier, si el proceso está separado de la caché compartida, si se llamó al inicializador de libSystem, puntero al encabezado Mach propio de dyls, puntero a la cadena de versión de dyld...
Variables de entorno de dyld
depurar dyld
Variables de entorno interesantes que ayudan a comprender qué está haciendo dyld:
DYLD_PRINT_LIBRARIES
Verifica cada biblioteca que se carga:
DYLD_PRINT_SEGMENTS
Verifique cómo se carga cada biblioteca:
DYLD_PRINT_INITIALIZERS
Imprime cuándo se está ejecutando cada inicializador de biblioteca:
Otros
DYLD_BIND_AT_LAUNCH
: Las vinculaciones perezosas se resuelven con las no perezosasDYLD_DISABLE_PREFETCH
: Deshabilita la precarga de contenido __DATA y __LINKEDITDYLD_FORCE_FLAT_NAMESPACE
: Vinculaciones de un solo nivelDYLD_[FRAMEWORK/LIBRARY]_PATH | DYLD_FALLBACK_[FRAMEWORK/LIBRARY]_PATH | DYLD_VERSIONED_[FRAMEWORK/LIBRARY]_PATH
: Rutas de resoluciónDYLD_INSERT_LIBRARIES
: Carga una biblioteca específicaDYLD_PRINT_TO_FILE
: Escribe la depuración de dyld en un archivoDYLD_PRINT_APIS
: Imprime llamadas de API de libdyldDYLD_PRINT_APIS_APP
: Imprime llamadas de API de libdyld realizadas por mainDYLD_PRINT_BINDINGS
: Imprime símbolos cuando se vinculanDYLD_WEAK_BINDINGS
: Solo imprime símbolos débiles cuando se vinculanDYLD_PRINT_CODE_SIGNATURES
: Imprime operaciones de registro de firma de códigoDYLD_PRINT_DOFS
: Imprime secciones de formato de objeto D-Trace cargadasDYLD_PRINT_ENV
: Imprime env visto por dyldDYLD_PRINT_INTERPOSTING
: Imprime operaciones de interposiciónDYLD_PRINT_LIBRARIES
: Imprime bibliotecas cargadasDYLD_PRINT_OPTS
: Imprime opciones de cargaDYLD_REBASING
: Imprime operaciones de reubicación de símbolosDYLD_RPATHS
: Imprime expansiones de @rpathDYLD_PRINT_SEGMENTS
: Imprime mapeos de segmentos Mach-ODYLD_PRINT_STATISTICS
: Imprime estadísticas de tiempoDYLD_PRINT_STATISTICS_DETAILS
: Imprime estadísticas detalladas de tiempoDYLD_PRINT_WARNINGS
: Imprime mensajes de advertenciaDYLD_SHARED_CACHE_DIR
: Ruta a utilizar para la caché de bibliotecas compartidasDYLD_SHARED_REGION
: "use", "private", "avoid"DYLD_USE_CLOSURES
: Habilita los cierres
Es posible encontrar más con algo como:
O descargando el proyecto dyld desde https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz y ejecutando dentro de la carpeta:
Referencias
Última actualización