الاستنتاجات وشروط القرار

  • سجّل التبعيات المباشرة وغير المباشرة، ونقاط دخول التحميل، وتوقيت التهيئة، والحد الأدنى لواجهات برمجة التطبيقات المستهدفة، والجهات المستدعية لكل ملف SO.
  • يختلف اكتشاف أسماء JNI الثابتة عن التسجيل الديناميكي عبر RegisterNatives في القيود؛ لذا تحقق من طريقة الربط الفعلية قبل معالجة الأسماء.
  • تمثل معماريات arm64-v8a وarmeabi-v7a وx86_64 عناصر ومصائف توافق مميزة؛ فنجاح الحماية على معمارية واحدة لا يعني بالضرورة نجاحها على المعماريات الأخرى.
  • يجب ربط قبول قوة الحماية واستقرار وقت التشغيل بنفس حزمة APK النهائية، والتوقيع، ومجموعة ملفات SO.

ارسم أولاً الرسم البياني لتبعيات ELF والجدول الزمني الفعلي للتحميل

قد يتكون تطبيق أندرويد من مكتبات رئيسية، ومكتبات منطق الأعمال، ومكتبات الخوارزميات، ومجموعات تطوير برمجيات (SDKs) تابعة لجهات خارجية، ومكتبات النظام. يمكن لكود Java أو Kotlin استدعاء System.loadLibrary مباشرة، بينما قد تقوم المكتبات الرئيسية بتحميل مكتبات أخرى عبر تبعيات ELF أو التحميل أثناء وقت التشغيل. قد تقع نقطة التعطل النهائية خارج المكتبة المحمية، حيث يتم تحديد حل الرموز وترتيب التهيئة بواسطة سلسلة التبعيات بأكملها.

يجب أن يسجل الرسم البياني للتبعيات ملفات المكتبات لكل معمارية ABI، وعلاقات DT_NEEDED، والحد الأدنى لمستويات واجهة برمجة تطبيقات النظام، ونقاط دخول التحميل، ودوال التهيئة، والواجهات المُصدَّرة، والجهات المستدعية. بالإضافة إلى ذلك، حدد ما إذا كانت المكتبات مطورة داخلياً، أو تبعيات مفتوحة المصدر، أو مجموعات SDK مغلقة المصدر، لأن هذا يحدد حدود التعديل ومسؤولية اختبار التراجع.

تنص وثائق Android NDK على أن الرموز الأصلية تُحل عادةً أثناء تحميل المكتبة. إذا أشار الكود إلى واجهة برمجة تطبيقات غير موجودة على النظام المستهدف، فقد يفشل dlopen فوراً، حتى لو اقترحت الفروع المنطقية في وقت التشغيل عدم تنفيذ مسار الكود هذا. لذلك، يجب تضمين الحد الأدنى لواجهات برمجة تطبيقات NDK وإصدار نظام الجهاز في رسم التبعيات البياني.

الحقول الدنيا لرسم بياني لتبعيات المكتبات الأصلية
الحقلما يجب تسجيلهالتأثير على عملية التعزيزطريقة التحقق
المكتبة والمصدرالمكتبة الرئيسية، والتبعيات غير المباشرة، ومكتبات الجهات الخارجية، ومكتبات النظاميحدد نطاق التعديلات المسموح بها ومسؤولية اختبار التراجعفك الحزمة والتحقق مقابل كل معمارية ABI في ملف APK النهائي
نقطة دخول التحميلالتبعيات الثابتة، أو System.loadLibrary، أو dlopen أثناء وقت التشغيليحدد مرحلة الفشل الأولى وموقع السجل المناسبتسجيل الجدول الزمني لبدء التشغيل ونتائج التحميل
الحد الأدنى لواجهة برمجة التطبيقات (API Floor)متغير APP_PLATFORM أثناء البناء ورموز النظام المستخدمةقد تحدث رموز مفقودة عند وقت التحميل إذا تجاوزت واجهة جهاز المستخدمبدء التشغيل على أجهزة حقيقية تعمل بأنظمة مستهدفة والتحقق من الرموز
ترتيب التهيئةدالة JNI_OnLoad، والدوال البانية (constructors)، وتهيئة منطق الأعمالقد تؤدي التغييرات في تخطيط الكود أو التوقيت إلى تضخيم التبعيات الضمنيةمقارنة الجداول الزمنية بين الإصدارات غير المحمية ومرشحات الإصدار (Release Candidates)
الاستثناءات ومؤشرات الترابط (Threads)استثناءات ++C، وملكية مؤشرات الترابط، واستخدام JNIEnvغالباً ما تسبب الأخطاء عبر المكتبات وعبر مؤشرات الترابط تعطلاً فورياًاستخدام CheckJNI، ومكدسات الرموز المشفرة (Symbolicated stacks)، واختبار تراجع وظائف الأعمال

التسجيل الثابت مقابل الديناميكي يحدد حدود معالجة الرموز

يمكن لـ JNI اكتشاف الدوال الأصلية عبر اتفاقيات التسمية، أو إنشاء خرائط بين دوال Java وعناوين الدوال باستخدام RegisterNatives أثناء التهيئة. يعتمد التسجيل الثابت على أسماء مُصدَّرة قابلة للتوقع؛ بينما يتطلب التسجيل الديناميكي عادةً كشف نقاط دخول قليلة للتهيئة فقط، لكنه يعتمد على البحث عن الفئات، وتواقيع الدوال، وتوقيت التسجيل، ومؤشر الترابط المسؤول عن التحميل.

إذا غيّر التصلب أو معالجة الرموز أسماء التسجيل الثابتة، فقد يفشل النظام في العثور على التنفيذ الأصلي. وبالمثل، فإن التغييرات في توقيعات الدوال، أو قواعد الاحتفاظ بأسماء الفئات، أو ترتيب التهيئة في جدول التسجيل الديناميكي قد تسبب أعطالاً قبل أول استدعاء للدالة. إن مجرد التحقق من عدد الرموز المُصدَّرة لا يثبت صحة روابط JNI.

تحذر إرشادات Android الرسمية لـ JNI أيضاً من أن JNIEnv لها قيود متعلقة بالخيوط؛ حيث يمكن للاستثناءات المعلقة، أو المراجع غير الصالحة، أو الاستخدام عبر خيوط متعددة أن تؤدي إلى تعطل التطبيق. يجب أن تغطي اختبارات التراجع ما بعد التصلب خيوطاً حقيقية ومسارات استثناء فعلية، وليس فقط استدعاء طريقة تجريبية بدون معاملات.

نقاط فحص رئيسية لطريقتي تسجيل JNI
طريقة التسجيلالتبعياتنقاط الحساسية للتصلبالحد الأدنى للتحقق
اكتشاف الأسماء الثابتةاسم فئة Java، اسم الدالة، التوقيع، والرموز المُصدَّرةإعادة التسمية، إخفاء الرموز، وعدم تطابق التوقيعاتاستدعاء كل نقطة دخول حرجة والتحقق من أخطاء الربط
RegisterNativesبحث الفئة، توقيعات الدوال، جدول التسجيل، وتوقيت التهيئةمعالجة أسماء الفئات، ترتيب التسجيل، وتغيير عناوين الدوالتأكيد اكتمال التسجيل وتنفيذ سيناريوهات إدخال/إخراج حقيقية
أغلفة الطرف الثالثقواعد SDK الداخلية وتنفيذات مغلقة المصدرالتحقق الذاتي، الصادرات الضمنية، واختلافات الإصداراتالتحقق مقابل مصفوفات دعم البائع وسيناريوهات العالم الحقيقي
  • سرد تعيينات Java-to-Native الرئيسية
  • تأكيد طريقة التسجيل (ثابتة أم ديناميكية)
  • الاحتفاظ بقواعد أسماء الفئات والتوقيعات الضرورية
  • التحقق من الاستثناءات، والخيوط، ودورات حياة المراجع

يجب قبول واجهة ثنائية للتطبيق (ABI) كعناصر إصدار مستقلة

إن ABI أكثر من مجرد تسمية دليل؛ إذ تحدد وثائق Android NDK الرسمية أن ABI تعرف مجموعة التعليمات، وترتيب البايتات، واتفاقيات الاستدعاء، واستخدام المكدس والمسجلات، وتنسيق الملفات التنفيذية، وتشويه أسماء C++. تختلف arm64-v8a عن armeabi-v7a في كود الآلة والتبعيات؛ لذا فإن النجاح على محاكي x86_64 لا يمثل نجاحاً على أجهزة ARM الحقيقية.

بالنسبة لكل ABI، تأكد من وجود جميع التبعيات المباشرة وغير المباشرة، وأن الحد الأدنى لواجهة برمجة التطبيقات (API) للمكتبة متوافق مع النظام المستهدف، وأن أدوات التعبئة لم تحذف ملفات خاصة بهندسة معينة عن طريق الخطأ. إذا احتوت المكتبة الرئيسية فقط على arm64-v8a بينما تفتقر تبعية طرف ثالث إلى العنصر المقابل، فسيفشل التطبيق أثناء التحميل.

إذا خطط المنتج لدعم مجموعة فرعية فقط من ABIs، فاذكر ذلك صراحةً في ملاحظات الإصدار، وإعدادات متجر التطبيقات، ونطاقات الاختبار. يجب وضع علامة "غير مغطى" على الهندسات التي لم يتم بناؤها، أو تثبيتها، أو حيث لم تُنفَّذ مسارات JNI الحرجة؛ فلا يمكن لنجاح الترجمة أن يحل محل الأدلة وقت التشغيل.

مثال لمصفوفة قبول ABI
عنصر الفحصarm64-v8aarmeabi-v7ax86_64قاعدة القرار
وجود جميع التبعياتالتحقق لكل مكتبةالتحقق لكل نطاق إصدارالتحقق فقط إذا لزم الأمرالحظر في حال فقدان أي تبعية مطلوبة
قابل للتحميل على الحد الأدنى للنظامجهاز حقيقي يعمل بـ API المستهدفجهاز حقيقي يعمل بـ API المستهدفمحاكي أو جهازلا تعتمد التحقق حصرياً على أحدث نظام
مسارات JNI الحرجةإدخال/إخراج حقيقيإدخال/إخراج حقيقيلا يمكن استبدال نتائج ARM بهاالتسجيل بشكل مستقل لكل ABI مُصدَر
إمكانية عزو سبب التعطلالاحتفاظ بالرموز المطابقةالاحتفاظ بالرموز المطابقةالاحتفاظ بالرموز المطابقةيجب أن تتوافق الرموز مع نفس مرشح الإصدار نفسه

ما هي ظروف وقت التشغيل الأصلية التي تؤثر عليها الحماية؟

يقلل إخفاء الرموز من القرائن الثابتة، ويخفض معالجة السلاسل النصية من التعرض للنص العادي، ويغير افتراضية تدفق التحكم أو الدوال شكل تنفيذ الكود المحدد. تؤثر هذه التقنيات بطرق مختلفة على الرموز المُصدَّرة، وحدود الدوال، وفك طي الاستثناءات، والاستدعاءات غير المباشرة، والمحاذاة، والأداء. لا يمثل انخفاض عدد الرموز بعد المعالجة حماية أصلية كاملة، كما أن القدرة على قراءة المكتبة لا تنفي جميع آثار الحماية.

تُعدّ تهيئة بدء التشغيل، ومعالجة الإشارات، واستثناءات ++C، ودوال الاستدعاء الخلفي (Callbacks)، وحالة الخيط المحلي (Thread-local state)، والبحث الانعكاسي عن الرموز، والتحقق الذاتي من مجموعات تطوير البرمجيات (SDK) التابعة لجهات خارجية، مجالات عالية الحساسية. عند تحديد النطاق، أعطِ الأولوية للوظائف التجارية ذات المدخلات والمخرجات الواضحة، وتكرار الاستدعاء القابل للقياس، والأعطال القابلة للعزل. تجنّب معالجة سلسلة التهيئة بأكملها في إثبات المفهوم الأولي (PoC).

