Debo decir de inmediato que, a pesar del fuerte, como podría pensar alguien, el título del artículo, no habrá ningún tipo de hardcore directo. Y también, a pesar del hecho de que la reversión se asocia con mayor frecuencia con la seguridad de la información, nuevamente no habrá nada como esto, porque no hubo necesidad de eludir ninguna protección (excepto una pequeña minificación). No hay ninguno de ellos. Mobile 1s es un producto gratuito en el que no se incluyen controles, protecciones ni funciones pagas.
Por lo tanto, hago un llamamiento a aquellos que esperan ver el uso de desofuscadores, el uso de Frida / Xposed, otro software complicado: no encontrará nada interesante para usted. Aquí solo usaremos apktool, baksmali, apksigner, adb, jadx, consola, editor de texto, javac, d8 y todo.
También quiero decepcionar de antemano a aquellos que esperaban un análisis profundo de la plataforma, o su fuerte modificación. Solo se realizarán pequeñas ediciones en solo unos pocos archivos, y de hecho, incluso estos pocos archivos que no hice completamente, todo en la parte superior.
Como empezó todo
Te contaré un poco sobre por qué de repente tuve la idea de entrar de alguna manera en el teléfono móvil durante 1 segundo. Por el momento, llevo haciendo un desarrollo nativo para Android desde hace un año, pero antes trabajé durante 4 años como programador, y durante el último año y medio, a menudo trabajamos específicamente con la plataforma móvil. A pesar de que satisfizo las necesidades básicas, también tuvo muchos inconvenientes (además del lenguaje de programación). En particular, era imposible integrar humanamente una biblioteca externa, al menos por medios regulares y con nuestra reserva de conocimiento. También todo fue muy triste, por ejemplo, con la funcionalidad de mostrar etiquetas en un mapa. Toda la posibilidad de su configuración era especificar texto para las etiquetas cuando las tocaba. En ese momento, la única forma de evitar esto era usar el objeto especial "Campo de documento HTML", pero había problemas con él. En el momento de trabajar con 1s, todo mi conocimiento en el desarrollo nativo para Android estaba en un par HelloWorld, por lo que ni siquiera pensé en revertir el teléfono móvil con 1s, o no resolvimos varias preguntas de los clientes sobre la extensión no estándar de 1s, o vimos unos nativos muy simples. las aplicaciones que se colocaron junto a él y que se integraron de forma torcida / oblicua con 1s (y el acuerdo de licencia de 1s parece prohibir las ediciones en la plataforma misma).
Entonces, la primera razón para hacer la reversión 1c fue que me interesé y qué puedo hacer con la base de conocimiento actual. Debo decir de inmediato que la experiencia del desarrollo nativo no fue para decir lo que se necesita, en el trabajo diario no se encuentra casi nada de lo que se describirá a continuación. Entonces, en principio, probablemente cualquier 1snik promedio sentado durante varios días / semanas podría resolverlo todo.
La segunda razón es que solo quería intentar elegir el apk de otras personas, porque antes me faltaba esta amplia capa de conocimiento, y como tenía que comenzar en alguna parte, se me ocurrió solo 1s.
Primeros pasos
Lo primero que hice, incluso cuando la idea de revertir 1s solo vagaba vagamente en mi cabeza, simplemente arrastré el archivo apk de la aplicación a la ventana de AndroidStudio con el mouse. Quiero decir de inmediato que me sentí un poco triste, porque la mayoría del código 1c está escrito en C ++ y se encuentra en las bibliotecas .so, y es más difícil revertirlos, y no fue interesante. Pero el archivo classes.dex capturó completamente mi atención, especialmente porque su tamaño modesto me dio la oportunidad de asumir que sería fácil revertirlo. En general, resultó de esa manera. Por cierto, una consecuencia interesante del hecho de que la mayoría del código en C ++ es que muchos métodos y clases han evitado el procesamiento con ProGuard. Es difícil hacer una interoperabilidad con un código minificado;).
Esto es lo que vi en la ventana del estudio (desmontó la versión x86 para que funcione con el emulador y no con el dispositivo real) Como puede ver en la captura de pantalla anterior, la aplicación apenas se minimizó (en términos de cambio de nombre de clases y métodos). Además, puede ver que hay muy poco código en Java y, por lo tanto, las bibliotecas ocupan casi todo el espacio.
Después de haber empujado la lista de clases durante algún tiempo, vi una curiosa clase de MapImpl, que arroja sospechas de que era él el responsable de una situación tan triste con la personalización de la exhibición de etiquetas.
La lista de métodos y sus firmas inspiraron la esperanza de que todo fuera muy simple, y decidí analizar el código smali, después de lo cual me puse muy tenso y fui a leer la lista de comandos smali y cómo leerlo / escribirlo. Sin embargo, fue en este punto que decidí que el asunto promete ser simple y de corta duración, y en consecuencia, aquí está, una ocasión para jugar con la inversión. Habiendo decidido dedicar un par de noches a esto (cuán cruelmente me equivoqué), me fui a la cama con un alma tranquila.
Haciendo planes
Al despertarme por la mañana, decidí que, hasta este punto, nunca había estado involucrado en la sustitución de recursos en aplicaciones, no como cambios en la lógica, no tenía nada que estafar de inmediato para reemplazar la actividad con una tarjeta, era mejor abordar la solución del problema con pequeños pasos. Después de eso, hice una breve lista de pasos que se suponía que me llevarían al punto final de mi viaje en forma de una plataforma móvil que funciona con mis ediciones. Esta lista fue la siguiente:
- Prepare la configuración 1c para la plataforma móvil, cuya funcionalidad es mostrar dos etiquetas en el mapa.
- Desempaquete el apk de la plataforma móvil, empaquete sin cambios, asegúrese de que la configuración esté funcionando.
- Realice pequeños cambios en los archivos pequeños que no cambian casi nada, pero que se pueden ver en los registros o cambian la lógica de trabajo, ensamblar y asegurarse de que funciona.
- Para superar la actividad pequeña con un mapa en Java, o escribir actividad con la misma interfaz, reemplace la actividad en la aplicación sin cambiar la funcionalidad. Como alternativa, si el primero es demasiado vago o complicado, escriba una clase que haga parte del trabajo para MapImpl y agregue una llamada a sus métodos desde smali MapImpl.
- Escriba este artículo (hace tiempo que quería escribir algo, pero todavía se me ocurren otras ideas, pero parecía que finalmente este era un tema bastante valioso)
Bueno, bueno, al menos con el primer párrafo no tuve problemas. Descargué la versión educativa gratuita de la plataforma, descargué la plataforma móvil, la implementé en mi computadora (1C, ¿ya tienes una versión de capacitación para Linux, solo tengo otro impulso para dejar Windows y ahora tengo que trabajar un poco en ella?). La configuración es muy simple, al inicio llama a la función ShowOnMap (), que transmite dos puntos en el mapa. Eso es todo.
Pero con el segundo punto, resultó más triste debido a mi analfabetismo. Pero lo que hay, los problemas estaban con todos los puntos, excepto el primero.
Vuelvo a empaquetar la aplicación sin cambiar el código
Google inmediatamente me dijo que existe una herramienta apktool tan excelente que permite literalmente dos comandos para analizar apk a archivos .smali, así como para ensamblarlos de nuevo. Decidí usarlo. Descomprimí apk con el siguiente comando (en adelante, a pesar de que a veces hice parte del trabajo en Linux, daré todos los comandos para Windows):
apktool.bat d .\sourceApk\1cem-x86.apk -o .\build\unpacked
y obtuve el directorio desempaquetado en el que se encontraban un montón de archivos, era trabajar con ellos en el futuro. APK empaquetado de nuevo con el comando
apktool.bat b .\build\unpacked -o .\build\1cem-x86.apk
Intentando instalarlo en un emulador
adb install .\build\1cem-x86.apk
Recibí el siguiente error:
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 general, era necesario prestar más atención a la base. Por supuesto, sabía que hay firmas y que se dividen en ventas y lanzamiento, pero no sabía que las aplicaciones no se instalan sin firmas en absoluto. Decidí usar mi firma de débito y firmar el equipo apk
apksigner.bat sign --ks C:\Users\neikist\.android\debug.keystore .\build\1cem-x86.apk
Logré ejecutar la aplicación. Es cierto que en lugar de un mapa con dos puntos, me estaba esperando una pantalla gris con una firma de Google a continuación. Después de rascar el nabo, decidí que el hecho es que se usa la clave para los mapas de Google de 1s, que no funciona con mi firma. Por lo tanto, yendo a la consola del desarrollador en el sitio web de Google, creé un nuevo proyecto para trabajar con la API de Google Maps en Android, recibí una clave de API, que especifiqué en res / values / strings.xml en la línea google_maps_key, y también agregué mi clave compartida a los permisos para el proyecto . Reempaqué y volví a firmar el apk, lo lancé y finalmente todo volvió a funcionar.
Agrego mis registros
Lo siguiente que quería hacer era automatizar el lanzamiento de la aplicación y agregar la configuración 1c a la plataforma. Manualmente, después de cada edición, hacer esto seguiría siendo un desastre.
Al principio busqué en Google e intenté usar las utilidades jadx o dex2jar, para no molestarme en leer smali, sino para leer el código java más familiar, pero por alguna razón no funcionaron (más tarde, jadx logró entrar en algún tipo de chamanismo). Tuve que desmontar smali, ya que resultó no ser tan terrible como tenía miedo.
Para comprender cómo la plataforma móvil recibe un comando de la plataforma de escritorio para iniciar la configuración cuando se conecta a través de adb, decidí mirar los puntos de entrada a la aplicación y agregar la salida a los registros de intentos y otra información útil. Decidí comenzar con la aplicación ( com.e1c.mobile.E1cApplication
) y la actividad con android.intent.action.MAIN
en intent-filter ( com.e1c.mobile.App
). También estaba interesado en el receptor com.e1c.mobile.Starter
con un filtro de intención interesante en com.e1c.mobile.START_TEMPLATE
y com.e1c.mobile.START_CMD
. Es muy similar al hecho de que acepta intentos con comandos 1s, y es él quien inicia la configuración desde la plantilla.
Desafortunadamente, no pude encontrar nada interesante en E1cApplication
, todo lo que sucede allí es instalar su controlador en cachés.
Pero en las otras dos clases, Starter
y App
, había mucha más información y resultó ser bastante útil. El App.onCreate(Landroid/os/Bundle;)V
es bastante grande, por lo que no lo daré en su totalidad, solo daré las partes que me interesan.
Aplicación 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
Se puede ver en la sección de código anterior que la aplicación recibe la intención, coloca un enlace en el registro p1
, agrega la StringBuilder
al nombre y método de la clase a través de StringBuilder
y pasa V(Ljava/lang/String;)V
de la clase Utils
al método estático. Al parecer, algún tipo de su propio registrador. Después de eso, se verifica la intención para obtener datos adicionales y, si está disponible, Uri
obtiene Uri
, de la cual, a su vez, se obtiene una cadena mediante el método getQuery()
y se pasa a la clase Starter
. onCreate
vi más estudios sobre onCreate
, onCreate
un vistazo y me aseguré de que las acciones fueran bastante estándar. A menos que la vista parezca haber sido creada programáticamente por una clase completamente diferente, en lugar de usar LayoutInflater
, pero no profundicé mucho, entonces todo es posible y no como pensaba. La siguiente clase a la que fui es Starter
. Afortunadamente, fue mencionado en activismo y me interesó en el manifiesto.
Desafortunadamente, el método que se llamó desde la actividad resultó ser nativo ( .method static native startCmd(Ljava/lang/String;)V
), por lo tanto, onRecieve
atención al método onRecieve
(en el que la aplicación acepta los eventos a los que está suscrita). No daré el código, estaba interesado en que la intención también se V(Ljava/lang/String;)V
utilizando el método V(Ljava/lang/String;)V
de la clase Utils
. Resulta que es suficiente agregar un pequeño código pequeño a este método, y además obtendré mis registros en los mismos lugares donde los desarrolladores de la plataforma los destinaron. Al ir a la clase Utils, inmediatamente vi 2 métodos vacíos, V y W. Aparentemente están vacíos porque al compilar la versión de lanzamiento se cortaron. Acabo de agregar un registro de la cadena pasada al estándar android.utils.Log
en ellos. Para hacer esto, cambie el número de registros locales requeridos de 0 a 1 (para la línea de etiqueta), coloque esta misma cadena en el registro v0
y también registró la llamada a los métodos de Log
Implementación de los métodos W y 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
Lancé el móvil desde el escritorio 1s y recibí los siguientes registros
Resultados de lanzamiento 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
Como puede ver, existe toda la información necesaria para automatizar el lanzamiento de la aplicación a través de adb. Es cierto que en este punto atrapé una palmada doble. En primer lugar, finalmente recogí las claves con las que jadx dominaba la traducción en java (está claro que de todos modos tendría que escribir en pequeño, pero aún así). Y el segundo facepalm resultó ser el hecho de que me di cuenta de que estaba atormentando la plataforma del desarrollador en vano (necesario solo para el desarrollo y la depuración de configuraciones), sería más correcto invertir la compilación de lanzamiento de las aplicaciones 1s, y hay proyectos de gradle semi listos para ensamblar, hay un archivo con una lista de dependencias y otros bollos Estaba un poco triste por esto, y aún así decidí terminar lo que comencé. De todos modos, por el bien del fanático, estoy haciendo todo esto, pero no por el beneficio práctico.
Decidí automatizar la instalación y el lanzamiento a través de la aplicación adb usando gradle. De todos modos, para ese momento ya tenía una pequeña lista de shuffles escritos (desempaquetando, copiando recursos modificados y archivos pequeños en el directorio con la aplicación desempaquetada, empaquetado, firma, de hecho todas las tareas son solo corredores para comandos de consola) A las tareas existentes se agregaron los siguientes comandos
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
Por la secuencia de comandos anterior, instalamos apk, le damos permiso a la aplicación instalada para leer el disco, copiamos la configuración de 1s en el dispositivo y damos comandos para iniciar 1s y cargar la configuración. Por cierto, esto no funciona en todas las versiones de Android, pero lo probé en la API 28 y, en consecuencia, esta secuencia hace todo correctamente. En versiones más jóvenes, puede haber problemas con la emisión de derechos.
Participo en el procesamiento de datos desde 1s y modifico etiquetas
Aquí tenía varias opciones para un mayor desarrollo de eventos.
- Dirijo directamente MapImpl.smali, que prometió ser una tarea bastante difícil, teniendo en cuenta que la biblioteca para trabajar con los mapas de Google está muy minimada, y que tendría que escribir todo en la sintaxis pequeña.
- Utilizo el archivo MapImpl.java obtenido gracias a jadx, le hago cambios, utilizo las clases que uso para reemplazarlo con stubs, destilarlo en smali, reemplazar el archivo y empacarlo. La opción me pareció tensa, ya que realmente me dolía tener muchos enchufes, además de que había algunos problemas que no quería elegir.
- Combino los enfoques 1 y 2, inserto las llamadas de una clase de extensión especial MapImplExtenstion.java en un código pequeño que extiende una parte de la lógica; como resultado, elegí esta opción, simplemente porque es más simple que las opciones primera y segunda, y me pareció incluso un poco más complicado que la opción 4, pero menos laborioso (sí, sueño, ingenuo).
- En general, reemplace los mapas de Google con algo más, por ejemplo Yandex. Para hacer todo el trabajo en otro proyecto, simplemente repitiendo las firmas de MapImpl, luego minimice desempaquetar y simplemente suelte los archivos pequeños terminados antes de empacar la aplicación en los lugares apropiados. No hay problemas con las bibliotecas minimizadas, pero tendría que registrarme para obtener una clave para las tarjetas, crear un proyecto separado, molestarme con la conexión de recursos y más.
- Igual que la opción 4, pero reemplaza Google con ... Google. Pero aquí tenía sospechas de que sería posible minificar 1 en 1 mapas SDK, y no había ningún deseo de experimentar.
Siguiendo la tercera opción, creé 2 subdirectorios en src: java, para MapImplExtenstion.java y stubJava para archivos que solo son necesarios para la compilación. Como decidí modificar un poco las etiquetas, me interesaron los siguientes dos campos:
Campos Xj: Ljava / util / ArrayList y 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
Usando jadx para descompilar en Java, encontré en el código descompilado un método responsable de llenarlos
Método 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 consecuencia, la clase MapImplExtension ha agregado el método ArrayList [] kN (String [] títulos, coordenadas dobles []) que en el primer elemento de la matriz devolverá una lista que deberá colocarse en Xj, y en el segundo una lista para Xk.
Clase 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; } }
Compilado con los siguientes comandos, primero en clase, luego en dex, luego descompilado en pequeños para luego empaquetar junto con el resto de los archivos
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
Se agregó el campo de extensión de tipo de nuestra nueva clase a MapImpl.smali y se agregó su inicialización
Agregar un campo # .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;
Y también reemplazó el procesamiento en la clase de datos MapImpl de 1s con el procesamiento en la clase MapImplExtension
Cambios en 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;
Después de empacar y lanzar la aplicación, alegremente vi que todo estaba funcionando. Pero por el momento no he hecho nada que amplíe las capacidades de la plataforma móvil. Acabo de cambiar los encabezados de las marcas en el mapa. Pero con el deseo de cambiar la imagen de las etiquetas, rompí muy bien y enterré bastante tiempo en las fuentes y la documentación.
Agregar nueva funcionalidad
Primero, una pequeña explicación de cómo se establece el marcador para la etiqueta. La clase MarkerOptions
tiene un método public MarkerOptions icon (BitmapDescriptor iconDescriptor)
al que se public MarkerOptions icon (BitmapDescriptor iconDescriptor)
el objeto creado por uno de los métodos estáticos de la clase BitmapDescriptorFactory
.
Y aquí, de hecho, un fastidio me estaba esperando. Como 1c no usa este negocio, las clases correspondientes simplemente se cortaron durante la minificación. Tuve que restaurarlos, y fue doloroso y largo. Más precisamente, las clases estaban allí, pero fueron renombradas y no contenían los métodos necesarios. Al elegir el tipo de biblioteca completa para trabajar con mapas, mediante firmas y constantes descubrí la correspondencia de las clases de la biblioteca con las clases de aplicación, agregué los métodos y campos necesarios, la reconstruí y me alegré de que esta búsqueda finalmente terminara, ya que finalmente funcionó, aunque no desde el primer intento (varias veces cortado con firmas y clases minimizadas confusas).
El resultado (exteriormente no es muy impresionante). Dos puntos con coordenadas (45; 45) y (46; 46) Dado que el volumen del código modificado es bastante grande, no lo incluiré en el artículo, solo daré los cambios de firma. Si alguien está interesado en todas las ediciones que hice en este paso, puede consultar este compromiso .
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) {
Para resumir, la experiencia fue bastante interesante. Al menos para mi. Ayudé a descubrir algunas herramientas, aprendí un poco mejor sobre el trabajo de Android y el formato de bytecode para dalvik / art, la experiencia de elegir código minificado también sería útil (ya hubo un caso cuando en la versión de lanzamiento de R8 eliminé los campos de clase que en realidad se usaron, en ese momento solo por el método Lo descubrí, ahora no causaría problemas).
Si alguien está interesado en repetir todo él mismo y posiblemente recogerlo aún más, publiqué en github todas las fuentes junto con un script de gradle torcido que se construye a partir del apk original modificado.