Nous inversons les mobiles 1 sous Android. Comment ajouter un peu de fonctionnalité et abandonner quelques soirées

Je dois dire tout de suite que malgré le titre fort, comme quelqu'un pourrait le penser, de l'article - il n'y aura pas du tout de hardcore direct. Et aussi, bien que l'inversion soit le plus souvent associée à la sécurité de l'information, il n'y aura encore rien de tel, car il n'était pas nécessaire de contourner les protections (à l'exception d'une petite minification). Il n'y en a pas. Mobile 1s est un produit gratuit dans lequel aucun chèque, protection et fonction payante n'est intégré.


Je lance donc un appel à ceux qui espèrent voir l'utilisation de désobfuscateurs, l'utilisation de Frida / Xposed, d'autres logiciels délicats - vous ne trouverez rien d'intéressant pour vous. Ici, nous allons simplement utiliser apktool, baksmali, apksigner, adb, jadx, console, éditeur de texte, javac, d8 et tout.


Je veux également décevoir à l'avance ceux qui attendaient une analyse approfondie de la plateforme, ou sa forte modification. Seules de petites modifications seront apportées à quelques fichiers, et en fait, même ces quelques fichiers que je n'ai pas bien compris, tout en haut.


Comment tout a commencé


Je vais vous expliquer pourquoi j'ai soudain eu l'idée de me lancer dans le téléphone mobile pendant 1 seconde. Pour le moment, je fais du développement natif pour Android depuis un an maintenant, mais avant cela, je travaillais en tant que programmeur depuis 4 ans, et au cours de la dernière année et demie, nous avons souvent travaillé spécifiquement avec la plate-forme mobile. Malgré le fait qu'elle répondait aux besoins de base, elle avait également de nombreux inconvénients (en plus du langage de programmation). En particulier, il était impossible d'intégrer humainement une bibliothèque externe, au moins par des moyens réguliers et avec notre réserve de connaissances d'alors. Tout était également très triste, par exemple, avec la fonctionnalité d'afficher des étiquettes sur une carte. Toute la possibilité de sa configuration était de spécifier du texte pour les étiquettes lorsque vous appuyez dessus. À cette époque, la seule façon de contourner ce problème était d'utiliser l'objet spécial «Champ de document HTML», mais il y avait des problèmes. Au moment de travailler avec 1s, toutes mes connaissances dans le développement natif pour Android étaient dans une paire HelloWorld, donc je n'ai même pas pensé à inverser le téléphone mobile avec 1s, nous n'avons pas résolu diverses questions des clients sur l'extension non standard de 1s, ou nous avons vu des natifs très simples les applications qui ont été placées à côté et intégrées de manière croisée / oblique avec 1s (et l'accord de licence 1s semble interdire les modifications dans la plate-forme elle-même).


Donc, la première raison de l'inversion 1c était que je me suis intéressé et ce que je peux faire avec la base de connaissances actuelle. Je dois dire tout de suite que l'expérience du développement natif n'était pas de dire ce qui est nécessaire, dans le travail quotidien presque rien de ce qui sera décrit ci-dessous n'est trouvé. Donc, en principe, tout 1snik moyen assis pendant plusieurs jours / semaines serait en mesure de tout comprendre.


La deuxième raison est que je voulais juste essayer de choisir l'apk des autres, parce qu'avant cela, il me manquait cette couche de connaissances assez large, et puisque je devais commencer quelque part, je me suis dit qu'il n'y avait que 1s.


Premiers pas


La première chose que j'ai faite, même lorsque l'idée d'inverser les 1 ne vaguait que vaguement dans ma tête, j'ai simplement fait glisser le fichier apk de l'application sur la fenêtre AndroidStudio avec la souris. Je veux dire tout de suite que je me sentais un peu triste, car la plupart du code 1c est écrit en C ++ et se trouve dans les bibliothèques .so, et il est plus difficile de les inverser, et ce n'était pas intéressant. Mais le fichier classes.dex a complètement attiré mon attention, d'autant plus que sa taille modeste m'a donné l'occasion de supposer qu'il serait facile de l'inverser. En général, cela s'est avéré de cette façon. Soit dit en passant, une conséquence intéressante du fait que la plupart du code en C ++ est que de nombreuses méthodes et classes ont évité le traitement avec ProGuard. Il est difficile de faire une interopérabilité avec un code minifié;).


