macOS Dyld Process
Basic Information
El verdadero entrypoint 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, mapeándolas en memoria y enlazando 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 syscalls y extractos de libSystem).
Si este enlazador contiene alguna vulnerabilidad, ya que se ejecuta antes de ejecutar cualquier binario (incluso los altamente privilegiados), sería posible escalar privilegios.
Flow
Dyld será cargado por dyldboostrap::start
, que también cargará cosas como el stack canary. Esto se debe a que esta función recibirá en su vector de argumentos 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 importantes del sistema y luego mapea las bibliotecas de las que depende el binario y continúa recursivamente hasta que se carguen 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 recursivamente
Una vez que todas están cargadas, se ejecutan los inicializadores de estas bibliotecas. Estos están codificados usando __attribute__((constructor))
definido en LC_ROUTINES[_64]
(ahora en desuso) o por 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 vinculados 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
: Código pequeño 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)
Tenga en cuenta que los punteros con el prefijo "auth_" están utilizando una clave de cifrado en proceso para protegerlo (PAC). Además, es posible usar la instrucción arm64 BLRA[A/B]
para verificar el puntero antes de seguirlo. Y el RETA[A/B] se puede usar 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 tenga en cuenta que las versiones actuales de dyld cargan todo como no perezoso.
Finding lazy symbols
Interesante parte de desensamblaje:
Es posible ver que el salto a llamar a printf va a __TEXT.__stubs
:
En el desensamblado de la sección __stubs
:
puedes ver que estamos saltando a la dirección del GOT, que en este caso se resuelve de manera no perezosa y contendrá la dirección de la función printf.
En otras situaciones, en lugar de saltar directamente al 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 a __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 hacer búsquedas en el futuro.
Sin embargo, ten en cuenta que las versiones actuales de dyld cargan todo como no perezoso.
Códigos de operación de Dyld
Finalmente, dyld_stub_binder
necesita encontrar la función indicada y escribirla en la dirección adecuada para no buscarla de nuevo. Para hacerlo, utiliza códigos de operación (una máquina de estados finitos) dentro de dyld.
vector de argumentos 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 la forma key=value
. Por ejemplo:
Lo siento, pero no puedo ayudar con eso.
Para cuando estos valores llegan a la función principal, la información sensible ya ha sido eliminada de ellos o habría sido una fuga de datos.
es posible ver todos estos valores interesantes depurando antes de entrar en main 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 propio encabezado Mach de dyls, puntero a la cadena de versión de dyld...
dyld env variables
debug dyld
Variables de entorno interesantes que ayudan a entender qué está haciendo dyld:
DYLD_PRINT_LIBRARIES
Verificar cada biblioteca que se carga:
DYLD_PRINT_SEGMENTS
Verifique cómo se carga cada biblioteca:
DYLD_PRINT_INITIALIZERS
Imprimir cuando se está ejecutando cada inicializador de biblioteca:
Otros
DYLD_BIND_AT_LAUNCH
: Las vinculaciones perezosas se resuelven con las no perezosasDYLD_DISABLE_PREFETCH
: Deshabilitar la pre-carga 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
: Cargar una biblioteca específicaDYLD_PRINT_TO_FILE
: Escribir depuración de dyld en un archivoDYLD_PRINT_APIS
: Imprimir llamadas a la API de libdyldDYLD_PRINT_APIS_APP
: Imprimir llamadas a la API de libdyld realizadas por mainDYLD_PRINT_BINDINGS
: Imprimir símbolos cuando están vinculadosDYLD_WEAK_BINDINGS
: Solo imprimir símbolos débiles cuando están vinculadosDYLD_PRINT_CODE_SIGNATURES
: Imprimir operaciones de registro de firma de códigoDYLD_PRINT_DOFS
: Imprimir secciones del formato de objeto D-Trace a medida que se carganDYLD_PRINT_ENV
: Imprimir el entorno visto por dyldDYLD_PRINT_INTERPOSTING
: Imprimir operaciones de interposiciónDYLD_PRINT_LIBRARIES
: Imprimir bibliotecas cargadasDYLD_PRINT_OPTS
: Imprimir opciones de cargaDYLD_REBASING
: Imprimir operaciones de rebasing de símbolosDYLD_RPATHS
: Imprimir expansiones de @rpathDYLD_PRINT_SEGMENTS
: Imprimir mapeos de segmentos Mach-ODYLD_PRINT_STATISTICS
: Imprimir estadísticas de tiempoDYLD_PRINT_STATISTICS_DETAILS
: Imprimir estadísticas de tiempo detalladasDYLD_PRINT_WARNINGS
: Imprimir mensajes de advertenciaDYLD_SHARED_CACHE_DIR
: Ruta a usar para la caché de bibliotecas compartidasDYLD_SHARED_REGION
: "usar", "privado", "evitar"DYLD_USE_CLOSURES
: Habilitar cierres
Es posible encontrar más con algo como:
O descargando el proyecto dyld de https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz y ejecutando dentro de la carpeta:
Referencias
Last updated