hacktricks/macos-hardening/macos-security-and-privilege-escalation/macos-gcd-grand-central-dispatch.md
2024-02-11 01:46:25 +00:00

9.2 KiB

macOS GCD - Grand Central Dispatch

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Podstawowe informacje

Grand Central Dispatch (GCD), znany również jako libdispatch, jest dostępny zarówno w systemie macOS, jak i iOS. Jest to technologia opracowana przez Apple, która optymalizuje obsługę aplikacji dla równoczesnego (wielowątkowego) wykonywania na sprzęcie wielordzeniowym.

GCD dostarcza i zarządza kolejkami FIFO, do których aplikacja może przesyłać zadania w postaci bloków kodu. Bloki przesłane do kolejek dystrybucji są wykonywane na puli wątków w pełni zarządzanej przez system. GCD automatycznie tworzy wątki do wykonywania zadań w kolejkach dystrybucji i planuje te zadania do uruchomienia na dostępnych rdzeniach.

{% hint style="success" %} Podsumowując, aby wykonać kod równolegle, procesy mogą wysyłać bloki kodu do GCD, który zajmie się ich wykonaniem. Dlatego procesy nie tworzą nowych wątków; GCD wykonuje podany kod za pomocą własnej puli wątków. {% endhint %}

Jest to bardzo pomocne w skutecznym zarządzaniu równoczesnym wykonywaniem, znacznie redukując liczbę wątków tworzonych przez procesy i optymalizując równoczesne wykonywanie. Jest to idealne rozwiązanie dla zadań, które wymagają dużej równoległości (np. łamanie haseł?) lub dla zadań, które nie powinny blokować głównego wątku: na przykład główny wątek w systemie iOS obsługuje interakcje z interfejsem użytkownika, więc wszelkie inne funkcje, które mogą spowodować zawieszenie aplikacji (wyszukiwanie, dostęp do sieci, odczyt pliku...), są obsługiwane w ten sposób.

Objective-C

W Objective-C istnieją różne funkcje do wysyłania bloku kodu do równoczesnego wykonania:

  • dispatch_async: Przesyła blok do asynchronicznego wykonania w kolejce dystrybucji i natychmiast zwraca.
  • dispatch_sync: Przesyła blok do wykonania i zwraca po zakończeniu wykonania tego bloku.
  • dispatch_once: Wykonuje blok tylko raz przez cały czas działania aplikacji.
  • dispatch_async_and_wait: Przesyła element pracy do wykonania i zwraca dopiero po zakończeniu jego wykonania. W przeciwieństwie do dispatch_sync, ta funkcja respektuje wszystkie atrybuty kolejki podczas wykonywania bloku.

Te funkcje oczekują tych parametrów: dispatch_queue_t queue, dispatch_block_t block

Oto struktura bloku:

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

A oto przykład użycia równoległości za pomocą dispatch_async:

#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 to biblioteka, która zapewnia powiązania Swift do frameworka Grand Central Dispatch (GCD), który jest pierwotnie napisany w języku C.
Biblioteka libswiftDispatch opakowuje interfejsy C GCD w bardziej przyjazny dla Swifta interfejs, ułatwiając programistom Swift pracę z 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"))

Przykład kodu:

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

Poniższy skrypt Frida może być używany do haczenia w kilka funkcji dispatch i wydobycia nazwy kolejki, śladu wywołań oraz bloku: 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

Obecnie Ghidra nie rozumie struktury dispatch_block_t w ObjectiveC ani struktury swift_dispatch_block.

Jeśli chcesz, aby je zrozumiał, możesz je po prostu zadeklarować:

Następnie znajdź miejsce w kodzie, gdzie są używane:

{% hint style="success" %} Zwróć uwagę na wszystkie odwołania do "block", aby zrozumieć, jak można ustalić, że struktura jest używana. {% endhint %}

Kliknij prawym przyciskiem myszy na zmienną -> Zmień typ zmiennej i wybierz w tym przypadku swift_dispatch_block:

Ghidra automatycznie przepisze wszystko:

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks: