macOS GCD - Grand Central Dispatch
Last updated
Last updated
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE) Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Grand Central Dispatch (GCD), también conocido como libdispatch (libdispatch.dyld
), está disponible tanto en macOS como en iOS. Es una tecnología desarrollada por Apple para optimizar el soporte de aplicaciones para la ejecución concurrente (multihilo) en hardware multinúcleo.
GCD proporciona y gestiona colas FIFO a las que tu aplicación puede enviar tareas en forma de objetos de bloque. Los bloques enviados a las colas de despacho son ejecutados en un grupo de hilos completamente gestionados por el sistema. GCD crea automáticamente hilos para ejecutar las tareas en las colas de despacho y programa esas tareas para que se ejecuten en los núcleos disponibles.
En resumen, para ejecutar código en paralelo, los procesos pueden enviar bloques de código a GCD, que se encargará de su ejecución. Por lo tanto, los procesos no crean nuevos hilos; GCD ejecuta el código dado con su propio grupo de hilos (que puede aumentar o disminuir según sea necesario).
Esto es muy útil para gestionar la ejecución en paralelo de manera exitosa, reduciendo en gran medida la cantidad de hilos que los procesos crean y optimizando la ejecución en paralelo. Esto es ideal para tareas que requieren gran paralelismo (¿fuerza bruta?) o para tareas que no deben bloquear el hilo principal: Por ejemplo, el hilo principal en iOS maneja las interacciones de la interfaz de usuario, por lo que cualquier otra funcionalidad que pueda hacer que la aplicación se bloquee (búsqueda, acceso a una web, lectura de un archivo...) se gestiona de esta manera.
Un bloque es una sección de código autocontenida (como una función con argumentos que devuelve un valor) y también puede especificar variables vinculadas.
Sin embargo, a nivel de compilador los bloques no existen, son os_object
s. Cada uno de estos objetos está formado por dos estructuras:
literal de bloque:
Comienza con el campo isa
, apuntando a la clase del bloque:
NSConcreteGlobalBlock
(bloques de __DATA.__const
)
NSConcreteMallocBlock
(bloques en el montón)
NSConcreateStackBlock
(bloques en la pila)
Tiene flags
(indicando los campos presentes en el descriptor de bloque) y algunos bytes reservados
El puntero a la función a llamar
Un puntero al descriptor de bloque
Variables importadas del bloque (si las hay)
descriptor de bloque: Su tamaño depende de los datos presentes (como se indica en las banderas anteriores)
Tiene algunos bytes reservados
Su tamaño
Por lo general, tendrá un puntero a una firma de estilo Objective-C para saber cuánto espacio se necesita para los parámetros (bandera BLOCK_HAS_SIGNATURE
)
Si se hacen referencia a variables, este bloque también tendrá punteros a un ayudante de copia (copiando el valor al principio) y un ayudante de liberación (liberándolo).
Una cola de despacho es un objeto nombrado que proporciona el orden FIFO de bloques para su ejecución.
Los bloques se establecen en colas para ser ejecutados, y estas admiten 2 modos: DISPATCH_QUEUE_SERIAL
y DISPATCH_QUEUE_CONCURRENT
. Por supuesto, el serial no tendrá problemas de condición de carrera ya que un bloque no se ejecutará hasta que el anterior haya terminado. Pero el otro tipo de cola podría tenerlo.
Colas predeterminadas:
.main-thread
: Desde dispatch_get_main_queue()
.libdispatch-manager
: Gestor de colas de GCD
.root.libdispatch-manager
: Gestor de colas de GCD
.root.maintenance-qos
: Tareas de prioridad más baja
.root.maintenance-qos.overcommit
.root.background-qos
: Disponible como DISPATCH_QUEUE_PRIORITY_BACKGROUND
.root.background-qos.overcommit
.root.utility-qos
: Disponible como DISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE
.root.utility-qos.overcommit
.root.default-qos
: Disponible como DISPATCH_QUEUE_PRIORITY_DEFAULT
.root.background-qos.overcommit
.root.user-initiated-qos
: Disponible como DISPATCH_QUEUE_PRIORITY_HIGH
.root.background-qos.overcommit
.root.user-interactive-qos
: Prioridad más alta
.root.background-qos.overcommit
Ten en cuenta que será el sistema quien decida qué hilos manejan qué colas en cada momento (varios hilos pueden trabajar en la misma cola o el mismo hilo puede trabajar en diferentes colas en algún momento)
Al crear una cola con dispatch_queue_create
el tercer argumento es un dispatch_queue_attr_t
, que generalmente es DISPATCH_QUEUE_SERIAL
(que en realidad es NULL) o DISPATCH_QUEUE_CONCURRENT
que es un puntero a una estructura dispatch_queue_attr_t
que permite controlar algunos parámetros de la cola.
Hay varios objetos que libdispatch utiliza y las colas y bloques son solo 2 de ellos. Es posible crear estos objetos con dispatch_object_create
:
block
data
: Bloques de datos
group
: Grupo de bloques
io
: Solicitudes de E/S asíncronas
mach
: Puertos Mach
mach_msg
: Mensajes Mach
pthread_root_queue
: Una cola con un grupo de hilos pthread y sin colas de trabajo
queue
semaphore
source
: Fuente de eventos
En Objetive-C hay diferentes funciones para enviar un bloque para ser ejecutado en paralelo:
dispatch_async: Envía un bloque para su ejecución asíncrona en una cola de despacho y retorna inmediatamente.
dispatch_sync: Envía un objeto de bloque para su ejecución y retorna después de que ese bloque termine de ejecutarse.
dispatch_once: Ejecuta un objeto de bloque solo una vez durante la vida útil de una aplicación.
dispatch_async_and_wait: Envía un elemento de trabajo para su ejecución y retorna solo después de que termine de ejecutarse. A diferencia de dispatch_sync
, esta función respeta todos los atributos de la cola cuando ejecuta el bloque.
Estas funciones esperan estos parámetros: dispatch_queue_t
queue,
dispatch_block_t
block
Esta es la estructura de un Bloque:
Y este es un ejemplo para usar paralelismo con dispatch_async
:
libswiftDispatch
es una biblioteca que proporciona enlaces Swift al marco Grand Central Dispatch (GCD) que originalmente está escrito en C.
La biblioteca libswiftDispatch
envuelve las API de C GCD en una interfaz más amigable para Swift, lo que facilita y hace más intuitivo para los desarrolladores de Swift trabajar con GCD.
DispatchQueue.global().sync{ ... }
DispatchQueue.global().async{ ... }
let onceToken = DispatchOnce(); onceToken.perform { ... }
async await
var (data, response) = await URLSession.shared.data(from: URL(string: "https://api.example.com/getData"))
Ejemplo de código:
El siguiente script de Frida se puede utilizar para engancharse en varias funciones de dispatch
y extraer el nombre de la cola, la traza de ejecución y el bloque: https://github.com/seemoo-lab/frida-scripts/blob/main/scripts/libdispatch.js
Actualmente Ghidra no entiende ni la estructura dispatch_block_t
de ObjectiveC, ni la estructura swift_dispatch_block
.
Entonces, si deseas que las entienda, simplemente puedes declararlas:
Luego, encuentra un lugar en el código donde se utilicen:
Ten en cuenta todas las referencias hechas a "block" para entender cómo podrías descubrir que se está utilizando la estructura.
Haz clic derecho en la variable -> Cambiar tipo de variable y selecciona en este caso swift_dispatch_block
:
Ghidra reescribirá automáticamente todo:
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE) Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)