DexPatcher: Patch Android APKs باستخدام جافا

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

ربما تكون قد سمعت عن كيفية تعديل ملفات APK عادة - يقوم المطورون بتوصيل المصفوفة ، والبدء في رؤية كل شيء في Smali ، واكتساب القدرة على تعديل الأشياء باستخدام قوة المصدر. مكالمة هاتفية كافية لإخراجها بمجرد الانتهاء ، وفي هذه المرحلة تكون مستعدة لمشاركة ملفات APK الجديدة اللامعة.

أكثر خطورة ... لنبدأ في البداية. إذا لم تكن معتادًا على تعديل تطبيقات Android ، فقد تتساءل عن نوع smali. عادةً ما يستخدم المطورون لغة برمجة Java لترميز تطبيقات Android فيها. برنامج (المترجم) ثم "يترجم" هذا الرمز إلى تنسيق آخر مناسب لجهازك ، مما ينتج عنه ملفات .dex مضمنة في حزمة التطبيق (أو APK).

في هذه المرحلة ، لا يمكنك الوصول إلى شفرة المصدر الأصلية بعد الآن (ما لم تكن المطور أو التطبيق مفتوح المصدر). ومع ذلك ، فإن ما لديك هو APK ، لأنه ما تم تثبيته على جهازك. منه ، يمكنك الحصول على ملفات dex (عادةً classes.dex) ثم محاولة ترجمتها إلى تنسيق يمكنك فهمه. هذا هو المكان الذي يأتي فيه smali ، كترجمة أكثر قابلية للقراءة ولكن وفية. يمكنك المضي قدمًا خطوة إلى الأمام وترجمتها مرة أخرى إلى Java ، على الرغم من أن هذه العملية ليست مخلصة بما يكفي - ستحصل على نتيجة مفهومة ، لكن من المحتمل أنك لن تتمكن من ترجمتها بطريقة أخرى حيث أن بعض التفاصيل سوف تضيع على طول الطريق. وبعبارة أخرى ، فإن أي تعديلات قد تجريها ستكون بلا شيء ، حيث لن تتمكن من إعادتها مرة أخرى إلى ملف APK مرة أخرى لتثبيتها على جهازك ... على الأقل ليس دون جهد كبير.

smali / baksmali هو في الواقع مجمع / مُشَغِّل لتنسيق dex - هذا ما يعنيه حرفيًا في الأيسلندية. ومع ذلك ، فإننا نشير عادةً إلى التنسيق الذي تفهمه smali عندما نقول "Smali" (فكر في الأمر كتعليمات تحدد كل التفاصيل الصغيرة ، حتى لو لم نكن جميعًا بحاجة إلينا نحن البشر - إنها بالتالي مطوّلة أكثر من Java). لاحظ أيضًا أن التفسير أعلاه مبسط بعض الشيء ، لكن يجب أن يكون تشبيهًا وثيقًا مع سهولة فهمه.

ما الذي يحتاج المطور إلى القيام به لتعديل التطبيق (دون الوصول إلى المصدر) ، إذن؟ العملية أكثر أو أقل كما يلي:

  1. احصل على APK (من الويب أو من الجهاز).
  2. استخدم شيئًا مثل apktool لفك ترجمة ملف APK إلى Smali. (يستخدم apktool smali / baksmali ، ولكنه يسهّل فك شفرة ملفات APK وإعادة بنائها ، كما يعتني بفك ترميز الموارد مثل ملفات XML.)
  3. قم باستخراج classes.dex من APK ، ثم استخدم dex2jar وأخيرًا أداة فك تشفير Java للحصول على كود Java (غير مكتمل ، وغالبًا ما يكون مكسورًا ، لكنه مفهوم في الغالب). (هذا اختياري ، ولكن يمكن أن يكون مفيدًا لأن Smali يصعب فهمه.)
  4. تحديد ما يجب تعديله.
  5. في الواقع تعديله عن طريق تحرير كود Smali مباشرة.
  6. بدلاً من ذلك ، اكتب التعديل في Java ، وقم بترجمته ، ثم قم بفك ترجمته إلى Smali ، ثم انسخ رمز Smali الناتج.
  7. بمجرد انتهاء كل شيء ، استخدم apktool مرة أخرى لإعادة إنشاء APK.
  8. قم بتوقيع APK (للتحقق من هوية المؤلف ؛ يجب توقيع جميع الحزم) ثم تثبيته نهائيًا.

كتابة رمز Smali صعب للغاية وعرضة للخطأ. يمكن إجراء تغييرات أصغر في Smali ، لكن إضافة ميزات جديدة بها أكثر صعوبة. بالإضافة إلى ذلك ، لن يكون لديك أي أخطاء في وقت الترجمة ، لذلك قد يتم اكتشاف الأخطاء المطبعية فقط في وقت التشغيل. توسيع ومشاركة بقع Smali يمكن أن تكون أيضًا مزعجة ، حيث أن الفوارق تميل إلى أن تكون خاصة جدًا بإصدار APK معين. على الرغم من وجود بعض الأدوات لجعل أجزاء العملية الموضحة أعلاه أسهل (Virtuous Ten Studio تتبادر إلى الذهن) ، إلا أنه لا يزال من الممكن أن تصبح مرهقة.

يهدف DexPatcher by Senior Member Lanchon إلى حل هذه المشكلات ، عن طريق جعل العملية أكثر بساطة والسماح للمطورين بتجنب التعامل مع Smali تمامًا. بدلاً من ذلك ، يمكن لـ devs كتابة تصحيحات في Java وحدها وجعل DexPatcher يتعامل مع كل شيء آخر.

هذا لديه الميزة الرئيسية من وجود ملفات التصحيح سهلة القراءة ويمكن التحكم فيها. Patching APKs يصبح أيضًا أكثر ملاءمة بشكل عام. سنرى مثالًا كاملاً على كيفية استخدام DexPatcher في فترة قصيرة ، ولكن إليك نظرة عامة سريعة على ما يقدمه أولاً:

  • المصدر المفتوح.
  • عبر منصة: يجب أن تعمل على لينكس ، ماك وويندوز.
  • ملفات التصحيح: التعديلات التي تجريها موجودة في ملفات تصحيح Java التي يمكنك مشاركتها بشكل مستقل.
  • جافا: ليس سمالي.

يمكنك أيضًا الاستفادة من فحص أخطاء وقت البناء ، لذلك تظهر الأخطاء في وقت مبكر من دورة التطوير. يوفر Java المترجم التحقق من وقت الترجمة المعتاد (مع إمكانية الوصول إلى رموز APK الأصلية) ، ويفرض DexPatcher توافق المصدر والتصحيح عند الترقيع ، ويوفر معلومات مفيدة ويصدر تحذيرات عندما يبدو أنك تقوم بشيء قانوني ولكنه مريب.

بالإضافة إلى ذلك ، يأتي DexPatcher مع مجموعة من البرامج النصية المساعدة (متوفرة فقط على Linux ، على الرغم من إمكانية نقلها إلى منصات أخرى أيضًا). هذه تهتم بإعداد مساحة العمل ، واستخراج فئات وموارد APK المستهدفة ، وإلغاء ترجمتها إلى Java (يتم استخدام أداة فك تشفير CFR Java في الأخير) ، وأخيراً إنشاء وتوقيع APK مصحح بمجرد الانتهاء.

دعنا نلقي نظرة على مثال (على نظام Linux):

تثبيت البرامج النصية DexPatcher

 $ # قم بعمل دليل حيث يمكننا اختبار الأشياء وإدخالها. $ mkdir xda-test $ cd xda-test $ git clone //github.com/Lanchon/DexPatcher-scripts.git dexpatcher # Clone the DexPatcher scripts repo repo. $ cd dexpatcher $ chmod + x dxp- * # ليس ضروريًا ، ولكن من أجل الوضوح: نحتاج إلى التأكد من أن الملفات التي سنتصل بها لاحقًا قابلة للتنفيذ. 

تكوين البرامج النصية DexPatcher

افتح dxp.config في محرر النصوص المفضل لديك وتأكد من تغيير المتغيرات اللازمة لتناسب نظامك. ما عليك سوى تغيير السطر التالي للإشارة إلى موقع تثبيت Android SDK بدلاً من ذلك:

 dxp_android_sdk_dir = (~ / android / sdk) 

(سيقوم DexPatcher تلقائيًا باختيار أعلى إصدار متوفر. بالإضافة إلى ذلك ، يمكنك أيضًا تعديل خيارات التكوين الأخرى لجعلها تستخدم إصداراتك الخاصة من بعض الأدوات بدلاً من الإعدادات الافتراضية المجمعة.)

لسهولة الوصول ، يمكننا إضافة دليل dexpatcher إلى PATH ، أو حتى ربط نصوص dxp- * المختلفة إلى موقع موجود بالفعل في PATH ، مثل ~ / bin:

 تصدير PATH = $ PWD: PATH $ 

تعديل تطبيق

في هذا المثال ، سنستخدم تطبيقًا بسيطًا ومفتوح المصدر. بالطبع ، يمكن تصحيح شفرة المصدر مباشرة في هذه الحالة بالذات ، لكن هذا ليس متعة على الإطلاق!

