DexPatcher: Patcha Android APKs med Java

DexPatcher låter utvecklare patcha APK-filer med Java. Detta har flera fördelar, och att använda DexPatcher är mycket enklare än den klassiska Smali-metoden.

Du har förmodligen sett eller installerat modifierade applikationer, vare sig det är en patchad dialer för din upplösning eller en anpassad WhatsApp-version med extra funktioner. Men hur gör utvecklare det? Oftast är applikationernas källkod inte ens tillgänglig, så hur fungerar det hela? Vi ska se det först, sedan ta en titt på ett nytt verktyg som syftar till att göra processen mycket enklare, och slutligen jämföra det med det populära Xposed-ramverket för att se hur de skiljer sig åt.

Du kanske har hört talas om hur APK-filer vanligtvis modifieras - utvecklare ansluter sig själva till matris, börja se allt på smali och få möjligheten att modifiera saker med hjälp av kraften i Källa. Ett telefonsamtal räcker för att få ut dem när det är klart, då är de redo att dela de skinande nya APK-filerna.

Mer allvarligt... Låt oss börja från början. Om du inte är bekant med modding av Android-applikationer kanske du undrar vad smali är. Utvecklare använder vanligtvis programmeringsspråket Java för att koda Android-appar i. Ett program (kompilatorn) "översätter" sedan den koden till ett annat format som passar din enhet, vilket resulterar i .dex-filer som finns i applikationspaketet (eller APK).

Vid den tidpunkten kan du inte längre komma åt den ursprungliga källkoden (såvida du inte är utvecklaren eller applikationen är öppen källkod). Men vad du har är APK, eftersom det är vad som är installerat på din enhet. Från den kan du hämta dex-filerna (vanligtvis classes.dex) och sedan försöka översätta den tillbaka till ett format som du kan förstå. Det är där smali kommer in, som en mer läsvärd men trogen översättning. Du kan gå ett steg längre och översätta det tillbaka till Java, även om den processen inte är trogen nog -- du får en förståeligt resultat, men chansen är stor att du inte kommer att kunna översätta det tvärtom igen eftersom vissa detaljer kommer att gå förlorade längs vägen. Med andra ord, alla ändringar du kan göra kommer att vara förgäves eftersom du inte kommer att kunna förvandla den tillbaka till en APK igen för att installera den på din enhet... åtminstone inte utan mycket ansträngning.

smali/baksmali är faktiskt en assembler/dissembler för dex-formatet -- det är vad det bokstavligen betyder på isländska. Men vi brukar hänvisa till formatet smali förstår när vi säger 'Smali' (tänk på det som instruktioner definiera varje liten detalj, även om allt inte behövs av oss människor -- det är därför mer omfattande än Java). Observera också att förklaringen ovan är lite förenklad, men bör vara en nära analogi samtidigt som den är lätt att förstå.

Vad skulle en utvecklare behöva göra för att modifiera en app (utan tillgång till källan) då? Processen är mer eller mindre enligt följande:

  1. Hämta APK-filen (från webben eller från enheten).
  2. Använd något liknande apktool för att dekompilera APK-filen till Smali. (apktool använder smali/baksmali, men gör det mycket lättare att dekompilera och bygga om APK-filer, och tar också hand om avkodning av resurser som XML-filer.)
  3. Extrahera classes.dex från APK: n och använd sedan dex2jar och slutligen en Java-dekompilator för att få (okomplett, ofta trasig, men mestadels begriplig) Java-kod. (Detta är valfritt, men kan vara till hjälp eftersom Smali är mycket svårare att förstå.)
  4. Identifiera vad som ska ändras.
  5. Ändra det faktiskt genom att redigera Smali-koden direkt.
  6. Alternativt kan du skriva ändringen i Java, kompilera den, dekompilera den igen till Smali och kopiera sedan den resulterande Smali-koden.
  7. När allt är över, använd apktool igen för att bygga om APK.
  8. Signera APK-filen (för att verifiera författarens identitet; alla paket måste signeras) och slutligen installera det.