Voici ce que j'ai vu dans la fenêtre du studio (démonté la version x86 pour fonctionner avec l'émulateur et non le vrai appareil)


Comme vous pouvez le voir dans la capture d'écran ci-dessus, l'application a été à peine réduite (en termes de changement de nom des classes et des méthodes). De plus, vous pouvez voir qu'il y a très peu de code en Java et que les bibliothèques occupent presque tout l'espace.


Après avoir poussé une liste de classes pendant un certain temps, j'ai vu une curieuse classe MapImpl qui jette le soupçon que c'est lui qui était responsable d'une situation aussi triste avec la personnalisation de l'affichage des étiquettes.


Structure de MapImpl


La liste des méthodes et leurs signatures ont inspiré l'espoir que tout serait très simple, et j'ai décidé d'examiner le code smali, après quoi je suis devenu très tendu et suis allé lire la liste des commandes smali, et comment le lire / écrire. Cependant, c'est à ce stade que j'ai décidé que la question s'annonçait simple et de courte durée et, en conséquence, la voici, l'occasion de jouer avec le renversement. Ayant décidé de lui consacrer quelques nuits (à quel point je me trompais cruellement), je me suis couché avec une âme calme.


Faire des plans


Après m'être réveillé le matin, j'ai décidé que jusqu'à ce point je n'avais même jamais été engagé dans la substitution de ressources dans les applications, pas comme les changements de logique, je n'avais rien à escroquer tout de suite pour remplacer l'activité par une carte, il valait mieux aborder la solution du problème par petites étapes. Après cela, je me suis fait une courte liste d'étapes qui devaient me conduire au point final de mon voyage sous la forme d'une plate-forme mobile fonctionnelle avec mes modifications. Cette liste était la suivante:


  1. Préparez la configuration 1c pour la plate-forme mobile, dont toutes les fonctionnalités sont d'afficher deux étiquettes sur la carte.
  2. Déballez l'apk de la plate-forme mobile, emballez sans modifications, assurez-vous que la configuration fonctionne.
  3. Apportez de petits changements aux fichiers smali qui ne changent presque rien, mais qui peuvent être vus dans les journaux ou modifiez la logique de travail, assemblez et assurez-vous que cela fonctionne.
  4. Pour dépasser l'activité smali avec une carte en java, ou écrire une activité avec la même interface, remplacez l'activité dans l'application sans changer de fonctionnalité. Comme alternative, si la première est trop paresseuse ou compliquée, écrivez une classe qui fera une partie du travail pour MapImpl et ajoutez un appel à ses méthodes depuis smali MapImpl.
  5. Ecrivez cet article (j'ai longtemps voulu écrire quelque chose, mais d'autres idées me trottent encore dans la tête, mais il semblait que c'était finalement un sujet assez digne)

Eh bien, au moins avec le premier paragraphe, je n'ai eu aucun problème. J'ai téléchargé la version éducative gratuite de la plate-forme, téléchargé la plate-forme mobile, déployé le tout sur mon ordinateur (1C, avez-vous déjà une version de formation pour linux, j'ai juste une autre impulsion pour quitter Windows, et maintenant je dois y travailler). La configuration est très simple, au démarrage elle appelle la fonction ShowOnMap (), qui transmet deux points sur la carte. C’est tout.


Mais avec le deuxième point, cela s'est avéré plus triste à cause de mon analphabétisme. Mais quoi, les problèmes étaient avec tous les points sauf le premier.


Je reconditionne l'application sans changer le code


Google m'a immédiatement dit qu'il existe un excellent outil apktool qui permet littéralement deux commandes pour analyser apk dans des fichiers .smali, ainsi que pour les réassembler. J'ai décidé de l'utiliser. J'ai déballé apk avec la commande suivante (ci-après, malgré le fait que parfois j'ai fait une partie du travail sur linux, je vais donner toutes les commandes pour Windows):


apktool.bat d .\sourceApk\1cem-x86.apk -o .\build\unpacked 

et a obtenu le répertoire décompressé dans lequel un tas de fichiers se trouvait, c'était pour travailler avec eux à l'avenir. Apk emballé en arrière avec la commande


 apktool.bat b .\build\unpacked -o .\build\1cem-x86.apk 

Essayer de l'installer sur un émulateur


 adb install .\build\1cem-x86.apk 

J'ai eu l'erreur suivante:


 Performing Streamed Install adb: failed to install .\build\1cem-x86.apk: Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed to collect certificates from /data/app/vmdl1115375036.tmp/base.apk: Attempt to get length of null array] 