سنتخذ تطبيق "Get ID" بواسطة basil2style ، وهو تطبيق يعرض عليك بعض التفاصيل حول جهازك. هدفنا هو تعديل زر "نسخ" لـ "معرف الجهاز" وجعله يشارك هذا المعرف بدلاً من ذلك:

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

يمكننا أيضًا أن نفعل كل شيء عبر الصدفة ، باستخدام البرامج النصية المساعدة:

 $ cd dexpatcher # انتقل إلى دليل العمل الخاص بنا. $ curl -O //f-droid.org/repo/makeinfo.com.getid_1.apk # قم بتنزيل APK. $ dxp-setup-for-apk makeinfo.com.getid_1.apk # قم بإلغاء فك تشفير APK وإلغاء ترجمته. $ cd makeinfo.com.getid_1 # انتقل إلى الدليل الذي تم إنشاؤه حديثًا حيث يتم فك / فك تجميع كل شيء. $ dxp-create-keystore # إنشاء مفتاح توقيع APK. اضغط 6 مرات (أو املأ المعلومات) ، ثم "نعم". 

ستلاحظ وجود بعض الأدلة المختلفة هناك:

  • فك شفرة : ستجد الموارد و Smali هنا ، كما تم فك تشفيرها بواسطة apktool.
  • src : دليل فارغ. هذا هو المكان الذي سنضع فيه ملفات التصحيح الخاصة بنا.
  • src-cfr : هذا هو المكان الذي قام فيه cfr بفك تشفير التطبيق (مع وجود أخطاء). من الجيد أن تضعه في الاعتبار لاتخاذ قرار بشأن ما يجب تغييره (قد تحتاج أيضًا إلى موارد ومعرفاتها من دليل فك الترميز أعلاه ، ولكن ليس لهذا المثال المحدد).
  • src-cfr-nodecode : كما هو مذكور أعلاه ، ولكن تحتوي فقط على بذرة فارغة (بدون كود ، فقط هياكل عظمية). يمكنك استخدام هذه الملفات كأساس للتصحيح الخاص بك كما سنرى قليلاً.

كما ذكرنا سابقًا ، نريد تغيير زر "نسخ" معرف الجهاز لمشاركة نص المعرف بدلاً من ذلك. إذا نظرنا حول الكود المصدري ، فسنلاحظ أن زر نسخ معرف الجهاز (device_copy) onClick يتم التعامل معه بواسطة فئة مجهولة في src-cfr / makeinfo / com / getid / MainActivity.java. بينما يمكننا تعديله هنا ، إلا أنه من الأفضل عادة إيجاد طريقة بديلة للقيام بذلك لأن الفئات المجهولة لها أسماء رقمية (MainClassName $ SomeNumber ، على سبيل المثال MainActivity $ 3) والتي قد تتغير بشكل غير متوقع بين الإصدارات.

بدلاً من ذلك ، سنقوم بتسجيل فئة خاصة بنا للحدث عن طريق تعديل فئة MainActivity. أولاً ، دعنا نسخت نسخة "الهيكل العظمي" من src-cfr-nocode / makeinfo / com / getid / MainActivity.java إلى src / makeinfo / com / getid / MainActivity.java (تذكر أن src هو المكان الذي سيعيش فيه التصحيح لدينا). (يمكنك أيضًا نسخ الإصدار بالشفرة الكاملة إذا كنت تفضل ذلك ، فهذا أمر محض الذوق).

