Wir kehren mobile 1s unter Android um. So fügen Sie ein wenig Funktionalität hinzu und lassen ein paar Abende aus

Ich muss gleich sagen, dass es trotz des lauten Titels des Artikels, wie man denkt, überhaupt keinen direkten Hardcore geben wird. Und trotz der Tatsache, dass das Zurücksetzen meistens mit Informationssicherheit verbunden ist, wird es wieder nichts Vergleichbares geben, da keinerlei Schutzmaßnahmen umgangen werden mussten (mit Ausnahme einer kleinen Einschränkung). Es gibt keine von ihnen. Mobile 1s ist ein kostenloses Produkt, in das keine Schecks, Schutzmaßnahmen und kostenpflichtigen Funktionen integriert sind.


Deshalb appelliere ich an diejenigen, die hoffen, den Einsatz von Deobfuscatoren, den Einsatz von Frida / Xposed und anderer kniffliger Software zu sehen - Sie werden nichts Interessantes für sich finden. Hier verwenden wir nur apktool, baksmali, apksigner, adb, jadx, console, text editor, javac, d8 und alles.


Ich möchte auch diejenigen im Voraus enttäuschen, die auf eine gründliche Analyse der Plattform oder ihrer starken Modifikation gewartet haben. Es werden nur kleine Änderungen an wenigen Dateien vorgenommen, und tatsächlich sind sogar diese wenigen Dateien, die ich nicht gründlich ausgearbeitet habe, ganz oben.


Wie alles begann


Ich erzähle dir ein bisschen, warum ich plötzlich auf die Idee gekommen bin, für 1s in das Handy zu steigen. Im Moment mache ich seit einem Jahr native Entwicklung für Android, aber vorher habe ich 4 Jahre als Programmierer gearbeitet und in den letzten eineinhalb Jahren haben wir oft speziell mit der mobilen Plattform gearbeitet. Trotz der Tatsache, dass sie die Grundbedürfnisse befriedigte, hatte sie auch eine Menge Minuspunkte (zusätzlich zur Programmiersprache). Insbesondere war es unmöglich, eine externe Bibliothek zumindest mit regelmäßigen Mitteln und mit unserem damaligen Wissensspeicher menschlich einzubetten. Sehr traurig war zum Beispiel auch die Funktionalität, Beschriftungen auf einer Karte anzuzeigen. Die gesamte Möglichkeit der Konfiguration bestand darin, Text für Beschriftungen anzugeben, wenn Sie darauf tippen. Zu dieser Zeit war der einzige Weg, dies irgendwie zu umgehen, die Verwendung des speziellen Objekts „HTML-Dokumentfeld“, aber es gab Probleme damit. Zum Zeitpunkt der Arbeit mit 1s steckte mein gesamtes Wissen in der nativen Entwicklung für Android in einem HelloWorld-Paar, sodass ich nicht einmal daran gedacht habe, das Mobiltelefon mit 1s umzukehren. Entweder haben wir verschiedene Fragen von Kunden zur nicht standardmäßigen Erweiterung von 1s nicht gelöst oder wir sahen sehr einfache native Die Anwendungen, die daneben platziert und schief in 1s integriert wurden (und die 1s-Lizenzvereinbarung scheint Änderungen auf der Plattform selbst zu untersagen).


Der erste Grund für 1c-Umkehrung war, dass ich mich dafür interessierte und wusste, was ich mit der aktuellen Wissensbasis anfangen kann. Ich muss sofort sagen, dass die Erfahrung der einheimischen Entwicklung nicht zu sagen war, was benötigt wird, in der täglichen Arbeit wird fast nichts von dem, was im Folgenden beschrieben wird, nicht gefunden. Im Prinzip wäre also wahrscheinlich jeder durchschnittliche 1snik, der mehrere Tage / Wochen sitzt, in der Lage, alles herauszufinden.


Der zweite Grund ist, dass ich nur versuchen wollte, die apk anderer Leute auszuwählen, weil mir vorher diese ziemlich breite Wissensschicht gefehlt hat, und da ich irgendwo anfangen musste, fiel es mir nur 1s ein.


Erste Schritte


