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
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.
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_object
lerdir. 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 grubuio
: Asenkron I/O isteklerimach
: Mach portlarımach_msg
: Mach mesajlarıpthread_root_queue
: Bir pthread iş parçacığı havuzuna sahip bir kuyruk ve iş kuyrukları değilqueue
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:
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:
#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:
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
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
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.