DexPatcher: תיקון APKs של Android באמצעות Java

DexPatcher מאפשר למפתחים לתקן חבילות APK באמצעות Java. יש לזה כמה יתרונות, והשימוש ב-DexPatcher הוא הרבה יותר קל מגישת הסמאלי הקלאסית.

סביר להניח שראיתם או התקנתם יישומים שהשתנו, בין אם זה חייגן מתוקן לרזולוציה שלכם או גרסת WhatsApp מותאמת אישית עם תכונות נוספות. אבל איך מפתחים עושים את זה? רוב הזמן, קוד המקור של היישומים אפילו לא זמין, אז איך כל זה עובד? תחילה נראה את זה, לאחר מכן נסתכל על כלי חדש שמטרתו להפוך את התהליך להרבה יותר קל, ולבסוף נשווה אותו למסגרת הפופולרית Xposed כדי לראות איך הם שונים.

אולי שמעתם על איך בדרך כלל משנים חבילות APK - מפתחים מחברים את עצמם ל- מטריצה, התחל לראות הכל בסמלי, והשיג את היכולת לשנות דברים באמצעות הכוח של ה- מָקוֹר. מספיקה שיחת טלפון כדי להוציא אותם ברגע שזה נעשה, ובשלב זה הם מוכנים לשתף את חבילות ה-APK החדשות והנוצצות.

יותר ברצינות... בואו נתחיל בהתחלה. אם אינך מכיר מודדינג יישומי אנדרואיד, אולי אתה תוהה מה זה smali. מפתחים בדרך כלל משתמשים בשפת התכנות Java כדי לקודד אפליקציות אנדרואיד. לאחר מכן תוכנית (המהדר) "מתרגמת" את הקוד הזה לפורמט אחר המתאים למכשיר שלך, וכתוצאה מכך קבצי .dex הכלולים בחבילת היישום (או APK).

בשלב זה, אינך יכול לגשת יותר לקוד המקור המקורי (אלא אם אתה המפתח או שהאפליקציה היא בקוד פתוח). עם זאת, מה שכן יש לך הוא ה-APK, מכיוון שזה מה שמותקן במכשיר שלך. ממנו, אתה יכול לקבל את קבצי dex (בדרך כלל classes.dex) ולאחר מכן לנסות לתרגם אותם בחזרה לפורמט שאתה יכול להבין. זה המקום שבו smali נכנס לתמונה, כתרגום קריא יותר אך נאמן. אתה יכול ללכת צעד אחד קדימה ולתרגם אותו בחזרה לג'אווה, למרות שהתהליך הזה לא מספיק נאמן -- תקבל תוצאה מובנת, אבל רוב הסיכויים שלא תוכל לתרגם אותה הפוך שוב מכיוון שחלק מהפרטים יאבדו בדרך. במילים אחרות, כל שינוי שתבצע יהיה לשווא מכיוון שלא תוכל להפוך אותו שוב ל-APK כדי להתקין אותו במכשיר שלך... לפחות לא בלי הרבה מאמץ.

smali/baksmali הוא למעשה אסמבלר/דיסמבלר לפורמט dex -- זה מה שזה אומר באופן מילולי באיסלנדית. עם זאת, בדרך כלל אנו מתייחסים לפורמט שמבין בו-smali כאשר אנו אומרים 'סמאלי' (חשבו על זה כעל הוראות להגדיר כל פרט קטן, גם אם לא כל זה נחוץ לנו, בני האדם -- לכן זה יותר מילולי מאשר Java). כמו כן, שים לב שההסבר לעיל הוא מעט מפושט, אבל צריך להיות אנלוגיה קרובה ועדיין קל להבנה.

מה יצטרך מפתח לעשות כדי לשנות אפליקציה (ללא גישה למקור), אם כך? התהליך הוא פחות או יותר כדלקמן:

  1. קבל את ה-APK (מהאינטרנט או מהמכשיר).
  2. השתמש במשהו כמו apktool כדי לפרק את ה-APK ל-Smali. (apktool עושה שימוש ב-smali/baksmali, אבל מקל הרבה יותר על פירוק ובנייה מחדש של APKs, וגם דואג לפענוח משאבים כמו קובצי XML.)
  3. חלץ classes.dex מה-APK, ולאחר מכן השתמש dex2jar ולבסוף מאפר ג'אווה כדי לקבל קוד ג'אווה (לא שלם, לרוב שבור, אבל בעיקר מובן). (זה אופציונלי, אבל יכול להיות מועיל שכן Smali הרבה יותר קשה להבנה.)
  4. זהה מה לשנות.
  5. שנה אותו למעשה על ידי עריכת קוד הסמאלי ישירות.
  6. לחלופין, כתוב את השינוי ב-Java, קומפיל אותו, הפעל אותו שוב ל-Smalali, ואז העתק את קוד ה-Smalali שהתקבל.
  7. ברגע שהכל נגמר, השתמש apktool שוב כדי לבנות מחדש את ה-APK.
  8. חתום על ה-APK (כדי לאמת את זיהוי המחבר; יש לחתום על כל החבילות) ולבסוף להתקין אותה.

כתיבת קוד Smali היא די קשה ונוטה לשגיאות. ניתן לבצע שינויים קטנים יותר ב-Smali, אך הוספת תכונות חדשות עם זה היא מאתגרת יותר. בנוסף, לא יהיו לך שגיאות זמן קומפילציה, כך שאפילו שגיאות הקלדה עשויות להתגלות רק בזמן ריצה. הרחבה ושיתוף של תיקוני Smali יכולים להיות גם בעיתיים, מכיוון שההבדלים נוטים להיות מאוד ספציפיים לגרסת APK מסוימת. למרות שקיימים כלים מסוימים כדי להקל על חלקים מהתהליך שהוסבר לעיל (Virtuous Ten Studio עולה על הדעת), זה עדיין יכול להיות מעייף.

DexPatcher מאת חבר בכיר ב-XDA לנצ'ון שואפת לתקן את הבעיות הללו, על ידי הפיכת התהליך לפשוט יותר ולאפשר למפתחים להימנע לחלוטין מהתמודדות עם Smali. במקום זאת, מפתחים יכולים לכתוב טלאים בג'אווה בלבד ולאפשר ל-DexPatcher לטפל בכל השאר.

יש לזה היתרון העיקרי של קבצי תיקון הניתנים לקריאה וניתנים לניהול בקלות. תיקון APKs גם הופך נוח יותר באופן כללי. עוד מעט נראה דוגמה מלאה כיצד להשתמש ב- DexPatcher, אך הנה סקירה מהירה של מה שהוא מציע תחילה:

  • קוד פתוח.
  • חוצה פלטפורמות: זה אמור לפעול ב-Linux, Mac ו-Windows.
  • קבצי תיקון: שינויים שאתה מבצע כלולים בקבצי תיקון Java שאתה יכול לשתף באופן עצמאי.
  • ג'אווה: זה לא סמאלי.

אתה גם מרוויח את היתרון של בדיקת שגיאות בזמן הבנייה, כך שבאגים מופיעים מוקדם במחזור הפיתוח. ה-Java קומפילד מספק את בדיקת זמן ההידור הרגיל שלו (עם גישה לסמלי ה-APK המקוריים), ו-DexPatcher אוכף תאימות של מקור ותיקון בעת ​​תיקון, מתן מידע מועיל ומתן אזהרות כאשר נראה שאתה עושה משהו חוקי אבל דגי.

בנוסף לכך, DexPatcher מגיע עם סט של תסריטי עוזר (זמין רק בלינוקס, אם כי ניתן להעביר אותם גם לפלטפורמות אחרות). אלה דואגים להגדיר את סביבת העבודה, לחלץ את המחלקות והמשאבים של ה-APK היעד, לפרק את המחלקות ל-Java (ה Decompiler Java CFR משמש עבור האחרון), ולבסוף בניית וחתימה על ה-APK המתוקן לאחר שתסיים.

בואו נסתכל על דוגמה (בלינוקס):

התקן את הסקריפטים של DexPatcher

$# Make a directory where we can test stuff out and enter it. 

$ mkdir xda-test

$cd xda-test

$ git clone https://github.com/Lanchon/DexPatcher-scripts.git dexpatcher # Clone the DexPatcher helper scripts repo.

$cd dexpatcher

$ chmod +x dxp-* # Not necessary, but for clarity: we need to make sure the files we'll call later are executable.

הגדר את הסקריפטים של DexPatcher

לִפְתוֹחַ dxp.config בעורך הטקסט המועדף עליך והקפד לשנות את המשתנים הדרושים כך שיתאימו למערכת שלך. אתה רק צריך לשנות את השורה הבאה כדי להצביע על מיקום ההתקנה של Android SDK שלך במקום זאת:

dxp_android_sdk_dir=(~/android/sdk)

(DexPatcher יבחר אוטומטית את גרסת הפלטפורמה הגבוהה ביותר שקיימת. בנוסף, אתה יכול גם לשנות אפשרויות תצורה אחרות כך שישתמש בגרסאות משלך של כמה כלים במקום ברירות המחדל המצורפות.)

כדי להקל על הגישה, אנו יכולים להוסיף את dexpatcher המדריך שלנו נָתִיב, או אפילו קישור בין השונה dxp-* סקריפטים למיקום שכבר נמצא אצלך נָתִיב, כמו ~/bin:

export PATH=$PWD:$PATH

שנה אפליקציה

עבור דוגמה זו, נשתמש ביישום פשוט וקוד פתוח. כמובן, תיקון ישיר של קוד המקור יהיה אפשרי במקרה הספציפי הזה, אבל זה לא כיף בכלל!

ניקח את האפליקציה "קבל מזהה" מאת basil2style, אפליקציה שמראה לך כמה פרטים על המכשיר שלך. המטרה שלנו היא לשנות את כפתור "העתק" עבור "מזהה מכשיר" ולשתף אותו במזהה זה במקום זאת:

  • ראשית, הבה נוריד את ה-APK שאנו הולכים לשנות: קבל תעודה מזהה.
  • בצע דה-קומפילציה של האפליקציה.
  • צור את מפתח החתימה שבו נשתמש מאוחר יותר כדי לחתום על ה-APK.

אנחנו יכולים גם לעשות הכל דרך המעטפת, באמצעות סקריפטים של עוזר:

$cd dexpatcher # Go to our working directory. 

$ curl -O https://f-droid.org/repo/makeinfo.com.getid_1.apk # Download the APK.

$ dxp-setup-for-apk makeinfo.com.getid_1.apk # Unpack and decompile the APK.

$cd makeinfo.com.getid_1 # Go to the newly created directory where everything is unpacked/decompiled to.

$ dxp-create-keystore # Create the APK signing key. Press 6 times (or fill out the info), then "yes".

תבחין בכמה ספריות שונות שם:

  • לְפַעֲנֵחַ: תמצא את המשאבים ואת Smali כאן, כפי שפוענח על ידי apktool.
  • src: ספרייה ריקה. זה המקום שבו נמקם את קבצי התיקון שלנו.
  • src-cfr: זה איפה cfr פירק את האפליקציה (יחד עם שגיאות). מקום טוב להסתכל בו כדי להחליט מה לשנות (ייתכן שתזדקק גם למשאבים ומזהותיהם מספריית הפענוח שלמעלה, אבל לא עבור הדוגמה הספציפית הזו).
  • src-cfr-nodecode: זהה לעיל, אבל מכיל רק בדלי ריקים (ללא קוד, רק שלדים). אתה יכול להשתמש בקבצים האלה כבסיס לתיקון שלך כפי שנראה עוד מעט.

כפי שציינו בעבר, אנו רוצים לשנות את כפתור מזהה ההתקן "העתק" כדי לשתף את טקסט המזהה במקום זאת. אם נסתכל סביב קוד המקור, נבחין שכפתור העתקת מזהה מכשיר (device_copy) בלחיצה האירוע מטופל בכיתה אנונימית ב src-cfr/makeinfo/com/getid/MainActivity.java. אמנם נוכל לשנות את זה כאן, אבל בדרך כלל עדיף למצוא דרך חלופית לעשות זאת מכיוון שלמחלקות אנונימיות יש שמות מספריים (MainClassName$SomeNumber, למשל MainActivity$3) שעלול להשתנות באופן בלתי צפוי בין גרסאות.

במקום זאת, נרשום כיתה משלנו לאירוע על ידי שינוי ה פעילות עיקרית מעמד. ראשית, בואו נעתיק את גרסת ה"שלד" מ src-cfr-nocode/makeinfo/com/getid/MainActivity.java ל src/makeinfo/com/getid/MainActivity.java (זכור את זה src שם יחיה התיקון שלנו). (אתה יכול גם להעתיק את הגרסה עם הקוד המלא אם אתה מעדיף, זה עניין של טעם בלבד.)

כעת נוכל לערוך אותו באופן הבא:

  • הוסף את הייבוא ​​הדרוש עבור ההערה של DexPatcher:
importlanchon.dexpatcher.annotation.*;
  • הוסף תג כדי לציין שאנחנו עורכים את הכיתה. אנחנו גם מגדירים את פעולת ברירת המחדל עבור חברי מחלקת התיקון ל להתעלם, מה שאומר שהחברים נמצאים שם כדי להתייחס אליהם בקוד שלנו במהלך הידור של Java, אך DexPatcher יתעלם מהם.