Als erstes habe ich die apk-Datei der Anwendung einfach mit der Maus auf das AndroidStudio-Fenster gezogen, auch wenn die Idee, 1s umzukehren, nur vage in meinem Kopf verirrt war. Ich möchte gleich sagen, dass ich ein wenig traurig war, weil der Großteil des 1c-Codes in C ++ geschrieben ist und in .so-Bibliotheken liegt, und es schwieriger ist, sie umzukehren, und es war nicht interessant. Aber die classes.dex-Datei hat meine Aufmerksamkeit vollständig auf sich gezogen, zumal ich aufgrund ihrer bescheidenen Größe davon ausgehen konnte, dass es leicht sein würde, sie umzukehren. Im Allgemeinen stellte sich heraus, dass dies so war. Eine interessante Folge der Tatsache, dass der Großteil des Codes in C ++ darin besteht, dass viele Methoden und Klassen die Verarbeitung mit ProGuard vermieden haben. Es ist schwierig, eine Interop mit einem minimierten Code durchzuführen;).


Folgendes habe ich im Studiofenster gesehen (die x86-Version wurde zerlegt, damit sie mit dem Emulator und nicht mit dem realen Gerät funktioniert)


Wie Sie im obigen Screenshot sehen können, wurde die Anwendung kaum verkleinert (hinsichtlich der Umbenennung von Klassen und Methoden). Außerdem können Sie feststellen, dass Java nur sehr wenig Code enthält und Bibliotheken daher fast den gesamten Speicherplatz belegen.


Nachdem ich die Klassenliste für einige Zeit gepusht hatte, sah ich eine merkwürdige MapImpl-Klasse, die den Verdacht aufkommen ließ, dass er für eine solch traurige Situation verantwortlich war, als er die Anzeige von Etiketten anpasste.


MapImpl-Struktur


Die Liste der Methoden und deren Signaturen weckte die Hoffnung, dass alles sehr einfach sein würde, und ich beschloss, mich mit dem SMALI-Code zu befassen. Dann wurde ich sehr angespannt und las die Liste der SMALI-Befehle und wie man sie liest / schreibt. Zu diesem Zeitpunkt entschied ich jedoch, dass die Angelegenheit einfach und kurzlebig zu sein verspricht, und dementsprechend ist es hier eine Gelegenheit, mit dem Umkehren herumzuspielen. Nachdem ich mich entschlossen hatte, ein paar Nächte damit zu verbringen (wie grausam ich mich geirrt hatte), ging ich mit ruhiger Seele ins Bett.


Pläne machen


Nachdem ich morgens aufgewacht war, entschied ich, dass es bis zu diesem Zeitpunkt besser war, die Lösung des Problems mit kleinen Schritten zu erreichen, da ich mich noch nicht einmal mit der Substitution von Ressourcen in Anwendungen befasst hatte, nicht mit Änderungen in der Logik. Danach habe ich mir eine kurze Liste von Schritten gemacht, die mich mit meinen Bearbeitungen in Form einer funktionierenden mobilen Plattform zum Endpunkt meiner Reise führen sollten. Diese Liste war wie folgt:


  1. Bereiten Sie die 1c-Konfiguration für die mobile Plattform vor, die zwei Beschriftungen auf der Karte anzeigt.
  2. Entpacke das apk der mobilen Plattform, packe ohne Änderungen, stelle sicher, dass die Konfiguration funktioniert.
  3. Nehmen Sie kleine Änderungen an SMALI-Dateien vor, die fast nichts ändern, aber in den Protokollen zu sehen sind, oder ändern Sie die Arbeitslogik. Stellen Sie sicher, dass sie funktionieren.
  4. Übernehmen Sie kleine Aktivitäten mit einer Karte in Java oder schreiben Sie eine Aktivität mit derselben Oberfläche. Ersetzen Sie Aktivitäten in der Anwendung, ohne die Funktionalität zu ändern. Wenn die erste zu faul oder zu kompliziert ist, schreiben Sie alternativ eine Klasse, die einen Teil der Arbeit für MapImpl erledigt, und fügen Sie ihren Methoden einen Aufruf von smali MapImpl hinzu.
  5. Schreiben Sie diesen Artikel (Ich wollte schon lange etwas schreiben, aber andere Ideen brauen sich immer noch in meinem Kopf auf, aber dann schien es, dass dies endlich ein ziemlich würdiges Thema war.)

Na ja, zumindest mit dem ersten Absatz hatte ich keine Probleme. Ich habe die kostenlose Lernversion der Plattform heruntergeladen, die mobile Plattform heruntergeladen und alles auf meinem Computer bereitgestellt (1C, haben Sie bereits eine Schulungsversion für Linux, ich habe nur einen weiteren Impuls, Windows zu verlassen, und jetzt muss ich etwas daran arbeiten). Die Konfiguration ist sehr einfach. Beim Start wird die ShowOnMap () -Funktion aufgerufen, die zwei Punkte auf der Karte überträgt. Das ist alles.


Aber mit dem zweiten Punkt wurde es wegen meines Analphabetentums trauriger. Aber was da war, die Probleme waren mit allen Punkten außer dem ersten.


Ich packe die Anwendung neu, ohne den Code zu ändern


Google hat mir sofort mitgeteilt, dass es ein so hervorragendes Tool für ApK-Tools gibt, mit dem zwei Befehle ApK-Dateien in .smali-Dateien analysieren und von diesen wieder abrufen können. Ich habe beschlossen, es zu benutzen. Ich habe apk mit folgendem Befehl entpackt (obwohl ich zeitweise einen Teil der Arbeit unter Linux gemacht habe, gebe ich alle Befehle für Windows):


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

und bekam das entpackte verzeichnis, in dem sich eine menge dateien befand, mit denen man in zukunft arbeiten sollte. Apk mit dem befehl zurück gepackt


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

Versucht, es auf einem Emulator zu installieren


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

Ich habe folgende Fehlermeldung erhalten:


 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] 

Generell war es notwendig, der Basis mehr Aufmerksamkeit zu schenken. Natürlich wusste ich, dass es Signaturen gibt und dass sie in Vertrieb und Freigabe unterteilt sind, aber ich wusste nicht, dass Anwendungen überhaupt nicht ohne Signaturen installiert werden. Ich beschloss, meine Debit-Signatur zu verwenden und das apk-Team zu unterzeichnen


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

Ich konnte die Anwendung ausführen. Richtig, statt einer Karte mit zwei Punkten wartete ein grauer Bildschirm mit einer Google-Signatur auf mich. Nach dem Kratzen der Rübe entschied ich, dass der Schlüssel für Google Maps von 1s verwendet wird, was mit meiner Signatur nicht funktioniert. Daher habe ich in der Entwicklerkonsole auf der Google-Website ein neues Projekt für die Arbeit mit Google Maps-API auf Android erstellt, einen API-Schlüssel erhalten, den ich in der Zeile "res / values ​​/ strings.xml" in "google_maps_key" angegeben und meinen Freigabeschlüssel zu den Berechtigungen für das Projekt hinzugefügt habe . Ich habe die apk neu gepackt und signiert, sie gestartet und endlich hat alles wieder funktioniert.


Ich füge meine Protokolle hinzu


Als nächstes wollte ich den Start der Anwendung automatisieren und die 1c-Konfiguration zur Plattform hinzufügen. Manuell, nach jeder Bearbeitung, wäre dies immer noch ein Chaos.


Zuerst habe ich gegoogelt und versucht, die Dienstprogramme "jadx" oder "dex2jar" zu verwenden, um mich nicht mit dem Lesen von "smali" zu beschäftigen, sondern um den vertrauten Java-Code zu lesen, aber aus irgendeinem Grund funktionierten sie nicht (später gelang es jadx, sich auf eine Art Schamanismus einzulassen). Ich musste smali zerlegen, da es sich als nicht so schrecklich herausstellte, wie ich befürchtet hatte.


Um zu verstehen, wie die mobile Plattform einen Befehl von der Desktop-Plattform zum Starten der Konfiguration erhält, wenn eine Verbindung über ADB hergestellt wird, habe ich beschlossen, die Einstiegspunkte der Anwendung zu überprüfen und die Ausgabe den Protokollen mit Absichten und anderen nützlichen Informationen hinzuzufügen. Ich habe mich entschieden, mit der Anwendung ( com.e1c.mobile.E1cApplication ) und der Aktivität mit android.intent.action.MAIN im Intent-Filter ( com.e1c.mobile.App ) zu beginnen. Ich habe mich auch für den Empfänger com.e1c.mobile.Starter mit einem interessanten Absichtsfilter für com.e1c.mobile.START_TEMPLATE und com.e1c.mobile.START_CMD . Es ist der Tatsache sehr ähnlich, dass er Absichten mit 1s-Befehlen akzeptiert und es ist er, der die Konfiguration von der Schablone aus startet.


Leider konnte ich in E1cApplication nichts Interessantes E1cApplication . Alles, was dort passiert, ist, Ihren Handler auf Caches zu installieren.


Aber in den beiden anderen Klassen, Starter und App , gab es viel mehr Informationen und es stellte sich als sehr nützlich heraus. Die App.onCreate(Landroid/os/Bundle;)V Methode ist ziemlich umfangreich, daher werde ich sie nicht App.onCreate(Landroid/os/Bundle;)V , sondern nur die Teile, die mich interessieren.


App onCreate Method Slice
 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 

Aus dem obigen Codeabschnitt ist ersichtlich, dass die Anwendung die Absicht empfängt, eine Verknüpfung dazu in das Register p1 einfügt, sie über StringBuilder an den Klassen- und StringBuilder anfügt und V(Ljava/lang/String;)V der Utils Klasse an die statische Methode V(Ljava/lang/String;)V . Anscheinend eine Art eigener Logger. Danach wird die Absicht auf zusätzliche Daten überprüft und, falls verfügbar, Uri abgerufen, woraus wiederum eine Zeichenfolge von der getQuery() -Methode abgerufen und an die Starter Klasse übergeben wird. onCreate habe keine weitere Studie zu onCreate . Ich habe einen Blick darauf onCreate und sichergestellt, dass die Aktionen ziemlich onCreate sind. Es sei denn, die Ansicht scheint programmgesteuert von einer völlig anderen Klasse erstellt worden zu sein, anstatt LayoutInflater , aber ich habe nicht sehr tief LayoutInflater , sodass alles möglich ist und nicht so, wie ich dachte. Die nächste Klasse, in die ich gegangen bin, ist Starter . Glücklicherweise wurde er im Aktivismus erwähnt und interessierte mich für das Manifest.


Leider hat sich herausgestellt, dass die von der Aktivität aufgerufene Methode nativ ist ( .method static native startCmd(Ljava/lang/String;)V ). Daher habe ich die onRecieve Methode onRecieve (in der die Anwendung die Ereignisse akzeptiert, für die sie abonniert ist). Ich werde den Code nicht V(Ljava/lang/String;)V , ich war daran interessiert, dass die Absicht auch mit der V(Ljava/lang/String;)V Methode der Utils Klasse protokolliert wird. Es hat sich herausgestellt, dass es ausreicht, dieser Methode einen kleinen kleinen Code hinzuzufügen. Außerdem werde ich meine Protokolle an den Stellen abrufen, an denen die Plattformentwickler sie erstellt haben. Als ich in die Utils-Klasse ging, sah ich sofort 2 leere Methoden, V und W. Anscheinend sind sie leer, weil sie beim Kompilieren der Release-Version herausgeschnitten wurden. Ich habe dem Standard android.utils.Log eine Aufzeichnung der übergebenen Zeichenfolge android.utils.Log . Ändern Sie dazu die Anzahl der erforderlichen lokalen Register von 0 auf 1 (für die Tag-Zeile), v0 diese Zeichenfolge in das Register v0 und registrieren Sie den Aufruf an die Log Methoden


Implementierung der Methoden W und 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 

Ich habe Mobile von 1s Desktop 1s gestartet und die folgenden Protokolle erhalten


Ergebnisse starten
  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 

Wie Sie sehen, gibt es alle notwendigen Informationen, um den Start der Anwendung über adb zu automatisieren. Zu diesem Zeitpunkt hatte ich allerdings einen doppelten Gesichtsausdruck. Zuerst habe ich endlich die Schlüssel abgeholt, mit denen jadx die Übersetzung in Java gemeistert hat (es ist klar, dass ich trotzdem auf smali schreiben müsste, aber immer noch). Und das zweite Problem war die Tatsache, dass ich erkannte, dass ich die Entwicklerplattform vergeblich quälte (nur für die Entwicklung und das Debuggen von Konfigurationen erforderlich). Es wäre richtiger, den Release-Build von 1s-Anwendungen rückgängig zu machen, und es gibt halbfertige Gradle-Projekte für die Assemblierung. Es gibt eine Datei mit einer Liste von Abhängigkeiten und anderen Brötchen. Ich war ein bisschen traurig darüber - und beschloss trotzdem zu beenden, was ich angefangen hatte. Jedenfalls mache ich das alles für den Fan, aber nicht für den praktischen Nutzen.


