Salta el contingut

Anàlisi de fitxers APK amb apktool

En aquesta pràctica veurem les vulnerabilitats a les que es troben exposats els fitxers APK (Android Application Package), que contenen aplicacions per dispositius Android.

Creació d'una aplicació amb APP Inventor

Per dur a terme una prova de concepte necessitarem un fitxer APK.

El podem obtenir de diverses fonts, per exemple extraient-lo d'un dispositiu on s'hagi instal·lat, descarregant-lo de repositoris com APKMirror, o bé creant-lo nosaltres mateixos utilitzant algun entorn de desenvolupament com Android Studio o una aplicació web com APP Inventor.

APP Inventor
MIT App Inventor és un entorn integrat de desenvolupament gratuït que permet crear aplicacions per Android de forma visual. A partir d'un conjunt d'eines bàsiques, l'usuari va enllaçant un conjunt de blocs per crear l'aplicació.

Per crear la nostra aplicació de prova en primer lloc haurem de registrar-nos a la web amb una adreça de correu. Un cop fer això, podrem crear la nostra primera aplicació clicant el botó "New Project".

Interfície l'APP Inventor

La pantalla que se'ns presenta a continuació està dividida en els següents blocs:

APP Inventor Screen

  1. Paleta de components: conté una lista de components agrupats per categoria (interfície d'usuari, disposició, mitjans, emmagatzematge, etc.) que podem arrossegar fins a la pantalla per crear la interfície de l'aplicació. Alguns són visibles (botons, etiquetes, etc.) i altres no (càmera, sons, etc.).
  2. Pantalla del dispositiu: mostra els components a la pantalla del dispositiu, que podem arrossegar per desplaçar i ordenar.
  3. Jerarquia de components de l'aplicació: mostra en forma d'arbre tots els components que utilitza la nostra pantalla. En clicar-los s'actualiza la llista de propietats, i també tenim botons per canviar el nom d'un component o esborrar-lo.
  4. Propietats del component seleccionat: conté una llista amb totes les propietats modificables del component seleccionat, de manera que en podem canviar el seu contingut, mida, posició o forma.
  5. Mode Disseny o Blocs: permet canviar entre el disseny de la interfície i els diagrames de blocs que defineixen el comportament de l'aplicació (l'equivalent al codi font).

Aplicació càmera de fotos

Crearem pas per pas una aplicació que permetrà a l'usuari fer una foto amb la càmera del dispositiu, però només si un interruptor està activat.

Els components que utilitzarà la nostra aplicació són:

  • Layout / VerticalArrangement
    Ens permet organitzar la resta de components, seleccionant una alineació horitzontal i vertical.

  • User Interface / Button
    Element cliclable, li podem posar un text

  • User Interface / Switch
    Interruptor amb un missatge, pot estar en estat de On o Off

  • Media / Camera
    Aquest component no és visible en pantalla, però ens dona accés a la càmera. Apareix a la part inferior dins de la zona "Non-visible components"

L'aspecte de la interfície un cop creats tots els components hauria de ser similar a aquest:

APP Inventor Designer

Diagrama de blocs

Un cop creada la interfície, canviem a l'editor de blocs amb el botó de la part superior dreta.

Blocks button

L'editor de blocs es divideix en:

  • un selector d'elements de bloc (banda esquerra), que conté dos grups:

    • blocs integrats (built-in)
    • blocs corresponents als components de la interfície
  • un editor de connexions de blocs (part central)

Fent clic a un element de bloc ens apareixeran les possibles variants que podem utilitzar.

La nostra aplicació utilitzarà 2 blocs:

1. Bloc d'inicialització

Aquest bloc demanarà a l'usuari permís per utilitzar la càmera quan s'obre l'aplicació.
Un cop un permís es concedeix, el sistema ho recorda i ja no ens el tornarà a demanar fins que sigui revocat.
Bloc 1

2. Bloc de clic al botó

Aquest bloc comprovarà si l'interruptor està en posició On i si és així farà una foto amb el component càmera. Si l'interruptor està en Off l'aplicació no ha de fer res.
Bloc 2

Generació de l'APK

Un cop construida l'aplicació, l'exportarem en format APK per poder-la instal·lar a un dispositiu o emulador d'Android.
Per fer això fem clic al botó desplegable Build de la part superior de la pantalla, i seleccionem l'opció "Android App (.apk)":

APP Inventor build

Un cop acabat el procés de compilat remot als servidors d'APP Inventor, podem descarregar el fitxer resultant amb el botó "Download .apk now"
APP Inventor download

Estructura d'un fitxer APK

Els fitxers APK són internament fitxers comprimits en format ZIP, però amb l'extensió .apk
Això ho podem comprovar si examinem la capçalera de qualsevol d'aquests fitxers.

Per exemple, amb VS Code podem instal·lar l'extensió Hex Editor, que ens permet obrir fitxers en format hexadecimal. En fer-ho veurem que els 2 primers bytes contenen els codis hexadecimals 50 4B, que corresponen als caràcters ASCII PK:

Magic Numbers

El valor dels 2 primers bytes d'un fitxer serveixen per identificar-ne el format independentment de l'extesió de l'arxiu, i s'anomenen també magic numbers.
Si busquem a una taula de magic numbers trobarem que PK correspon a un fitxer comprimit en format ZIP.

Extracció de dades amb apktool

Encara que podem obrir i descomprimir un fitxer .apk amb qualsevol descompressor compatible amb el format zip, ens trobarem amb que podem veure l'estructura de fitxers però no examinar el seu contingut. Això és degut a que aquest es troba comprimit i emmagatzemat en binari.

Contigut ZIP

Contingut del fitxer AndroidManifest.xml sense descodificar:

