Petite histoire de reverse engineering appliqué

Jonathan Gérardin
Meetech - We Love Tech
10 min readJul 6, 2020

Où comment en essayant de faire fonctionner un petit jeu Android sur ma tablette Amazon Fire HD 10, j'ai débarrassé les appareils de mon réseau de leurs publicités, j’ai amélioré la protection de ma vie privée, j’ai appris la structure d’un fichier APK, j’ai décompilé/recompilé/signé, et bien d’autres choses.

Tout d’abords, pour les plus pressés d’entre-vous, si vous voulez faire fonctionner XCOM®: Enemy Within sur une tablette Amazon, vous pouvez vous rendre directement à la fin de cet article.

Pour les autres, prenons le temps.

Le reverse engineering selon l’Hollywood du début des années 2000

Un peu de contexte

J’ai une tablette Fire HD 10 que j’utilise pour quelques mini-jeux, lire des BD/Comics, et 2–3 choses de domotique. Cette tablette serait parfaite pour ces usages, si elle n’avait pas été complètement polluée par Amazon et coupée par défaut de tout l’éco-système Android standard. Pour pouvoir décemment en profiter, heureusement il existe une toolbox développée par quelques hackers de xda-developers, qui permet de facilement changer le launcher, retirer/masquer les applications Amazon, activer les services Google, dont le Play Store en tête. Bref, c’est top et indispensable.

Malheureusement (ou heureusement, ça dépend), il s’avère qu’il soit parfois nécessaire de se salir les mains.

Etape 1 : déception et diagnostic

J’ai donc téléchargé récemment le jeu XCOM®: Enemy Within sur le Google Play Store, en prévision de mes congés cet été. Cependant, le jeu, au lieu de s’exécuter sagement, essaie sans succès de télécharger une mise à jour, puis se ferme. Ce qui n’est pas exactement le comportement attendu.

Face à l’adversité, je vérifie d’abord les commentaires sur le store et visiblement le jeu fonctionne bien ailleurs. Je passe donc en mode avion, pour essayer de contourner cette mise à jour inutile (le dernier patch datant de 2017). Second échec.

OK, il est donc temps d’essayer l’application sur un autre device. Je réinstalle le jeu sur un téléphone (One Plus, si vous êtes curieux). Et là miracle. Pas de mise à jour, pas de problème. La cinématique d’intro se lance, nous voilà en 2013, mais en version portable.

Le problème vient donc du device. Le diagnostique est simple. Il suffit de voir ce qui bloque au niveau du réseau sur la tablette et ça devrait fonctionner, non ?

Etape 2 : analyse des trames réseaux

C’est le moment de recourir à un petit outil bien pratique : Packet Capture, pour analyser le trafic réseau et jeter un oeil à ce qui s’y passe. En 3 clics, ou plutôt 3 tapotements, me voilà avec l’application installée et un certificat pour lui permettre d’analyser les flux SSL, selon un bon principe Man-in-the-middle.

Et c’est parti. On lance l’enregistrement des packets, on relance l’application, elle essaie de télécharger sa mise à jour, échoue, puis se ferme. Mais cette fois elle a laissé des traces!

Version texte

Elle essaie donc de télécharger sa mise à jour d’un bucket S3 :

Host: dx-s3-2k-xcom.s3.amazonaws.com

En fournissant de quoi s’authentifier :

Authorization: AWS AKIAJ6ONSYEYNIVWUB5A:MNlqMRd08Jk4tJk894VkUZ1ghRQ=

Mais ça ne fonctionne pas …

<Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message><Key>main.17.com.tt2kgames.xcomew.obb</Key><RequestId>B29DC944053FEE91</RequestId><HostId>sX/M5v/pNZJFDOMwAbYePdOC1dnhkZdLdaTCjziF0pDrER4rCfv+jBtZyjRM2JSJTenXOlhvC3o=</HostId></Error>

Dommage. Allons voir quand même voir ce bucket de plus près.

Bonne nouvelle, le bucket existe. Mauvaise nouvelle, son accès est restreint (mais on s’en doutait). Essayons donc de proposer à l’application une ressource plus conciliante …

Etape 3 : rediriger la requête vers une ressource locale

C’est à ce moment là que j’ai commencé à digresser un peu.

Pour rediriger la requête dx-s3–2k-xcom.s3.amazonaws.com vers une ressource locale, je me suis dit que c’était l’occasion d’installer également Pi-hole sur le Raspberry Pi en charge de la domotique à la maison.

Pi-hole, si vous ne connaissez pas, est un bloqueur de publicité. L’application sert de serveur DNS pour un réseau privé et bloque les domaines indésirables en empêchant leur résolution (c’est à dire leur traduction en adresse IP). Mais il peut également rediriger un domaine vers l’adresse de son choix. Bref, c’était l’occasion idéale de faire d’une pierre deux coups.

