לדלג לתוכן

שיעור 05: אפליקציות ותהליכים

**חלק א': מאגר ידע למדריך **

1. סוגי התקנות: חבילות (PKG), תמונות דיסק (DMG) ו-App Store

תיאוריה והיסטוריה ברמה גבוהה

במשך שנים רבות, התקנת תוכנות במערכות הפעלה מסורתיות כמו Windows דרשה תהליכים מורכבים: פיזור קבצים ברחבי המערכת, רישום מורכב של קבצי DLL ושינויים ב-Registry המרכזי. אפל שינתה לחלוטין את התפיסה הזו עם התפתחותה של macOS, תוך הצגת מודל "Drag and Drop" פשוט המבוסס על App Bundles. למשתמש הקצה, ה-Bundle נראה כאייקון הרצה בודד שניתן ללחוץ עליו. מתחת למכסה המנוע, מדובר במבנה תיקיות קפדני המכיל את כלל הקוד המקומפל, ה-frameworks, והמשאבים הוויזואליים הנדרשים לפעולתה.

היסטורית, כלי ההפצה (Deployment) העיקריים ל-Bundles אלו היו תמונות דיסק (DMG) וחבילות התקנה (PKG). תמונות דיסק סיפקו למפתחים דרך לארוז אפליקציה כמעין "כונן USB וירטואלי", בעוד ש-PKG שימר מודל התקנה קלאסי שאפשר הרצת סקריפטים ומיקום קבצים ברמת המערכת. עם הגעת ה-Mac App Store, אפל יצרה ערוץ הפצה מרכזי הדורש חתימות קריפטוגרפיות קפדניות, מספק עדכונים אוטומטיים ואוכף אבטחה מחמירה באמצעות Sandboxing.