En général, il fallait accorder plus d'attention à la base. Bien sûr, je savais qu'il y avait des signatures, et qu'elles étaient divisées en ventes et en versions, mais je ne savais pas que les applications n'étaient pas installées sans signatures du tout. J'ai décidé d'utiliser ma signature de débit et de signer l'équipe apk


 apksigner.bat sign --ks C:\Users\neikist\.android\debug.keystore .\build\1cem-x86.apk 

J'ai réussi à exécuter l'application. Certes, au lieu d'une carte à deux points, un écran gris avec une signature google ci-dessous m'attendait. Après avoir gratté le navet, j'ai décidé que le fait est que la clé pour Google maps à partir de 1s est utilisée, ce qui ne fonctionne pas avec ma signature. Par conséquent, en accédant à la console du développeur sur le site Web Google, j'ai créé un nouveau projet pour travailler avec Google Maps API sur Android, j'ai reçu une clé API, que j'ai spécifiée dans res / values ​​/ strings.xml dans la ligne google_maps_key, et j'ai également ajouté ma clé de partage aux autorisations pour le projet . J'ai remballé et re-signé apk, je l'ai lancé, et finalement tout a fonctionné à nouveau.


J'ajoute mes logs


La prochaine chose que je voulais faire était d'automatiser le lancement de l'application et d'ajouter la configuration 1c à la plate-forme. Manuellement, après chaque montage, cela serait toujours un gâchis.


Au début, j'ai googlé et essayé d'utiliser les utilitaires jadx ou dex2jar, afin de ne pas déranger la lecture de smali, mais de lire le code java plus familier, mais pour une raison quelconque, cela n'a pas fonctionné (plus tard, jadx a réussi à entrer dans une sorte de chamanisme). J'ai dû démonter Smali, car il s'est avéré que ce n'était pas aussi terrible que j'avais peur.


Pour comprendre comment la plate-forme mobile reçoit une commande de la plate-forme de bureau pour lancer la configuration lors de la connexion via adb, j'ai décidé de regarder les points d'entrée de l'application et d'ajouter la sortie aux journaux d'intentions et autres informations utiles. J'ai décidé de commencer avec l'application ( com.e1c.mobile.E1cApplication ) et l'activité ayant android.intent.action.MAIN dans le filtre d'intention ( com.e1c.mobile.App ). J'étais également intéressé par le récepteur com.e1c.mobile.Starter avec un filtre d'intention intéressant sur com.e1c.mobile.START_TEMPLATE et com.e1c.mobile.START_CMD . C'est très similaire au fait qu'il accepte des intentions avec des commandes 1s, et c'est lui qui démarre la configuration à partir du modèle.


Malheureusement, je n'ai rien trouvé d'intéressant dans E1cApplication , tout ce qui se passe là-bas est d'installer votre gestionnaire sur les caches.


Mais dans les deux autres classes, Starter et App , il y avait beaucoup plus d'informations et cela s'est avéré très utile. La App.onCreate(Landroid/os/Bundle;)V est assez grande, donc je ne la donnerai pas dans son intégralité, je ne donnerai que les parties qui m'intéressent.


App onCreate, tranche de méthode
 invoke-virtual {p0}, Lcom/e1c/mobile/App;->getIntent()Landroid/content/Intent; #     p1 move-result-object p1 new-instance v0, Ljava/lang/StringBuilder; invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V invoke-virtual {v0, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder; const-string v1, ".onCreate() " invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; invoke-virtual {v0, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder; invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; move-result-object v0 # ,  ? invoke-static {v0}, Lcom/e1c/mobile/Utils;->V(Ljava/lang/String;)V 

