
غوص تعليمي عميق في حزم تطبيقات macOS، وملفات plist، وسلوك عملية launchd، مع ملاحظات أمن هجومي حول تعبئة الحمولات كملفات .app وتجاوز مراقبة شجرة العمليات.
الانتقال إلى macOS من Linux أو Windows قد يبدو وكأنك تمشي في أرض جديدة غريبة. نظرًا لأن Linux مفتوح المصدر وWindows موثق جيدًا وشائع جدًا (وmacOS ليس كذلك تمامًا)، فقد يكون macOS صعبًا في بعض الأحيان. في هذه التدوينة، أنوي مناقشة بعض أول الأشياء التي قد تلاحظها على macOS - التطبيقات، تطبيقات في كل مكان!
إذا كنت قادمًا من خلفية Windows أو Linux، فقد يبدو مفهوم التطبيقات غريبًا. نعلم جميعًا أن الخيوط (threads) هي "وحدات تنفيذ" وأن العمليات هي حاويات للخيوط بمساحة عنوان خاصة بها -- ما الجديد إذن؟ حسنًا، نادرًا ما تُنشر العمليات في ملف واحد. على كل من Windows وLinux هناك أشياء كثيرة قد يحتاجها الكود ليعمل، بعضها:
.dll, .so). على سبيل المثال، مكتبة وقت تشغيل C (msvcr<version>.dll على Windows، وlibc-<version>.so) بالإضافة إلى تبعيات أخرى.PE، والتي تحتوي على أدلة - أحدها هو دليل الموارد (حتى أنه موثق إلى حد ما هنا) وقد يحتوي على موارد (صور، سلاسل نصية وغيرها). يمكن أيضًا تحميل الموارد من القرص ديناميكيًا، بالطبع.PE نفسه (اقرأ هنا) أو في ملفات الكتالوج (أي خارجيًا).xml، ini، json) وسجل Windows.حسنًا، يضع macOS تركيزًا كبيرًا على حزم التطبيقات. الفكرة هي تجميع (تقريبًا) كل ما هو مطلوب لتشغيل البرنامج في هيكل دليل - بما في ذلك الموارد ومعلومات الترجمة وما إلى ذلك. بالطبع، لا يمكن تجميع كل شيء بشكل أنيق (مثل مكتبة وقت تشغيل C، على سبيل المثال) - لكن هذا لا يزال يعني أن الأشياء مجمعة معًا بشكل مرتب - لا حاجة للتنقل في سجل ضخم أو قراءة صفحات الدليل لمواقع ملفات إعدادات غامضة. حزم التطبيقات هي مجرد أدلة تنتهي بـ .app - حتى لو كانت الواجهة تخفي امتداد .app (وحقيقة أنه دليل).
من منظور المهاجم، هذا مثير للاهتمام - نظرًا لأن حزمة التطبيق يمكن أن تحتوي على أيقونات عشوائية وتخفي امتداد .app - يمكن تحقيق تسليم البرمجيات الخبيثة عن طريق خداع مستخدم غير منتبه للنقر على مثل هذا التطبيق. على سبيل المثال، فكر في ملف Resume.app بأيقونة PDF.
يمكن فحص هيكل الدليل لـحزمة التطبيق بسهولة، بواسطة تطبيق Calculator المدمج بالطبع:
jbo@McJbo ~ % cd /System/Applications/Calculator.app
jbo@McJbo Calculator.app % ll
total 0
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 .
drwxr-xr-x 43 root wheel 1376 Mar 17 21:34 ..
drwxr-xr-x 9 root wheel 288 Mar 17 21:34 Contents
jbo@McJbo Calculator.app % cd Contents
jbo@McJbo Contents % ll
total 16
drwxr-xr-x 9 root wheel 288 Mar 17 21:34 .
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 ..
-rw-r--r-- 1 root wheel 2147 Mar 17 21:34 Info.plist
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 MacOS
-rw-r--r-- 204 root wheel 8 Mar 17 21:34 PkgInfo
drwxr-xr-x 4 root wheel 128 Mar 17 21:34 PlugIns
drwxr-xr-x 54 root wheel 1728 Mar 17 21:34 Resources
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 _CodeSignature
-rw-r--r-- 1 root wheel 461 Mar 17 21:34 version.plist
jbo@McJbo Contents % cd MacOS
jbo@McJbo MacOS % ll
total 344
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 .
drwxr-xr-x 9 root wheel 288 Mar 17 21:34 ..
-rwxr-xr-x 1 root wheel 540912 Mar 17 21:34 Calculator
jbo@McJbo MacOS %
كما ترى، Calculator.app هو دليل. تحته عنصر واحد - دليل آخر يسمى Contents.
تحت Contents هناك عناصر متعددة:
Info.plist - يحتوي على بيانات وصفية حول التطبيق. المزيد عن ذلك لاحقًا.MacOS - يحتوي على الملف التنفيذي الرئيسي للتطبيق (كما يظهر في قائمة الدليل الثالثة).PkgInfo - غير إلزامي. ملف ثنائي يحتوي على معلومات الحزمة.PlugIns - غير إلزامي. دليل قد يحتوي على إضافات للتطبيق. لدى Calculator اثنتان - واحدة لـ"الأساسية والعلمية" وواحدة لـ"السداسي عشر" (لا أعرف حقًا لماذا قاموا بهذا الفصل، ولا يهمني).Resources - غير إلزامي. كما يوحي الاسم، يحتوي على الموارد. قد تجد عدة عناصر هناك، بما في ذلك ملفات .icns تحتوي على أيقونات، بالإضافة إلى أدلة باللاحقة .lprroj تتعلق بالترجمة._CodeSignature - غير إلزامي. كما يوحي الاسم - يحتوي على معلومات توقيع الكود.version.plist - غير إلزامي، يحتوي على معلومات الإصدار.لاحظ أن هناك عناصر قليلة جدًا مطلوبة رسميًا. في الواقع، يمكننا إنشاء أول تطبيق خاص بنا دون حتى تجميع أي شيء!
لكن يجب أولًا أن نناقش ملف Info.plist هذا.
كلما نظرت أكثر إلى macOS، كلما وجدت المزيد من هذه الملفات الغريبة. تلك ليست سوى ملفات إعدادات مموهة.
ستحمل دائمًا الامتداد .plist، وهو مجرد طريقة مختصرة لاسمها الرسمي: ملفات قائمة الخصائص.
لسوء الحظ، هناك 3 صيغ مختلفة لـplist تديرها Apple:
xml، قابلة للقراءة من البشر.json، غير مستخدمة على نطاق واسع.bplist كعلامة سحرية.لحسن الحظ، هناك أداة تسمى plutil تدعم جميع الصيغ. لإخراج ملف plist، استخدم ببساطة plutil -p. على سبيل المثال:
jbo@McJbo Contents % plutil -p Info.plist | head -n 20
{
"BuildMachineOSBuild" => "22A380007"
"CFBundleDevelopmentRegion" => "English"
"CFBundleExecutable" => "Calculator"
"CFBundleGetInfoString" => "10.14, Copyright © 2000-2018, Apple Inc."
"CFBundleHelpBookFolder" => "Calculator.help"
"CFBundleHelpBookName" => "com.apple.Calculator.help"
"CFBundleIconFile" => "AppIcon"
"CFBundleIconName" => "AppIcon"
"CFBundleIdentifier" => "com.apple.calculator"
"CFBundleInfoDictionaryVersion" => "6.0"
"CFBundleName" => "Calculator"
"CFBundlePackageType" => "APPL"
"CFBundleShortVersionString" => "10.16"
"CFBundleSignature" => "????"
"CFBundleSupportedPlatforms" => [
0 => "MacOSX"
]
"CFBundleVersion" => "223"
"CTIgnoreUserFonts" => 1
jbo@McJbo Contents %
هناك أيضًا وظائف تحويل مدمجة في plutil - لن نعرضها الآن.
توثق Apple هنا عدة متطلبات في ملف Info.plist للتطبيق، لكن هناك حقول قليلة جدًا إلزامية فعلًا. إليك بعض الحقول المثيرة للاهتمام:
CFBundleExecutable - اسم الملف التنفيذي الرئيسي، المتوقع أن يكون تحت دليل MacOS.CFBundleIconFile - اسم ملف الأيقونة. غير إلزامي.CFBundleIdentifier - معرّف لحزمة التطبيق. توصي Apple باستخدام ترميز DNS عكسي (مثل com.apple.calculator).CFBundleName - اسم الحزمة.مع أخذ ذلك في الاعتبار، يمكننا إنشاء أول تطبيق رائع لنا، دون حتى كتابة كود! ألقِ نظرة:
#!/bin/zsh
# Create Bundle structure
mkdir -p ./MyApp.app/Contents/MacOS
# Create main executable file - a shell script in our case
cat <<EOF > ./MyApp.app/Contents/MacOS/MyApp
#!/bin/zsh
osascript -e 'tell app "Finder" to display dialog "Hello from MyApp!"'
EOF
chmod +x ./MyApp.app/Contents/MacOS/MyApp
# Create the Info.plist file
cat <<EOF > ./MyApp.app/Contents/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>MyApp</string>
<key>CFBundleIdentifier</key>
<string>com.myapp</string>
<key>CFBundleName</key>
<string>MyApp</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
</dict>
</plist>
EOF