[!TIP] הקשר היסטורי: בעידן ה-Classic Mac OS, האפליקציות היו קבצים בודדים שהכילו "Resource Fork" עבור המשאבים שלהן. המעבר למבנה התיקיות של App Bundles שאב השראה כבדה ממערכת ההפעלה NeXTSTEP (שנרכשה ע"י אפל ב-1996). בנוסף, תמונות הדיסק (DMG) התפתחו במקור כייצוג דיגיטלי של תקליטונים (Floppy Disks) לצורך ייצור ושכפול המוני במפעלים, הרבה לפני שהפכו לסטנדרט בהפצת תוכנה דרך האינטרנט.

חתימות קוד, Gatekeeper ונוטריזציה (עדכוני Tahoe 26)

חתימת קוד (Code Signing) היא התשתית הקריפטוגרפית לאבטחת יישומים ב-macOS. המנגנון הוצג ב-Mac OS X 10.5 Leopard (2007) ע"י מהנדס אפל Peter Kiehtreiber ("Perry the Cynic"), במקור כדי להקביל לדרישות החתימה הנוקשות של ה-iPhone הראשון, אך מאז עבר התפתחויות רבות.

בבסיס הארכיטקטורה עומד ה-CDHash (Code Directory Hash). ה-CDHash משמש כחותם המגן על דפי הקוד שבזיכרון, המשאבים הפנימיים, ה-entitlements, וקובץ ה-Info.plist. במערכות macOS מודרניות, אלגוריתם ה-SHA-1 הפגיע הוחלף כליל ב-SHA-256 (למרות שאפליקציות ישנות עשויות להכיל Hashing כפול לתאימות). macOS משתמשת בבדיקות זמן ריצה (Lazy Checking): במקום לבדוק את כל הקובץ בעת ההפעלה, היא מוודאת כל דף זיכרון (Memory Page) בעת טעינתו ל-RAM. אם אפילו תו אחד ישתנה בקובץ ה-Info.plist, ערך ה-CDHash ייפסל והמערכת תחסום את האפליקציה עם השגיאה: "App is damaged and can't be opened".

קיימים סוגים שונים של חתימות:

  • Ad-hoc Signature: חתימה עצמית ללא שרשרת אמון (Chain of Trust) לשרשראות של אפל. היא מספקת זהות חלשה אך מבטיחה את שלמות הקובץ המקומי.
  • Developer ID Application: תעודה המונפקת למפתחים רשומים להפצת תוכנה מחוץ ל-Mac App Store.

[!TIP] הקשר היסטורי: עד macOS 10.14 Catalina, ה-Gatekeeper שמר מסד נתונים מקומי (/private/var/db/gkopaque.bundle) למעקב אחר אפליקציות מאושרות. macOS המודרנית זנחה את מסד הנתונים הזה לטובת בדיקות מקוונות בזמן אמת דרך OCSP (Online Certificate Status Protocol) אל מול שרתי אפל, כדי לוודא בו ברגע אם תעודת המפתח בוטלה.

ב-macOS Mojave (2018), אפל הציגה את הנוטריזציה (Notarization). בעוד שאפליקציות App Store נבדקות ידנית, רצות ב-Sandbox, ונחתמות מחדש ע"י אפל, נוטריזציה היא תהליך סריקת נוזקות אוטומטי עבור תוכנות עצמאיות. אפליקציה מנוטרזת נסרקת ע"י אפל, ה-CDHash שלה נשמר בענן של אפל, ו"כרטיס" (Ticket) מהודק אליה. זה מאפשר לאפל לבטל גלובלית תוכנה זדונית.

[!IMPORTANT] שינוי קריטי ב-macOS Sequoia ו-Tahoe: בגרסאות קודמות, משתמשים יכלו לעקוף את Gatekeeper לאפליקציות שאינן מנוטרזות פשוט ע"י קליק ימני (Control-Click) ב-Finder ובחירה ב-"Open". ב-macOS Sequoia ו-Tahoe, אפשרות עקיפה זו הוסרה לחלוטין מ-Finder. כעת, על המשתמש לנווט יזומית ל-System Settings > Privacy & Security, לגלול למטה ל-Security, וללחוץ מפורשות על "Open Anyway". עקיפת הגדרות אלו היא הדרך הנפוצה ביותר שבה מחשבי מק נדבקים בנוזקות, כך שהחיכוך הזה הוא שיפור אבטחה מכוון.

בנוסף, בארכיטקטורת Apple Silicon, כל קוד ARM64 טבעי חייב להיות חתום (לפחות ב-Ad-hoc) כדי לרוץ. אפילו אפליקציות רשת ללא קוד (Web Apps) הנשמרות מ-Safari מקבלות UUID מ-LaunchServices וחתימת Ad-hoc מקומית כדי לעקוב אחר שלמותן.

תרגום Apple Silicon דרך Rosetta 2 & OAH

כדי להקל על המעבר מ-Intel ל-Apple Silicon, אפל פיתחה את Rosetta 2. Rosetta 2 אינה מכונה וירטואלית (VM) או אמולטור מסורתי. זהו מנוע תרגום מסוג Ahead-Of-Time (AOT). בעת הפעלת אפליקציית Intel x86_64, רוזטה 2 מתרגמת מראש את הוראות ה-Intel להוראות ARM64 טבעיות ושומרת אותן בזיכרון מטמון, כך שהפעלות עתידיות מתבצעות במהירות קרובה לנייטיב.

השם הפנימי של רוזטה ביומני המערכת הוא OAH. רכיביה מאוחסנים בכונן ה-Data (ולא בכונן ה-System האטום), מאחר שהיא מותקנת לפי דרישה:

  • נתיב ראשי: /Library/Apple/
  • קובץ הרצה: /usr/libexec/oah
  • תהליך רקע: com.apple.oahd.plist

הפרדת תהליכים (Process Isolation) נאכפת בקפידה: חל איסור מוחלט לערבב קוד Intel ו-ARM באותו תהליך. אם אפליקציית ARM64 נייטיב תנסה לטעון פלאגין ישן של Intel, היא תקרוס. המשתמש חייב לאלץ את כל האפליקציה לרוץ במצב תרגום ע"י סימון "Open using Rosetta" בחלון Get Info.

[!TIP] התנהגות וביצועים: תרגום AOT הוא מהיר להפליא, ומתרגם בינארי של 242KB בפחות מ-0.0125 שניות. מעניין לציין שקריאות ממשק משתמש (UI Frameworks) בתוך רוזטה מועברות ישירות לריצה טבעית על שכבת ה-Apple Silicon, מה שהופך אפליקציות ישנות למהירות במיוחד. יש לציין כי רוזטה 2 אינה יכולה להריץ Kernel Extensions (kexts) של אינטל, סביבות וירטואליזציה x86_64, פקודות AVX, או קוד 32-bit.

ארכיטקטורה טכנית עמוקה (מתקינים)

הארכיטקטורה של DMG נשענת על יצירת התקן בלוק וירטואלי, לרוב מפורמט כ-APFS או HFS+. בעת פתיחת DMG, תהליך רקע בשם diskimages-helper מעגן (Mount) את ה-Volume לנתיב /Volumes/. התקנת האפליקציה היא פשוט פעולת העתקה סטנדרטית (I/O) של ה-Bundle ל-/Applications.

לעומת זאת, קבצי PKG הם ארכיוני XAR המכילים "Bill of Materials" (קובץ .bom המפרט את הקבצים, הרשאות POSIX ונתיבי היעד), את ה-Payload הפיזי, ותיקיית סקריפטים. ההתקנה מנוהלת ע"י Installer Framework בקרנל, שמקבל הרשאות גבוהות כדי לפזר קבצים מחוץ לסביבת המשתמש (למשל פריסת LaunchDaemon ב-/Library/LaunchDaemons/).

ה-Mac App Store עוקף את שתי השיטות הללו בעזרת תהליכי מערכת כמו appstored ו-softwareupdated. תהליכים אלו מורידים חבילות מוצפנות, מאמתים חתימות Gatekeeper ומתקינים היישר ל-/Applications בשקיפות מלאה, תוך שימוש במנוע ה-DRM של FairPlay לניהול רישיונות.

LaunchServices וטיפול במסמכים

LaunchServices היא תשתית הליבה ב-macOS שאחראית על הפעלת אפליקציות, ניהול שיוכי קבצים ובחירת תוכנות ברירת מחדל לפתיחת מסמכים. כאשר משתמש לוחץ לחיצה כפולה על קובץ, macOS עוקבת אחר שרשרת החלטה ברורה:

  1. הגדרה מותאמת: המערכת בודקת האם לקובץ יש תכונת הרחבה (xattr) בשם com.apple.LaunchServices.OpenWith. אם כן, היא עוקפת הכל ופותחת את הקובץ באפליקציה שצוינה בה.
  2. זיהוי UTI: המערכת מזהה את ה-Uniform Type Identifier של הקובץ (למשל com.adobe.pdf).
  3. שאילתה במסד נתונים: המערכת מחפשת במסד הנתונים של LaunchServices איזו אפליקציה רשומה עבור ה-UTI הנ"ל.
  4. הפעלה: אירוע AppleEvent נשלח לפתיחת האפליקציה והקובץ.

ה-Finder סורק בקביעות אפליקציות חדשות ומעדכן את מסד הנתונים של LaunchServices בהתאם.

פקודות טרמינל, לוגים ופליסטים

  • התקנת PKG כ-root (הכי נפוץ במערכי IT): sudo installer -pkg /path/to/installer.pkg -target /
  • פריסת תוכן ה-PKG מבלי להתקין (לאנליזה): pkgutil --expand /path/to/installer.pkg ~/Desktop/ExpandedPkg
  • בדיקת חתימה של PKG: pkgutil --check-signature /path/to/installer.pkg
  • ניהול LaunchServices (הכלי lsappinfo): הצגת סטטוס ריצה בזמן אמת של אפליקציות: lsappinfo -all list
  • איפוס מסד נתונים LaunchServices (הכלי lsregister): /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -kill -r -v -apps u,s,l

  • לוגים (Installer ו-OAH): log show --predicate 'subsystem == "com.apple.installer"' --info עבור רוזטה 2 חפשו: Aot lookup request או Translating image

מקרי קצה ואבחון

מקרה נפוץ בארגונים הוא כשל בהתקנת PKG בשל תעודת מפתח פגת תוקף (Expired Certificate), שיגרום ל-Gatekeeper לחסום את ההתקנה. יש להשתמש ב-pkgutil כדי לוודא את תוקף התעודה.

מקרה קצה מרכזי נוסף הוא App Translocation (Gatekeeper Path Randomization). אם משתמש מנסה להפעיל אפליקציה ישירות מה-DMG או מתיקיית ה-Downloads מבלי להעביר אותה, macOS מתערבת. כדי למנוע מהאפליקציה לטעון ספריות (Dylibs) זדוניות הנמצאות בסמוך לה, macOS מעתיקה אותה בשקיפות לנתיב אקראי ב-/private/var/folders/ לפני ההרצה. הדבר גורם לרוב לקריסת תוכנות התלויות בנתיבים יחסיים. הפתרון הוא תמיד להורות למשתמש לגרור את האפליקציה לתיקיית Applications.

אם ה-CDHash שונה או נפגע, המערכת תשמיד את התהליך מיד בהפעלה. קריאת דו"חות קריסה תחשוף שגיאת EXC_BAD_ACCESS (SIGKILL) עם סיבת הסיום: CODESIGNING, Code 2 Invalid Page.

2. ארגזי חול (Sandboxing): איפה אפליקציות שומרות את המידע שלהן ואיך לאפס אותן

תיאוריה והיסטוריה ברמה גבוהה

מנגנון ה-App Sandbox הבשיל ב-OS X Lion כמהפכה באבטחת נתונים. בעבר, כל אפליקציה ירשה את הרשאות המשתמש המלאות, כלומר אפליקציה פרוצה יכלה לקרוא או להצפין בשקט את כל ספריית הבית של המשתמש. ה-Sandbox מצמצם סיכון זה ע"י הגבלת הגישה לסביבה וירטואלית מבוקרת.

[!TIP] אנלוגיה היסטורית: ניתן לדמות את ה-Container של האפליקציה לבובת מטריושקה. כל אפליקציה מקבלת תיקיית 'בית' קטנה הכוללת תיקיות מפוברקות של Desktop, Documents ו-Downloads. האפליקציה פועלת תחת האשליה שיש לה שליטה מלאה על המערכת, כשבפועל היא כלואה בתא מבודד.

ארכיטקטורה טכנית עמוקה

ה-Sandbox נאכף ברמה הנמוכה ביותר ע"י מודול קרנל בשם sandbox_init, שקורא פרופיל שהוגדר בעת קימפול האפליקציה. מערכת ההפעלה יוצרת דינמית "תיקיית בית וירטואלית" (Container) בנתיב: ~/Library/Containers/com.developer.appname/Data.

כדי לאפשר לאפליקציה לגשת לעולם החיצון, מפתחים משתמשים ב-Entitlements (הרשאות) - חריגים חתומים קריפטוגרפית המוטמעים בחתימת הקוד. הרשאת הבסיס היא com.apple.security.app-sandbox. גישה למסמכים מתבצעת לרוב דרך הרשאת com.apple.security.files.user-selected.read-write, המשתמשת ב-Powerbox של macOS. ה-Powerbox הוא ממשק בחירת קבצים מאובטח; כאשר המשתמש בוחר קובץ, ה-Powerbox מעביר אסימון מאובטח חזרה לאפליקציה ומאשר לה גישה חד-פעמית אליו.

אפליקציות מה-Mac App Store חייבות לפעול בתוך ה-Sandbox, אך אפליקציות עצמאיות (מנוטרזות) אינן מחויבות לכך. חברות רבות מוסיפות את ה-Sandbox כרבדה נוספת של הגנה יחד עם מנגנון הפרטיות (TCC).

פקודות טרמינל, לוגים ופליסטים

  • איפוס לאפליקציית Sandbox (הגדרות יצרן): rm -rf ~/Library/Containers/com.apple.Safari

  • איפוס הרשאות TCC לאפליקציה ספציפית: tccutil reset All com.developer.appname

  • מעקב אחרי חסימות Sandbox: log show --predicate 'subsystem == "com.apple.sandbox.reporter"' --info

  • בדיקת סטטוס ב-Activity Monitor: ניתן להוסיף את עמודת Sandbox. כדי לנתח Entitlements ניתן להשתמש בכלים צד-שלישי כמו Apparency או Taccy.

מקרי קצה ואבחון

כאב ראש נפוץ לצוותי IT מתרחש כשמשתמשים מנסים לאפס אפליקציה ע"י מחיקת פליסטים מהנתיב הישן ב-~/Library/Preferences/. הגדרות האפליקציות המודרניות כלואות בתוך ה-Container תחת ~/Library/Containers/com.developer.appname/Data/Library/Preferences/.

מקרה אף קשה יותר הוא עניין "הפליסטים הזומביים". תהליך בשם cfprefsd שומר קבצי פליסט ב-RAM לטובת ביצועים מהירים. אם טכנאי מוחק קובץ פליסט פיזי בעוד האפליקציה או התהליך פועלים, ה-daemon ישחזר אותו באופן מיידי מהזיכרון. סדר הפעולות הנכון תמיד יהיה: לצאת מהאפליקציה, למחוק את ה-Container, ולהריץ killall cfprefsd בטרמינל כדי לאלץ שטיפה של המטמון.

3. אבחון תקיעות: Force Quit וטיפול באפליקציות קורסות (Not Responding)

תיאוריה והיסטוריה ברמה גבוהה

במערכות הפעלה ישנות של אפל (לפני OS X), נעשה שימוש ב-"Cooperative Multitasking", כלומר האפליקציה הייתה צריכה לשחרר את העיבוד למערכת ההפעלה מרצונה החופשי. אפליקציה אחת תקועה הקריסה את כל המערכת. עם המעבר לארכיטקטורת קרנל Darwin, הוכנס מנגנון ה-Preemptive Multitasking. כעת הליבה מתפקדת כדיקטטור ויכולה להשהות או להרוס כל תהליך סורר מבלי להשפיע על המערכת כולה. כשאפליקציה שותקת, המערכת מסמנת אותה כ-"Not Responding", מציגה את כדור הים (Beachball), ומאפשרת פעולת Force Quit מיידית.

ארכיטקטורה טכנית עמוקה

"כדור הים המסתובב" מנוהל ע"י ה-WindowServer, תהליך הליבה הגרפי. לכל אפליקציה גרפית יש "Main Thread" (חוט הרצה ראשי) שמטפל ברינדור המסך ובאירועי משתמש. כשה-WindowServer שולח אירוע (למשל לחיצה) אל האפליקציה, היא חייבת לעבד אותו בתוך מילישניות בודדות. אם ה-Thread נתקע בלולאה אינסופית או במחסום I/O, הוא מפסיק להגיב. לאחר כשתיים עד ארבע שניות של התעלמות, ה-WindowServer מצייר אוטומטית את הסמן המסתובב מעל חלון האפליקציה.

פעולת Force Quit אינה יציאה רגילה. יציאה רגילה שולחת SIGTERM (Signal 15) המאפשר שחרור זיכרון נקי, בעוד ש-Force Quit יורה פקודת SIGKILL (Signal 9) קטלנית דרך הליבה. התהליך נרצח בו במקום, ללא התראה, וכל מידע שלא נשמר - אובד.

פקודות טרמינל, לוגים ופליסטים

  • יציאה מאולצת מהטרמינל: killall -9 AppName
  • שליחת סיגנל עדינה (שמירת נתונים): kill -15 <PID>
  • איתור PID מסוים: ps aux | grep AppName
  • דו"חות ספין (Spin Reports): כאשר אפליקציה קופאת זמן ממושך, המערכת מייצרת Spin Report המציג את מצב ה-Threads ברגע הקריסה: /Library/Logs/DiagnosticReports/AppName_*.spin.

מקרי קצה ואבחון

המצב המתסכל ביותר הוא תהליך זומבי. מצב זה מתרחש כשה-Thread הראשי חצה את הגבול מ-User Space ל-Kernel Space וביקש לבצע קריאה ברמה הפיזית (כגון תקשורת מול דרייבר כונן חיצוני תקול). במידה והליבה נתקעת, היא לא תאשר קבלת SIGKILL מהסיבה הפשוטה שהרס ה-Thread עלול לגרום ל-Kernel Panic ולהקריס את כל המערכת. במצבים אלו האפליקציה תסרב להיסגר למרות Force Quit חוזר ונשנה, והפתרון הבלעדי יהיה ניתוק הכונן הפוגעני, או ביצוע Restart קשיח ע"י לחיצה על כפתור ההפעלה.

כמו כן, אם אפליקציה ישנה של אינטל קורסת מיד בהפעלתה תחת רוזטה 2, יש לוודא שהיא אינה טוענת מודולים של ARM64 (איסור ערבוב ארכיטקטורות המנוהל תחת מפתח ה-LSArchitecture ב-LaunchServices).

4. תיבול ארגוני: הפצת אפליקציות דרך VPP וקטלוג Self-Service

תיאוריה והיסטוריה ברמה גבוהה

בעבר הרחוק, הפצת תוכנות המונית חייבה שימוש בצילומי כונן מונוליתיים (Imaging) או אנשי IT שהקלידו סיסמאות Admin מעמדה לעמדה. אפל פתרה זאת בעזרת תכנית ה-VPP (Volume Purchase Program), שכיום משולבת בתוך Apple Business Manager (ABM).

ABM מאפשר לארגונים לרכוש רישיונות תוכנה ולחלק אותם דרך שרת ניהול מכשירים (MDM) באמצעות רישוי מבוסס מכשיר. היתרון העצום: אין צורך בשום Apple ID מצידו של המשתמש, והרישיונות נשארים תמיד בבעלות הארגון. בעזרת פורטל ה-Self Service, החזון של Least Privilege (הרשאה מינימלית) מתממש - משתמשים בעלי הרשאות רגילות יכולים להתקין תוכנות ארגוניות מורכבות ללא צורך בסיסמת מנהל.

ארכיטקטורה טכנית עמוקה

הקסם של Self Service נובע מהפרדת הסמכויות. קטלוג ה-Self Service שאותו רואה המשתמש פועל בהרשאות User רגילות, אולם הסוכן שמבצע את ההתקנה (MDM Agent) הוא LaunchDaemon שפועל כ-root (מערכת).

כשהמשתמש לוחץ "התקן", אפליקציית הקטלוג אינה מעתיקה את הקבצים. היא מעבירה מסר ב-IPC ל-MDM Agent. במידה ומדובר באפליקציית VPP, סוכן ה-MDM פונה לשרת הענן, שדוחף פקודת InstallApplication שקטה למק. המערכת המקומית מאמתת מול שרתי ה-CDN של אפל (עם אסימון ה-VPP) ומזריקה את ה-Payload ישירות אל /Applications בשקיפות מלאה מחוץ לטווח הראייה של משתמש הקצה.

פקודות טרמינל, לוגים ופליסטים

  • אילוץ בקשת מדיניות MDM: sudo mdmclient checkin
  • רענון סטטוס לשרת ב-Jamf: sudo jamf recon
  • מעקב אחרי התקנת VPP בלוג המאוחד: log show --predicate 'subsystem == "com.apple.mdmclient"' --info

  • הצגת פרופילים קיימים: sudo profiles show -type enrollment

מקרי קצה ואבחון

מקרה הקצה המרגיז ביותר עבור אנשי התמיכה מתרחש כאשר אפליקציה נשארת תקועה בסטטוס "Pending" בקונסולת ה-MDM, או שהמערכת טוענת שהיא מותקנת אבל היא איננה בנמצא. מעל ל-90% מהמקרים אלו מתרחשים בשל אסימון VPP שפג תוקפו ב-Apple Business Manager, או משום שמנהל המערכת שינה סיסמה לפורטל וגרם להתנתקות האסימון. הפתרון דורש מאדמין להנפיק אסימון VPP חדש ולהזין אותו בחזרה למערכת ה-MDM.

בעיה סמויה נוספת עלולה לקרות כשפיירוול ארגוני (Firewall) חוסם גישה לשרתי התוכן (CDN) של אפל. סוכן ה-MDM מצליח להעביר את הפקודה, אך התהליך שפועל ברקע (appstored) נחסם וקורס בשל Network Timeout. הטריק הידוע ביותר לאנשי תמיכה במצב כזה הוא לנתק את המק מה-Wi-Fi הארגוני, לחברו ל-Hotspot (נקודת גישה אישית מסלולרי), ולצפות אם האפליקציה מחליקה פנימה באופן מיידי - מה שמוכיח את חפותה של מערכת הניהול ומפנה את האצבע לעבר מחלקת אבטחת הרשת.


Generated by Antigravity Autonomous Workflow - Course Version 3

💡 עזרים ויזואליים להרצאה (Presentation Visuals)

תמונות אלו ניתנות להקרנה בכיתה בעת הסבר על הנושא, או לשילוב במצגות.

המחשה ויזואלית (עזר לתלמיד)

תמונות אלו ממחישות את הממשק או המנגנון הרלוונטי לנושא השיעור.

Slide103_image33 Slide121_image134 Slide66_image11 26-Tahoe-App-Store-scaled 26-Tahoe-Force-Quit-scaled