Plattformstart über adb


Ich entschied mich, die Installation zu automatisieren und über die ADB-Anwendung mit Gradle zu starten. Wie auch immer, zu diesem Zeitpunkt hatte ich bereits eine kleine Liste von Shuffles geschrieben (Entpacken, Kopieren geänderter Ressourcen und SMALI-Dateien in das Verzeichnis mit der entpackten Anwendung, Verpackung, Signatur, in der Tat sind alle Aufgaben nur Läufer für Konsolenbefehle). Zu den vorhandenen Aufgaben wurden die folgenden Befehle hinzugefügt


  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 

Mit der obigen Befehlsfolge installieren wir apk, erteilen der installierten Anwendung die Berechtigung zum Lesen des Datenträgers, kopieren die 1s-Konfiguration auf das Gerät und geben Befehle zum Starten von 1s und Laden der Konfiguration. Das funktioniert übrigens nicht bei allen Android-Versionen, aber ich habe es auf der API 28 getestet, und dementsprechend macht diese Sequenz alles richtig. Bei jüngeren Versionen kann es zu Problemen bei der Vergabe von Rechten kommen.


Ich bin an der Datenverarbeitung von 1s beteiligt und ändere Tags


Hier hatte ich mehrere Möglichkeiten zur Weiterentwicklung von Veranstaltungen.


  1. Ich ordne MapImpl.smali direkt an, was eine ziemlich schwierige Aufgabe sein sollte, wenn man bedenkt, dass die Bibliothek für die Arbeit mit Google Maps ziemlich stark verkleinert ist und ich alles in der kleinen Syntax schreiben müsste.
  2. Ich verwende die MapImpl.java-Datei, die ich dank jadx erhalten habe, ändere sie, verwende die Klassen, die ich verwende, um sie durch Stubs zu ersetzen, destilliere in smali, ersetze die Datei und packe sie. Die Option schien mir angespannt, da es wirklich weh tat, viele Stecker zu haben, außerdem gab es Probleme mit einigen von ihnen, die ich nicht auswählen wollte.
  3. Ich kombiniere die Ansätze 1 und 2, füge die Aufrufe einer speziellen Erweiterungsklasse MapImplExtenstion.java in den SMALI-Code ein, der einen Teil der Logik erweitert. Aus diesem Grund habe ich diese Option gewählt, weil sie einfacher als die erste und zweite Option ist und mir sogar etwas komplizierter erscheint als Option 4. aber weniger mühsam (ja, träume, naiv).
  4. Ersetzen Sie in der Regel Google Maps durch etwas anderes, z. B. Yandex. Um die gesamte Arbeit in einem anderen Projekt zu erledigen, wiederholen Sie einfach die MapImpl-Signaturen, minimieren Sie das Entpacken und legen Sie die fertigen SMALI-Dateien ab, bevor Sie die Anwendung an die entsprechenden Stellen packen. Es gibt keine Probleme mit verkleinerten Bibliotheken, aber ich müsste mich registrieren, um einen Schlüssel für Karten zu erhalten, ein separates Projekt zu erstellen, Ressourcen zu verbinden und vieles mehr.
  5. Wie Option 4, aber ersetzen Sie Google durch ... Google. Aber hier hatte ich den Verdacht, dass es möglich sein würde, 1 in 1 Karten sdk zu verkleinern, und es gab keine Lust zu experimentieren.

Nach der dritten Option habe ich in src zwei Unterverzeichnisse erstellt: java für MapImplExtenstion.java und stubJava für Dateien, die nur zum Kompilieren benötigt werden. Da ich mich entschlossen habe, die Labels ein wenig zu optimieren, haben mich die folgenden beiden Bereiche interessiert:


