DexPatcher: виправлення APK для Android за допомогою Java

DexPatcher дозволяє розробникам виправляти APK за допомогою Java. Це має кілька переваг, і використання DexPatcher набагато легше, ніж класичний підхід Smali.

Ви, напевно, бачили або встановлювали модифіковані програми, будь то виправлений номеронабирач для вашої роздільної здатності або спеціальна версія WhatsApp із додатковими функціями. Але як це роблять розробники? У більшості випадків вихідний код програми навіть недоступний, тож як це все працює? Спочатку ми побачимо це, потім розглянемо новий інструмент, який має на меті значно спростити процес, і, нарешті, порівняємо його з популярним фреймворком Xposed, щоб побачити, чим вони відрізняються.

Можливо, ви чули про те, як зазвичай модифікуються файли APK – розробники підключаються до них матриці, почніть бачити все в Smali та отримайте можливість змінювати речі, використовуючи силу Джерело. Коли це буде зроблено, достатньо телефонного дзвінка, щоб вони звільнилися, і після цього вони готові поділитися новими блискучими файлами APK.

Якщо серйозніше... Почнемо спочатку. Якщо ви не знайомі з модифікацією програм для Android, можливо, вам цікаво, що таке smali. Розробники зазвичай використовують мову програмування Java для кодування програм Android. Потім програма (компілятор) «перекладає» цей код в інший формат, придатний для вашого пристрою, у результаті чого у пакеті програми (або APK) з’являються файли .dex.

У цей момент ви більше не можете отримати доступ до оригінального вихідного коду (якщо тільки ви не розробник або програма з відкритим кодом). Однак те, що у вас є, це 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 спадає на думку), це все ще може стати втомливим.

DexPatcher від XDA Senior Member Ланшон має на меті вирішити ці проблеми, спростивши процес і дозволивши розробникам повністю уникнути роботи зі Smali. Натомість розробники можуть писати патчі лише на Java, а DexPatcher обробляє все інше.

Це має головну перевагу у тому, що ви легко читаєте та керуєте файлами латок. Виправлення APK також стає зручнішим загалом. Невдовзі ми побачимо повний приклад використання DexPatcher, але спочатку наведемо короткий огляд того, що він пропонує:

  • Відкрите джерело.
  • Кросплатформенність: вона повинна працювати на Linux, Mac і Windows.
  • Файли виправлень: внесені вами зміни містяться у файлах виправлень Java, якими ви можете ділитися незалежно.
  • Java: це не Smali.

Ви також отримуєте перевагу перевірки помилок під час збирання, тому помилки виявляються на ранніх стадіях циклу розробки. Скомпільована Java забезпечує звичайну перевірку часу компіляції (з доступом до оригінальних символів APK), а DexPatcher забезпечує сумісність джерела та патча під час патча, надання корисної інформації та попередження, коли ви, здається, щось робите легально, але сумнівно.

На додаток до цього, DexPatcher поставляється з набором допоміжні скрипти (доступні лише для Linux, хоча їх також можна перенести на інші платформи). Вони піклуються про налаштування робочого простору, вилучення класів і ресурсів цільового APK, декомпіляцію класів у Java ( Декомпілятор CFR Java використовується для останнього), і, нарешті, створення та підписання виправленого APK, коли ви закінчите.

Давайте розглянемо приклад (для Linux):

Встановіть сценарії 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 автоматично вибере найвищу доступну версію платформи. Крім того, ви також можете змінити інші параметри конфігурації, щоб вони використовували ваші власні версії деяких інструментів замість стандартних у комплекті.)

Для зручності доступу ми можемо додати експедитор каталог до нашого ШЛЯХабо навіть символічне посилання на інше dxp-* сценарії до розташування, яке вже є у вашому ШЛЯХ, як от ~/bin:

export PATH=$PWD:$PATH

Змінити програму

Для цього прикладу ми використаємо просту програму з відкритим кодом. Звичайно, у цьому конкретному випадку було б можливо виправити вихідний код безпосередньо, але це зовсім не весело!

Ми візьмемо програму «Get ID» від 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) onClick Подія обробляється анонімним класом src-cfr/makeinfo/com/getid/MainActivity.java. Хоча ми могли б змінити це тут, зазвичай краще знайти альтернативний спосіб зробити це, оскільки анонімні класи мають числові імена (MainClassName$SomeNumber, напр. Основна діяльність$3), які можуть непередбачувано змінюватися між версіями.

Натомість ми зареєструємо власний клас для події, змінивши Основна діяльність клас. Спочатку скопіюємо «скелетну» версію з src-cfr-nocode/makeinfo/com/getid/MainActivity.java до src/makeinfo/com/getid/MainActivity.java (пам'ятайте, що src де буде жити наш патч). (Ви також можете скопіювати версію з повним кодом, якщо бажаєте, це суто справа смаку.)

Тепер ми можемо редагувати його наступним чином:

  • Додайте необхідний імпорт для анотації DexPatcher:
importlanchon.dexpatcher.annotation.*;
  • Додайте тег, щоб вказати, що ми редагуємо клас. Ми також встановили дію за замовчуванням для членів класу patch ІГНОРУЙТЕ, що означає, що члени є для того, щоб на них посилався наш код під час компіляції 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
  • Давайте подивимося на результат:

(Дякую Ланчону за допомогу із зразком коду!)

Xposed надзвичайно популярний і з поважної причини — він значно спрощує створення, обмін і встановлення модів як для розробників, так і для користувачів. Існує кілька відмінностей між DexPatcher і Xposed, через які деякі можуть віддати перевагу одному над іншим:

  1. Xposed робить свою магію, підключаючи методи під час виконання та дозволяючи розробникам робити щось до, після або замість будь-якого методу. DexPatcher, з іншого боку, змінює все перед виконанням і створює окремий модифікований APK -- запуск коду до, після або замість методів все ще можливий, і ви фактично маєте деякі додаткові свобода.
  2. Створення окремого файлу APK означає, що він не залежить від жодної зовнішньої структури. Це також означає, що root не потрібен для зміни програм користувача.
  3. Оскільки ви створили новий файл .apk за допомогою DexPatcher, він матиме інший підпис. Це означає, що користувачі не можуть отримувати офіційні оновлення від оригінального автора, і можуть спричинити певні проблеми з такими програмами, як Google Apps, якщо перевіряються підписи.
  4. Вихідний код як модулів, так і патчів DexPatcher можна легко поширювати та змінювати. Вони також мають багато спільного, якщо ви трохи ознайомитеся з кожним.

Ми вже достатньо говорили про DexPatcher. Тепер ваша черга спробувати, тож переходьте до Тема форуму DexPatcher щоб почати відразу!