Pi-hole peut être configuré pour utiliser un serveur DNS parmi une liste pré-configurée, mais on peut également définir une configuration spécifique. J’utilise depuis longtemps DNSCrypt (via Simple DNS), qui permet de chiffrer ses requêtes DNS, et donc j’ai voulu bien entendu le réutiliser ici.

Vous pouvez suivre le pas à pas suivant, si vous souhaitez également vous lancer.

How-to: Pi-Hole Plus DNSCrypt Setup

Bref, une fois Pi-Hole configuré, j’ai ajouté un Local DNS Record pour rediriger les requêtes vers dx-s3–2k-xcom.s3.amazonaws.com, vers un petit serveur HTTP tout simple.

Pour aller vite j’ai utilisé le module Python SimpleHTTPServer, en adaptant un bout de code trouvé sur Internet. Comme vous voyez, ce n’est pas du grand art.

Vu que la communication est en HTTPS il a également fallu que je produise un certificat. Quelque chose d’auto-signé étant très suffisant dans mon cas :

openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes

OK, parfait. Avec tout ça je peux au moins renvoyer un code HTTP 200 et un minimum de choses avec. Donc c’est reparti : on relance l’application, elle essaie de télécharger sa mise à jour, échoue, puis se ferme.

Troisième échec. Et c’est à ce moment là que je commence à en faire une affaire personnelle !

Etape 4 : décompilation du fichier APK

Si je ne m’en sors pas en essayant de deviner le fonctionnement interne de l’application et lui fournir ce dont elle pense avoir besoin, je vais devoir l’ouvrir et examiner ce qu’elle a dans le ventre !

La suite, pour des raisons évidentes d’efficacité et de clarté du récit, sera présentée au lecteur comme un enchaînement d’actions parfaitement maîtrisées et où chacune des étapes réussira du premier coup. Alors que dans les faits, j’étais fatigué, et ça c’est plutôt passé comme ça (avec moins de sex appeal en vrai).

Bref, un fichier APK est comme son nom l’indique un Android Package, c’est donc une archive qui contient d’autre fichiers : ressources, certificat, code et un manifest. Le point d’entré naturel est d’aller voir ce qu’il y a dans le manifest avant de s’enfoncer plus profondément dans le code. Pour faire simple on peut utiliser Android Studio pour jeter un coup d’œil à l’intérieur de l’APK.

Comme vous pouvez le constater, le fichier est assez petit, c’est parce que les éléments “lourds” : vidéos, sons, textures, etc… sont en fait stockés dans d’autres fichiers (.obb) dont nous n’aurons pas besoin ici.

Le fichier AndroidManifest.xml est le premier qui nous intéresse. Je cherche donc tout ce qui pourrait être relatif à un téléchargement de mise à jour, pour essayer de l’empêcher.

Bingo, je trouve 2 éléments :

<service
android:name="com.tt2kgames.xcomew.UE3JavaFileDownloader" />

[…]

<service
android:name=".AWSDownloadService" />

J’ai donc 2 services pour télécharger des fichiers (une mise à jour, peut-être ?). Il semblerait que le premier soit un service Java “Standard” et le second en lien avec AWS. Ma tablette est un device Amazon, elle a un comportement différent du comportement standard, Amazon est la maison mère d’AWS… Mon instinct de détective me pousse à aller regarder de ce côté là.

Pour fouiller un peu plus loin il faut demander à Android Studio de décompiler l’APK (File > Profile or debug APK). Le résultat :

On accède ainsi au code source et à la liste des classes compilées. Assez rapidement, les fichiers AWSDownloadService* attirent l’œil. Jetons un coup d’oeil au premier AWSDownloadService.

Morceau choisi, la déclaration des constantes :

# static fields
.field public static final AWS_ACCESS_KEY:Ljava/lang/String; = "AKIAJ6ONSYEYNIVWUB5A"

.field public static final AWS_ACTION_DONE:Ljava/lang/String; = "android.intent.action.AWS_DOWNLOAD_DONE"

.field public static final AWS_ACTION_RETRY:Ljava/lang/String; = "android.intent.action.AWS_Download_WAKEUP"

.field public static final AWS_ACTION_UPDATE:Ljava/lang/String; = "android.intent.action.AWS_DOWNLOAD_UPDATE"

.field public static final AWS_BUCKET:Ljava/lang/String; = "dx-s3-2k-xcom"

.field public static final AWS_DOWNLOAD_CHANGED:I = 0x1

.field public static final AWS_DOWNLOAD_PROGRESS:I = 0x0

.field public static final AWS_DOWNLOAD_REQUIRED:I = 0x1

.field public static final AWS_NO_DOWNLOAD_REQUIRED:I = 0x0

.field public static final AWS_SECRET_KEY:Ljava/lang/String; = "IqNaoClCIxG2Uj6Nq1FL2XsDR1RrjT3rrH3nDrtW"

