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

في هذه المقالة ، أود أن أشارك رؤيتي في التعتيم ، وكذلك الحديث عن طريقة مثيرة للاهتمام لإخفاء منطق العمل في التطبيقات مع NDK ، والتي وجدتها مؤخرًا نسبيًا. لذلك إذا كنت مهتمًا بأمثلة حية من الكود المبهم في Android - أطلب القط.
تحت التعتيم في إطار هذه المقالة ، نحن نفهم تقليل الشفرة القابلة للتنفيذ لأحد تطبيقات Android إلى نموذج يصعب تحليله. هناك عدة أسباب لجعل تحليل الشفرة صعباً:
- لا يوجد عمل يريد أن يتورط في "الدواخل".
- حتى إذا كان لديك تطبيق وهمية ، يمكنك دائمًا العثور على أشياء مثيرة للاهتمام هناك (مثال على instagram ).
يحل العديد من المطورين المشكلة ببساطة عن طريق التهيئة ProGuard config. ليست هذه هي أفضل طريقة لحماية البيانات (إذا كانت هذه هي المرة الأولى التي تسمع فيها ، فراجع الويكي ).
أرغب في تقديم مثال على ذلك ، لماذا لا تعمل "الحماية" المزعومة مع ProGuard. خذ أي مثال بسيط من Google Samples.

بعد توصيل ProGuard بتكوين قياسي لها ، نحصل على رمز تم إلغاء ترجمته:

"أوه ، لا شيء واضح" ، نقول ونهدأ. ولكن بعد بضع دقائق من التبديل بين الملفات ، نجد قطعًا مماثلة من التعليمات البرمجية:

في هذا المثال ، يبدو رمز التطبيق ضعيفًا إلى حد ما (تسجيل البيانات ، إنشاء التقاط فيديو) ، لذلك يمكن فهم بعض الطرق المستخدمة في التعليمة الأصلية بسهولة حتى بعد المعالجة باستخدام تهيئة ProGuard.
علاوة على ذلك ، ألق نظرة على فئات البيانات في Kotlin. تنشئ فئة البيانات الافتراضية طريقة "toString" ، والتي تحتوي على أسماء متغيرات المثيلات واسم الفئة نفسها.
فئة البيانات المصدر:

يمكن أن يتحول إلى طعام سريع للخلف:

(Kotlin toString الإنشاء التلقائي)
اتضح أن ProGuard يخفي كل شفرة المصدر للمشروع.
إذا كنت ما زلت لم أقنعك بعدم كفاية حماية الشفرة بهذه الطريقة ، فلنحاول ترك سمة ".source" في مشروعنا.
-keepattributes SourceFile
هذا الخط في العديد من المشاريع مفتوحة المصدر. يتيح لك عرض StackTrace عند تعطل التطبيق. ومع ذلك ، بسحب ".source" من كود smali ، نحصل على التسلسل الهرمي للمشروع بأكمله بأسماء فئة كاملة.
بحكم التعريف ، فإن التشويش هو "تحويل الكود المصدري إلى شكل غير قابل للقراءة من أجل مواجهة أنواع مختلفة من الاستقبال". ومع ذلك ، ProGuard (عند استخدامه مع التكوين القياسي) لا يجعل الكود غير قابل للقراءة - إنه يعمل على أنه مصغر ، وضغط الأسماء وإلقاء فئات إضافية خارج المشروع.
مثل هذا الاستخدام لـ ProGuard سهل ولكنه ليس مناسبًا تمامًا لحل التشويش الجيد. يحتاج المطور الجيد إلى جعل الموزع (أو المهاجم) خائفًا من "الأحرف الصينية" ، والتي يصعب فصلها.
إذا كنت مهتمًا بمعرفة المزيد حول ProGuard ، فأنا أقترح المقالة الإخبارية التالية.
ماذا نختبئ؟
الآن دعونا نرى ما هو مخفي عادة في التطبيقات.


غالبًا ما يمكن إخفاء شيء غير متوقع في الشفرة (الملاحظات من التجربة الشخصية) ، على سبيل المثال:
- أسماء مطوري المشروع
- المسار الكامل للمشروع
- Client_secret لبروتوكول Oauth2
- كتاب PDF "كيفية التطوير لنظام Android" (من المحتمل أن يكون دائمًا في متناول اليد)
نحن الآن نعرف ما الذي يمكن أن يخفيه في تطبيقات Android ويمكنه الانتقال إلى الشيء الرئيسي ، أي طرق إخفاء هذه البيانات.
طرق لإخفاء البيانات
الخيار 1: لا تخفي أي شيء ، وترك كل شيء في الأفق
في هذه الحالة ، أنا فقط أريك هذه الصورة :)
"ساعد داشا في العثور على منطق العمل"

