macOS GCD - Grand Central Dispatch
Last updated
Last updated
Вивчайте та практикуйте взлом AWS:Навчання HackTricks AWS Red Team Expert (ARTE) Вивчайте та практикуйте взлом GCP: Навчання HackTricks GCP Red Team Expert (GRTE)
Grand Central Dispatch (GCD), також відомий як libdispatch (libdispatch.dyld
), доступний як в macOS, так і в iOS. Це технологія, розроблена Apple для оптимізації підтримки програм для одночасного (багатопотокового) виконання на багатоядерному обладнанні.
GCD надає та керує чергами FIFO, до яких ваша програма може надсилати завдання у вигляді блок-об'єктів. Блоки, надіслані в черги розподілу, виконуються в пулі потоків, повністю керованих системою. GCD автоматично створює потоки для виконання завдань у чергах розподілу та планує виконання цих завдань на доступних ядрах.
У підсумку, для виконання коду паралельно, процеси можуть надсилати блоки коду в GCD, який буде відповідати за їх виконання. Таким чином, процеси не створюють нові потоки; GCD виконує заданий код зі своїм власним пулом потоків (який може збільшуватися або зменшуватися за необхідності).
Це дуже корисно для успішного управління паралельним виконанням, значно зменшуючи кількість потоків, які створюють процеси, та оптимізуючи паралельне виконання. Це ідеально підходить для завдань, які вимагають великої паралельності (брутфорс?) або для завдань, які не повинні блокувати основний потік: наприклад, основний потік на iOS обробляє взаємодію з користувачем, тому будь-яку іншу функціональність, яка може призвести до зависання додатка (пошук, доступ до веб-сайту, читання файлу...), керується цим способом.
Блок - це самостійний фрагмент коду (схожий на функцію з аргументами, що повертає значення) і може також вказувати зв'язані змінні.
Однак на рівні компілятора блоки не існують, вони є os_object
s. Кожен з цих об'єктів складається з двох структур:
літерал блоку:
Він починається з поля isa
, яке вказує на клас блоку:
NSConcreteGlobalBlock
(блоки з __DATA.__const
)
NSConcreteMallocBlock
(блоки в купі)
NSConcreateStackBlock
(блоки в стеку)
Він має flags
(вказує на поля, присутні в описнику блоку) та деякі зарезервовані байти
Вказівник на функцію для виклику
Вказівник на описник блоку
Імпортовані змінні блоку (якщо є)
описник блоку: Його розмір залежить від даних, які присутні (як вказано в попередніх прапорцях)
Він має деякі зарезервовані байти
Розмір цього
Зазвичай він має вказівник на підпис у стилі Objective-C, щоб знати, скільки місця потрібно для параметрів (прапорець BLOCK_HAS_SIGNATURE
)
Якщо змінні посилаються, цей блок також матиме вказівники на допоміжник копіювання (копіювання значення на початку) та допоміжник видалення (звільнення його).
Черга розподілу - це іменований об'єкт, який забезпечує чергування блоків для виконання.
Блоки встановлюються в черги для виконання, і вони підтримують 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
: Асинхронні запити введення/виведення
mach
: Порти Mach
mach_msg
: Повідомлення Mach
pthread_root_queue
: Черга з пулом потоків pthread та без робочих черг
queue
semaphore
source
: Джерело подій
У Objetive-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
обгортає API GCD на C в більш дружній для 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"))
Приклад коду:
Наступний скрипт Фріди може бути використаний для підключення до кількох функцій 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 автоматично перепише все: