QB Logo

Apkpatcher

Workshop on application patching.

Benoît FORGETTE (MadSquirrels)

27/06/2026

Who am I ?


Benoît FORGETTE
Software and hardware Security Researcher
topic (Hardware/Android)

Link for challenge

Let's go back to basics.


Why we would like to patch

Root an Android

center

Root an Android

center

  • ❌ A full chain exploitation is needed;
  • ❌ It is more commonly know and integrated on detection mechanism;
  • ❌ Could not be able to deliver a new APK with modified behavior;
  • ✅ Have a full access on system.

Emulate Android

center

  • ❌ Is more commonly know and integrated on detection mechanism;
  • ❌ Could not be able to deliver a new APK with modified behavior;
  • ❌ Do not have not a full access to external feature;
  • ❌ Limited to OS architecture (X86);
  • ✅ Have a full access on system.

Patched an APK

center

  • ❌ Could not by-pass Package manager if a vendor certificate is setup;
  • ❌ Need to have an ADB access on Android target;
  • ✅ Enable to modify the apk;
  • ✅ Can works on any Android system.

Different format

Application structure

Demo unpack

Only unpack or repack

apkpatcher -a <apk> --only-unpack dir
apkpatcher -a <new_apk> --only-repack dir

Application structure

Application structure

Application structure

Application structure

Application structure

Demo Reading of AXML

pip install pyaxml

you can have some example script here:

# To print Manifest in XML form
pyaxml axml2xml -i AndroidManifest.xml
# To print resources.arsc in XML form
pyaxml arsc2xml -i resources.arsc
# To convert XML to AXML
pyaxml xml2axml -i AndroidManifest.xml -o AndroidManifest.axml

Build process

Extract process

Android application structure

Android application structure

Activity

MyActivity.java is the Declaration file.

import android.app.Activity;
public class MyActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
  ...
  }
...
}

Activty

Registration of MyActivity inside AndroidManifest

<activity android:name="com.example.MyActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

Activity

Lifcycle of an activity:

Service

MyActivity.java is the Declaration file.

public class HelloService extends Service {

  @Override
  public void onCreate() {
    ...
  }
  ...
}

Service

Registration of MyService inside AndroidManifest

<manifest ... >
  ...
  <application ... >
      <service android:name="com.example.MyService" />
      ...
  </application>
</manifest>

Service

Lifcycle of a service:

Broadcast receiver

MyBroadcastReceiver.java is the Declaration file.

public static class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Objects.equals(intent.getAction(), "com.example.snippets.ACTION_UPDATE_DATA")) {
            String data = intent.getStringExtra("com.example.snippets.DATA");
            // Do something with the data, for example send it to a data repository:
            if (data != null) { dataRepository.updateData(data); }
        }
    }
}

Broadcast receiver

Registration of MyBroadcastReceiver inside AndroidManifest

<manifest ... >
  ...
  <receiver android:name=".MyBroadcastReceiver" android:exported="false">
      <intent-filter>
          <action android:name="com.example.snippets.ACTION_UPDATE_DATA" />
      </intent-filter>
  </receiver>
</manifest>

Content Provider

MyContentProvider.java is the Declaration file.

public class MyContentProvider extends ContentProvider {

    public static final String AUTHORITY = "com.example.myapp.provider";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/data");

    @Override
    public boolean onCreate() {
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        ...
    }
}

Content Provider

Registration of MyContentProvider inside AndroidManifest

<manifest ... >
  ...
  <provider
              android:name=".MyContentProvider"
              android:authorities="com.example.myapp.provider"
              android:exported="true"
              android:grantUriPermissions="true" />
</manifest>

Deeplink

DeepLinkActivity.java is the Declaration file.

public class DeepLinkActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Uri data = getIntent().getData();
        if (data != null) {
            // Handle the deep link here
        }

        setContentView(R.layout.activity_deep_link);
    }
}

Deeplink

Registration of DeepLinkActivity inside AndroidManifest

<manifest ... >
  ...
  <activity android:name=".DeepLinkActivity">
    <intent-filter>
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data
        android:scheme="https"
        android:host="www.example.com"
        android:pathPrefix="/open" />
    </intent-filter>
  </activity>
</manifest>

How to use Apkpatcher

A quick way to use it:

docker run --rm -v .:/pwd -it madsquirrels/apkpatcher -a base.apk

The hard way

# Java dependencies
apt install -y default-jre

# sdktools dependendies installation
wget https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip
mkdir /usr/lib/android-sdk
cd /usr/lib/android-sdk
unzip commandlinetools-linux-6200805_latest.zip
mkdir cmdline-tools
mv tools/ cmdline-tools/
echo 'export ANDROID_SDK_ROOT=/usr/lib/android-sdk' >> ~/.bashrc

# installation of platform-tools
sdkmanager "platform-tools" "platforms;android-36" "build-tools;36.0.0" "emulator"

# install apkpatcher
pip install apkpatcher

Some example of usage

apkpatcher -a <apk> -m <split_apk1> <split_apk2>

What is the file struture at runtime

It exists 2 types of storages:

Internal Stockage

location: /data/data/
permissions: Not needed but only APK itself (unique uid) can access.

External Stockage

location: /storage/emulated/0/Android/data/<package_name>/
permissions:

  • READ_EXTERNAL_STORAGE
  • WRITE_EXTERNAL_STORAGE

Some other storages and permission need to access/read/write, picture, audio, video.

What inside internal stockage

/data/data/com.example.app/
├── cache/
├── files/
├── databases/
├── shared_prefs/
├── code_cache/
└── no_backup/

What inside insternal stockage

files:

  • configuration (some file found from resources)
  • content persistent

shared_prefs:

  • UI display preferent
  • user secret

no_backup:

  • File exclude from backup (token, temp key)

What inside insternal stockage

database:

  • sqlite database (provider for instance)

cache:

  • temporary content (image, HTTP response)

code_cache:

  • Art and DEX file

To enable debug mode

apkpatcher -a <apk>  --enable-debug

If you want to begin to debug you follow this tutorial or use for instance jadx:

adb shell am set-debug-app --persistent

Debug allow user to run command with uid of APK

adb shell run-as...

To inject frida or a library

To do it simply:

apkpatcher -a <apk> --download_frida_version <version>

To choose a specific library:

apkpatcher -a <apk> -g <file.so> -r arm64

To change location which inject:

apkpatcher -a <apk> -g <file.so> -r arm64 --entrypoint com.example.entrypoint

Demo frida

installation:

$ pip install frida-tools
$ frida --version
16.7.13
$ apkpatcher -a <apk> --download_frida_version 16.7.13

Documentation for frida here:

Let's open JADX

center

Resources Directory

Let's check the entrypoint

center

Sources Directory

center

To add a permission

apkpatcher -a <apk> --add-permissions <my_permission>

Add a plugin or set a pause during the process

To pause the process:

apkpatcher -a <apk> -p

To execute a script during the process:

apkpatcher -a <apk> --plugin

Little Smali introduction

Java code:

public static int compare(long j, long j2) {
    if (j < j2) {return -1;}
    return j == j2 ? 0 : 1;
}

Smali code:

.method public static compare(JJ)I
    .registers 4
    cmp-long            v0, v0, v2
    ...
    return              v0
.end method

Little Smali introduction

  • Dalvik Intermediate Representation is called Smali: It can be seen as an assembly
    language that is human readable and can be used to generate Dalvik bytecode.
  • Primitive types are simply mangled with a letter.
  • Object types are mangled as follows: Ljava/lang/String;

Little Smali introduction

Native types are the following:

  • V void, which can only be used for return value types
  • Z boolean
  • B byte
  • S short
  • C char
  • I int
  • J long (64 bit)
  • F float
  • D double (64 bit)

Little Smali introduction

Call to methods:

Class definition in JAVA

package challenge.examples;
class MyClass {
  bool method();
}

Call in Smali

Lchallenge/examples/MyClass;->method()Z

Little Smali introduction

more information on smali here: https://blog.quarkslab.com/smali-the-parseltongue-language.html

Patch the smali code

from apkpatcher.smalipatching import Method, get_smali_file_from_class, replace_methods
import click


def replace_method_wrapper(input_dir, classname, prototype, patch):
    m = Method(prototype, patch)
    fname = get_smali_file_from_class(input_dir, classname)
    with open(f"{fname}", "r") as f:
        content = f.read()
    with open(f"{fname}", "w") as f:
        text = replace_methods([m], content)
        print(text)
        content = f.write(text)

Patch the smali code

@click.command()
@click.argument('input_dir', nargs=-1)
def main(input_dir):
    main_dir=input_dir[0]
    prototype = ".method public setEndColor(I)V"
    patch = "return-void"
    classname = "com.github.mikephil.charting.model.GradientColor"
    replace_method_wrapper(main_dir, classname, prototype, patch)

Demo patching

Inject a burp certificate

Accept user certificate:

apkpatcher -a <apk> -e

Add a custom certificate:

apkpatcher -a <apk> -c burp.der

Just a little reminder about ADB and enable debug mode

Enable ADB

On the device, go to Settings > About .
Tap the Build number seven times to make Settings > Developer options available.
Then enable the USB Debugging option.

Some little ADB command

adb install <apk>
adb shell
adb logcat

Link for challenge

https://ci-yow.com/workshop-android.apk

Thank you!

Contact information:

Bienvenu à cette présentation sur l'outil Apkpatcher

</div> <div style="text-align:left;margin-top:15%;">

**Contributions** - A 4 years tool experience on APK patching with documentation; - A pythonic tool to manipulate APK; - A benchmark of patching tool.

</div>

Je suis en chercheur en cybersécurité plutot sur des sujets bas niveau embarqué et vous allez commencez à me connaitre j'aime bien aussi joué avec Android et je présente ses side project au SSTIC. Le but de cette contribution est d'aporté: - Un outil ayant 4 ans d'experience permettant de récupérer une application Android, la modifier et la réinstaller. - Cet outil permet aussi d'être intégrer dans n'importe quel autre outil développé en python. - Et enfin un benchmark sur les solutions de patching.

Why we need to patch an Android Application

![width:1000px center](img/blackbox-full.svg)

Une APK est une boite noir q'on aimerait comprendre. Sans se plonger dans les détails, d'un application, regardons d'une vue macro comment cela fonctionne. Chaque APK a son propre utilisateur avec des droit configuré présenté sous forme de permissions permettant de communiqué avec d'autres application, accéder au stockage interne ou des composants externes comme le WiFi ou BLE et d'autres en étandant au système android appliqué au Automobile ou au IOT. Si on souhaite comprendre ce qui se passe dans chacune de ces applications pour auditer leur sécurité, ou permettre de l'interopérabilité ou encore mieux controler ce que les applications font sur nos systèmes. Il existe 3 solutions: --- Android est un système ou les accès peuvent etre limité et plutot bien segmenté "image d'une apk en black box avec ces imput output (Wifi, stockage, BLE, IOT pinout) via un environment Android" L'environement peut etre controlé soit via émulation, téléphone rooté mais quid des environements que l'on ne peut pas rooté et pourquoi s'embeter à rooté un téléphone quand on peut utilisé n'importe quel téléphone et faire tout ce qu'il est possible avec un téléphone rooté D'un autre coté on voudra aussi supprimer des portions de code comme du tracking et idéalement automatisé au mieux la tache et l'intégré dans un déploiement continu par exemple. Bien entendu, et il reste important de le préciser, ce type d'outil peuvent rendre le développement plus abordable de certains malwares permettant d'ajouter des fonctionalités ou redirigé d'une application légitime.

Router l'Android afin de controler l'ensemble du support mais cela demande d'avoir une exploit pour l'appareil cible. En plus c'est la solution la plus commune et les applications s'en protège de mieux en mieux. Et pour finir on ne peut appliqué nos modification que sur le système rooté permettant pas de délivré des applications modifié avec par exemples des trackers de supprimé.

On aura de meme avec un système émulé sans avoir la nécessité d'avoir un exploit par contre on perdra l'accès au composants externe.

Pour la solution présenté aujourd'hui

split apk APK XAPK graphique poupée russe XAPK contient APK et split APK on ne prend pas en compte les XAPK

TODO ajouter resources.arsc

TODO ajouter resources.arsc

--- # Application structure

![width:1100px](img/manifest-resources-1.svg)

<img src="img/manifest-resources-1-1.png" style="position=absolute;width:1100px;display:block;" /> <img src="img/manifest-resources-1.png" style="position=absolute;width:1100px;display:block;margin-top:-450px" />

Les resources externe dans le Manifest sont identifié par un id renseigné dans le fichier de resources

![width:1100px](img/manifest-resources-1.svg)

Pour changer la configuration réseau pour par exemple y ajouter un certificat TLS autosigné, ce qui pourrait etre utilisé pour intercepter les requetes HTTPS et mieux comprendre les communications réseaux. Pour cela comment on peut faire dans le cas d'un téléphone rooté en général il est possible d'ajouter un certificat à la liste des certificats de confiance d'android. Mais sans rooté une application il est possible d'y ajouter dans le Manifest une resources externe networkSecurityConfig que l'on retrouve dans le fichier resources.arsc qui renvoie vers un fichier 8G.xml qui contient une configuration qu'il est possible de modifier pour ajouter un certificat spécifique qui sera lui même renseigné dans le fichier resources.arsc.

![width:1100px](img/manifest-resources-1.svg)

![width:1100px](img/manifest-resources-1.svg)

Dans les donées interessantes pour le patching d'application, on aura le AndroidManifest qui décrira l'ensemble de la structure des fichiers contenu dans l'APK: - si l'application peut être débugguer avec l'option: android:debuggable="true" permettant l'utilisation d'un gdbserver pour les applications native et jdb pour le code Java - la présence ou non d'une configuration réseaux notament utilisé quand des certificats TLS particuliers sont utilisé, on spécifiera dans ce cas la un lien vers le fichier contenant la configuration et le lien vers le certificat TLS. Les fichiers classes.dex contiendra le code JAVA au format Smali Le dossier Lib contenant les binaire natif au format ELF

Lors de la compilation, on va compiler le code avec les fichiers de code source Java et la bibliothèque standard android.jar puis on utilisera aapt pour y ajouter toutes nos resources XML pour construire notre application et enfin la signer.

Cependant lors de l'extraction la plus naive, en dézippant ou avec le programme apktool avec l'option without-resource, on recupère notre code source compilé au format dex et nos resources cette fois-ci non pas au format XML mais au format AXML. On obtient aussi un fichier non présent initialement le fichier "resource.arsc". Ce fichier est aussi un fichier AXML mais au format légèrement différent qu'on abordera pas durant la présentation bien que l'outil présenté aujourd'hui peut également manipuler ce type de fichier. Ce fichier bien particulier permet entre autres d'associer des fichiers comme le fichier MY_XML_FILE à un id, permettant d'optimiser la place dans le fichier zip.

See for share uid if share uid can acess

TODO: Tuto explication sur ce que contient une APK quand elle est installé Lister les APKs sur le telephone adb shell pm list packages --user 10 -f -3 adb shell run-as com.example.mychallengeapp ls /data/data/com.example.mychallengeapp/ debu allow us to run-as https://developer.android.com/training/data-storage Internal stockage /data/data/com.example.app/ ├── cache/ ├── files/ ├── databases/ ├── shared_prefs/ ├── code_cache/ └── no_backup/ files: - configuration (some file found from resources) - content persistent cache: - temporary content (image, HTTP response) database: - sqlite database (provider for instance) shared_prefs: - UI display preferent - user secret code_cache: - Art and DEX file no_backup: - File exclude from backup (token, temp key) External Stockage /storage/emulated/0/Android/data/<package_name>/ file share with others app and user

--- TODO Ajouter slide jadx et montrer le reverse du level1 jusqu'au onCreate