macOS GCD - Grand Central Dispatch
Reading time: 8 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Grundinformationen
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, der vollständig vom System verwaltet wird. 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.
tip
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 je 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ße Parallelität 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.
Blöcke
Ein Block ist ein selbstständiger 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 Aufruf
- 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).
Warteschlangen
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 die andere Art von Warteschlange könnte dies haben.
Standardwarteschlangen:
.main-thread
: Vondispatch_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 alsDISPATCH_QUEUE_PRIORITY_BACKGROUND
.root.background-qos.overcommit
.root.utility-qos
: Verfügbar alsDISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE
.root.utility-qos.overcommit
.root.default-qos
: Verfügbar alsDISPATCH_QUEUE_PRIORITY_DEFAULT
.root.background-qos.overcommit
.root.user-initiated-qos
: Verfügbar alsDISPATCH_QUEUE_PRIORITY_HIGH
.root.background-qos.overcommit
.root.user-interactive-qos
: Höchste Priorität.root.background-qos.overcommit
Beachten Sie, dass das System entscheiden wird, 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).
Attribute
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.
Dispatch-Objekte
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öckegroup
: Gruppe von Blöckenio
: Asynchrone I/O-Anfragenmach
: Mach-Portsmach_msg
: Mach-Nachrichtenpthread_root_queue
: Eine Warteschlange mit einem pthread-Thread-Pool und nicht Arbeitswarteschlangenqueue
semaphore
source
: Ereignisquelle
Objective-C
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:
struct Block {
void *isa; // NSConcreteStackBlock,...
int flags;
int reserved;
void *invoke;
struct BlockDescriptor *descriptor;
// captured variables go here
};
Und dies ist ein Beispiel, um Parallelismus mit dispatch_async
zu verwenden:
#import <Foundation/Foundation.h>
// Define a block
void (^backgroundTask)(void) = ^{
// Code to be executed in the background
for (int i = 0; i < 10; i++) {
NSLog(@"Background task %d", i);
sleep(1); // Simulate a long-running task
}
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Create a dispatch queue
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.example.backgroundQueue", NULL);
// Submit the block to the queue for asynchronous execution
dispatch_async(backgroundQueue, backgroundTask);
// Continue with other work on the main queue or thread
for (int i = 0; i < 10; i++) {
NSLog(@"Main task %d", i);
sleep(1); // Simulate a long-running task
}
}
return 0;
}
Swift
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:
import Foundation
// Define a closure (the Swift equivalent of a block)
let backgroundTask: () -> Void = {
for i in 0..<10 {
print("Background task \(i)")
sleep(1) // Simulate a long-running task
}
}
// Entry point
autoreleasepool {
// Create a dispatch queue
let backgroundQueue = DispatchQueue(label: "com.example.backgroundQueue")
// Submit the closure to the queue for asynchronous execution
backgroundQueue.async(execute: backgroundTask)
// Continue with other work on the main queue
for i in 0..<10 {
print("Main task \(i)")
sleep(1) // Simulate a long-running task
}
}
Frida
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
frida -U <prog_name> -l libdispatch.js
dispatch_sync
Calling queue: com.apple.UIKit._UIReusePool.reuseSetAccess
Callback function: 0x19e3a6488 UIKitCore!__26-[_UIReusePool addObject:]_block_invoke
Backtrace:
0x19e3a6460 UIKitCore!-[_UIReusePool addObject:]
0x19e3a5db8 UIKitCore!-[UIGraphicsRenderer _enqueueContextForReuse:]
0x19e3a57fc UIKitCore!+[UIGraphicsRenderer _destroyCGContext:withRenderer:]
[...]
Ghidra
Derzeit versteht Ghidra weder die ObjectiveC dispatch_block_t
Struktur noch die swift_dispatch_block
.
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:
tip
Beachten Sie alle Verweise auf "block", um zu verstehen, wie Sie herausfinden können, 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:
References
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.