macOS GCD - Grand Central Dispatch

Reading time: 7 minutes

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Podržite HackTricks

Osnovne informacije

Grand Central Dispatch (GCD), takođe poznat kao libdispatch (libdispatch.dyld), dostupan je i na macOS-u i na iOS-u. To je tehnologija koju je razvila Apple kako bi optimizovala podršku aplikacijama za konkurentno (multithreaded) izvršavanje na višekorisničkom hardveru.

GCD pruža i upravlja FIFO redovima u koje vaša aplikacija može podneti zadatke u obliku blok objekata. Blokovi podneti redovima za raspodelu se izvršavaju na skupu niti koje u potpunosti upravlja sistem. GCD automatski kreira niti za izvršavanje zadataka u redovima za raspodelu i zakazuje te zadatke da se izvrše na dostupnim jezgrama.

tip

Ukratko, da bi se izvršio kod u paraleli, procesi mogu slati blokove koda GCD-u, koji će se pobrinuti za njihovo izvršavanje. Stoga, procesi ne kreiraju nove niti; GCD izvršava dati kod sa svojim sopstvenim skupom niti (koji se može povećavati ili smanjivati po potrebi).

Ovo je veoma korisno za uspešno upravljanje paralelnim izvršavanjem, značajno smanjujući broj niti koje procesi kreiraju i optimizujući paralelno izvršavanje. Ovo je idealno za zadatke koji zahtevaju veliki paralelizam (brute-forcing?) ili za zadatke koji ne bi trebali blokirati glavnu nit: Na primer, glavna nit na iOS-u upravlja UI interakcijama, tako da se svaka druga funkcionalnost koja bi mogla da uzrokuje zamrzavanje aplikacije (pretraga, pristup vebu, čitanje datoteke...) upravlja na ovaj način.

Blokovi

Blok je samostalna sekcija koda (poput funkcije sa argumentima koja vraća vrednost) i može takođe specificirati vezane promenljive.
Međutim, na nivou kompajlera blokovi ne postoje, oni su os_objects. Svaki od ovih objekata se sastoji od dve strukture:

  • blok literal:
  • Počinje sa isa poljem, koje pokazuje na klasu bloka:
  • NSConcreteGlobalBlock (blokovi iz __DATA.__const)
  • NSConcreteMallocBlock (blokovi u heap-u)
  • NSConcreateStackBlock (blokovi u steku)
  • Ima flags (koji označavaju polja prisutna u opisu bloka) i nekoliko rezervisanih bajtova
  • Pokazivač na funkciju koja se poziva
  • Pokazivač na opis bloka
  • Uvezene promenljive bloka (ako ih ima)
  • opis bloka: Njegova veličina zavisi od podataka koji su prisutni (kako je naznačeno u prethodnim oznakama)
  • Ima nekoliko rezervisanih bajtova
  • Njegova veličina
  • Obično će imati pokazivač na Objective-C stil potpis kako bi znao koliko prostora je potrebno za parametre (oznaka BLOCK_HAS_SIGNATURE)
  • Ako su promenljive referencirane, ovaj blok će takođe imati pokazivače na pomoćnika za kopiranje (kopiranje vrednosti na početku) i pomoćnika za oslobađanje (oslobađanje).

Redovi

Red za raspodelu je imenovani objekat koji pruža FIFO redosled blokova za izvršavanje.

Blokovi se postavljaju u redove za izvršavanje, a ovi podržavaju 2 moda: DISPATCH_QUEUE_SERIAL i DISPATCH_QUEUE_CONCURRENT. Naravno, serijski neće imati probleme sa uslovima trke jer blok neće biti izvršen dok prethodni ne završi. Ali drugi tip reda može imati.