On y retrouve le bucket que l’application essayait de télécharger, le couple AWS Access Key / Secret, et également une petite constante qui a l’air bien sympatique : AWS_DOWNLOAD_REQUIRED. Ça commence à sentir plutôt bon.

Pour voir un peu ce qui se passe plus concrètement, je continue l’exploration du fichier. Malheureusement le format du fichier que je consulte est du Smali. Android Studio ne me montre pas directement le bytecode Android (Dalvik Virtual Machine bytecode) mais le code assembleur correspondant. Je ne sais pas si vous avez fait de l’assembleur, mais quelque soit le type d’assembleur, ce n’est pas ce qu’il y a de plus user friendly. Néanmoins on peut voir que certaines methods ont un peu de potentiel pour résoudre mon problème :

Il est donc temps de lâcher Android Studio et de commencer à regarder des outils un peu plus élaborés pour les tâches qui nous concernent. J’ai besoin de 3 choses :

  1. De quoi convertir le code Smali en Java, pour en faciliter la lecture
  2. Quelque chose pour décompiler mon APK et pouvoir effectivement le modifier
  3. De quoi recompiler tout ça après modification et proprement tout remettre en place pour passer les contrôles d’intégrité du système

Etape 5 : activer le mode facile et conclure

Ok, pour décompiler l’APK je suis tombé sur un outil magique : APK Easy Tool. L’outil parfait pour décompiler un APK, le recompiler et le signer.

L’utilisation est tellement simple qu’il suffit juste d’appuyer sur les bons boutons, dans le bon ordre, et au bon moment.

Et pour lire le code ? Pas de soucis, il suffit de demander, et voilà une 2ème application magique : https://bytecodeviewer.com/

A exécuter directement sur l’APK pour le décompiler et vous permettre de voir côte à côte le code Smali et Java :

OK, maintenant qu’on a tout ça on va aller voir un peu ailleurs, juste par curiosité. Histoire de voir si on ne pourrait pas tout simplement faire croire au device qu’il n’est pas sur une tablette Amazon, mais sur un Android standard. Tant qu’à faire …

Le fichier le plus volumineux et également le premier exécuté est “com/tt2kgames.xcomew/UE3JavaApp”, allons donc l’inspecter. Je ne vous fait pas un copier/coller complet du fichier (445 Ko, imaginez) mais on y trouve un attribut “bIsAmazon” qui est fixé par défaut à “false”. Et en le regardant plus attentivement, on trouve le code suivant :

Bref, si le Manufacturer contient “Amazon” notre exécutable fixe son attribut “bIsAmazon” à vrai, ce qui déclenche ensuite tous les comportements néfastes que l’on commence à connaitre, mais surtout cette satanée mise à jour qui ne fonctionne pas sur ma tablette !

Il suffit donc de retrouver cette toute petite comparaison dans le code Smali, ce qui est assez simple, car on peut faire une recherche sur la chaîne de caractère “Amazon”, car celle-ci est déclarée en tant que constante et utilisée directement. On tombe sur une seule occurrence :

Il ne reste plus qu’à aller modifier le fichier “UE3JavaApp.smali” décompilé par APK Easy Tool et remplacer “Amazon” à la ligne 6223 par absolument tout ce que l’on souhaite. Moi j’ai mis “Rosebud”.

Attention, Bytecode Viewer s’exécute normalement directement sur votre APK si vous m’avez suivi jusque là. Donc si vous modifiez la ligne avec l’éditeur intégré, il ne s’agira pas du fichier manipulé par APK Easy Tool, ce qui ne servira à rien. Je préfère préciser. Utilisez donc votre éditeur préféré pour aller modifier le fichier dans “*\APK Easy Tool\1-Decompiled APKs\com.tt2kgames.xcomew_1.7.0-APK_Award-Mod\smali\com\tt2kgames\xcomew”.

Ensuite ? Et bien, dans APK Easy Tool, 4 clics : Compile → Sign APK → ZipAlign → Install APK.

Je lance le jeu, il cherche la mise à jour. Et VOILÀ !

Victoire !

Le jeu fonctionne très bien, de manière parfaitement fluide et très agréable. Et j’espère qu’il sera au moins aussi amusant d’y jouer que d’essayer de le faire fonctionner.

Autres ressources de reverse engineering

Si vous êtes encore là, c’est probablement que cette petite histoire vous a plus. Pour perdre encore un peu de temps et si vous appréciez ce type de choses, je ne peux que vous encourager à suivre le compte Foone sur Twitter, c’est un véritable magicien steampunk.

Pour vous faire démarrer, un petit thread avec une télécommande de téléviseur Logitech, un serveur IIS7, un shell linux et encore bien d’autres choses.

--

--

Jonathan Gérardin
Meetech - We Love Tech

Consultant en Archi SI, je parle des tendances actuelles: #Blockchain, #IA, #API-M, mais également des fondamentaux. Manager chez #Wavestone - #EPITA Promo 2008