14 KiB
macOS GCD - Grand Central Dispatch
Μάθετε το χάκινγκ του AWS από το μηδέν μέχρι τον ήρωα με το htARTE (Ειδικός Red Team του HackTricks AWS)!
Άλλοι τρόποι υποστήριξης του HackTricks:
- Αν θέλετε να δείτε την εταιρεία σας διαφημισμένη στο HackTricks ή να κατεβάσετε το HackTricks σε μορφή PDF ελέγξτε τα ΣΧΕΔΙΑ ΣΥΝΔΡΟΜΗΣ!
- Αποκτήστε το επίσημο PEASS & HackTricks swag
- Ανακαλύψτε την Οικογένεια PEASS, τη συλλογή μας από αποκλειστικά NFTs
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα τηλεγραφήματος ή ακολουθήστε μας στο Twitter 🐦 @carlospolopm.
- Μοιραστείτε τα χάκινγκ κόλπα σας υποβάλλοντας PRs στα HackTricks και HackTricks Cloud αποθετήρια του github.
Βασικές Πληροφορίες
Το Grand Central Dispatch (GCD), γνωστό και ως libdispatch (libdispatch.dyld
), είναι διαθέσιμο τόσο σε macOS όσο και σε iOS. Είναι μια τεχνολογία που αναπτύχθηκε από την Apple για τη βελτιστοποίηση της υποστήριξης εφαρμογών για την ταυτόχρονη (πολυνηματική) εκτέλεση σε πολυπύρηνο υλικό.
Το GCD παρέχει και διαχειρίζεται ουρές FIFO στις οποίες η εφαρμογή σας μπορεί να υποβάλει εργασίες σε μορφή block objects. Τα blocks που υποβάλλονται στις ουρές αποστολής εκτελούνται σε ένα pool νημάτων πλήρως διαχειριζόμενο από το σύστημα. Το GCD δημιουργεί αυτόματα νήματα για την εκτέλεση των εργασιών στις ουρές αποστολής και προγραμματίζει αυτές τις εργασίες να τρέχουν στους διαθέσιμους πυρήνες.
{% hint style="success" %} Συνοπτικά, για να εκτελέσετε κώδικα παράλληλα, οι διεργασίες μπορούν να στείλουν blocks κώδικα στο GCD, το οποίο θα φροντίσει για την εκτέλεσή τους. Συνεπώς, οι διεργασίες δεν δημιουργούν νέα νήματα. Το GCD εκτελεί τον δοθέντα κώδικα με το δικό του pool νημάτων (το οποίο μπορεί να αυξηθεί ή να μειωθεί ανάλογα με τις ανάγκες). {% endhint %}
Αυτό είναι πολύ χρήσιμο για τη διαχείριση της παράλληλης εκτέλεσης με επιτυχία, μειώνοντας σημαντικά τον αριθμό των νημάτων που δημιουργούν οι διεργασίες και βελτιστοποιώντας την παράλληλη εκτέλεση. Αυτό είναι ιδανικό για εργασίες που απαιτούν μεγάλη παράλληλη διαδικασία (brute-forcing;) ή για εργασίες που δεν πρέπει να μπλοκάρουν το κύριο νήμα: Για παράδειγμα, το κύριο νήμα στο iOS χειρίζεται τις αλληλεπιδράσεις με το UI, οπότε οποιαδήποτε άλλη λειτουργικότητα που θα μπορούσε να κάνει την εφαρμογή να κολλήσει (αναζήτηση, πρόσβαση σε ιστοσελίδα, ανάγνωση αρχείου...) διαχειρίζεται με αυτόν τον τρόπο.
Blocks
Ένα block είναι μια αυτόνομη ενότητα κώδικα (όπως μια συνάρτηση με ορίσματα που επιστρέφει μια τιμή) και μπορεί επίσης να καθορίσει δεσμευμένες μεταβλητές.
Ωστόσο, σε επίπεδο μεταγλωττιστή τα blocks δεν υπάρχουν, είναι os_object
s. Κάθε ένα από αυτά τα αντικείμενα αποτελείται από δύο δομές:
- block literal:
- Ξεκινά με το πεδίο
isa
, που δείχνει στην κλάση του block: NSConcreteGlobalBlock
(blocks από__DATA.__const
)NSConcreteMallocBlock
(blocks στη στοίβα)NSConcreateStackBlock
(blocks στη στοίβα)- Έχει
flags
(που υποδεικνύουν τα πεδία που υπάρχουν στην περιγραφή του block) και μερικά δεσμευμένα bytes - Ο δείκτης συνάρτησης για κλήση
- Ένας δείκτης στην περιγραφή του block
- Εισαγόμενες μεταβλητές block (αν υπάρχουν)
- block descriptor: Το μέγεθός του εξαρτάται από τα δεδομένα που υπάρχουν (όπως υποδεικνύεται στα προηγούμενα flags)
- Έχει μερικά δεσμευμένα bytes
- Το μέγεθός του
- Συνήθως θα έχει ένα δείκτη σε μια υπογραφή στυλ Objective-C για να γνωρίζει πόσο χώρο χρειάζεται για τις παραμέτρους (σημαία
BLOCK_HAS_SIGNATURE
) - Αν υπάρχουν αναφερόμενες μεταβλητές, αυτό το block θα έχει επίσης δείκτες σε ένα βοηθό αντιγραφής (αντιγράφοντας την τιμή στην αρχή) και βοηθό διάθεσης (απελευθέρωσή του).
Ουρές
Μια ουρά αποστολής είναι ένα αντικείμενο με όνομα που παρέχει ταξινόμηση FIFO των blocks για εκτέλεση.
Τα blocks τοποθετούνται σε ουρές για να εκτελεστούν, και αυτές υποστηρίζουν 2 λειτουργίες: DISPATCH_QUEUE_SERIAL
και DISPATCH_QUEUE_CONCURRENT
. Φυσικά η σειριακή δεν θα έχει προβλήματα race condition καθώς ένα block δεν θα εκτελεστεί μέχρι να ολοκληρωθεί το προηγούμενο. Αλλά ο άλλος τύπος ουράς μπορεί να το έχει.
Προεπιλεγμένες ουρές:
.main-thread
: Απόdispatch_get_main_queue()
.libdispatch-manager
: Διαχειριστής ουράς του GCD.root.libdispatch-manager
: Διαχειριστής ουράς του GCD.root.maintenance-qos
: Εργασίες χαμηλότερης προτεραιότητας.root.maintenance-qos.overcommit
.root.background-qos
: Διαθέσιμη ωςDISPATCH_QUEUE_PRIORITY_BACKGROUND
.root.background-qos.overcommit
.root.utility-qos
: Διαθέσιμη ωςDISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE
.root.utility-qos.overcommit
.root.default-qos
: Διαθέσιμη ωςDISPATCH_QUEUE_PRIORITY_DEFAULT
.root.background-qos.overcommit
.root.user-initiated-qos
: Διαθέσιμη ωςDISPATCH_QUEUE_PRIORITY_HIGH
.root.background-qos.overcommit
.root.user-interactive-qos
: Υψηλότερη προτεραιότητα.root.background-qos.overcommit
Σημειώστε ότι θα είναι το σύστημα που θα αποφασίσει ποια νήματα θα χειριστούν ποιες ουρές κάθε φορά (πολλά νήματα μπορεί να εργαστούν στην ίδια ουρά ή το ίδιο νήμα μπορεί να εργαστεί σε διαφορετικές ουρές σε κάποιο σημείο)
Χαρακτηριστικά
Όταν δημιουργείτε μια ουρά με το dispatch_queue_create
το τρίτο όρισμα είναι ένα dispatch_queue_attr_t
, το οποίο συνήθως είναι είτε DISPATCH_QUEUE_SERIAL
(που στην πραγματικότητα είναι NULL) είτε DISPATCH_QUEUE_CONCURRENT
που είναι ένας δείκτης σε μια δομή dispatch_queue_attr_t
που επιτρέπει τον έλεγχο ορισμένων παραμέτρων της ουράς.
Αντικείμενα αποστολής
Υπάρχουν αρκετά αντικείμενα που χρησιμοποιεί το libdispatch
struct Block {
void *isa; // NSConcreteStackBlock,...
int flags;
int reserved;
void *invoke;
struct BlockDescriptor *descriptor;
// captured variables go here
};
Και αυτό είναι ένα παράδειγμα χρήσης παραλληλισμού με το dispatch_async
:
#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
είναι μια βιβλιοθήκη που παρέχει δεσμεύσεις Swift στο πλαίσιο Grand Central Dispatch (GCD) το οποίο αρχικά γράφτηκε σε C.
Η βιβλιοθήκη libswiftDispatch
τυλίγει τα C GCD APIs σε ένα πιο φιλικό προς τη γλώσσα Swift περιβάλλον, κάνοντας ευκολότερη και πιο ευανάγνωστη την εργασία με το GCD για τους προγραμματιστές Swift.
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"))
Παράδειγμα κώδικα:
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
Το παρακάτω script του Frida μπορεί να χρησιμοποιηθεί για ενσωμάτωση σε αρκετές λειτουργίες dispatch
και εξαγωγή του ονόματος της ουράς, του backtrace και του block: 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
Προς το παρόν το Ghidra δεν κατανοεί ούτε τη δομή dispatch_block_t
της ObjectiveC, ούτε αυτήν της swift_dispatch_block
.
Έτσι, αν θέλετε να τις κατανοήσει, μπορείτε απλά να τις δηλώσετε:
Στη συνέχεια, βρείτε ένα σημείο στον κώδικα όπου χρησιμοποιούνται:
{% hint style="success" %} Σημειώστε όλες τις αναφορές που γίνονται στο "block" για να καταλάβετε πώς μπορείτε να καταλάβετε ότι η δομή χρησιμοποιείται. {% endhint %}
Κάντε δεξί κλικ στη μεταβλητή -> Αλλαγή τύπου μεταβλητής και επιλέξτε σε αυτήν την περίπτωση swift_dispatch_block
:
Το Ghidra θα επαναγράψει αυτόματα τα πάντα: