DexPatcher umożliwia programistom łatanie plików APK przy użyciu języka Java. Ma to kilka zalet, a używanie DexPatchera jest znacznie łatwiejsze niż klasyczne podejście Smali.
Prawdopodobnie widziałeś lub instalowałeś zmodyfikowane aplikacje, czy to poprawiony dialer dla Twojej rozdzielczości, czy niestandardową wersję WhatsApp z dodatkowymi funkcjami. Jak jednak deweloperzy to robią? W większości przypadków kod źródłowy aplikacji nie jest nawet dostępny, więc jak to wszystko działa? Najpierw to zobaczymy, potem przyjrzymy się nowemu narzędziu, które ma znacznie ułatwić ten proces, a na koniec porównamy je z popularnym frameworkiem Xposed, aby zobaczyć, czym się różnią.
Być może słyszałeś o tym, jak zwykle modyfikowane są pliki APK — programiści podłączają się do matrix, zacznij widzieć wszystko w Smali i zdobądź możliwość modyfikowania rzeczy za pomocą mocy Źródło. Gdy już to zrobią, wystarczy telefon i będą mogli udostępnić nowe, błyszczące pliki APK.
A tak poważniej… Zacznijmy od początku. Jeśli nie jesteś zaznajomiony z modowaniem aplikacji na Androida, możesz zastanawiać się, czym jest smali. Programiści zwykle używają języka programowania Java do kodowania aplikacji na Androida. Następnie program (kompilator) „tłumaczy” ten kod na inny format odpowiedni dla Twojego urządzenia, w wyniku czego powstają pliki .dex zawarte w pakiecie aplikacji (lub APK).
W tym momencie nie możesz już uzyskać dostępu do oryginalnego kodu źródłowego (chyba że jesteś programistą lub aplikacja jest open source). Jednak to, co masz, to plik APK, ponieważ jest on zainstalowany na Twoim urządzeniu. Można z niego pobrać pliki dex (zazwyczajclasss.dex), a następnie spróbować przetłumaczyć je z powrotem na format, który można zrozumieć. W tym miejscu pojawia się małe, jako bardziej czytelne, ale wierne tłumaczenie. Możesz pójść o krok dalej i przetłumaczyć to z powrotem na Javę, chociaż ten proces nie jest wystarczająco wierny — otrzymasz plik zrozumiały wynik, ale istnieje prawdopodobieństwo, że nie będziesz w stanie przetłumaczyć go ponownie na odwrót, ponieważ niektóre szczegóły zostaną utracone po drodze. Innymi słowy, wszelkie modyfikacje, które możesz wprowadzić, będą na nic, ponieważ nie będziesz mógł ponownie zamienić go w plik APK, aby zainstalować go na swoim urządzeniu… przynajmniej nie bez dużego wysiłku.
smali/baksmali jest w rzeczywistości asemblerem/dissemblerem dla formatu dex - to dosłownie oznacza w języku islandzkim. Jednakże, gdy mówimy „Smali”, zwykle odnosimy się do formatu, który rozumie mały (potraktuj to jako instrukcję definiowanie każdego najdrobniejszego szczegółu, nawet jeśli nie jest on wszystkim potrzebny nam, ludziom - jest zatem bardziej szczegółowy niż Jawa). Należy również pamiętać, że powyższe wyjaśnienie jest nieco uproszczone, ale powinno stanowić bliską analogię, a jednocześnie być łatwe do zrozumienia.
Co zatem musiałby zrobić programista, aby zmodyfikować aplikację (bez dostępu do źródła)? Proces wygląda mniej więcej następująco:
- Pobierz plik APK (z Internetu lub z urządzenia).
- Użyj czegoś takiego apktool zdekompilować APK do Smali. (apktool korzysta z smali/baksmali, ale znacznie ułatwia dekompilację i odbudowę plików APK, a także zajmuje się dekodowaniem zasobów, takich jak pliki XML.)
- Wyodrębnijclasses.dex z pliku APK, a następnie użyj dex2jar i wreszcie dekompilator Java, aby uzyskać (niekompletny, często uszkodzony, ale w większości zrozumiały) kod Java. (Jest to opcjonalne, ale może być pomocne, ponieważ Smali jest znacznie trudniejszy do zrozumienia.)
- Określ, co należy zmodyfikować.
- Właściwie zmodyfikuj go, edytując bezpośrednio kod Smali.
- Alternatywnie, napisz modyfikację w Javie, skompiluj ją, dekompiluj ponownie do Smali, a następnie skopiuj powstały kod Smali.
- Gdy wszystko się skończy, użyj apktool ponownie, aby odbudować plik APK.
- Podpisz plik APK (w celu sprawdzenia tożsamości autora; wszystkie pakiety muszą być podpisane) i na koniec go zainstalować.
Pisanie kodu Smali jest dość trudne i podatne na błędy. Mniejsze zmiany można wprowadzić w Smali, ale dodanie nowych funkcji jest większym wyzwaniem. Dodatkowo nie będziesz mieć żadnych błędów w czasie kompilacji, więc nawet literówki mogą zostać wykryte dopiero w czasie wykonywania. Rozszerzanie i udostępnianie poprawek Smali może być również kłopotliwe, ponieważ różnice są zwykle bardzo specyficzne dla konkretnej wersji APK. Chociaż istnieją pewne narzędzia ułatwiające część opisanego powyżej procesu (Studio Cnotliwa Dziesięć przychodzi mi na myśl), to wciąż może być męczące.
DexPatcher autorstwa starszego członka XDA Lanchona ma na celu rozwiązanie tych problemów, upraszczając proces i umożliwiając programistom całkowite uniknięcie kontaktu ze Smali. Zamiast tego programiści mogą pisać łatki w samej Javie i pozwolić DexPatcherowi zająć się całą resztą.
Ma to główną zaletę polegającą na łatwym do odczytania i zarządzaniu plikami poprawek. Łatanie plików APK staje się ogólnie wygodniejsze. Za chwilę zobaczymy pełny przykład korzystania z DexPatchera, ale najpierw krótki przegląd tego, co oferuje:
- Otwarte źródło.
- Wieloplatformowość: powinna działać na systemach Linux, Mac i Windows.
- Pliki poprawek: wprowadzone przez Ciebie modyfikacje są zawarte w plikach poprawek Java, które możesz niezależnie udostępniać.
- Java: to nie Smali.
Zyskujesz także przewagę polegającą na sprawdzaniu błędów w czasie kompilacji, dzięki czemu błędy pojawiają się na początku cyklu rozwojowego. Kompilacja Java zapewnia zwykłe sprawdzanie czasu kompilacji (z dostępem do oryginalnych symboli APK), a DexPatcher wymusza zgodność źródła i łatki podczas łatania, dostarczanie przydatnych informacji i ostrzeganie, gdy wydaje się, że coś robisz legalne, ale podejrzane.
Oprócz tego DexPatcher jest wyposażony w zestaw skrypty pomocnicze (dostępne tylko w systemie Linux, chociaż można je również przenieść na inne platformy). Zajmują się one konfiguracją obszaru roboczego, wyodrębnianiem klas i zasobów docelowego pliku APK, dekompilacją klas do języka Java (tzw. Dekompilator Java CFR jest używany w tym drugim przypadku), a na koniec zbuduj i podpisz poprawiony plik APK, gdy już skończysz.
Spójrzmy na przykład (w systemie Linux):
Zainstaluj skrypty 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.
Skonfiguruj skrypty DexPatcher
otwarty dxp.config w swoim ulubionym edytorze tekstu i pamiętaj o zmianie niezbędnych zmiennych, aby pasowały do Twojego systemu. Wystarczy zmienić następujący wiersz, aby zamiast tego wskazywał lokalizację instalacji zestawu SDK systemu Android:
dxp_android_sdk_dir=(~/android/sdk)
(DexPatcher automatycznie wybierze najwyższą dostępną wersję platformy. Dodatkowo możesz także zmodyfikować inne opcje konfiguracji, aby korzystały z własnych wersji niektórych narzędzi zamiast dołączonych ustawień domyślnych.)
Dla ułatwienia dostępu możemy dodać dekspatcher katalog do naszego ŚCIEŻKA, lub nawet dowiązanie symboliczne różne dxp-* skrypty do lokalizacji, która już znajduje się w pliku ŚCIEŻKA, Jak na przykład ~/kosz:
export PATH=$PWD:$PATH
Zmodyfikuj aplikację
W tym przykładzie użyjemy prostej aplikacji typu open source. Oczywiście w tym konkretnym przypadku możliwe byłoby bezpośrednie łatanie kodu źródłowego, ale to wcale nie jest zabawne!
Weźmiemy aplikację „Get ID” firmy basil2style, aplikacji, która pokazuje niektóre szczegóły na temat Twojego urządzenia. Naszym celem jest zmodyfikowanie przycisku „Kopiuj” dla „Identyfikatora urządzenia” i udostępnienie mu zamiast tego tego identyfikatora:
- Najpierw pobierzmy plik APK, który będziemy modyfikować: Zdobądź identyfikator.
- Zdekompiluj aplikację.
- Utwórz klucz podpisywania, którego będziemy później używać do podpisania pliku APK.
Możemy to wszystko zrobić także poprzez powłokę, korzystając ze skryptów pomocniczych:
$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".
Zauważysz tam kilka różnych katalogów:
- rozszyfrować: znajdziesz tutaj zasoby i Smali, zdekodowane przez apktool.
- źródło: Pusty katalog. Tutaj będziemy umieszczać nasze pliki poprawek.
- src-cfr: to jest gdzie por zdekompilowałem aplikację (wraz z błędami). Dobre miejsce do sprawdzenia, aby zdecydować, co zmienić (możesz także potrzebować zasobów i ich identyfikatorów z powyższego katalogu dekodowania, ale nie w tym konkretnym przykładzie).
- kod węzła src-cfr: tak samo jak powyżej, ale zawiera tylko puste kody pośredniczące (bez kodu, tylko szkielety). Możesz użyć tych plików jako podstawy swojej łatki, jak zobaczymy za chwilę.
Jak wspomnieliśmy wcześniej, chcemy zmienić przycisk „Kopiuj” identyfikator urządzenia, aby zamiast tego udostępniał tekst identyfikatora. Jeśli rozejrzymy się po kodzie źródłowym, zauważymy, że przycisk Kopiuj identyfikator urządzenia (kopia_urządzenia) na kliknięcie zdarzenie jest obsługiwane przez anonimową klasę in src-cfr/makeinfo/com/getid/MainActivity.java. Chociaż moglibyśmy to tutaj zmodyfikować, zwykle lepiej jest znaleźć alternatywny sposób, aby to zrobić, ponieważ klasy anonimowe mają nazwy numeryczne (NazwaKlasy Głównej$JakiśNumber, np. Główna aktywność 3 USD), które mogą zmieniać się w nieprzewidywalny sposób pomiędzy wersjami.
Zamiast tego zarejestrujemy własną klasę na wydarzenie, modyfikując plik Główna aktywność klasa. Najpierw skopiujmy wersję „szkieletową” z src-cfr-nocode/makeinfo/com/getid/MainActivity.java Do src/makeinfo/com/getid/MainActivity.java (Zapamietaj to źródło tam będzie znajdować się nasza łatka). (Jeśli wolisz, możesz także skopiować wersję z pełnym kodem, jest to wyłącznie kwestia gustu.)
Możemy go teraz edytować w następujący sposób:
- Dodaj niezbędny import dla adnotacji DexPatcher:
importlanchon.dexpatcher.annotation.*;
- Dodaj znacznik wskazujący, że edytujemy zajęcia. Ustawiliśmy także domyślną akcję dla członków klasy patch na IGNOROWAĆ, co oznacza, że nasz kod będzie się odwoływał do tych elementów podczas kompilacji Java, ale zostaną one zignorowane przez 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{
- Dodatkowo dodaj puste ciała do konstruktora i na Utwórz metody, a także wszystkich innych metod, które planujemy zastosować (pamiętaj, że zostaną one zignorowane, gdy nasza łatka zostanie faktycznie zastosowana - po prostu je dodajemy, abyśmy mogli się do nich tutaj odwołać, jeśli zajdzie taka potrzeba). Możesz także po prostu dodać rodzinny zamiast tego słowo kluczowe.
- Jeśli jesteś ciekawy, możemy już zbudować łatkę:
$ dxp-make # Output: `patched.apk`.
Całkiem proste, prawda? Kontynuujmy jednak – to jeszcze nie koniec. - Edytujmy na Utwórz teraz wyznaczyć własne Słuchacz OnClick abyśmy mogli udostępnić identyfikator urządzenia zamiast kopiować go do schowka:
// 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();}}}
- Wygląda na to, że już skończyliśmy! Pełna łatka powinna wyglądać Ten. Możemy teraz zbudować poprawiony plik APK i zainstalować go:
$ dxp-make$ adb install patched.apk
- Rzućmy okiem na wynik:
(Dzięki Lanchon za pomoc przy przykładowym kodzie!)
Xposed jest niezwykle popularny i nie bez powodu — sprawia, że tworzenie, udostępnianie i instalowanie modów jest znacznie prostsze zarówno dla programistów, jak i użytkowników. Istnieje kilka różnic między DexPatcher i Xposed, które mogą sprawić, że niektórzy będą preferować jeden od drugiego:
- Xposed robi swoją magię, przechwytując metody w czasie wykonywania i umożliwiając programistom zrobienie czegoś przed, po lub zamiast dowolnej metody. Z drugiej strony DexPatcher modyfikuje wszystko przed uruchomieniem i tworzy samodzielny, zmodyfikowany plik APK -- uruchamianie kodu przed, po lub zamiast metod jest nadal możliwe, a tak naprawdę masz trochę więcej wolność.
- Utworzenie samodzielnego pakietu APK oznacza, że nie jest on zależny od żadnego zewnętrznego środowiska. Oznacza to również, że root nie jest wymagany do modyfikowania aplikacji użytkownika.
- Ponieważ utworzyłeś nowy plik APK za pomocą DexPatcher, będzie on podpisany inaczej. Oznacza to, że użytkownicy nie mogą otrzymywać oficjalnych aktualizacji od pierwotnego autora i mogą powodować pewne problemy z aplikacjami takimi jak Google Apps, jeśli zostaną sprawdzone podpisy.
- Kod źródłowy zarówno modułów, jak i łatek DexPatcher można łatwo dystrybuować i modyfikować. Mają także wiele podobieństw, jeśli trochę się z nimi zapoznasz.
Wystarczająco dużo rozmawialiśmy o DexPatcher. Teraz Twoja kolej, aby spróbować, więc udaj się do Wątek na forum DexPatcher aby zacząć od razu!