يجب أن تسجّل تكوينات الحماية المبرّر، ومرحلة الاستدعاء، وواجهة الثنائية التطبيقية (ABI)، والتبعيات، وميزانية الأداء، ومجموعة التراجع لكل دالة أو مجموعة دوال. يوضّح التالي فقط تنسيق قائمة تحقق عامة وآمنة للتحقق، باستبعاد الرموز الداخلية أو صيغة تكوين المنتج.

تنسيق السجل العام الآمن لمجموعات الحماية الأصلية (Native)
native_group: protocol-core-v1
abis: [arm64-v8a, armeabi-v7a]
load_phase: post-authentication
jni_registration: dynamic
execution:
  frequency: bounded
  main_thread: false
dependencies:
  - business-runtime
  - system-crypto
acceptance:
  - dependency-resolution
  - jni-registration
  - exception-path
  - output-parity
  - latency-budget
rollback: native-group-v0

تحديد أسباب الأعطال بعد تقوية مكتبات SO باستخدام earliest difference (أقرب اختلاف زمني)

عند استكشاف الأخطاء، ثبّت أولاً هوية الملف، والتوقيع، والجهاز، والنظام، وطريقة التثبيت، ومدخلات العمل لكل من الخط الأساسي غير المحمي ومرشح الإصدار (Release Candidate). ثم ابحث في الجدول الزمني عن أقرب اختلاف: إنشاء العملية، بدء التطبيق، تحميل المكتبة، تنفيذ JNI_OnLoad، اكتمال التسجيل، أول استدعاء أصلي (Native)، أو عودة وظيفة تجارية رئيسية. قد يكون آخر إدخال في سجل الأعطال مجرد تفاعل متسلسل.

يشير خطأ UnsatisfiedLinkError عادةً إلى مكتبات مفقودة، أو عدم تطابق واجهة الثنائية التطبيقية (ABI)، أو مشاكل في التبعيات، أو مشاكل في حل الرموز؛ وقد تظهر أخطاء التسجيل كدوال أصلية (Native Methods) مفقودة؛ بينما قد تتسبب بيئة JNIEnv غير الصحيحة، أو المراجع، أو الاستثناءات المعلقة في حدوث أعطال أثناء استدعاءات الأعمال. احتفظ بالرموز الأصلية المطابقة لمرشح الإصدار لأغراض العزو الداخلي، ولكن لا تعرض الرموز الحقيقية أو العناوين في التقارير العامة.

يمكن لأداة CheckJNI المساعدة في كشف بعض سوء استخدام واجهة JNI، لكنها أداة تشخيصية ولا تمثّل حالة وقت التشغيل في بيئة الإنتاج، كما أنها لا تغطي جميع مشاكل الذاكرة الأصلية والتزامن. بعد الإصلاحات، يجب إعادة تشغيل المسارات الحرجة تحت تكوين الإصدار المستهدف.

الأعراض الشائعة وخطوات جمع الأدلة التالية
أقرب عرض للأعراضفحوصات ذات أولويةالأدلة المطلوبةما لا يجب فعله أولاً
فشل dlopenواجهة الثنائية التطبيقية (ABI)، وDT_NEEDED، الحد الأدنى لوحدات واجهة برمجة التطبيقات (API floor)، والرموزقائمة المكتبات وأخطاء التحميل المطابقة لمرشح الإصدارتبديل مفاتيح حماية غير ذات صلة بشكل متكرر
عدم العثور على الدالة الأصلية (Native Method Not Found)طريقة التسجيل، واسم الفئة، وتوقيع الدالة، والتوقيتجدول التسجيل، وقواعد الاحتفاظ، ونقاط دخول الاستدعاءالنظر فقط في أعداد الرموز المُصدَّرة
تعطل عند أول استدعاءالمعاملات، والمراجع، والخيوط، والاستثناءات، ومعالجة الدوالمكدس الأعطال مع رموز محلولة (Symbolicated stack)، والمدخلات، ومقارنة الخط الأساسيالشرح باستخدام ملفات الرموز من إصدار آخر
فشل على بعض الأجهزةواجهات برمجة تطبيقات النظام (System API)، وفروقات البائعين، وواجهة الثنائية التطبيقية (ABI)، ومكتبات الجهات الخارجيةمصفوفة أنظمة الأجهزة وأقرب الاختلافاتاستبدال استنتاجات الأجهزة الحقيقية بنجاح المحاكي