يمكننا الآن تعديله على النحو التالي:

  • أضف الاستيراد الضروري للتعليق التوضيحي DexPatcher:
 import lanchon.dexpatcher.annotation. *؛ 
  • أضف علامة للإشارة إلى أننا نقوم بتحرير الفصل. لقد قمنا أيضًا بتعيين الإجراء الافتراضي لأعضاء فئة التصحيح على IGNORE ، مما يعني أن الأعضاء موجودون ليتم الرجوع إليهم بواسطة الكود الخاص بنا أثناء تجميع Java ، ولكن سيتم تجاهلها بواسطة DexPatcher.
 DexEdit (defaultAction = DexAction. IGNORE) MainActivity للفئة العامة // سيتم استيفاء الإشارة إلى ActionBarActivity بواسطة الرموز // المستخرجة من التطبيق عندما نصمم التصحيح. يمتد ActionBarActivity { 
  • بالإضافة إلى ذلك ، أضف أجسامًا فارغة إلى المُنشئ وطريقة onCreate ، بالإضافة إلى جميع الطرق الأخرى التي نخطط لاستخدامها (تذكر أنه سيتم تجاهلها عند تطبيق التصحيح لدينا بالفعل - نحن فقط نضيفها حتى نتمكن من الرجوع إليها هنا إذا كنا بحاجة إلى). يمكنك أيضًا إضافة الكلمة الأساسية الأصلية بدلاً من ذلك.
  • يمكننا بالفعل بناء التصحيح في هذه المرحلة ، إذا كنت فضوليًا:
     $ dxp-make # Output: `patched.apk`. 

    بسيط جدا ، أليس كذلك؟ دعنا نستمر ، رغم أننا لم ننته بعد.

  • دعنا نحرر onCreate الآن لتعيين OnClickListener الخاص بنا حتى نتمكن من مشاركة معرف الجهاز بدلاً من نسخه إلى الحافظة:
     / / أعد تسمية الطريقة المستهدفة بحيث لا يزال بإمكاننا تسميتها (الأصل) // إذا لزم الأمر. DexEdit (target = "onCreate") محمية void source_onCreate (Bundle var1) {} // أضف أسلوبنا المخصص الجديد. OverrideDexAdd محمية void onCreate (Bundle var1) {// اتصل بالطريقة الأصلية: source_onCreate (var1)؛ / / استبدل النص والمعالج: device_copy. setText ("مشاركة") ؛ device_copy. setOnClickListener (DeviceCopyOnClick ()) الجديد ؛ } // لاحظ أننا لا نستخدم فصلًا مجهولًا لتجنب التسمية مع // MainActivity $ 1 ، الموجود بالفعل. // يمكننا أيضا تعريف فئة MainActivity.Patch متداخلة واستخدمنا // فئة مجهولة في MainActivity.Patch.onCreate () ، ثم أطلقنا عليها // MainActivity.Patch.onCreate () من MainActivity.onCreate (). DexAdd class يطبق DeviceCopyOnClick طريقة العرض. OnClickListener {Override public void onClick (عرض الكائن) {if (MainActivity. this. val) {Intent intent = new Intent (Intent. ACTION_SEND)؛ نوايا . setType ("نص / عادي") ؛ نوايا . putExtra (القصد. EXTRA_SUBJECT ، "معرف الجهاز") ؛ نوايا . putExtra (القصد. EXTRA_TEXT ، الجهاز. getText (). toString ())؛ startActivity (الهدف. createChooser (القصد ، "مشاركة معرف الجهاز")) ؛ } آخر {توست. makeText (MainActivity. this. getApplicationContext () ، "لا شيء للمشاركة" ، 0). تبين ()؛ }}} 
  • يبدو أننا انتهينا الآن! التصحيح الكامل يجب أن يبدو مثل هذا. يمكننا الآن إنشاء APK patched وتثبيته:
     $ dxp-make $ adb install patched.apk 
  • دعونا نلقي نظرة على النتيجة:

(شكرًا Lanchon للمساعدة في نموذج التعليمات البرمجية!)

يتمتع Xposed بشعبية كبيرة ولسبب وجيه - فهو يجعل إنشاء ومشاركة وتثبيت التعديلات أسهل بكثير للمطورين والمستخدمين على حد سواء. هناك بعض الاختلافات بين DexPatcher و Xposed والتي قد تجعل البعض يفضل واحدًا على الآخر:

  1. يقوم Xposed بسحره عن طريق ربط الأساليب في وقت التشغيل والسماح للمطورين بعمل شيء قبل أو بعده أو بدلاً من ذلك بأي طريقة. DexPatcher ، من ناحية أخرى ، يعدل كل شيء قبل وقت التشغيل وينتج APK مستقلًا ومعدلاً - لا يزال بإمكانك تشغيل التعليمات البرمجية قبل أو بعد أو بدلاً من الأساليب ، ولديك بالفعل بعض الحرية الإضافية.
  2. إنتاج APK مستقل يعني أنه لا يعتمد على أي إطار خارجي. هذا يعني أيضًا أن الجذر ليس مطلوبًا لتعديل تطبيقات المستخدم.
  3. منذ أن قمت بإنشاء APK جديد باستخدام DexPatcher ، سيتم توقيعه بشكل مختلف. هذا يعني أن المستخدمين لا يمكنهم تلقي تحديثات رسمية من المؤلف الأصلي ، وقد يتسبب ذلك في بعض المشكلات المتعلقة بتطبيقات مثل تطبيقات Google إذا تم التحقق من التوقيعات.
  4. يمكن توزيع وتعديل التعليمات البرمجية المصدرية لكل من الوحدات النمطية و DexPatcher. كما أنها تشترك في العديد من أوجه التشابه إذا كنت على دراية ببعضها البعض.

لقد تحدثنا بما فيه الكفاية عن DexPatcher. حان الوقت لإعطاء فرصة الآن ، لذلك توجه إلى موضوع منتدى DexPatcher لتبدأ على الفور!