@DexEdit(defaultAction=DexAction.IGNORE)

publicclassMainActivity

// The reference to ActionBarActivity will be satisfied by symbols

// extracted from the app when we build the patch.

extendsActionBarActivity{

  • בנוסף, הוסף גופים ריקים לבנאי ו onCreate השיטה, כמו גם כל השיטות האחרות שאנו מתכננים להשתמש בהן (זכור שהן יתעלמו כאשר התיקון שלנו יוחל בפועל -- אנחנו רק מוסיפים אותן כדי שנוכל להתייחס אליהן כאן אם נצטרך). אתה יכול גם פשוט להוסיף את יָלִיד מילת מפתח במקום זאת.
  • אנחנו כבר יכולים לבנות את התיקון בשלב זה, אם אתה סקרן:
    $ dxp-make # Output: `patched.apk`.
    די פשוט, נכון? בוא נמשיך, אם כי - עדיין לא סיימנו.
  • בואו לערוך onCreate עכשיו לצאת לדרך משלו OnClickListener כדי שנוכל לשתף את מזהה המכשיר במקום להעתיק אותו ללוח:
    // Rename the target method so that we can still call it (the original)// if needed.@DexEdit(target="onCreate")protectedvoidsource_onCreate(Bundlevar1){}// Add our new custom method.@Override@DexAddprotectedvoidonCreate(Bundlevar1){// Call the original method:source_onCreate(var1);// Replace the text and handler:device_copy.setText("Share");device_copy.setOnClickListener(newDeviceCopyOnClick());}// Note that we don't use an anonymous class to avoid nameclashing with// MainActivity$1, which already exists.// We also could've defined a nested MainActivity.Patch class and used// an anonymous class in MainActivity.Patch.onCreate(), and then called// MainActivity.Patch.onCreate() from MainActivity.onCreate().@DexAddclassDeviceCopyOnClickimplementsView.OnClickListener{@OverridepublicvoidonClick(Viewobject){if(MainActivity.this.val){Intentintent=newIntent(Intent.ACTION_SEND);intent.setType("text/plain");intent.putExtra(Intent.EXTRA_SUBJECT,"Device ID");intent.putExtra(Intent.EXTRA_TEXT,device.getText().toString());startActivity(Intent.createChooser(intent,"Share Device ID"));}else{Toast.makeText(MainActivity.this.getApplicationContext(),"Nothing to Share",0).show();}}}
  • נראה שסיימנו עכשיו! התיקון המלא אמור להיראות כך זֶה. כעת נוכל לבנות את ה-APK המתוקן ולהתקין אותו:
    $ dxp-make$ adb install patched.apk
  • בואו נסתכל על התוצאה:

(תודה ל-Lanchon על העזרה בקוד לדוגמה!)

Xposed פופולרי מאוד, ומסיבה טובה - הוא הופך את הבנייה, השיתוף וההתקנה של אופנים להרבה יותר פשוטים עבור מפתחים ומשתמשים כאחד. ישנם כמה הבדלים בין DexPatcher ל-Xposed שעשויים לגרום לחלק להעדיף אחד על פני השני:

  1. Xposed עושה את הקסם שלו על ידי חיבור שיטות בזמן ריצה ומאפשר למפתחים לעשות משהו לפני, אחרי או במקום כל שיטה. DexPatcher, לעומת זאת, משנה הכל לפני זמן הריצה ומייצר APK עצמאי ומשונה -- הפעלת קוד לפני, אחרי או במקום מתודות עדיין אפשרית, ולמעשה יש לך עוד קצת חוֹפֶשׁ.
  2. הפקת APK עצמאי פירושה שהוא לא תלוי בשום מסגרת חיצונית. זה גם אומר ש-root אינו נדרש לשינוי אפליקציות משתמש.
  3. מכיוון שיצרת APK חדש עם DexPatcher, הוא ייחתם אחרת. המשמעות היא שמשתמשים לא יכולים לקבל עדכונים רשמיים מהמחבר המקורי, ועלולים לגרום לבעיות מסוימות עם אפליקציות כמו Google Apps אם החתימות מסומנות.
  4. ניתן להפיץ ולשנות בקלות את קוד המקור של שני המודולים וגם של תיקוני DexPatcher. הם גם חולקים קווי דמיון רבים אם אתה קצת מכיר את כל אחד מהם.

דיברנו מספיק על DexPatcher. זה תורך לתת לזה הזדמנות עכשיו, אז פנה אל שרשור הפורום של DexPatcher להתחיל מיד!