Felder Xj: Ljava / util / ArrayList und 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 

Unter Verwendung von jadx zum Dekompilieren in Java habe ich in dekompiliertem Code eine Methode gefunden, die dafür verantwortlich ist, sie zu füllen


KN-Methode
  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])); } } } } 

Dementsprechend hat die MapImplExtension-Klasse die ArrayList [] -kN-Methode (String [] -Titel, double [] -Koordinaten) hinzugefügt, die im ersten Element des Arrays eine Liste zurückgibt, die in Xj und im zweiten eine Liste für Xk platziert werden muss.


Klasse 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; } } 

Kompiliert mit den folgenden Befehlen, zuerst in class, dann in dex, dann in smali dekompiliert, um dann zusammen mit den restlichen Dateien zu packen


  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 

Das Erweiterungsfeld des Typs unserer neuen Klasse wurde zu MapImpl.smali hinzugefügt und die Initialisierung hinzugefügt


Ein Feld hinzufügen
 #    .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; 

Außerdem wurde die Verarbeitung in der MapImpl-Datenklasse von 1s durch die Verarbeitung in der MapImplExtension-Klasse ersetzt


Änderungen in 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; 

Nachdem ich die Anwendung gepackt und gestartet hatte, sah ich voller Freude, dass alles funktionierte. Im Moment habe ich jedoch noch nichts unternommen, um die Funktionen der mobilen Plattform zu erweitern. Nur die Überschriften der Marken auf der Karte geändert. Da ich das Image der Tags ändern wollte, brach ich ab und vergrub es ziemlich lange in den Quellen und der Dokumentation.


Neue Funktionalität hinzufügen


Zunächst eine kleine Erklärung, wie der Marker für das Etikett gesetzt wird. Die MarkerOptions Klasse verfügt über ein public MarkerOptions icon (BitmapDescriptor iconDescriptor) an das das von einer der statischen Methoden der BitmapDescriptorFactory Klasse erstellte Objekt BitmapDescriptorFactory wird.


Und hier wartete tatsächlich ein Mist auf mich. Da 1c dieses Geschäft nicht nutzt, wurden die entsprechenden Klassen bei der Minifizierung einfach herausgeschnitten. Ich musste sie wiederherstellen, und es war schmerzhaft und lang. Genauer gesagt, die Klassen waren da, aber sie wurden umbenannt und enthielten nicht die erforderlichen Methoden. Indem ich die Art der gesamten Bibliothek für die Arbeit mit Karten auswählte - anhand von Signaturen und Konstanten - fand ich die Entsprechung der Bibliotheksklassen zu Anwendungsklassen heraus, fügte die erforderlichen Methoden und Felder hinzu, baute sie neu auf und war froh, dass diese Suche endlich abgeschlossen war, da sie schließlich funktionierte, wenn auch nicht vom ersten Versuch an (mehrmals gemäht mit Unterschriften und verwirrten reduzierten Klassen).


Das Ergebnis (äußerlich nicht sehr beeindruckend). Zwei Punkte mit den Koordinaten (45; 45) und (46; 46)


Da das Volumen des geänderten Codes ziemlich groß ist - ich werde es nicht in den Artikel aufnehmen, werde ich nur Signaturänderungen geben. Wenn sich jemand für alle Änderungen interessiert, die ich in diesem Schritt vorgenommen habe, können Sie sich dieses Commit ansehen.


    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); 

Zusammenfassend war die Erfahrung sehr interessant. Zumindest für mich. Ich half beim Herausfinden einiger Werkzeuge, lernte etwas mehr über die Arbeit des Android und das Bytecode-Format für Dalvik / Art. Die Erfahrung mit der Auswahl von minimiertem Code wäre auch nützlich (es gab bereits einen Fall, in dem ich in der Release-Version von R8 Klassenfelder herausschnitt, die zu diesem Zeitpunkt nur von der Methode verwendet wurden Ich habe es herausgefunden, jetzt würde es keine Probleme verursachen).


Wenn jemand daran interessiert ist, alles selbst zu wiederholen und möglicherweise noch mehr aufzuheben, habe ich auf Github alle Quellen zusammen mit einem krummen Gradle-Skript gepostet, das sich aus der modifizierten Original-Apk zusammensetzt.

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


All Articles