Att skriva Smalisk kod är ganska svårt och risk för fel. Mindre ändringar kan göras på smali, men att lägga till nya funktioner med det är mer utmanande. Dessutom kommer du inte att ha några kompileringstidsfel, så även stavfel kan bara upptäckas under körning. Att utöka och dela Smali-patchar kan också vara besvärligt, eftersom diffarna tenderar att vara mycket specifika för en viss APK-version. Även om det finns några verktyg för att göra delar av processen som förklaras ovan enklare (Virtuous Ten Studio kommer att tänka på), kan det fortfarande bli tröttsamt.

DexPatcher av XDA Senior Member Lanchon syftar till att åtgärda dessa problem genom att göra processen enklare och låta utvecklare helt undvika att hantera Smali. Istället kan utvecklare skriva patchar enbart i Java och låta DexPatcher hantera allt annat.

Detta har den största fördelen att ha lättläsbara och hanterbara patchfiler. Att patcha APK-filer blir också bekvämare i allmänhet. Vi kommer att se ett fullständigt exempel på hur man använder DexPatcher om en stund, men här är en snabb översikt över vad den erbjuder först:

  • Öppen källa.
  • Cross-platform: det bör köras på Linux, Mac och Windows.
  • Patchfiler: ändringar du gör finns i Java-patchfiler som du kan dela självständigt.
  • Java: det är inte Smali.

Du får också fördelen av felkontroll under byggtiden, så buggar dyker upp tidigt i utvecklingscykeln. Den kompilerade Java tillhandahåller sin vanliga kompileringstidskontroll (med tillgång till de ursprungliga APK-symbolerna), och DexPatcher tillämpar kompatibilitet av källa och patch vid patchning, tillhandahåller användbar information och ger varningar när du verkar göra något lagligt men skumt.

Utöver det kommer DexPatcher med en uppsättning hjälpmanus (endast tillgängligt på Linux, även om de kan överföras till andra plattformar också). Dessa tar hand om att ställa in arbetsytan, extrahera mål-APK: s klasser och resurser, dekompilera klasserna till Java (den CFR Java-dekompilator används för det senare), och slutligen bygger och signerar den korrigerade APK-filen när du är klar.

Låt oss ta en titt på ett exempel (på Linux):

Installera DexPatcher-skripten

$# 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.

Konfigurera DexPatcher-skripten

Öppen dxp.config i din favorittextredigerare och se till att ändra de nödvändiga variablerna så att de passar ditt system. Du behöver bara ändra följande rad för att peka på din Android SDK: s installationsplats istället:

dxp_android_sdk_dir=(~/android/sdk)

(DexPatcher väljer automatiskt den högsta tillgängliga plattformsversionen. Dessutom kan du också ändra andra konfigurationsalternativ så att den använder dina egna versioner av vissa verktyg istället för de medföljande standardinställningarna.)

För enkel åtkomst kan vi lägga till dexpatcher katalog till vår VÄG, eller till och med symboliska de olika dxp-* skript till en plats som redan finns i din VÄG, Till exempel ~/bin:

export PATH=$PWD:$PATH

Ändra en applikation

För det här exemplet kommer vi att använda en enkel och öppen källkodsapplikation. Självklart skulle det vara möjligt att patcha källkoden direkt i det här fallet, men det är inte alls kul!

Vi tar applikationen "Hämta ID" av basil2style, en applikation som visar dig lite detaljer om din enhet. Vårt mål är att ändra knappen "Kopiera" för "Enhets-ID" och låta den dela detta ID istället:

  • Låt oss först ladda ner APK-filen som vi ska ändra: Få ID.
  • Dekompilera applikationen.
  • Skapa signeringsnyckeln som vi senare kommer att använda för att signera APK: n.

Vi kan också göra allt via skalet, med hjälp av hjälpskripten:

$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".

Du kommer att märka några olika kataloger där:

  • avkoda: du hittar resurserna och Smali här, som avkodats av apktool.
  • src: Tom katalog. Det är här vi ska placera våra patchfiler.
  • src-cfr: det är här cfr dekompilerade appen (tillsammans med fel). Ett bra ställe att titta in för att bestämma vad som ska ändras (du kan också behöva resurser och deras ID från avkodningskatalogen ovan, men inte för det här exemplet).
  • src-cfr-nodkod: samma som ovan, men innehåller bara tomma stubbar (ingen kod, bara skelett). Du kan använda dessa filer som grund för din patch som vi kommer att se om en stund.

