macOS GCD - Grand Central Dispatch
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Grand Central Dispatch (GCD), auch bekannt als libdispatch (libdispatch.dyld
), ist sowohl in macOS als auch in iOS verfügbar. Es ist eine von Apple entwickelte Technologie zur Optimierung der Anwendungsunterstützung für parallele (multithreaded) Ausführung auf Multicore-Hardware.
GCD stellt FIFO-Warteschlangen bereit und verwaltet diese, in die Ihre Anwendung Aufgaben in Form von Blockobjekten einreichen kann. Blöcke, die an Dispatch-Warteschlangen übergeben werden, werden auf einem Pool von Threads ausgeführt, die vollständig vom System verwaltet werden. GCD erstellt automatisch Threads zur Ausführung der Aufgaben in den Dispatch-Warteschlangen und plant diese Aufgaben zur Ausführung auf den verfügbaren Kernen.
Zusammenfassend lässt sich sagen, dass Prozesse Code parallel ausführen können, indem sie Codeblöcke an GCD senden, das sich um deren Ausführung kümmert. Daher erstellen Prozesse keine neuen Threads; GCD führt den gegebenen Code mit seinem eigenen Pool von Threads aus (der nach Bedarf erhöht oder verringert werden kann).
Dies ist sehr hilfreich, um die parallele Ausführung erfolgreich zu verwalten, da die Anzahl der Threads, die Prozesse erstellen, erheblich reduziert und die parallele Ausführung optimiert wird. Dies ist ideal für Aufgaben, die großen Parallelismus erfordern (Brute-Forcing?) oder für Aufgaben, die den Hauptthread nicht blockieren sollten: Zum Beispiel verarbeitet der Hauptthread in iOS UI-Interaktionen, sodass jede andere Funktionalität, die die App zum Hängen bringen könnte (Suchen, Zugriff auf das Web, Lesen einer Datei...), auf diese Weise verwaltet wird.
Ein Block ist ein selbstenthaltener Abschnitt von Code (wie eine Funktion mit Argumenten, die einen Wert zurückgibt) und kann auch gebundene Variablen angeben.
Auf Compiler-Ebene existieren Blöcke jedoch nicht, sie sind os_object
s. Jedes dieser Objekte besteht aus zwei Strukturen:
Blockliteral:
Es beginnt mit dem isa
-Feld, das auf die Klasse des Blocks zeigt:
NSConcreteGlobalBlock
(Blöcke aus __DATA.__const
)
NSConcreteMallocBlock
(Blöcke im Heap)
NSConcreateStackBlock
(Blöcke im Stack)
Es hat flags
(die Felder im Block-Descriptor anzeigen) und einige reservierte Bytes
Der Funktionszeiger zum Aufrufen
Ein Zeiger auf den Block-Descriptor
Importierte Blockvariablen (falls vorhanden)
Block-Descriptor: Die Größe hängt von den vorhandenen Daten ab (wie in den vorherigen Flags angegeben)
Es hat einige reservierte Bytes
Die Größe davon
Es wird normalerweise einen Zeiger auf eine Objective-C-Stil-Signatur haben, um zu wissen, wie viel Platz für die Parameter benötigt wird (Flag BLOCK_HAS_SIGNATURE
)
Wenn Variablen referenziert werden, hat dieser Block auch Zeiger auf einen Kopierhelfer (der den Wert zu Beginn kopiert) und einen Entsorgungshelfer (der ihn freigibt).
Eine Dispatch-Warteschlange ist ein benanntes Objekt, das FIFO-Anordnung von Blöcken für die Ausführung bereitstellt.
Blöcke werden in Warteschlangen gesetzt, um ausgeführt zu werden, und diese unterstützen 2 Modi: DISPATCH_QUEUE_SERIAL
und DISPATCH_QUEUE_CONCURRENT
. Natürlich hat die serielle Warteschlange keine Probleme mit Race Conditions, da ein Block nicht ausgeführt wird, bis der vorherige abgeschlossen ist. Aber der andere Warteschlangentyp könnte dies haben.
Standardwarteschlangen:
.main-thread
: Von dispatch_get_main_queue()
.libdispatch-manager
: GCDs Warteschlangenmanager
.root.libdispatch-manager
: GCDs Warteschlangenmanager
.root.maintenance-qos
: Aufgaben mit der niedrigsten Priorität
.root.maintenance-qos.overcommit
.root.background-qos
: Verfügbar als DISPATCH_QUEUE_PRIORITY_BACKGROUND
.root.background-qos.overcommit
.root.utility-qos
: Verfügbar als DISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE
.root.utility-qos.overcommit
.root.default-qos
: Verfügbar als DISPATCH_QUEUE_PRIORITY_DEFAULT
.root.background-qos.overcommit
.root.user-initiated-qos
: Verfügbar als DISPATCH_QUEUE_PRIORITY_HIGH
.root.background-qos.overcommit
.root.user-interactive-qos
: Höchste Priorität
.root.background-qos.overcommit
Beachten Sie, dass das System entscheidet, welche Threads zu welchem Zeitpunkt welche Warteschlangen bearbeiten (mehrere Threads können in derselben Warteschlange arbeiten oder derselbe Thread kann zu einem bestimmten Zeitpunkt in verschiedenen Warteschlangen arbeiten).
Beim Erstellen einer Warteschlange mit dispatch_queue_create
ist das dritte Argument ein dispatch_queue_attr_t
, das normalerweise entweder DISPATCH_QUEUE_SERIAL
(was tatsächlich NULL ist) oder DISPATCH_QUEUE_CONCURRENT
ist, was ein Zeiger auf eine dispatch_queue_attr_t
-Struktur ist, die es ermöglicht, einige Parameter der Warteschlange zu steuern.
Es gibt mehrere Objekte, die libdispatch verwendet, und Warteschlangen und Blöcke sind nur 2 davon. Es ist möglich, diese Objekte mit dispatch_object_create
zu erstellen:
block
data
: Datenblöcke
group
: Gruppe von Blöcken
io
: Asynchrone I/O-Anfragen
mach
: Mach-Ports
mach_msg
: Mach-Nachrichten
pthread_root_queue
: Eine Warteschlange mit einem pthread-Thread-Pool und nicht Arbeitswarteschlangen
queue
semaphore
source
: Ereignisquelle
In Objective-C gibt es verschiedene Funktionen, um einen Block zur parallelen Ausführung zu senden:
dispatch_async: Reicht einen Block zur asynchronen Ausführung in einer Dispatch-Warteschlange ein und gibt sofort zurück.
dispatch_sync: Reicht ein Blockobjekt zur Ausführung ein und gibt zurück, nachdem dieser Block die Ausführung abgeschlossen hat.
dispatch_once: Führt ein Blockobjekt nur einmal während der Lebensdauer einer Anwendung aus.
dispatch_async_and_wait: Reicht ein Arbeitsobjekt zur Ausführung ein und gibt nur zurück, nachdem es die Ausführung abgeschlossen hat. Im Gegensatz zu dispatch_sync
respektiert diese Funktion alle Attribute der Warteschlange, wenn sie den Block ausführt.
Diese Funktionen erwarten diese Parameter: dispatch_queue_t
queue,
dispatch_block_t
block
Dies ist die Struktur eines Blocks:
Und dies ist ein Beispiel, um Parallelismus mit dispatch_async
zu verwenden:
libswiftDispatch
ist eine Bibliothek, die Swift-Bindings für das Grand Central Dispatch (GCD) Framework bereitstellt, das ursprünglich in C geschrieben wurde.
Die libswiftDispatch
-Bibliothek umschließt die C GCD-APIs in einer benutzerfreundlicheren Swift-Schnittstelle, was es für Swift-Entwickler einfacher und intuitiver macht, mit GCD zu arbeiten.
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"))
Codebeispiel:
Das folgende Frida-Skript kann verwendet werden, um in mehrere dispatch
Funktionen einzuhaken und den Warteschafennamen, den Backtrace und den Block zu extrahieren: https://github.com/seemoo-lab/frida-scripts/blob/main/scripts/libdispatch.js
Derzeit versteht Ghidra weder die ObjectiveC dispatch_block_t
Struktur noch die swift_dispatch_block
Struktur.
Wenn Sie möchten, dass es sie versteht, könnten Sie sie einfach deklarieren:
Suchen Sie dann einen Ort im Code, an dem sie verwendet werden:
Notieren Sie alle Verweise auf "block", um zu verstehen, wie Sie herausfinden könnten, dass die Struktur verwendet wird.
Rechtsklick auf die Variable -> Variable umbenennen und in diesem Fall swift_dispatch_block
auswählen:
Ghidra wird automatisch alles umschreiben:
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)