الهندسة العكسية لتطبيقات الآندرويد

وليد الهاجري
13/02/2021


مقدمة
في هذه المدونة سأشرح كيف تتم الهندسة العكسية (Reverse Engineering) لتطبيق الاندرويد وكيف يتم إسترجاع الـSource Code, وهذا سيساعدنا في فهم طريقة عمل التطبيق والحماية المطبقة عليه وفي النهاية سنتمكن من فحص التطبيق بشكل أفضل.

ماهي الهندسة العكسية
ببساطة هي طريقة لتفكيك التطبيق نحاول فيها إسترجاع التطبيق لشكله الأول (Source Code),في هذه الطريقة سنتعرف على مكوناته وأيضا طريقة عمله. ويوجد طريقتين لفك تطبيق الاندرويد

  • Decompile
    • هذه الطريقة تعيد لنا التطبيق تقريبا لSource Code نسخة مشابه للنسخة الأصلية, لكن سيصعب علينا التعديل عليها أو بنائها مرة أخرى (Rebulid) , لكنها مفيدة لأنها تساعدنا على فهم طريقة عمل التطبيق.
  • Disassemble
    • هذة الطريقة نستطيع فك التطبيق بطريقة تساعدنا ببنائه مرة أخرى (Rebuild أو Reassemble) وميزة هذه الطريقة أننا نستطيع تعديل طريقة عمل التطبيق, لكن الSource Code سيصبح عبارة عن Smali Instruction (صعبة القراءة).

كيف يبنى التطبيق
تحت هي الخطوات التي تتم لتحويل الـSource Code إلى تطبيق يعمل على أنظمة الآندرويد. طبعاً توجد عناصر أخرى لبناء تطبيق الآندرويد لكن هنا سنركز فقط على الجزئية التي كتبها المبرمج لسهولة الشرح.

  • النص البرمجي (Source Code) المكتوب بلغة الـJava سيتم عمل Compile له ليتحول إلى Java Bytecode ثم سيتم تحويل الـExtension للملفات من Java إلى Class
  • بعدها بإستخدام الـDEX Compiler سيتم تحويل الـJava Bytecode إلى Dalvik Bytecode لتتحول Extension الملفات إلى DEX, ومع العلم أن ملف الـDEX الواحد يتحمل فقط 65,536 Methods من ضمنها Methods تابعة للآتي Android Framework و Libraries وأيضا الMethods التي كتبها المبرمج, فمن الطبيعي تواجد أكثر من ملف DEX داخل التطبيق الواحد.
  • في النهاية سيتم جمع الملفات المذكورة سابقاً مع ملفات الـResources وأشياء أخرى لتكوين ملف واحد الذي هو ملف التطبيق
الصورة في الأسفل تمثل الخطوات أعلاها

محتوى تطبيق آندرويد
تطبيق الاندرويد "application.apk" عبارة عن ملف مضغوط يمكنك فك ضغطه والإطلاع على الملفات التي بداخله عن طريق الأمر التالي

unzip <android applicaion>

ملفات تطبيق الاندرويد
هذه هي الملفات التي تكون ملف الآندرويد APK
  • AndroidManifest.xml: هنا كل شيء عن التطبيق مثل
    • Package name
    • Target and minimum API level
    • App configuration
    • App components
    • Permissions
  • META-INF: تحتوي الـmetadata للتطبيق
  • classes.dex: هذه الملفات عبارة عن الـcompiled code بصيغة dex
  • assets: مجلد يحتوي ملفات مثل
    • صور
    • خطوط كتابة
    • ملفات فيديو
  • lib: هنا تجد الـnative libraries التي يستعملها التطبيق
  • res: في هذا المجلد ستجد جميع الـResources التابعة للتطبيق مثل الصور, ألوان, أشكال,…


تحدي UnCrackable
سنجرب أحد الطرق المشهورة في الهندسة العكسية على تطبيق UnCrackable من OWASP_MSTG. في البداية نحتاج تحميل التطبيق من صفحتهم على github

بعد تحميل التطبيق نثبته على جهاز اندرويد "يفضل أن يكون Rooted"

adb install UnCrackable-Level1.apk
adb: أداة تسمح لنا في التحكم بجوال الآندرويد عن طريق Command-line
الآن نشغل التطبيق
نلاحظ أن التطبيق يطبيق Root Detection وسوف يتم إجبارنا على ضغط زر OK لإقفال
التطبيق, الآن نحتاج لفك التطبيق للتعرف على كيفية تطبيق هذه الحماية

الآن بإستخدام Jadx نحتاج لعمل Decompile للتطبيق. .