Il peut être vu à partir de la section de code ci-dessus que l'application reçoit l'intention, met un lien vers elle dans le registre p1 , ajoute la StringBuilder au nom et à la méthode de classe via StringBuilder et transmet V(Ljava/lang/String;)V de la classe Utils à la méthode statique. Apparemment, une sorte de votre propre enregistreur. Après cela, l'intention est vérifiée pour des données supplémentaires, et, si disponible, Uri obtenu à partir de celle-ci, à partir de laquelle, à son tour, une chaîne est obtenue par la méthode getQuery() et transmise à la classe Starter . onCreate n'ai vu aucune autre étude sur onCreate , j'ai jeté un coup d'œil et je me suis assuré que les actions étaient assez standard. Sauf si la vue semble être créée par programme par une classe complètement différente, au lieu d'utiliser LayoutInflater , mais je n'ai pas creusé très profondément, donc tout est possible et pas comme je le pensais. La prochaine classe où je suis allé est Starter . Heureusement, il a été mentionné dans l'activisme et m'a intéressé au manifeste.


Malheureusement, la méthode qui a été appelée à partir de l'activité s'est avérée être native ( .method static native startCmd(Ljava/lang/String;)V ), j'ai donc fait attention à la méthode onRecieve (dans laquelle l'application accepte les événements auxquels elle est abonnée). Je ne donnerai pas le code, cela m'intéressait que l'intention soit également enregistrée en utilisant la méthode V(Ljava/lang/String;)V de la classe Utils . Il s’avère qu’il suffit d’ajouter un peu de code smali à cette méthode - et je vais d'ailleurs récupérer mes logs aux mêmes endroits où les développeurs de la plateforme les ont conçus. En allant à la classe Utils, j'ai immédiatement vu 2 méthodes vides, V et W. Apparemment, elles sont vides car lors de la compilation de la version finale, elles ont été supprimées. Je viens d'ajouter un enregistrement de la chaîne passée au android.utils.Log standard. Pour ce faire, modifiez le nombre de registres locaux requis de 0 à 1 (pour le slogan), placez cette chaîne dans le registre v0 et enregistrez également l'appel aux méthodes Log


Implémentation des méthodes W et V
  .method public static V(Ljava/lang/String;)V .locals 1 const-string v0, "neikist_V" invoke-static {v0, p0}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I return-void .end method .method public static W(Ljava/lang/String;)V .locals 1 const-string v0, "neikist_W" invoke-static {v0, p0}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I return-void .end method 

J'ai lancé le mobile à partir du bureau 1s et j'ai reçu les journaux suivants


Résultats de lancement
  2019-10-25 19:21:31.537 29960-29960/? V/neikist_V: com.e1c.mobile.Starter@139df1c.onReceive( android.app.ReceiverRestrictedContext@346b725, Intent { act=com.e1c.mobile.START_TEMPLATE flg=0x400010 cmp=com.e1c.mobile/.Starter (has extras) } ) 2019-10-25 19:21:31.537 29960-29960/? V/neikist_V: context.getPackageName() = com.e1c.mobile 2019-10-25 19:21:31.537 29960-29960/? V/neikist_V: intent.getAction() = com.e1c.mobile.START_TEMPLATE 2019-10-25 19:21:31.537 29960-29960/? V/neikist_V: App.sActivity = null 2019-10-25 19:21:31.537 29960-29960/? V/neikist_V: templatePath = /sdcard/Download/mobilegeoMA/1cema.xml 2019-10-25 19:21:31.537 29960-29960/? V/neikist_V: debugUrl = tcp://127.0.0.1:1560 2019-10-25 19:21:42.216 29960-29960/com.e1c.mobile V/neikist_V: com.e1c.mobile.App@423609b.onCreate() Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.e1c.mobile/.App bnds=[35,252][237,505] } 2019-10-25 19:21:42.651 29960-29960/com.e1c.mobile V/neikist_V: com.e1c.mobile.App$4@4c70381.run() adMobAppId = ca-app-pub-3940256099942544~3347511713 2019-10-25 19:21:42.687 29960-30009/com.e1c.mobile V/neikist_V: App.requestPermission() shouldShowRequestPermissionRationale = false 2019-10-25 19:21:46.138 29960-30009/com.e1c.mobile V/neikist_V: App.isLowBattery() sLastBatteryPercent = 1.0 