Podrazumevajući redovi:

  • .main-thread: Iz dispatch_get_main_queue()
  • .libdispatch-manager: GCD-ov menadžer redova
  • .root.libdispatch-manager: GCD-ov menadžer redova
  • .root.maintenance-qos: Zadaci najniže prioriteta
  • .root.maintenance-qos.overcommit
  • .root.background-qos: Dostupno kao DISPATCH_QUEUE_PRIORITY_BACKGROUND
  • .root.background-qos.overcommit
  • .root.utility-qos: Dostupno kao DISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE
  • .root.utility-qos.overcommit
  • .root.default-qos: Dostupno kao DISPATCH_QUEUE_PRIORITY_DEFAULT
  • .root.background-qos.overcommit
  • .root.user-initiated-qos: Dostupno kao DISPATCH_QUEUE_PRIORITY_HIGH
  • .root.background-qos.overcommit
  • .root.user-interactive-qos: Najviši prioritet
  • .root.background-qos.overcommit

Obratite pažnju da će sistem odlučiti koje niti upravljaju kojim redovima u svakom trenutku (više niti može raditi u istom redu ili ista nit može raditi u različitim redovima u nekom trenutku)

Atributi

Kada kreirate red sa dispatch_queue_create treći argument je dispatch_queue_attr_t, koji obično može biti ili DISPATCH_QUEUE_SERIAL (što je zapravo NULL) ili DISPATCH_QUEUE_CONCURRENT, što je pokazivač na dispatch_queue_attr_t strukturu koja omogućava kontrolu nekih parametara reda.

Dispatch objekti

Postoji nekoliko objekata koje libdispatch koristi, a redovi i blokovi su samo 2 od njih. Moguće je kreirati ove objekte sa dispatch_object_create:

  • block
  • data: Podaci blokovi
  • group: Grupa blokova
  • io: Asinhroni I/O zahtevi
  • mach: Mach portovi
  • mach_msg: Mach poruke
  • pthread_root_queue: Red sa pthread nitnim bazenom, a ne radnim redovima
  • queue
  • semaphore
  • source: Izvor događaja

Objective-C

U Objective-C postoje različite funkcije za slanje bloka na izvršavanje u paraleli:

  • dispatch_async: Podnosi blok za asinhrono izvršavanje na redu za raspodelu i odmah se vraća.
  • dispatch_sync: Podnosi blok objekat za izvršavanje i vraća se nakon što taj blok završi sa izvršavanjem.
  • dispatch_once: Izvršava blok objekat samo jednom tokom trajanja aplikacije.
  • dispatch_async_and_wait: Podnosi radni predmet za izvršavanje i vraća se samo nakon što završi sa izvršavanjem. Za razliku od dispatch_sync, ova funkcija poštuje sve atribute reda kada izvršava blok.

Ove funkcije očekuju sledeće parametre: dispatch_queue_t queue, dispatch_block_t block

Ovo je struktura Bloka:

c
struct Block { void *isa; // NSConcreteStackBlock,... int flags; int reserved; void *invoke; struct BlockDescriptor *descriptor; // captured variables go here };

I ovo je primer korišćenja paralelizma sa dispatch_async:

objectivec
#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 je biblioteka koja pruža Swift bindings za Grand Central Dispatch (GCD) okvir koji je prvobitno napisan u C.
Biblioteka libswiftDispatch obavija C GCD API-je u interfejs koji je više prilagođen Swift-u, olakšavajući i čineći intuitivnijim rad za Swift programere sa GCD-om.

  • 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"))

Primer koda:

swift
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

Sledeći Frida skript može se koristiti za hook-ovanje u nekoliko dispatch funkcija i ekstrakciju imena reda, backtrace-a i bloka: https://github.com/seemoo-lab/frida-scripts/blob/main/scripts/libdispatch.js

bash
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

Trenutno Ghidra ne razume ni ObjectiveC dispatch_block_t strukturu, ni swift_dispatch_block.

Dakle, ako želite da ih razume, možete ih jednostavno deklarisati:

Zatim, pronađite mesto u kodu gde se koriste:

tip

Zabeležite sve reference na "block" da biste razumeli kako možete da shvatite da se struktura koristi.

Desni klik na promenljivu -> Ponovno definiši promenljivu i izaberite u ovom slučaju swift_dispatch_block:

Ghidra će automatski prepraviti sve:

References

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Podržite HackTricks