يجب أن تغطي استنتاجات الإصدار القوة، والتوافقية، والقابلية للصيانة

يمكن لمراقبة القوة الثابتة تسجيل التغييرات في الرموز، والسلاسل النصية، وهيكل الكود، وسطح التعرض لنقاط الدخول الرئيسية؛ بينما يتحقق القبول أثناء وقت التشغيل من التحميل، وواجهة JNI، ومنطق الأعمال الحرج، والاستثناءات، وواجهات الثنائية التطبيقية (ABIs) المستهدفة. يكمل هذان النوعان من الأدلة بعضهما البعض؛ ولا يمكن لأي منهما استبدال الآخر.

يجب أن يُكمل مرشح الإصدار النهائي أيضاً ترقيات التثبيت، والتحقق من التوقيع، وفحوصات القنوات، واختبارات بدء التشغيل، ومراقبة الأعطال، وأرشفة الرموز الأصلية، وتمارين التراجع. يجب أن تسمح أرشيفات الرموز بإيجاد النسخة المطابقة عبر هوية الملف؛ وإلا فلا يمكن تحليل أعطاء الوقت الفعلي الأصلية (Online native crashes) بشكل صحيح.

تقدّم هذه المقالة إطار عمل للفحص الهندسي، وليس استنتاجات اختبارية لمكتبة SO محددة أو تكوين حماية معين. بدون حزمة مستهدفة، وواجهة ثنائية تطبيقية (ABI) مستهدفة، ومسارات استدعاء حقيقية، لا يمكن سوى تقييم ما إذا كانت أعمال التحضير مكتملة، دون وعد بالتوافقية أو الأداء.

  • كل واجهة ثنائية تطبيقية (ABI) مُصدَرة لديها أدلة وقت تشغيل
  • تغطي نقاط دخول JNI الحرجة المسارات الطبيعية ومسارات الاستثناءات
  • تطابق الرموز الأصلية هوية المرشح النهائي
  • نتائج التحميل، والأعمال، والتراجع قابلة لإعادة الإنتاج
  • الأجهزة والأنظمة غير المغطاة مقيدة صراحةً

حدود الأدلة وقابلية التطبيق

يفصل هذا القسم حقائق النظام الموثقة، والأحكام الهندسية، والحدود التي لا يمكن تعميمها على مطالبات المنتج التي لم يتم التحقق منها.