هذا الحل الفعال من حيث التكلفة وخالٍ تمامًا مناسب لـ:
- التطبيقات البسيطة التي لا تتفاعل مع الشبكة ولا تخزن معلومات المستخدم الحساسة ؛
- التطبيقات التي تستخدم واجهة برمجة التطبيقات العامة فقط.
الخيار 2: استخدم ProGuard بالإعدادات الصحيحة
لا يزال لهذا القرار الحق في الحياة ، لأنه أولاً وقبل كل شيء بسيط وحر. على الرغم من العيوب المذكورة أعلاه ، إلا أن لها ميزة إضافية: إذا تم تكوين قواعد ProGuard بشكل صحيح ، فيمكن أن يصبح التطبيق غير واضح.
ومع ذلك ، يجب أن تفهم أن مثل هذا الحل بعد كل تجميع يتطلب من المطور إلغاء ترجمته والتحقق مما إذا كان كل شيء على ما يرام. بعد قضاء عدة دقائق في دراسة ملف APK ، يمكن أن يصبح المطور (وشركته) أكثر ثقة في سلامة منتجهم.
كيفية دراسة ملف APKالتحقق من تطبيق التعتيم بسيط للغاية.
للحصول على ملف APK من المشروع ، هناك عدة طرق:
- تأخذ من دليل المشروع (في Android Studio ، عادة ما يكون اسم المجلد "build") ؛
- قم بتثبيت التطبيق على هاتفك الذكي والحصول على APK باستخدام تطبيق "Apk Extractor".
بعد ذلك ، باستخدام الأداة المساعدة Apktool ، نحصل على رمز Smali (تعليمات للوصول إلى هنا https://ibotpeaches.imtqy.com/Apktool/documentation ) ونحاول العثور على شيء يقرأ بشكل مثير للريبة في سطور المشروع. بالمناسبة ، للبحث عن رموز قابلة للقراءة ، يمكنك تخزين أوامر bash المعدة مسبقًا.
هذا الحل مناسب ل:
- تطبيقات الألعاب ، تطبيقات المتجر على الإنترنت ، إلخ ؛
- التطبيقات التي هي عملاء رفيع المستوى حقًا ، وجميع البيانات تصل حصريًا من جانب الخادم ؛
- التطبيقات التي لا تكتب على جميع لافتاتها "التطبيق الآمن رقم 1".
الخيار 3: استخدام Open Source Obfuscator
لسوء الحظ ، لا أعرف أدوات التعتيم المجانية الجيدة للتطبيقات المحمولة. كما أن أجهزة التعتيم الموجودة على الشبكة يمكن أن تجلب لك الكثير من الصداع ، لأنه سيكون من الصعب للغاية تجميع مثل هذا المشروع للإصدارات الجديدة من واجهة برمجة التطبيقات.
تاريخيا ، تصنع مواد التشويش الباردة الموجودة تحت كود الآلة (من أجل C / C ++). أمثلة جيدة:
على سبيل المثال ، يستبدل Movfuscator جميع الشفرات مع movs ، ويجعل الكود خطيًا ، ويزيل جميع الفروع. ومع ذلك ، فإنه من المحبط للغاية استخدام طريقة التعتيم هذه في مشروع قتالي ، لأن الكود يخاطر في أن يصبح بطيئًا وثقيلًا جدًا.
هذا الحل مناسب للتطبيقات التي يكون فيها الجزء الرئيسي من الكود هو NDK.
الخيار 4: استخدام حل الملكية
هذا هو الخيار الأكثر كفاءة للتطبيقات الخطيرة ، مثل البرمجيات الاحتكارية:
أ) المدعومة ؛
ب) ستكون دائما ذات صلة.
مثال على التعليمة البرمجية المبهمة عند استخدام هذه الحلول:

في مقتطف الشفرة هذا ، يمكنك رؤية:
- أسماء أكثر من غير مفهومة من المتغيرات (مع وجود الحروف الروسية) ؛
- الأحرف الصينية في الخطوط التي لا توضح ما يحدث بالفعل في المشروع ؛
- هناك الكثير من الفخاخ التي تمت إضافتها إلى المشروع ("switch" ، "goto") ، والتي تغيّر بشكل كبير تدفقات التطبيق.
هذا الحل مناسب ل:
- البنوك
- شركات التأمين
- مشغلي شبكات الهاتف النقال ، تطبيقات تخزين كلمات المرور ، إلخ.
الخيار 5: استخدام React-Native
قررت تسليط الضوء على هذه النقطة ، لأن كتابة التطبيقات عبر الأنظمة أصبحت الآن نشاطًا شائعًا حقًا.
بالإضافة إلى مجتمع كبير جدًا ، يوجد لدى JS عدد كبير جدًا من أدوات التشويش المفتوحة. على سبيل المثال ، يمكنهم تحويل التطبيق الخاص بك إلى رموز:

أود حقًا أن أنصحك بهذا الحل ، لكن بعد ذلك سوف يعمل مشروعك بشكل أسرع قليلاً من السلاحف.
ولكن من خلال تقليل متطلبات تشويش الكود ، يمكننا إنشاء مشروع محمي بشكل جيد حقًا. لذا google "js obfuscator" و تعترض ملف حزمة الإخراج.
هذا الحل مناسب لأولئك الذين هم على استعداد لكتابة تطبيق عبر النظام الأساسي على React Native.
Xamarinسيكون من المثير للاهتمام معرفة معلومات عن التعتيم على Xamarin ، إذا كانت لديك خبرة في استخدامها ، فيرجى إخبارنا بذلك في التعليقات.
الخيار 6: استخدام NDK
أنا نفسي غالباً ما كان علي استخدام NDK في الكود. وأنا أعلم أن بعض المطورين يعتقدون أن استخدام NDK يحفظ تطبيقهم من المحولات. هذا ليس صحيحا تماما. تحتاج أولاً إلى فهم كيفية عمل الإخفاء مع NDK.

اتضح بسيط جدا. يوجد بعض اصطلاحات JNI في التعليمات البرمجية التي عند استدعاء رمز C / C ++ في مشروع ، سيتم تحويله على النحو التالي.
NativeSummator الفئة الأصلية:

تنفيذ طريقة المبلغ الأصلي:

تنفيذ طريقة المبلغ الثابت الأصلي:

يصبح من الواضح أنه للاتصال بالطريقة الأصلية ، يمكنك استخدام Java_<package name>_<Static?><class>_<method>
في المكتبة الديناميكية.
إذا نظرت إلى رمز Dalvik / ART ، فسنجد الأسطر التالية:

( المصدر )
أولاً ، Java_<package name>_<class>_<method>
السطر التالي Java_<package name>_<class>_<method>
من كائن Java ، ثم نحاول حل الطريقة في المكتبة الديناميكية باستخدام استدعاء "dlsym" ، الذي سيحاول إيجاد الوظيفة التي نحتاجها في NDK.
هذه هي الطريقة التي يعمل JNI. مشكلتها الرئيسية هي أنه من خلال إلغاء ترجمة المكتبة الديناميكية ، سوف نرى كل الطرق في مرأى ومسمع:

لذلك ، نحن بحاجة إلى التوصل إلى مثل هذا الحل بحيث يكون عنوان الوظيفة مبهمًا.
في البداية حاولت كتابة البيانات مباشرة على جدول JNI ، لكنني أدركت أن آليات ASLR والإصدارات المختلفة من Android ببساطة لن تسمح لي لجعل هذه الطريقة تعمل على جميع الأجهزة. ثم قررت معرفة الأساليب التي توفرها NDK للمطورين.
وها ، هناك طريقة "RegisterNatives" التي تعمل بالضبط ما نحتاجه (تسمى الوظيفة الداخلية dvmRegisterJNIMethod ).
نحدد مجموعة تصف الطريقة الأصلية لدينا:

ونحن نسجل طريقتنا المعلنة في وظيفة JNI_OnLoad (تسمى هذه الطريقة بعد تهيئة المكتبة الديناميكية ، ألف ):

الصيحة ، نحن أنفسنا أخفينا وظيفة "hideFunc". الآن قم بتطبيق أداة تشفير ملفات الفيديو المفضلة لدينا واستمتع بأمان الكود في شكله النهائي.
هذا الحل مناسب للتطبيقات التي تستخدم بالفعل NDK (ربط NDK يجلب الكثير من الصعوبات للمشروع ، لذلك هذا الحل غير مناسب للتطبيقات بخلاف NDK).
الخاتمة
في الواقع ، يجب ألا يخزن التطبيق أي بيانات حساسة ، أو يجب ألا يكون الوصول إليها ممكنًا إلا بعد مصادقة المستخدم. ومع ذلك ، يحدث أن يفرض منطق العمل على المطورين تخزين الرموز والمفاتيح وعناصر محددة من منطق الكود داخل التطبيق. آمل أن يساعدك هذا المقال إذا كنت لا ترغب في مشاركة هذه البيانات الحساسة وأن تكون "كتابًا مفتوحًا" للكتبة.
أعتقد أن التشويش هو جزء هيكلي مهم في أي تطبيق حديث.
فكر جيدًا في مشكلات إخفاء التعليمات البرمجية ولا تبحث عن طرق سهلة! :)
بالمناسبة ، بفضل miproblema المستخدم للحصول على مساعدة في بعض القضايا. اشترك في قناة برقية لها ، ومن المثير للاهتمام هناك.
وكذلك جزيل الشكر لمستخدمي sverkunchik و SCaptainCAP لمساعدتهم في تحرير المقالة.