macOS GCD - Grand Central Dispatch

Reading time: 7 minutes

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks'i Destekleyin

Temel Bilgiler

Grand Central Dispatch (GCD), ayrıca libdispatch (libdispatch.dyld) olarak da bilinir, hem macOS hem de iOS'ta mevcuttur. Bu, Apple tarafından çok çekirdekli donanımda eşzamanlı (çok iş parçacıklı) yürütme için uygulama desteğini optimize etmek amacıyla geliştirilmiş bir teknolojidir.

GCD, uygulamanızın blok nesneleri şeklinde görevler gönderebileceği FIFO kuyrukları sağlar ve yönetir. Dağıtım kuyruklarına gönderilen bloklar, sistem tarafından tamamen yönetilen bir iş parçacığı havuzunda yürütülür. GCD, dağıtım kuyruklarındaki görevleri yürütmek için otomatik olarak iş parçacıkları oluşturur ve bu görevleri mevcut çekirdeklerde çalışacak şekilde planlar.

tip

Özetle, paralel kod yürütmek için, süreçler GCD'ye kod blokları gönderebilir, bu da yürütmelerini üstlenir. Bu nedenle, süreçler yeni iş parçacıkları oluşturmaz; GCD verilen kodu kendi iş parçacığı havuzuyla yürütür (gerekirse artabilir veya azalabilir).

Bu, paralel yürütmeyi başarılı bir şekilde yönetmek için çok yardımcıdır, süreçlerin oluşturduğu iş parçacığı sayısını büyük ölçüde azaltır ve paralel yürütmeyi optimize eder. Bu, büyük paralellik gerektiren görevler (brute-forcing?) veya ana iş parçacığını engellememesi gereken görevler için idealdir: Örneğin, iOS'taki ana iş parçacığı UI etkileşimlerini yönetir, bu nedenle uygulamanın donmasına neden olabilecek herhangi bir başka işlev (arama, web erişimi, dosya okuma...) bu şekilde yönetilir.

Bloklar

Bir blok, kendi kendine yeterli bir kod bölümü (bir değer döndüren argümanlı bir fonksiyon gibi) olup, bağlı değişkenleri de belirtebilir.
Ancak, derleyici seviyesinde bloklar mevcut değildir, bunlar os_objectlerdir. Bu nesnelerin her biri iki yapıdan oluşur:

  • blok literal:
  • Blok sınıfına işaret eden isa alanıyla başlar:
  • NSConcreteGlobalBlock (bloklar __DATA.__const'dan)
  • NSConcreteMallocBlock (yığın içindeki bloklar)
  • NSConcreateStackBlock (yığın içindeki bloklar)
  • flags (blok tanımında mevcut alanları gösterir) ve bazı ayrılmış baytlar
  • Çağrılacak fonksiyon işaretçisi
  • Blok tanımına işaretçi
  • İçe aktarılan blok değişkenleri (varsa)
  • blok tanımı: Boyutu mevcut veriye bağlıdır (önceki bayraklarda belirtildiği gibi)
  • Bazı ayrılmış baytları vardır
  • Boyutu
  • Genellikle parametreler için ne kadar alan gerektiğini bilmek için bir Objective-C tarzı imzaya işaretçi içerir (bayrak BLOCK_HAS_SIGNATURE)
  • Değişkenler referans alınıyorsa, bu blok ayrıca bir kopyalama yardımcı programına (değeri başta kopyalama) ve bir serbest bırakma yardımcı programına (serbest bırakma) işaretçiler içerir.

Kuyruklar

Bir dağıtım kuyruğu, yürütme için blokların FIFO sıralamasını sağlayan adlandırılmış bir nesnedir.

Bloklar, yürütülmek üzere kuyruklara yerleştirilir ve bu kuyruklar 2 modu destekler: DISPATCH_QUEUE_SERIAL ve DISPATCH_QUEUE_CONCURRENT. Elbette seri olan yarış durumu sorunları yaşamayacaktır çünkü bir blok, önceki blok bitene kadar yürütülmeyecektir. Ancak diğer türdeki kuyrukta bu sorun olabilir.

Varsayılan kuyruklar:

  • .main-thread: dispatch_get_main_queue()'dan
  • .libdispatch-manager: GCD'nin kuyruk yöneticisi
  • .root.libdispatch-manager: GCD'nin kuyruk yöneticisi
  • .root.maintenance-qos: En düşük öncelikli görevler
  • .root.maintenance-qos.overcommit
  • .root.background-qos: DISPATCH_QUEUE_PRIORITY_BACKGROUND olarak mevcut
  • .root.background-qos.overcommit
  • .root.utility-qos: DISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE olarak mevcut
  • .root.utility-qos.overcommit
  • .root.default-qos: DISPATCH_QUEUE_PRIORITY_DEFAULT olarak mevcut
  • .root.background-qos.overcommit
  • .root.user-initiated-qos: DISPATCH_QUEUE_PRIORITY_HIGH olarak mevcut
  • .root.background-qos.overcommit
  • .root.user-interactive-qos: En yüksek öncelik
  • .root.background-qos.overcommit

Her zaman hangi iş parçacıklarının hangi kuyrukları yöneteceğine sistemin karar vereceğini unutmayın (birden fazla iş parçacığı aynı kuyrukta çalışabilir veya aynı iş parçacığı farklı kuyruklarda bir noktada çalışabilir).

Özellikler

dispatch_queue_create ile bir kuyruk oluştururken üçüncü argüman bir dispatch_queue_attr_t'dir, bu genellikle ya DISPATCH_QUEUE_SERIAL (aslında NULL'dur) ya da bazı kuyruk parametrelerini kontrol etmeye olanak tanıyan bir dispatch_queue_attr_t yapısına işaret eden DISPATCH_QUEUE_CONCURRENT'dır.

