Met DexPatcher kunnen ontwikkelaars APK's patchen met behulp van Java. Dit heeft verschillende voordelen en het gebruik van DexPatcher is veel eenvoudiger dan de klassieke Smali-aanpak.
Je hebt waarschijnlijk gewijzigde applicaties gezien of geïnstalleerd, of het nu een gepatchte dialer is voor jouw resolutie of een aangepaste WhatsApp-versie met extra functies. Maar hoe doen ontwikkelaars dat? Vaak is de broncode van de applicaties niet eens beschikbaar, dus hoe werkt het allemaal? We zullen dat eerst zien, dan kijken naar een nieuwe tool die tot doel heeft het proces veel eenvoudiger te maken, en het ten slotte vergelijken met het populaire Xposed-framework om te zien hoe ze verschillen.
Je hebt misschien gehoord over hoe APK's gewoonlijk worden aangepast: ontwikkelaars sluiten zichzelf aan op de matrix, begin alles in Smali te zien en krijg de mogelijkheid om dingen aan te passen met behulp van de kracht van de Bron. Zodra dat is gebeurd, is een telefoontje voldoende om ze eruit te krijgen, waarna ze klaar zijn om de glimmende nieuwe APK's te delen.
Serieuzer… Laten we bij het begin beginnen. Als je niet bekend bent met het modden van Android-applicaties, vraag je je misschien af wat smali is. Ontwikkelaars gebruiken meestal de programmeertaal Java om Android-apps te coderen. Een programma (de compiler) "vertaalt" die code vervolgens naar een ander formaat dat geschikt is voor uw apparaat, wat resulteert in .dex-bestanden in het applicatiepakket (of APK).
Op dat moment heb je geen toegang meer tot de originele broncode (tenzij je de ontwikkelaar bent of de applicatie open source is). Wat u echter wel heeft, is de APK, aangezien deze op uw apparaat is geïnstalleerd. Hieruit kunt u de dex-bestanden halen (meestal klassen.dex) en deze vervolgens proberen terug te vertalen naar een formaat dat u begrijpt. Dat is waar smalli om de hoek komt kijken, als een beter leesbare maar getrouwe vertaling. Je kunt nog een stap verder gaan en het terugvertalen naar Java, hoewel dat proces niet betrouwbaar genoeg is - je krijgt een begrijpelijk resultaat, maar de kans is groot dat u het niet meer andersom kunt vertalen, omdat sommige details verloren gaan onderweg. Met andere woorden: alle wijzigingen die u aanbrengt, zijn voor niets, aangezien u er niet meer een APK van kunt maken om deze op uw apparaat te installeren... althans niet zonder veel moeite.
smali/baksmali is eigenlijk een assembler/dissembler voor het dex-formaat -- dat is wat het letterlijk betekent in het IJslands. Meestal verwijzen we echter naar het formaat dat Smalli begrijpt als we 'Smali' zeggen (zie het als instructies). het definiëren van elk klein detail, zelfs als het niet allemaal nodig is voor ons mensen – het is daarom uitgebreider dan dat Java). Merk ook op dat de bovenstaande uitleg een beetje vereenvoudigd is, maar een goede analogie zou moeten zijn en toch gemakkelijk te begrijpen is.
Wat zou een ontwikkelaar dan moeten doen om een app aan te passen (zonder toegang tot de broncode)? Het proces verloopt min of meer als volgt:
- Haal de APK op (via internet of vanaf het apparaat).
- Gebruik zoiets als apktool om de APK naar Smali te decompileren. (apktool maakt gebruik van smali/baksmali, maar maakt het veel eenvoudiger om APK's te decompileren en opnieuw op te bouwen, en zorgt ook voor het decoderen van bronnen zoals XML-bestanden.)
- Pak klassen.dex uit de APK en gebruik deze vervolgens dex2jar en ten slotte een Java-decompiler om (onvolledige, vaak kapotte, maar meestal begrijpelijke) Java-code te krijgen. (Dit is optioneel, maar kan nuttig zijn omdat Smali veel moeilijker te begrijpen is.)
- Identificeer wat u moet wijzigen.
- Pas het daadwerkelijk aan door de Smali-code rechtstreeks te bewerken.
- U kunt de wijziging ook in Java schrijven, compileren, opnieuw decompileren naar Smali en vervolgens de resulterende Smali-code kopiëren.
- Zodra alles voorbij is, gebruik apktool opnieuw om de APK opnieuw op te bouwen.
- Onderteken de APK (om de identiteit van de auteur te verifiëren; alle pakketten moeten ondertekend zijn) en installeer het uiteindelijk.
Het schrijven van Smali-code is behoorlijk moeilijk en foutgevoelig. Kleinere wijzigingen kunnen worden aangebracht in Smali, maar het toevoegen van nieuwe functies is een grotere uitdaging. Bovendien zul je geen compileerfouten tegenkomen, zodat zelfs typefouten alleen tijdens runtime kunnen worden gedetecteerd. Het uitbreiden en delen van Smali-patches kan ook lastig zijn, omdat verschillen vaak heel specifiek zijn voor een bepaalde APK-versie. Hoewel er enkele hulpmiddelen bestaan om delen van het hierboven uitgelegde proces eenvoudiger te maken (Deugdzame Tien Studio in je opkomt), kan het nog steeds vermoeiend worden.
DexPatcher door XDA Senior Member Lanchon heeft tot doel deze problemen op te lossen door het proces eenvoudiger te maken en ontwikkelaars in staat te stellen de omgang met Smali volledig te vermijden. In plaats daarvan kunnen ontwikkelaars alleen patches in Java schrijven en de rest door DexPatcher laten afhandelen.
Dit heeft als belangrijkste voordeel dat het gemakkelijk leesbare en beheerbare patchbestanden heeft. Het patchen van APK's wordt over het algemeen ook handiger. We zullen straks een volledig voorbeeld zien van het gebruik van DexPatcher, maar hier is eerst een kort overzicht van wat het te bieden heeft:
- Open source.
- Cross-platform: het zou moeten draaien op Linux, Mac en Windows.
- Patchbestanden: wijzigingen die u aanbrengt, worden opgeslagen in Java-patchbestanden die u onafhankelijk kunt delen.
- Java: het is niet Smali.
Je profiteert ook van het voordeel van foutcontrole tijdens het bouwen, zodat bugs al vroeg in de ontwikkelingscyclus opduiken. Het gecompileerde Java biedt de gebruikelijke controle op de compilatietijd (met toegang tot de originele APK-symbolen), en DexPatcher dwingt af compatibiliteit van bron en patch bij het patchen, het verstrekken van nuttige informatie en het geven van waarschuwingen wanneer u iets lijkt te doen legaal maar visachtig.
Daarnaast wordt DexPatcher geleverd met een set hulpscripts (alleen beschikbaar op Linux, hoewel ze ook naar andere platforms kunnen worden overgedragen). Deze zorgen voor het opzetten van de werkruimte, het extraheren van de klassen en bronnen van de doel-APK, het decompileren van de klassen naar Java (de CFR Java-decompiler wordt voor dit laatste gebruikt) en ten slotte het bouwen en ondertekenen van de gepatchte APK als je klaar bent.
Laten we een voorbeeld bekijken (op Linux):
Installeer de DexPatcher-scripts
$# 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.
Configureer de DexPatcher-scripts
Open dxp.config in uw favoriete teksteditor en zorg ervoor dat u de benodigde variabelen aanpast aan uw systeem. U hoeft in plaats daarvan alleen de volgende regel te wijzigen zodat deze naar de installatielocatie van uw Android SDK verwijst:
dxp_android_sdk_dir=(~/android/sdk)
(DexPatcher kiest automatisch de hoogst beschikbare platformversie. Bovendien kunt u ook andere configuratieopties wijzigen, zodat uw eigen versies van sommige tools worden gebruikt in plaats van de gebundelde standaardinstellingen.)
Voor gemakkelijke toegang kunnen we de verzender map naar onze PAD, of zelfs een symbolische link maken tussen de verschillende dxp-* scripts naar een locatie die al in uw PAD, zoals ~/bak:
export PATH=$PWD:$PATH
Wijzig een applicatie
Voor dit voorbeeld gebruiken we een eenvoudige en open source-applicatie. Natuurlijk zou het in dit specifieke geval mogelijk zijn om de broncode rechtstreeks te patchen, maar dat is helemaal niet leuk!
We nemen de applicatie "Get ID" van basilicum2style, een applicatie die u enkele details over uw apparaat laat zien. Ons doel is om de knop 'Kopiëren' voor de 'Apparaat-ID' te wijzigen en in plaats daarvan deze ID te laten delen:
- Laten we eerst de APK downloaden die we gaan aanpassen: Krijg een identiteitsbewijs.
- Decompileer de applicatie.
- Maak de ondertekeningssleutel die we later zullen gebruiken om de APK te ondertekenen.
We kunnen het ook allemaal via de shell doen, met behulp van de helperscripts:
$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".
Je zult daar een paar verschillende mappen zien:
- decoderen: je vindt de bronnen en Smali hier, zoals gedecodeerd door apktool.
- src: Lege map. Dit is waar we onze patchbestanden plaatsen.
- src-cfr: dit is waar cfr de app gedecompileerd (samen met fouten). Een goede plek om naar te kijken om te beslissen wat je moet veranderen (je hebt mogelijk ook bronnen en hun ID's nodig uit de decodeermap hierboven, maar niet voor dit specifieke voorbeeld).
- src-cfr-nodecode: hetzelfde als hierboven, maar bevat alleen lege stubs (geen code, alleen skeletten). Je kunt deze bestanden gebruiken als basis voor je patch, zoals we straks zullen zien.
Zoals we eerder hebben vermeld, willen we de knop 'Kopiëren' van apparaat-ID wijzigen om in plaats daarvan de ID-tekst te delen. Als we rondkijken in de broncode, zullen we merken dat de knop Apparaat-ID kopiëren (apparaat_kopie) bij klikken gebeurtenis wordt afgehandeld door een anonieme klasse in src-cfr/makeinfo/com/getid/MainActivity.java. Hoewel we het hier zouden kunnen aanpassen, is het meestal beter om een alternatieve manier te vinden om dit te doen, aangezien anonieme klassen numerieke namen hebben (HoofdKlasseNaam$SomeNumber, b.v. Hoofdactiviteit$3) die tussen versies onvoorspelbaar kunnen veranderen.
In plaats daarvan zullen we onze eigen klas voor het evenement registreren door de Hoofdactiviteit klas. Laten we eerst de "skelet" -versie kopiëren src-cfr-nocode/makeinfo/com/getid/MainActivity.java naar src/makeinfo/com/getid/MainActivity.java (onthoud dat src is waar onze patch zal leven). (Je kunt desgewenst ook de versie met de volledige code kopiëren, dit is puur een kwestie van smaak.)
We kunnen het nu als volgt bewerken:
- Voeg de benodigde import toe voor de DexPatcher-annotatie:
importlanchon.dexpatcher.annotation.*;
- Voeg een tag toe om aan te geven dat we de klas bewerken. We hebben ook de standaardactie voor leden van de patchklasse ingesteld op NEGEREN, wat betekent dat er tijdens de Java-compilatie naar de leden wordt verwezen door onze code, maar dat ze door DexPatcher worden genegeerd.
@DexEdit(defaultAction=DexAction.IGNORE)publicclassMainActivity
// The reference to ActionBarActivity will be satisfied by symbols
// extracted from the app when we build the patch.
extendsActionBarActivity{
- Voeg bovendien lege lichamen toe aan de constructor en opCreëren methode, evenals alle andere methoden die we van plan zijn te gebruiken (onthoud dat ze genegeerd zullen worden wanneer onze patch daadwerkelijk wordt toegepast - we voegen ze alleen toe zodat we ze hier kunnen raadplegen als dat nodig is). Je kunt ook gewoon de oorspronkelijk trefwoord in plaats daarvan.
- We kunnen de patch nu al bouwen, als je nieuwsgierig bent:
$ dxp-make # Output: `patched.apk`.
Vrij eenvoudig, toch? Maar laten we doorgaan: we zijn nog steeds niet klaar. - Laten we bewerken opCreëren nu zelf aan de slag OnClickListener zodat we de apparaat-ID kunnen delen in plaats van deze naar het klembord te kopiëren:
// 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();}}}
- Het lijkt erop dat we nu klaar zijn! De volledige patch zou er zo uit moeten zien dit. We kunnen nu de gepatchte APK bouwen en installeren:
$ dxp-make$ adb install patched.apk
- Laten we eens kijken naar het resultaat:
(Bedankt aan Lanchon voor het helpen met de voorbeeldcode!)
Xposed is immens populair, en met een goede reden: het maakt het bouwen, delen en installeren van mods veel eenvoudiger voor zowel ontwikkelaars als gebruikers. Er zijn een paar verschillen tussen DexPatcher en Xposed waardoor sommigen de voorkeur geven aan de een boven de ander:
- Xposed doet zijn magie door methoden tijdens runtime te koppelen en ontwikkelaars in staat te stellen iets vóór, na of in plaats van welke methode dan ook te doen. DexPatcher daarentegen past alles vóór de runtime aan en produceert een zelfstandige, aangepaste APK -- code uitvoeren voor, na of in plaats van methoden is nog steeds mogelijk, en je hebt eigenlijk wat extra's vrijheid.
- Het produceren van een zelfstandige APK betekent dat deze niet afhankelijk is van een extern raamwerk. Dit betekent ook dat root niet vereist is voor het wijzigen van gebruikersapps.
- Omdat u met DexPatcher een nieuwe APK hebt gemaakt, wordt deze anders ondertekend. Dit betekent dat gebruikers geen officiële updates van de oorspronkelijke auteur kunnen ontvangen en dat dit problemen kan veroorzaken met apps zoals Google Apps als de handtekeningen worden gecontroleerd.
- De broncode van zowel de modules als de DexPatcher-patches kan eenvoudig worden gedistribueerd en aangepast. Ze delen ook veel overeenkomsten als je er een beetje vertrouwd mee raakt.
We hebben genoeg over DexPatcher gesproken. Het is jouw beurt om het nu eens te proberen, dus ga naar de DexPatcher-forumthread om meteen aan de slag te gaan!