macOS GCD - Grand Central Dispatch

HackTricks'i Destekleyin

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.

Ö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).

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 grubu

  • io: Asenkron G/Ç istekleri

  • mach: Mach bağlantı noktaları

  • mach_msg: Mach mesajları

  • pthread_root_queue: İş parçacığı havuzuna sahip bir kuyruk ve iş kuyrukları yok

  • queue

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

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

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:

Referanslar

Last updated