Comme vous pouvez le voir, il y a toutes les informations nécessaires pour automatiser le lancement de l'application via adb. Certes, à ce stade, j'ai attrapé un double visage. Tout d'abord, j'ai finalement récupéré les clés avec lesquelles jadx a maîtrisé la traduction en java (il est clair que je devrais quand même écrire en smali, mais quand même). Et le deuxième facespalm s'est avéré être le fait que j'ai réalisé que je tourmentais la plate-forme du développeur en vain (nécessaire uniquement pour le développement et le débogage des configurations), il serait plus correct d'inverser la version de génération des applications 1s, et il y a des projets gradle semi-prêts pour l'assemblage, il y a un fichier avec une liste de dépendances et d'autres petits pains. J'étais un peu triste à ce sujet - et j'ai quand même décidé de terminer ce que j'avais commencé. Quoi qu'il en soit, pour le bien du fan, je fais tout cela, et pas pour des raisons pratiques.


Lancement de la plateforme via adb


J'ai décidé d'automatiser l'installation et le lancement via l'application adb en utilisant gradle. Quoi qu'il en soit, à ce moment-là, j'avais déjà écrit une petite liste de shuffles (décompression, copie des ressources modifiées et des fichiers smali dans le répertoire avec l'application décompressée, empaquetage, signature, en fait, toutes les tâches ne sont que des coureurs pour les commandes de la console). Aux tâches existantes ont ajouté les commandes suivantes


  adb install ./build/1cem-x86.apk adb shell pm grant com.e1c.mobile android.permission.READ_EXTERNAL_STORAGE adb push src/1cConfiguration /storage/emulated/0/Download adb shell am start -n com.e1c.mobile/.App -a android.intent.action.MAIN -c android.intent.category.LAUNCHER adb shell am broadcast -n com.e1c.mobile/.Starter -a com.e1c.mobile.START_TEMPLATE -e templatepath /storage/emulated/0/Download/1cConfiguration/1cema.xml 

Par la séquence de commandes ci-dessus, nous installons apk, donnons à l'application installée l'autorisation de lire le disque, copions la configuration 1s sur le périphérique et donnons des commandes pour démarrer 1s et charger la configuration. Cela ne fonctionne pas en passant sur toutes les versions de l'androïde, mais j'ai testé sur l'api 28, et en conséquence, cette séquence fait tout correctement. Sur les versions plus récentes, il peut y avoir des problèmes avec la délivrance des droits.


Je suis impliqué dans le traitement des données à partir de 1s et modifie les tags


Ici, j'avais plusieurs options pour le développement ultérieur des événements.


  1. Je règle directement MapImpl.smali, ce qui promettait d'être une tâche assez difficile, étant donné que la bibliothèque pour travailler avec Google maps est très sérieusement minimisée, et je devrais tout écrire dans la syntaxe smali.
  2. J'utilise le fichier MapImpl.java obtenu grâce à jadx, j'y apporte des modifications, j'utilise les classes que j'utilise pour le remplacer par des talons, distiller en smali, remplacer le fichier et l'emballer. L'option m'a semblé tendue car cela faisait vraiment mal d'avoir beaucoup de prises, en plus il y avait des problèmes avec certaines d'entre elles que je ne voulais pas choisir.
  3. Je combine les approches 1 et 2, j'insère les appels d'une classe d'extension spéciale MapImplExtenstion.java dans du code smali qui étend une partie de la logique - en conséquence j'ai choisi cette option, tout simplement parce qu'elle est plus simple que les première et deuxième options, et me semblait encore plus compliquée que l'option 4, mais moins laborieux (ouais, rêve, naïf).
  4. En général, remplacez Google maps par autre chose, par exemple Yandex. Pour effectuer tout le travail dans un autre projet, simplement en répétant les signatures MapImpl, puis réduisez la décompression et déposez simplement les fichiers smali finis avant de compresser l'application aux endroits appropriés. Il n'y a aucun problème avec les bibliothèques minifiées, mais je devrais m'inscrire pour obtenir une clé pour les cartes, créer un projet séparé, s'embêter avec la connexion des ressources et plus encore.
  5. Identique à l'option 4, mais remplacez Google par ... Google. Mais ici, je soupçonnais qu'il serait possible de minimiser les cartes SDK 1 en 1, et il n'y avait aucune envie d'expérimenter.

Après la troisième option, j'ai créé 2 sous-répertoires dans src: java, pour MapImplExtenstion.java et stubJava pour les fichiers qui sont nécessaires uniquement pour la compilation. Depuis que j'ai décidé de modifier un peu les étiquettes, je me suis intéressé aux deux domaines suivants:


Champs Xj: Ljava / util / ArrayList et Xk: Ljava / util / ArrayList
  .field private final Xj:Ljava/util/ArrayList; .annotation system Ldalvik/annotation/Signature; value = { "Ljava/util/ArrayList<", "Lcom/google/android/gms/maps/model/LatLng;", ">;" } .end annotation .end field .field private Xk:Ljava/util/ArrayList; .annotation system Ldalvik/annotation/Signature; value = { "Ljava/util/ArrayList<", "Lcom/google/android/gms/maps/model/MarkerOptions;", ">;" } .end annotation .end field 

En utilisant jadx pour décompiler en java, j'ai trouvé dans le code décompilé une méthode chargée de les remplir


Méthode KN
  void kN() { Bundle extras = getIntent().getExtras(); if (extras != null) { this.Sd = extras.getLong("native"); String[] stringArray = extras.getStringArray("titles"); double[] doubleArray = extras.getDoubleArray("coords"); if (doubleArray != null && doubleArray.length > 0) { for (int i = 0; i < stringArray.length; i++) { int i2 = i * 2; LatLng latLng = new LatLng(doubleArray[i2], doubleArray[i2 + 1]); this.Xj.add(latLng); this.Xk.add(new MarkerOptions().c(latLng).dS(stringArray[i])); } } } } 

En conséquence, la classe MapImplExtension a ajouté la méthode ArrayList [] kN (titres String [], coordonnées [] doubles) qui, dans le premier élément du tableau, renverra une liste qui devra être placée dans Xj, et dans le second une liste pour Xk.


Classe MapImplExtension
  package com.e1c.mobile; import java.util.ArrayList; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.MarkerOptions; class MapImplExtension { private MapImpl mapImpl; MapImplExtension(MapImpl mapImpl){ this.mapImpl = mapImpl; } ArrayList[] kN(String[] titles, double[] coordinates) { ArrayList[] result = new ArrayList[2]; ArrayList<LatLng> xj = new ArrayList<>(); ArrayList<MarkerOptions> xk = new ArrayList<>(); result[0] = xj; result[1] = xk; int coordinatesOffset; if (coordinates != null && coordinates.length > 0) { for (int i = 0; i < titles.length; i++) { coordinatesOffset = i * 2; LatLng latlng = new LatLng(coordinates[coordinatesOffset], coordinates[coordinatesOffset + 1]); xj.add(latlng); xk.add(new MarkerOptions().c(latlng).dS(" \n" + titles[i])); } } return result; } } 

Compilé avec les commandes suivantes, d'abord en classe, puis en dex, puis décompilé en smali pour ensuite emballer avec le reste des fichiers


  javac -encoding UTF-8 -d .\build -source 1.8 -target 1.8 -sourcepath .\src\stubJava .\src\java\com\e1c\mobile\MapImplExtenstion.java d8.bat .\build\com\e1c\mobile\MapImplExtension.class --output .\build java -jar C:\Path\to\baksmali\baksmali.jar d .\build\classes.dex -o .\build\unpackedOwn 

Ajout du champ d'extension de type de notre nouvelle classe à MapImpl.smali, et ajout de son initialisation


Ajout d'un champ
 #    .field private extension:Lcom/e1c/mobile/MapImplExtension; #  ,  return-void new-instance v0, Lcom/e1c/mobile/MapImplExtension; invoke-direct {v0, p0}, Lcom/e1c/mobile/MapImplExtension;-><init>(Lcom/e1c/mobile/MapImpl;)V iput-object v0, p0, Lcom/e1c/mobile/MapImpl;->extension:Lcom/e1c/mobile/MapImplExtension; 

Et également remplacé le traitement dans la classe MapImpl des données de 1 par le traitement dans la classe MapImplExtension


Modifications de MapImpl.kN ()
  # v0 - ; v1 - ; v2 - extension; #  MapImplExtension.kN(...) iget-object v2, p0, Lcom/e1c/mobile/MapImpl;->extension:Lcom/e1c/mobile/MapImplExtension; invoke-virtual {v2, v1, v0}, Lcom/e1c/mobile/MapImplExtension;->kN([Ljava/lang/String;[D)[Ljava/util/ArrayList; # v0 -   ; v1 - | ; v2 -    move-result-object v0 const/4 v2, 0x0 aget-object v1, v0, v2 iput-object v1, p0, Lcom/e1c/mobile/MapImpl;->Xj:Ljava/util/ArrayList; const/4 v2, 0x1 aget-object v1, v0, v2 iput-object v1, p0, Lcom/e1c/mobile/MapImpl;->Xk:Ljava/util/ArrayList; 

Après avoir emballé et lancé l'application, j'ai vu avec joie que tout fonctionnait. Mais pour le moment, je n'ai rien fait qui puisse étendre les capacités de la plate-forme mobile. Je viens de changer les en-têtes des marques sur la carte. Mais voulant changer l'image des tags, je me suis bien détaché et enfoui assez longtemps dans les sources et la documentation.


Ajout de nouvelles fonctionnalités


Tout d'abord, une petite explication de la façon dont le marqueur de l'étiquette est défini. La classe MarkerOptions possède une public MarkerOptions icon (BitmapDescriptor iconDescriptor) à laquelle un objet créé par l'une des méthodes statiques de la classe BitmapDescriptorFactory est BitmapDescriptorFactory .


Et ici, en fait, une déception m'attendait. Comme 1c n'utilise pas cette activité, les classes correspondantes ont simplement été supprimées lors de la minification. J'ai dû les restaurer, et c'était douloureux et long. Plus précisément, les classes étaient là, mais elles ont été renommées et ne contenaient pas les méthodes nécessaires. En sélectionnant les types de bibliothèque complète pour travailler avec les cartes - par des signatures et des constantes, j'ai découvert la correspondance des classes de bibliothèque avec les classes d'application, ajouté les méthodes et les champs nécessaires, reconstruit, et j'étais heureux que cette quête soit finalement terminée, car elle a finalement fonctionné, mais pas dès la première tentative (plusieurs fois tondu avec des signatures et des classes minifiées confuses).


Le résultat (extérieurement pas très impressionnant). Deux points avec les coordonnées (45; 45) et (46; 46)


Étant donné que le volume du code modifié est assez important - je ne l'inclurai pas dans l'article, je ne donnerai que des modifications de signature. Si quelqu'un s'intéresse à toutes les modifications que j'ai apportées à cette étape - vous pouvez regarder dans ce commit .


    Lcom/google/android/gms/maps/model/MarkerOptions -   .method public final icon(Lcom/google/android/gms/maps/model/a;)Lcom/google/android/gms/maps/model/MarkerOptions;   com.google.android.gms.internal.fh   : b zza(int) throws RemoteException; b zza(String) throws RemoteException; b zzb(String) throws RemoteException; b zzi() throws RemoteException; b zza(float) throws RemoteException; b zza(Bitmap) throws RemoteException; b zzc(String) throws RemoteException;   com.google.android.gms.internal.fj     h       com.google.android.gms.maps.model.b (BitmapDescriptorFactory)    public static a fromResource(int) public static a fromAsset(String) public static a fromFile(String) public static a fromPath(String) public static a defaultMarker() public static a defaultMarker(float) public static a fromBitmap(Bitmap)      MapImplExtension.java   xk.add(new MarkerOptions().c(latlng).dS("  " + titles[i]));     MarkerOptions markerOptions = new MarkerOptions() .c(latlng) .dS("  " + titles[i]); if (i == 0) { //      markerOptions.icon(b.defaultMarker(b.HUE_YELLOW)); } xk.add(markerOptions); 

Pour résumer - l'expérience a été assez intéressante. Du moins pour moi. J'ai aidé à comprendre certains outils, j'ai appris un peu mieux le travail du format android et bytecode pour dalvik / art, l'expérience de la sélection de code minifié serait également utile (il y avait déjà un cas où dans la version finale de R8 je coupais les champs de classe qui étaient réellement utilisés, à l'époque uniquement par la méthode Je l'ai compris, maintenant cela ne causerait pas de problèmes).


Si quelqu'un est intéressé à tout répéter lui-même et éventuellement à le ramasser encore plus - j'ai posté sur github toutes les sources avec un script gradle tordu qui se construit à partir de l'apk original modifié.

Source: https://habr.com/ru/post/fr475266/


All Articles