Dağıtım nesneleri

libdispatch'in kullandığı birkaç nesne vardır ve kuyruklar ile bloklar bunlardan sadece 2'sidir. Bu nesneleri dispatch_object_create ile oluşturmak mümkündür:

  • block
  • data: Veri blokları
  • group: Blok grubu
  • io: Asenkron I/O istekleri
  • mach: Mach portları
  • mach_msg: Mach mesajları
  • pthread_root_queue: Bir pthread iş parçacığı havuzuna sahip bir kuyruk ve iş kuyrukları değil
  • queue
  • semaphore
  • source: Olay kaynağı

Objective-C

Objective-C'de bir bloğu paralel olarak yürütmek için gönderme işlevleri vardır:

  • dispatch_async: Bir dağıtım kuyruğunda asenkron yürütme için bir blok gönderir ve hemen döner.
  • dispatch_sync: Yürütme için bir blok nesnesi gönderir ve o blok yürütmeyi bitirdikten sonra döner.
  • dispatch_once: Bir blok nesnesini yalnızca bir kez uygulamanın ömrü boyunca yürütür.
  • dispatch_async_and_wait: Yürütme için bir iş öğesi gönderir ve yalnızca yürütmeyi bitirdikten sonra döner. dispatch_sync ile karşılaştırıldığında, bu işlev blok yürütüldüğünde kuyruk özelliklerinin tümüne saygı gösterir.

Bu işlevler şu parametreleri bekler: dispatch_queue_t queue, dispatch_block_t block

Bu, bir Blok'un yapısıdır:

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

Ve bu, dispatch_async ile parallelism kullanmanın bir örneğidir:

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, C dilinde yazılmış olan Grand Central Dispatch (GCD) çerçevesine Swift bağlamaları sağlayan bir kütüphanedir.
libswiftDispatch kütüphanesi, C GCD API'lerini daha Swift dostu bir arayüzde sararak, Swift geliştiricilerinin GCD ile çalışmasını daha kolay ve sezgisel hale getirir.

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

Code example:

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

Aşağıdaki Frida script'i birçok dispatch fonksiyonuna hook yapmak ve kuyruk adını, geri izlemeyi ve bloğu çıkarmak için kullanılabilir: 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

Şu anda Ghidra, ne ObjectiveC dispatch_block_t yapısını ne de swift_dispatch_block yapısını anlamıyor.

Eğer bunları anlamasını istiyorsanız, sadece tanımlayabilirsiniz:

Sonra, kodda bunların kullanıldığı bir yer bulun:

tip

"block" ile yapılan tüm referansları not edin, böylece yapının nasıl kullanıldığını anlayabilirsiniz.

Değişkene sağ tıklayın -> Değişkeni Yeniden Yazın ve bu durumda swift_dispatch_block'ı seçin:

Ghidra her şeyi otomatik olarak yeniden yazacaktır:

References

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks'i Destekleyin