jadx-gui UnCrackable-Level1.apk
Jadx: أداة تقوم بإسترجاع تطبيق الاندرويد تقريبا إلى الـSource Code
أول ما يتم فك المشروع نحتاج التعرف من أين يبدأ التطبيق وهذه المعلومة موجودة داخل ملف الـAndroidManifest.xml طبعا نحتاج للبحث عن الـActivity التي تحمل داخلها الـ android.intent.action.MAIN لأنها ستكون المكان التي يبدأ منها التطبيق
طبعا من أسم الـActivity بإمكاننا الوصول للشاشة المطلوبة
في الـlifecycle للـActivity في الآندرويد أول شيء المفروض يبدأ هي دالة الـonCreate, داخل هذه الدالة أول سطر التطبيق يتأكد من ثلاث أشياء إذا أي شيء صحيح سيتم طباعة رسالة المنع الظاهر عند تشغيل التطبيق ثم تم ضغط زر OK سيتم إقفال التطبيق.

إذا تتبعنا إستدعاء الـClass c سيتبين لنا الدوال الثلاثة a,b and c
بشكل بسيط الدوال هذه تبحث عن أدوات وطرق تستعمل لأخذ صلاحيات أعلى "Root" إذا أي من هذه الدوال أعطى لنا قيمة true سيتم طباعة الرسالة التي تجبرنا على إقفال التطبيق
الآن إذا كانت أي من الدوال الموجودة داخل (c) صحيحة سيتم إعطاء النص التالي "Root detected!" لدالة a لكن لو لاحظنا الCode داخل الصندوق الأحمر سنلاحظ أن التطبيق يتم إقفاله فقط إذا تم ضغط زر "OK" و أيضا لانستطيع إلغاء الـalert dialog لأن القيمة المعطاة لها هي false والظاهرة بسطر 25 فإذا بطريقة ما أستطعنا تغير هذه القيمة عندها سنستطيع تخطى هذه الحماية.
حتى نستبدل أي من المتغيرات سنحتاج أولا التطبيق أن يكون Debuggable حتى نستطيع عمل Debug له لكي نتمكن من تعديل قيم التطبيق أثناء وقت التشغيل (Runtime). حتى يكون بإمكاننا تطبيق هذه الطريقة في البداية نحتاج أن نعمل Disassemble للتطبيق

apktool d UnCrackable-Level1.apk -o base
apktool: أداة تساعدنا أننا نعمل Disassemble وأيضا تساعدنا في عملية الـRebuild للتطبيق
  • d : فك التطبيق (Disassemble)
  • o : يكتب لك مجلد جديد يضع فيه جميع الملفات
الآن نضيف القيمة التالية في ملف الـAndroidManifest.xm
android:debuggable=truel
ثم يتم بناء التطبيق بإستعمال الطريقة التالية

apktool b base/ -o base.apk
  • b : بناء التطبيق (Reassemble)
  • o : يجمع الملفات في ملف اسمه base.apk
الآن نحتاج إنشاء مفتاح حتى نستطيع توقيع (Sign) التطبيق, لأن نظام الآندرويد يطلب من كل التطبيقات أنها تكون موقعه من قبل مطور التطبيق أو نظام الآندرويد سيرفض تثبيت التطبيق

keytool -genkey -v -keystore WH_keys.keystore -alias WH -keyalg RSA -keysize 2048 -validity 10000
keytool: أداة تنشأ لك مفتاح تسمح لك بتوقيع تطبيقك بها
  • genkey: خيار لإنشاء مفتاح إلكتروني
  • v: لعرض معلومات أكثر على الشاشة
  • keystore: اسم المفتاح
  • alias: اسم بديل
  • keyalg: خوارزمية المفتاح
  • keysize: طول حجم المفتاح
  • validity: مدة صلاحية المفتاح
ثم ندخل المعلومات الخاصة للمفتاح

الآن نستعمل المفتاح لنوقع التطبيق

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore WH_keys.keystore base.apk Wh
jarsigner: أداة تستعمل الـKey المنشأ سابقا للتوقيع على التطبيق
  • verbose: لعرض معلومات أكثر على الشاشة
  • sigalg: اسم الخوارزمية المراد استخدامها
  • digestalg: اسم الخوارزمية المراد استخدامها في عملية الـhashing
  • keystore: اسم ومكان ملف الـkeystore التي تم إنشائه في الخطوة السابقة
  • base.apk: هذا اسم التطبيق المراد توقيعه
  • Wh: الاسم المستعار للمفتاح
آخر خطوة نحتاج إستعمال أداة اسمها zipalign هدفها ترتييب ملفات التطبيق

zipalign -v 4 base.apk debuggable-crackme.apk
  • v: لعرض معلومات أكثر على الشاشة
  • 4: خيار يسمح لك أن تعمل Alignment للـBytes على شكل 32-bit
نحتاج الآن تثبيت التطبيق المعدل
الآن في الجوال أول ما يتم تشغيل التطبيق سيتم طباعة رسالة مختلفة عن السابقة تبين أن التطبيق أكتشف التغيير.
أيضا والتطبيق يعمل بإمكاننا التأكد أن التطبيق Debuggable بهذه الطريقة

adb jdwp
  • jdwp: يستعرض جميع الـProcesses الـDebuggable على الجوال
الآن بإمكاننا الإطلاع على التطبيق الذي يعمل على الprocess