ìÄ7ø4ZœºÞö2n¬žÎÜð*TrŒ¶ÊÜ48J~²Æ
$F€¶Êäö†¢Òæú(`tžÌj€versionCodeversionNamecompileSdkVersioncompileSdkVersionCodenamename
minSdkVersiontargetSdkVersion
debuggablelabelnetworkSecurityConfigrequestLegacyExternalStoragepreserveLegacyExternalStorageicon   roundIconthemerequiredexportedscreenOrientationwindowSoftInputMode
configChangesauthoritiesgrantUriPermissionsresourceandroid*http://schemas.android.com/apk/res/androidpackageplatformBuildVersionCodeplatformBuildVersionNamemanifest appinventor.ai_egallegos.Prova_11.01434uses-permissionandroid.permission.INTERNETandroid.permission.CAMERAuses-sdkapplicationProva_1Fcom.google.appinventor.components.runtime.multidex.MultiDexApplicationuses-libraryorg.apache.http.legacyactivity.Screen1
intent-filteractionandroid.intent.action.MAINcategory android.intent.category.LAUNCHERprovider"androidx.core.content.FileProvider)appinventor.ai_egallegos.Prova_1.provider meta-data#android.support.FILE_PROVIDER_PATHS€drsp',Ž+%ÿÿÿÿ°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"  ÿÿÿÿÿÿÿÿ!"ÿÿÿÿ 8ÿÿÿÿÿÿÿÿ"##ÿÿÿÿÿÿÿÿ"8ÿÿÿÿÿÿÿÿ"$$ÿÿÿÿÿÿÿÿ"Lÿÿÿÿÿÿÿÿ%ÿÿÿÿÿÿÿÿ"ÿÿÿÿÿÿÿÿ%Øÿÿÿÿÿÿÿÿ&    ÿÿÿÿ   ''ÿÿÿÿ((ÿÿÿÿ  ÿÿÿÿ
ÿÿÿÿ
ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLÿÿÿÿÿÿÿÿ)**ÿÿÿÿÿÿÿÿÿÿÿÿ)ˆÿÿÿÿÿÿÿÿ+,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°
ÿÿÿÿ$ÿÿÿÿÿÿÿÿ-8ÿÿÿÿÿÿÿÿ.//ÿÿÿÿÿÿÿÿ.8ÿÿÿÿÿÿÿÿ011ÿÿÿÿÿÿÿÿ0   ÿÿÿÿÿÿÿÿ-
ÿÿÿÿÿÿÿÿ+tÿÿÿÿÿÿÿÿ233ÿÿÿÿ44ÿÿÿÿÿÿÿÿLÿÿÿÿÿÿÿÿ566ÿÿÿÿÿÿÿÿÿÿÿÿ5ÿÿÿÿÿÿÿÿ2ÿÿÿÿÿÿÿÿ&ÿÿÿÿÿÿÿÿÿÿÿÿ

Per poder-lo convertir a text tractable necessitem la utilitat apktool.

Apktool és una eina per a l'enginyeria inversa d'aplicacions Android.
Pot descodificar recursos i també reconstruir-los després de que siguin modificats.

Instal·lar apktool

A les distribucions basades en Debian (com ara Kali) podem instal·lar apktool amb:

sudo apt install apktool

Execució d'apktool online

A banda de la versió instal·lable, també podem utilitzar aquesta versió online d'apktool:
http://www.javadecompilers.com/apktool

Per descomprimir el nostre fitxer APK i accedir als seus recursos descodificats, la instrucció a executar serà:

apktool decode fitxer.apk

Amb el paràmetre decode (o només d) descodifiquem el contingut de l'APK:

$ apktool decode test.apk
I: Using Apktool 2.5.0-dirty on test.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: ~/.local/share/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Baksmaling classes2.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

Un cop acabi el procés obrindrem una carpeta amb el contingut del fitxer .apk, ara sí, descodificat.

Contingut del fitxer AndroidManifest.xml descodificat:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:compileSdkVersion="34" android:compileSdkVersionCodename="14" package="appinventor.ai_egallegos.Prova_1" platformBuildVersionCode="34" platformBuildVersionName="14">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <application android:debuggable="false" android:icon="@mipmap/ic_launcher" android:label="Prova_1" android:name="com.google.appinventor.components.runtime.multidex.MultiDexApplication" android:networkSecurityConfig="@xml/network_security_config" android:preserveLegacyExternalStorage="true" android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher" android:theme="@style/AppTheme">
        <uses-library android:name="org.apache.http.legacy" android:required="false"/>
        <activity android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize" android:exported="true" android:name=".Screen1" android:screenOrientation="unspecified" android:windowSoftInputMode="stateHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <provider android:authorities="appinventor.ai_egallegos.Prova_1.provider" android:exported="false" android:grantUriPermissions="true" android:name="androidx.core.content.FileProvider">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

Modificació del fitxer APK

Un cop tenim accés al contingut del fitxer .apk descodificar, tenim llibertat per examinar tots els seus continguts i també modificar-los.
Per exemple, canviarem el text del botó de l'aplicació que serveix per fer una foto.

Sabem que quan l'aplicació està en marxa, el botó mostra el text "Fer una foto".
Per tant obrirem tota la carpeta que conté l'apk descodificat amb VS Code i utilitzarem la lupa per buscar globalment aquesta cadena:

Buscar dins APK

Trobem dues ocurrències de la cadena, es tracta de recursos duplicats pel procés de compilació de l'APK.
Com que no sabem segur quin dels dos és el que s'acaba mostrant en pantalla, els modificarem tots dos i hi posarem un text diferent però el mateix als dos llocs.

Construcció del fitxer APK modificat

En aquest punt hem descodificat el nostre APK i hem fet canvis a un recurs, en aquest cas el text d'un botó. Fent una investigació més exhaustiva del codi podríem modificar qualsevol recurs i fins i tot el comportament de l'aplicació, però això no ens serveix de res si no podem tornar a convertir tots aquests fitxers a un instal·lable per Android vàlid.

Apktool els permet fer el procés invers i tornar a reconstruir l'APK si l'executem amb el paràmetre build (o només b), en aquest cas passant com a paràmetre el directori amb el contingut descodificat (en aquest exemple, test). Amb el paràmetre -o (output) podem indicar on volem que ens deixi el fitxer APK resultant:

apktool build test -o modificat.apk

Un cop acabi el procés obrindrem un fitxer .apk codificat, però si intentem instal·lar-lo a un dispositiu o emulador d'Android veurem que ens retorna un error. Això passa perquè els fitxers APK han d'estar signats per poder-se instal·lar.

Optimització de l'APK

Alineació de fitxers zip

Aquest pas és opcional i serveix per garantir que tots els fitxers sense comprimir del fitxer estiguin alineats respecte al seu inici. Això estalvia la necessitat de copiar primer aquestes dades a la memòria RAM, i per tant reduir l'ús de memòria que utilitza l'aplicació.
Només cal fer servir zipalign per optimitzar un fitxer APK abans de distribuir-lo als usuaris finals, per exemple a través de Google Play.

zipalign rep com a primer paràmetre el fitxer .apk a optimitzar i com a segon el nom del fitxer .apk de sortida optimitzat:

zipalign test.apk optimitzat.apk

Execució de zipalign online

Hi ha disponible aquesta versió online de zipalign que s'executa en local al nostre navegador:
https://sisik.eu/zipalign

Signar un fitxer APK

Per signar un fitxer .apk necessitarem una utilitat de signat (apksigner) i un magatzem de claus (keystore) que contingi la nostra signatura.
Podem crear un keystore amb la utilitat keytool, que forma part del JDK de Java.

keytool -genkey -v -keystore nomKeyStore -keyalg RSA -keysize 2048 -validity 10000

Contrasenya del keystore

En el moment de generar el keystore se'ns demanarà una contrasenya d'almenys 6 caràcters de longitud. Cal que la recordem perquè la necessitarem cada cop que signem un fitxer .apk

Paràmetre Valor Descripció
genkey o genkeypair - Genera una parella de claus (pública i privada)
v - Mostra més informació per pantalla (verbose)
keystore nomKeyStore Nom del fitxer corresponent al keystore
keyalg RSA Nom de l'algorisme de claus (DSA, RSA, EC, etc.)
keysize 2048 Mida de la clau (en bits)
validity 10000 Temps de validesa (en dies)

Durant el procés de generació del keystore se'ns demanarà tota una sèrie d'informació personal que serà emmagatzemada a la clau.
Finalment s'haurà de confirmar que tot sigui correcte i es generarà un fitxer amb el keystore.

Enter keystore password:
Re-enter new password:
What is your first and last name?
  [Unknown]:
What is the name of your organizational unit?
  [Unknown]:
What is the name of your organization?
  [Unknown]:
What is the name of your City or Locality?
  [Unknown]:
What is the name of your State or Province?
  [Unknown]:
What is the two-letter country code for this unit?
  [Unknown]:
Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct?
  [no]:  yes

Un cop generat el keystore podem signar el nostre fitxer .apk amb apksigner.

Instal·lar apksigner

A les distribucions basades en Debian (com ara Kali) podem instal·lar apksigner amb:

sudo apt install apksigner

apksigner sign --ks nomKeyStore --v1-signing-enabled true --v2-signing-enabled true signat.apk
Paràmetre Valor Descripció
sign - Indiquem que volem signar el fitxer .apk
--ks nomKeyStore Fitxer que conté el magatzem de claus (generat amb keytool)
--v1-signing-enabled true Signa amb l'esquema tradicional basat en JAR
--v2-signing-enabled true Signa amb l'esquema de d'APK v2
signat.apk nom de fitxer Nom del fitxer signat que es generarà

Un cop executem la comanda haurem d'introduir la contrasenya del keystore i finalment obtindrem un nou fitxer .apk signat. Aquest fitxer el podrem instal·lar sense problemes a dispositius i emuladors d'Android.

Modificació de codi executable

En el moment que tenim el fitxer .apk descodificat podem fer-hi qualsevol modificació abans de tornar-lo a codificar.
A l'exemple anterior hem canviat el text que mostra un botó, però si analitzem el codi que ens retorna apktool podem modificar també l'execució del programa.

Existeixen eines per convertir el codi en format smali que genera apktool en Java, de manera que sigui més senzill interpretar què fa l'aplicació. jadx és un descompilador que converteix el codi en format DEX (Dalvik EXecutable) d'Android a Java. Un cop analitzat el codi podem localitzar i modificar el seu equivalent dins dels fitxers .smali descodificats.

Execució de jadx online

Tot i que el podem instal·lar al nostre sistema, jadx també està disponible online:
http://www.javadecompilers.com/apk

Exemple

Un cop descodificat l'APK que hem creat amb APP Inventor, descobrim un mètode que té per nom FerFoto$Click() dins del fitxer Screen1.smali.
Això ens posa sobre la pista de que és el codi que s'executarà quan fem clic al botó "Fer una foto".

.method public FerFoto$Click()Ljava/lang/Object;

Dins del codi d'aquest mètode localitzem aquestes línies que comproven el valor d'un objecte definit per un booleà:

sget-object v1, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;
goto :cond_0

Per tant, aquestes línies comproven si l'interruptor de l'aplicació està en posició Off per saltar a una línia del codi on no es fa la foto en prèmer el botó.

Si eliminem aquestes línies i tornem a generar i signar l'APK, ara la nostra aplicació farà sempre una foto amb el botó, independentment de l'estat de l'interruptor.