Som vi har nämnt tidigare vill vi ändra knappen "Kopiera" för att dela ID-texten istället. Om vi ​​tittar runt i källkoden kommer vi att märka att knappen Device ID Copy (device_copy) påKlicka händelsen hanteras av anonym klass i src-cfr/makeinfo/com/getid/MainActivity.java. Även om vi kan modifiera det här, är det vanligtvis bättre att hitta ett alternativt sätt att göra det eftersom anonyma klasser har numeriska namn (MainClassName$SomeNumber, t.ex. MainActivity$3) som kan ändras oförutsägbart mellan versionerna.

Istället kommer vi att registrera vår egen klass för evenemanget genom att ändra Huvudaktivitet klass. Låt oss först kopiera "skelett"-versionen från src-cfr-nocode/makeinfo/com/getid/MainActivity.java till src/makeinfo/com/getid/MainActivity.java (kom ihåg det src är där vår patch kommer att bo). (Du kan också kopiera versionen med den fullständiga koden om du föredrar det, detta är enbart en smaksak.)

Vi kan nu redigera den enligt följande:

  • Lägg till den nödvändiga importen för DexPatcher-anteckningen:
importlanchon.dexpatcher.annotation.*;
  • Lägg till en tagg för att indikera att vi redigerar klassen. Vi ställer också in standardåtgärden för medlemmar i patchklassen till IGNORERA, vilket betyder att medlemmarna är där för att refereras av vår kod under Java-kompilering, men kommer att ignoreras av 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{

  • Lägg dessutom till tomma kroppar i konstruktorn och på Skapa metod, såväl som alla andra metoder vi planerar att använda (kom ihåg att de kommer att ignoreras när vår patch faktiskt tillämpas -- vi lägger bara till dem så att vi kan hänvisa till dem här om vi behöver). Du kan också bara lägga till inföding sökord istället.
  • Vi kan redan bygga patchen vid det här laget, om du är nyfiken:
    $ dxp-make # Output: `patched.apk`.
    Ganska enkelt, eller hur? Men låt oss fortsätta – vi är fortfarande inte klara än.
  • Låt oss redigera på Skapa nu att sätta ut egna OnClickListener så att vi kan dela enhets-ID istället för att kopiera det till urklipp:
    // 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();}}}
  • Det verkar som att vi är klara nu! Hela plåstret ska se ut detta. Vi kan nu bygga den korrigerade APK-filen och installera den:
    $ dxp-make$ adb install patched.apk
  • Låt oss ta en titt på resultatet:

(Tack till Lanchon för hjälpen med provkoden!)

Xposed är oerhört populärt, och av goda skäl - det gör det mycket enklare att bygga, dela och installera moddar för både utvecklare och användare. Det finns några skillnader mellan DexPatcher och Xposed som kan göra att vissa föredrar den ena framför den andra:

  1. Xposed gör sin magi genom att koppla in metoder under körning och låta utvecklare göra något före, efter eller istället vilken metod som helst. DexPatcher, å andra sidan, modifierar allt före körning och producerar en fristående, modifierad APK -- Att köra kod före, efter eller istället för metoder är fortfarande möjligt, och du har faktiskt lite extra frihet.
  2. Att producera en fristående APK betyder att den inte är beroende av något externt ramverk. Detta betyder också att root inte krävs för att ändra användarappar.
  3. Eftersom du har skapat en ny APK med DexPatcher kommer den att signeras annorlunda. Detta innebär att användare inte kan ta emot officiella uppdateringar från den ursprungliga författaren och kan orsaka vissa problem med appar som Google Apps om signaturerna kontrolleras.
  4. Både modulernas och DexPatcher-lapparnas källkod kan enkelt distribueras och modifieras. De delar också många likheter om du blir lite bekant med var och en.

Vi har pratat tillräckligt om DexPatcher. Det är din tur att ge det ett försök nu, så gå över till DexPatcher forumtråd för att komma igång direkt!