حكم المادةالحقيقة أو الأساس الهندسيحد قابلية التطبيق
يجب قبول واجهة ثنائية للتطبيق (ABI) كبُعد مستقل للقطع الأثرية.تحدد مجموعة تطوير نيتف لأندرويد (NDK) الرسمية واجهة ABI لتشمل مجموعات التعليمات، واتفاقيات الاستدعاء، والسجلات، والمكدس، وتنسيق ELF، وتشويه الأسماء.لا تثبت تعريفات الوثائق أن التطبيق يتضمن تبعيات كاملة أو يعمل بنجاح على الأجهزة المستهدفة.
قد تفشل مراجعات واجهة برمجة التطبيقات النيتف (Native API) الأعلى من نظام الجهاز عند وقت التحميل.تنص وثائق المشاكل الشائعة في NDK على أن الرموز تُحل عادةً عند تحميل المكتبة، ولا يمكن تجاوز الإشارة إلى واجهات برمجة تطبيقات غير موجودة عبر التفرع أثناء التشغيل.يجب تأكيد حالات الفشل المحددة عبر معاملات البناء، والتبعيات، وسجلات الجهاز.
تتطلب أخطاء JNI التحقق على مستويات الخيط، والمراجع، والاستثناءات.تدرج إرشادات JNI لأندرويد مشاكل مثل JNIEnv غير الصالح، والمراجع، والاستثناءات المعلقة، وتسجيل الطرق التي قد تسبب تعطلًا.يساعد CheckJNI فقط في اكتشاف مجموعة فرعية من المشاكل ولا يمكنه استبدال الاختبار الشامل لسلامة الأعمال والذاكرة.
لا يمكن تعميم نتائج محاكي x86_64 واحد لتشمل أجهزة ARM الحقيقية.تستخدم واجهات ABI المختلفة مجموعات تعليمات واتفاقيات استدعاء مختلفة؛ وقد تختلف أيضًا تركيبات المكتبات النهائية والتبعيات.إذا لم يصدر المنتج صراحةً واجهة ABI معينة، فيمكن وضع علامة "غير قابل للتطبيق" عليها بدلاً من اختبارها قسرًا.
يجب إغلاق قوة الحماية والتوافق على نفس مرشح الإصدار (Release Candidate).إعادة بناء أو استبدال ملفات SO يغير الكود، والرموز، والتبعيات، والسلوك المحتمل أثناء التشغيل.هذا مبدأ حوكمة للقطع الأثرية ولا يعني اجتياز أي مستوى حماية محدد.

أسئلة هندسية

إذا كان هناك ملف SO رئيسي واحد فقط، فهل لا يزال رسم بياني للتبعيات مطلوبًا؟

نعم. قد يعتمد ملف SO الرئيسي لا يزال على مكتبات النظام، أو أوقات تشغيل C++، أو مكتبات طرف ثالث، ويتفاعل مع طبقة Java عبر JNI. يؤكد رسم التبعيات نقطة التحميل earliest وحدود المسؤولية.

إذا نجح اختبار arm64-v8a، فهل ما زلت بحاجة لاختبار armeabi-v7a؟

إذا كانت حزمة الإصدار تتضمن وتدعم armeabi-v7a، فإن التحقق المستقل مطلوب. تختلف التعليمات، واتفاقيات الاستدعاء، وقطع التبعيات بين واجهتي ABI ولا يمكن أن تحل إحداهما محل الأخرى.

هل إخفاء جميع الرموز المُصدَّرة أكثر أمانًا؟

ليس بالضرورة. قد تعتمد نقاط دخول JNI الثابتة الضرورية، واستدعاءات الطرف الثالث، واتفاقيات النظام على رموز مُصدَّرة. تحقق أولاً من طرق التسجيل والاستدعاء، ثم قلل التعرض.

هل يمكنني الإصدار إذا لم يبلغ CheckJNI عن أي أخطاء؟

لا، هذا وحده غير كافٍ للإصدار. يجب عليك أيضًا التحقق من تكوين الهدف الفعلي، ومدخلات الأعمال، ومسارات الاستثناءات، وواجهة ABI، ونطاق النظام، وترقيات التثبيت، وقدرات التراجع.

هل يجب تعطيل كل الحماية فورًا بعد تعطل تقوية SO؟

أصلح أولاً هوية المرشح وابحث عن earliest فرق. يمكنك استخدام البحث الثنائي لمجموعات الحماية أو التراجع، لكن غيّر متغيرًا واحدًا قابلًا للتسجيل في كل مرة لتجنب توليد قطع أثرية جديدة غير قابلة للعزو.

هل تريد اختبار ذلك على تطبيقك الخاص؟

أرسل الإصدار المرشح والأنظمة المستهدفة ومسارات الأعمال المهمة لتقييم Yudun PoC والتوافق.

تواصل مع: قائمة التحقق من تصلب SO والتوافق الأصلي