macOS GCD - Grand Central Dispatch
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Grand Central Dispatch (GCD), також відомий як libdispatch (libdispatch.dyld
), доступний як в macOS, так і в iOS. Це технологія, розроблена Apple для оптимізації підтримки додатків для паралельного (мультитредового) виконання на багатоядерному апаратному забезпеченні.
GCD надає та керує FIFO чергами, до яких ваш додаток може подавати завдання у формі блок-об'єктів. Блоки, подані до черг, виконуються на пулі потоків, повністю керованих системою. GCD автоматично створює потоки для виконання завдань у чергах і планує ці завдання для виконання на доступних ядрах.
У підсумку, для виконання коду в паралельному режимі процеси можуть надсилати блоки коду до GCD, який подбає про їх виконання. Тому процеси не створюють нові потоки; GCD виконує даний код зі своїм власним пулом потоків (який може збільшуватися або зменшуватися за необхідності).
Це дуже корисно для успішного управління паралельним виконанням, значно зменшуючи кількість потоків, які створюють процеси, і оптимізуючи паралельне виконання. Це ідеально підходить для завдань, які вимагають великого паралелізму (брутфорс?) або для завдань, які не повинні блокувати основний потік: наприклад, основний потік на iOS обробляє взаємодії з UI, тому будь-яка інша функціональність, яка може призвести до зависання програми (пошук, доступ до вебу, читання файлу...) управляється таким чином.
Блок — це самостійна секція коду (як функція з аргументами, що повертає значення) і може також вказувати зв'язані змінні.
Однак на рівні компілятора блоки не існують, вони є os_object
s. Кожен з цих об'єктів складається з двох структур:
блок-літерал:
Він починається з поля isa
, що вказує на клас блоку:
NSConcreteGlobalBlock
(блоки з __DATA.__const
)
NSConcreteMallocBlock
(блоки в купі)
NSConcreateStackBlock
(блоки в стеку)
Має flags
(які вказують на поля, присутні в дескрипторі блоку) і кілька зарезервованих байтів
Вказівник на функцію для виклику
Вказівник на дескриптор блоку
Імпортовані змінні блоку (якщо є)
дескриптор блоку: Його розмір залежить від даних, які присутні (як вказано в попередніх флагах)
Має кілька зарезервованих байтів
Розмір його
Зазвичай матиме вказівник на підпис у стилі Objective-C, щоб знати, скільки місця потрібно для параметрів (флаг BLOCK_HAS_SIGNATURE
)
Якщо змінні посилаються, цей блок також матиме вказівники на допоміжний засіб копіювання (копіюючи значення на початку) і допоміжний засіб звільнення (вивільняючи його).
Черга розподілу — це іменований об'єкт, що забезпечує FIFO порядок блоків для виконання.
Блоки встановлюються в черги для виконання, і ці черги підтримують 2 режими: DISPATCH_QUEUE_SERIAL
і DISPATCH_QUEUE_CONCURRENT
. Звичайно, послідовна черга не матиме проблем з гонками, оскільки блок не буде виконуватись, поки попередній не закінчиться. Але інший тип черги може мати їх.
Черги за замовчуванням:
.main-thread
: З dispatch_get_main_queue()
.libdispatch-manager
: Менеджер черг GCD
.root.libdispatch-manager
: Менеджер черг GCD
.root.maintenance-qos
: Завдання з найнижчим пріоритетом
.root.maintenance-qos.overcommit
.root.background-qos
: Доступно як DISPATCH_QUEUE_PRIORITY_BACKGROUND
.root.background-qos.overcommit
.root.utility-qos
: Доступно як DISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE
.root.utility-qos.overcommit
.root.default-qos
: Доступно як DISPATCH_QUEUE_PRIORITY_DEFAULT
.root.background-qos.overcommit
.root.user-initiated-qos
: Доступно як DISPATCH_QUEUE_PRIORITY_HIGH
.root.background-qos.overcommit
.root.user-interactive-qos
: Найвищий пріоритет
.root.background-qos.overcommit
Зверніть увагу, що саме система вирішує, які потоки обробляють які черги в кожен момент часу (кілька потоків можуть працювати в одній черзі або один і той же потік може працювати в різних чергах в певний момент)
При створенні черги з dispatch_queue_create
третій аргумент є dispatch_queue_attr_t
, який зазвичай є або DISPATCH_QUEUE_SERIAL
(що насправді є NULL), або DISPATCH_QUEUE_CONCURRENT
, що є вказівником на структуру dispatch_queue_attr_t
, яка дозволяє контролювати деякі параметри черги.
Існує кілька об'єктів, які використовує libdispatch, і черги та блоки — це лише 2 з них. Можна створити ці об'єкти за допомогою dispatch_object_create
:
block
data
: Блоки даних
group
: Група блоків
io
: Асинхронні запити I/O
mach
: Порти Mach
mach_msg
: Повідомлення Mach
pthread_root_queue
: Черга з пулом потоків pthread і не робочими чергами
queue
semaphore
source
: Джерело подій
В Objective-C є різні функції для надсилання блоку для виконання в паралельному режимі:
dispatch_async: Подає блок для асинхронного виконання в черзі розподілу та повертає негайно.
dispatch_sync: Подає об'єкт блоку для виконання та повертає після завершення виконання цього блоку.
dispatch_once: Виконує об'єкт блоку лише один раз протягом життєвого циклу програми.
dispatch_async_and_wait: Подає робочий елемент для виконання та повертає лише після його завершення. На відміну від dispatch_sync
, ця функція поважає всі атрибути черги під час виконання блоку.
Ці функції очікують такі параметри: dispatch_queue_t
queue,
dispatch_block_t
block
Це структура блоку:
І це приклад використання паралелізму з dispatch_async
:
libswiftDispatch
- це бібліотека, яка надає Swift прив'язки до фреймворку Grand Central Dispatch (GCD), який спочатку написаний на C.
Бібліотека libswiftDispatch
обгортає C GCD API в більш дружній до Swift інтерфейс, що робить роботу з GCD легшою та інтуїтивно зрозумілішою для розробників Swift.
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:
Наступний скрипт Frida можна використовувати для перехоплення кількох dispatch
функцій та витягнення назви черги, зворотного сліду та блоку: https://github.com/seemoo-lab/frida-scripts/blob/main/scripts/libdispatch.js
Наразі Ghidra не розуміє ні структуру ObjectiveC dispatch_block_t
, ні swift_dispatch_block
.
Отже, якщо ви хочете, щоб вона їх розуміла, ви можете просто оголосити їх:
Потім знайдіть місце в коді, де вони використовуються:
Зверніть увагу на всі посилання на "block", щоб зрозуміти, як ви могли б зрозуміти, що структура використовується.
Клацніть правою кнопкою миші на змінній -> Змінити тип змінної і виберіть у цьому випадку swift_dispatch_block
:
Ghidra автоматично перепише все:
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)