frida-ps -Uai | grep  ID
frida: أداة تسعمل في الـDynimac Analysis شرحتها في هذه المقالة كيف يتم تثبيتها وكيف يتم إستعمالها
الآن نحتاج تشغيل التطبيق على الـDebug Mode ثم نتصل عليه

adb shell am start -D "owasp.mstg.uncrackable1/sg.vantagepoint.uncrackable1.MainActivity"
التطبيق الآن يعمل لكن ينتظر الـDebugger يتصل عليه
فأول شيء نحتاج رقم الـProcess

adb jdwp
بعدها نحتاج نسحب الـProcess إلى Port على الجهاز الشخصي أخترت Port رقم 1234

adb forward tcp:1234 jdwp:11561
forward: تبني طريق بين الـprocess داخل الجوال مع port على الجهاز الشخصي
tcp: تعطى أي رقم port على أن لا تكون مستخدمة
jdwp: رقم الـprocess على الجوال
الآن نحتاج نشبك الـDebugger بإستخدام jdb طبعاً نحتاج إعطائها في البداية suspend التي هي أحد الخيارات في الأداة وفائدتها أنها توقف التطبيق أول مايتصل الـDebugger

(echo suspend && cat) | jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=1234
  • jdb : أداة تستخدم في الـjava debugging
هدفنا من هذا كله تغير قيمة الـsetCancelable من false إلى true حتى نتخطى الحماية الموضوعة, الآن نحتاج نعرف اسم الـclass الكامل حتى نتمكن من إيقافه. إذا رجعنا لـjadx سيتبين لنا أن هذه الدالة مستدعاة من
android.app.AlertDialog
لكن لو حاولنا أن نوقف الدالة سيتم طباعة أنه لايوجد دالة أسمها setCancelable داخل الـ android.app.AlertDialog
فإذا أطلعنا على الـdocumentation التابعة الـandroid سنجد أن AlertDialog عامل extend لـclass اسمه Dialog
وإذا بحثنا عن هذه الدالة داخل Dialog سنجدها
في السابق كنا نكتب

stop in android.app.AlertDialog.setCancelable
نعدلها إلى هذا الأمر

stop in android.app.Dialog.setCancelable
الآن تعرف الـDebugger على الدالة المراد التوقف عندها, الآن نكتب resume إلى أن التطبيق يوقف عند الدالة الموضوع عندها الـBreakpoint
الآن فقط نكتب locals ثم نطلع على قيمة الـflag إذا false نستبدل القيمة بtrue, أما إذا كانت القيمة true نكتب resume
ثم نكرر العملية هذه إلى أن تطلع لنا هذه الشاشة في الجوال
الآن نضغط على المكان المظلل وبيختفي الـAlert Dialog
بهذه الطريقة قدرنا نتخطى الحماية الموضوعة, الآن نحتاج نعرف الكلمة السرية المحفوظة داخل التطبيق, طبعا في البداية نحتاج نحذف الـBreakpoint الموضوعة سابقاً حتى مانتوقف عندها
طبعا لو رجعنا للـSource Code بنلاحظ النص الذي بيتم كتابته بيعطى للدالة التي أسمها a.a وإذا رجعت true بيطبع أن النص صحيح, الآن نحتاج نعرف كيف الدالة a.a تفرق بين الصح والخطأ
داخل الـClass a يوجد Method اسمها a لايهمنا تفاصيل عملها لأنها في النهاية بيتم مطابقة النص الذي أعطي لها من الدالة السابقة مع النص السري في آخر سطر داخل الدالة
أولا نحتاج نحط Breakpoint عند الـMethod a ثم نكتب أي نص ونضغط كلمة verify في الجوال
بعدها إذا كنا داخل الدالة a نحط Breakpoint ثانية عند equals طبعا كيف نعرف الـClass Path لدالة equals أولا هي عبارة عن Object اسمه str من الـString Class
فإذا بحثنا عن الـString Class في الـAndroid Documentation سنجد أن الـClass Path هو
java.lang.String.equals
فنكتب

stop in java.lang.String.equals
الآن نطبع الـlocals ثم resume ونكررها إلى أن نصل للدالة التي تطابق بين النص المدخل والنص المحفوظ في التطبيق ونحصل على النص السري
الآن فقط نحتاج نلغي الـBreakpoints الموضوعة سابقاً ثم نجرب النص الموجود من الصورة السابقة ونتأكد إذا صحيح أو لا
وبكذا قدرنا نحصل على النص السري
ختاما
في الهندسة العكسية لايوجد طريقة عامة كل تطبيق وله طريقته الخاصة فلا يوجد طريقة صحيحة أو طريقة خاطئة, وأيضا يوجد طرق وأدوات كثيرة في الهندسة العكسية والمجال كبير فأفضل شيء أن يبدأ الشخص بتعلم طريقة أو أداة إلى أن يتمكن منها ثم ينتقل للتي بعدها إلى أن يتمكن من المجال. ويمكن البدء بـowasp-mstg لتعلم المهارات الأساسية.

لمشاركة هذه المدونة
تابعنا
طور مهاراتك من خلال متابعة آخر منشورات فريقنا
مدونات آخرى