12 KiB
macOS GCD - Grand Central Dispatch
{% hint style="success" %}
AWS Hacking öğrenin ve uygulayın:HackTricks Eğitim AWS Kırmızı Takım Uzmanı (ARTE)
GCP Hacking öğrenin ve uygulayın: HackTricks Eğitim GCP Kırmızı Takım Uzmanı (GRTE)
HackTricks'i Destekleyin
- Abonelik planlarını kontrol edin!
- 💬 Discord grubuna katılın veya telegram grubuna katılın veya bizi Twitter 🐦 @hacktricks_live** takip edin.**
- Hacking püf noktalarını paylaşarak HackTricks ve HackTricks Cloud github depolarına PR gönderin.
Temel Bilgiler
Grand Central Dispatch (GCD), aynı zamanda libdispatch (libdispatch.dyld
) olarak da bilinir ve macOS ve iOS'te mevcuttur. Apple tarafından geliştirilen bir teknolojidir ve çok çekirdekli donanımlarda eşzamanlı (çoklu iş parçacıklı) yürütme için uygulama desteğini optimize etmek amacıyla geliştirilmiştir.
GCD, uygulamanızın blok nesneleri şeklinde görevleri gönderebileceği FIFO kuyruklarını sağlar ve yönetir. Gönderilen bloklar, sistem tarafından tamamen yönetilen bir iş parçacığı havuzunda yürütülür. GCD, görevleri yürütmek için otomatik olarak iş parçacıkları oluşturur ve bu görevleri mevcut çekirdeklere çalışacak şekilde planlar.
{% hint style="success" %} Özetle, paralel olarak kodu yürütmek için işlemler, GCD'ye kod blokları gönderebilir, bu da onların yürütümüyle ilgilenir. Bu nedenle, işlemler yeni iş parçacıkları oluşturmaz; GCD, verilen kodu kendi iş parçacığı havuzuyla yürütür (gerektiğinde artabilir veya azalabilir). {% endhint %}
Bu, paralel yürütümü başarılı bir şekilde yönetmek için çok yardımcı olur, işlemlerin oluşturduğu iş parçacığı sayısını büyük ölçüde azaltır ve paralel yürütümü optimize eder. Bu, büyük paralelizm gerektiren görevler için (kaba kuvvet?) 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 başka bir işlev (arama, web'e erişim, dosya okuma...) bu şekilde yönetilir.
Bloklar
Bir blok, kendi başına bir kod bölümü (argüman döndüren bir işlev gibi) ve bağlı değişkenleri de belirtebilir.
Ancak, derleyici düzeyinde bloklar mevcut değildir, bunlar os_object
'lerdir. Bu nesnelerin her biri iki yapıdan oluşur:
- blok literali:
- Bloğun sınıfına işaret eden
isa
alanıyla başlar: NSConcreteGlobalBlock
(__DATA.__const
blokları)NSConcreteMallocBlock
(heap'teki bloklar)NSConcreateStackBlock
(yığında bloklar)- Blok tanımlayıcısında bulunan alanları gösteren
flags
ve bazı ayrılmış baytlar - Çağrılacak işlev işaretçisi
- Bir blok tanımlayıcısına işaretçi
- İçe aktarılan blok değişkenleri (varsa)
- blok tanımlayıcısı: Bu, mevcut veriye bağlı olarak boyutu değişen bir yapıdır (önceki bayraklarda belirtildiği gibi)
- Bazı ayrılmış baytlar içerir
- Boyutu
- Genellikle, parametreler için ne kadar alanın gerektiğini bilmek için bir Objective-C tarzı imza işaretçisine işaret eder (bayrak
BLOCK_HAS_SIGNATURE
) - Değişkenler referans alınıyorsa, bu blok ayrıca bir kopya yardımcısına (değeri başlangıçta kopyalayan) ve atma yardımcısına (serbest bırakan) işaretçilere sahip olacaktır.
Kuyruklar
Bir dağıtım kuyruğu, blokların yürütülmesi için FIFO sıralaması sağlayan adlandırılmış bir nesnedir.
Blokların yürütülmesi için kuyruklara yerleştirilir ve bunlar 2 modu destekler: DISPATCH_QUEUE_SERIAL
ve DISPATCH_QUEUE_CONCURRENT
. Tabii ki, seri olan yarış koşulu sorunlarına sahip olmayacak çünkü bir blok, önceki blok bitene kadar yürütülmeyecektir. Ancak diğer kuyruk türü bunu yapabilir.
Varsayılan kuyruklar:
.main-thread
:dispatch_get_main_queue()
'den.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 öncelikli.root.background-qos.overcommit
Her zaman hangi iş parçacıklarının hangi kuyrukları her zaman ele alacağını (çoklu iş parçacıkları aynı kuyrukta çalışabilir veya aynı iş parçacığı farklı kuyruklarda çalışabilir) sistem belirleyecektir.
Özellikler
dispatch_queue_create
ile bir kuyruk oluşturulurken üçüncü argüman bir dispatch_queue_attr_t
olup genellikle ya DISPATCH_QUEUE_SERIAL
(aslında NULL) ya da DISPATCH_QUEUE_CONCURRENT
olabilir, bu da kuyruğun bazı parametrelerini kontrol etmeye olanak tanıyan bir dispatch_queue_attr_t
yapısına işaret eder.
Dağıtım nesneleri
Libdispatch'in kullandığı ve kuyruklar ve blokların sadece 2 tanesidir. Bu nesneleri dispatch_object_create
ile oluşturmak mümkündür:
block
data
: Veri bloklarıgroup
: Blok grubuio
: Asenkron G/Ç isteklerimach
: Mach bağlantı noktalarımach_msg
: Mach mesajlarıpthread_root_queue
: İş parçacığı havuzuna sahip bir kuyruk ve iş kuyrukları yokqueue
semaphore
source
: Olay kaynağı
Objective-C
Objective-C'de bir bloğun paralel olarak yürütülmesi için farklı işlevler bulunmaktadır:
- dispatch_async: Bir bloğu bir dağıtım kuyruğunda asenkron olarak yürütmek için gönderir ve hemen döner.
- dispatch_sync: Bir blok nesnesini yürütüm için gönderir ve o blok yürütüldükten sonra döner.
- dispatch_once: Bir uygulamanın ömrü boyunca bir blok nesnesini yalnızca bir kez yürütür.
- dispatch_async_and_wait: Bir iş öğesi için yürütümü gönderir ve yalnızca o iş öğesi yürütüldükten sonra döner.
dispatch_sync
gibi, bu işlev, bloğu yürütürken kuyruğun tüm özelliklerine saygı duyar.
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 dispatch_async
kullanarak paralelizm kullanımına bir örnek:
#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
is a library that provides Swift bindings to the Grand Central Dispatch (GCD) framework which is originally written in C.
The libswiftDispatch
library wraps the C GCD APIs in a more Swift-friendly interface, making it easier and more intuitive for Swift developers to work with GCD.
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"))
Kod örneği:
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 betiği, birkaç dispatch
fonksiyonuna kanca takmak ve sıra 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.
Bu nedenle, onları anlamasını istiyorsanız, sadece bildirmeniz gerekebilir:
Ardından, kodun içinde kullanıldığı yeri bulun:
{% hint style="success" %} "block" ile yapılan tüm referansları not alarak, yapının nasıl kullanıldığını anlayabilirsiniz. {% endhint %}
Değişkenin üzerine sağ tıklayın -> Değişkeni Yeniden Türle ve bu durumda swift_dispatch_block
'u seçin:
Ghidra otomatik olarak her şeyi yeniden yazacaktır: