macOS GCD - Grand Central Dispatch

Μάθετε το χάκινγκ του AWS από το μηδέν μέχρι τον ήρωα με το htARTE (Ειδικός Red Team του HackTricks AWS)!

Άλλοι τρόποι υποστήριξης του HackTricks:

Βασικές Πληροφορίες

Το Grand Central Dispatch (GCD), γνωστό και ως libdispatch (libdispatch.dyld), είναι διαθέσιμο τόσο σε macOS όσο και σε iOS. Είναι μια τεχνολογία που αναπτύχθηκε από την Apple για τη βελτιστοποίηση της υποστήριξης εφαρμογών για την ταυτόχρονη (πολυνηματική) εκτέλεση σε πολυπύρηνο υλικό.

Το GCD παρέχει και διαχειρίζεται ουρές FIFO στις οποίες η εφαρμογή σας μπορεί να υποβάλει εργασίες σε μορφή block objects. Τα blocks που υποβάλλονται στις ουρές αποστολής εκτελούνται σε ένα pool νημάτων πλήρως διαχειριζόμενο από το σύστημα. Το GCD δημιουργεί αυτόματα νήματα για την εκτέλεση των εργασιών στις ουρές αποστολής και προγραμματίζει αυτές τις εργασίες να τρέχουν στους διαθέσιμους πυρήνες.

Συνοπτικά, για να εκτελέσετε κώδικα παράλληλα, οι διεργασίες μπορούν να στείλουν blocks κώδικα στο GCD, το οποίο θα φροντίσει για την εκτέλεσή τους. Συνεπώς, οι διεργασίες δεν δημιουργούν νέα νήματα. Το GCD εκτελεί τον δοθέντα κώδικα με το δικό του pool νημάτων (το οποίο μπορεί να αυξηθεί ή να μειωθεί ανάλογα με τις ανάγκες).

Αυτό είναι πολύ χρήσιμο για τη διαχείριση της παράλληλης εκτέλεσης με επιτυχία, μειώνοντας σημαντικά τον αριθμό των νημάτων που δημιουργούν οι διεργασίες και βελτιστοποιώντας την παράλληλη εκτέλεση. Αυτό είναι ιδανικό για εργασίες που απαιτούν μεγάλη παράλληλη διαδικασία (brute-forcing;) ή για εργασίες που δεν πρέπει να μπλοκάρουν το κύριο νήμα: Για παράδειγμα, το κύριο νήμα στο iOS χειρίζεται τις αλληλεπιδράσεις με το UI, οπότε οποιαδήποτε άλλη λειτουργικότητα που θα μπορούσε να κάνει την εφαρμογή να κολλήσει (αναζήτηση, πρόσβαση σε ιστοσελίδα, ανάγνωση αρχείου...) διαχειρίζεται με αυτόν τον τρόπο.

Blocks

Ένα block είναι μια αυτόνομη ενότητα κώδικα (όπως μια συνάρτηση με ορίσματα που επιστρέφει μια τιμή) και μπορεί επίσης να καθορίσει δεσμευμένες μεταβλητές. Ωστόσο, σε επίπεδο μεταγλωττιστή τα blocks δεν υπάρχουν, είναι os_objects. Κάθε ένα από αυτά τα αντικείμενα αποτελείται από δύο δομές:

  • 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.

Έτσι, αν θέλετε να τις κατανοήσει, μπορείτε απλά να τις δηλώσετε:

Στη συνέχεια, βρείτε ένα σημείο στον κώδικα όπου χρησιμοποιούνται:

Σημειώστε όλες τις αναφορές που γίνονται στο "block" για να καταλάβετε πώς μπορείτε να καταλάβετε ότι η δομή χρησιμοποιείται.

Κάντε δεξί κλικ στη μεταβλητή -> Αλλαγή τύπου μεταβλητής και επιλέξτε σε αυτήν την περίπτωση swift_dispatch_block:

Το Ghidra θα επαναγράψει αυτόματα τα πάντα:

Αναφορές

Last updated