Devo dizer imediatamente que, apesar do título alto do artigo, como alguém poderia pensar, não haverá nenhum hardcore direto. Além disso, apesar do fato de a reversão estar frequentemente associada à segurança da informação, novamente não haverá nada assim, porque não havia necessidade de ignorar nenhuma proteção (com exceção de uma pequena minificação). Não há nenhum deles. O Mobile 1s é um produto gratuito no qual não há cheques, proteções e funções pagas.
Portanto, apelo para aqueles que esperam ver o uso de desofuscadores, o uso do Frida / Xposed, outro software complicado - você não encontrará nada interessante para si. Aqui vamos usar apenas apktool, baksmali, apksigner, adb, jadx, console, editor de texto, javac, d8 e tudo.
Também quero decepcionar antecipadamente aqueles que aguardavam uma análise profunda da plataforma ou sua forte modificação. Apenas pequenas edições serão feitas em apenas alguns arquivos e, na verdade, mesmo esses poucos arquivos que eu não fiz completamente, todos no topo.
Como tudo começou
Vou falar um pouco sobre por que de repente tive a ideia de entrar no celular por 1s. No momento, estou desenvolvendo nativamente o Android há um ano, mas antes trabalhei como programador por 4 anos e, no último ano e meio, trabalhamos especificamente com a plataforma móvel. Apesar de ter atendido às necessidades básicas, ela também teve muitos desvantagens (além da linguagem de programação). Em particular, era impossível incorporar humanamente qualquer biblioteca externa, pelo menos por meios regulares e com nosso estoque de conhecimento. Tudo também foi muito triste, por exemplo, com a funcionalidade de exibir etiquetas em um mapa. Toda a possibilidade de sua configuração era especificar texto para etiquetas quando você as toca. Naquela época, a única maneira de contornar isso era usar o objeto especial “HTML Document Field”, mas havia problemas com ele. No momento em que trabalhei com 1s, todo o meu conhecimento no desenvolvimento nativo para Android estava em um par HelloWorld, então nem pensei em reverter o telefone celular com 1s, não resolvemos várias perguntas dos clientes com a extensão não padrão de 1s ou vimos nativas muito simples os aplicativos que foram colocados próximos a ele e integrados de maneira oblíqua / oblíqua com 1s (e o contrato de licença 1s parece proibir edições na própria plataforma).
Portanto, a primeira razão para fazer a reversão 1c foi o interesse e o que posso fazer com a base de conhecimento atual. Devo dizer imediatamente que a experiência do desenvolvimento nativo não foi dizer o que é necessário; no trabalho cotidiano, quase nada do que será descrito abaixo não é encontrado. Portanto, em princípio, provavelmente qualquer 1snik médio sentado por vários dias / semanas seria capaz de descobrir tudo.
A segunda razão é que eu só queria escolher o apk de outras pessoas, porque antes eu estava perdendo essa camada bastante ampla de conhecimento e, como eu tinha que começar em algum lugar, ocorreu-me apenas 1s.
Primeiros passos
A primeira coisa que fiz, mesmo quando a ideia de reverter 1s vagou vagamente na minha cabeça, simplesmente arrastei o arquivo apk do aplicativo para a janela do AndroidStudio com o mouse. Quero dizer imediatamente que fiquei um pouco triste, porque a maior parte do código 1c está escrita em C ++ e está em bibliotecas .so, e é mais difícil revertê-las, e isso não foi interessante. Mas o arquivo classes.dex chamou minha atenção completamente, especialmente porque seu tamanho modesto me deu a oportunidade de supor que seria fácil revertê-lo. Em geral, ficou assim. A propósito, uma conseqüência interessante do fato de que a maior parte do código em C ++ é que muitos métodos e classes evitaram o processamento com o ProGuard. É difícil fazer uma interoperabilidade com um código minificado;).
Aqui está o que eu vi na janela do estúdio (desmontou a versão x86 para trabalhar com o emulador e não com o dispositivo real) Como você pode ver na captura de tela acima, o aplicativo dificilmente foi minificado (em termos de renomeação de classes e métodos). Além disso, você pode ver que há muito pouco código em java e, portanto, as bibliotecas ocupam quase todo o espaço.
Depois de empurrar a lista de classes por algum tempo, vi uma curiosa classe MapImpl, que desconfia que foi ele quem foi responsável por uma situação tão triste com a personalização da exibição de rótulos.
A lista de métodos e suas assinaturas inspirou a esperança de que tudo seria muito simples, e eu decidi examinar o código smali, após o qual fiquei muito tenso e fui ler a lista de comandos smali e como ler / escrever. No entanto, foi nesse ponto que decidi que o assunto promete ser simples e de vida curta e, portanto, aqui está, uma ocasião para brincar com a inversão. Tendo decidido dedicar algumas noites a isso (quão cruelmente eu estava enganado), fui para a cama com uma alma calma.
Fazendo planos
Depois de acordar de manhã, decidi que, até o momento, nunca havia me envolvido em substituir recursos em aplicativos, não como mudanças na lógica, não tinha nada para me enganar imediatamente ao substituir a atividade por um cartão, era melhor abordar a solução do problema com pequenas etapas. Depois disso, fiz uma pequena lista de etapas que deveriam me levar ao ponto final da minha jornada na forma de uma plataforma móvel funcional com minhas edições. Essa lista era a seguinte:
- Prepare a configuração 1c para a plataforma móvel, cuja funcionalidade é exibir dois rótulos no mapa.
- Descompacte o apk da plataforma móvel, faça as malas sem alterações, verifique se a configuração está funcionando.
- Faça pequenas alterações nos arquivos smali que não mudam quase nada, mas que podem ser vistas nos logs ou alteram a lógica do trabalho, monte e certifique-se de que funcione.
- Para ultrapassar a atividade smali com um mapa em java ou gravar atividade com a mesma interface, substitua a atividade no aplicativo sem alterar a funcionalidade. Como alternativa, se o primeiro for muito preguiçoso ou complicado, escreva uma classe que fará parte do trabalho do MapImpl e adicione uma chamada aos seus métodos no smali MapImpl.
- Escreva este artigo (há muito tempo eu queria escrever algo, mas outras idéias ainda estão surgindo na minha cabeça, mas então parecia que esse era finalmente um tópico bastante interessante)
Bem, pelo menos no primeiro parágrafo não tive problemas. Eu baixei a versão educacional gratuita da plataforma, baixei a plataforma móvel, implantei tudo no meu computador (1C, você já possui a versão educacional para linux, só tenho outro impulso para sair do Windows e agora tenho que trabalhar nela). A configuração é muito simples, na inicialização, ele chama a função ShowOnMap (), que transmite dois pontos no mapa. Só isso.
Mas com o segundo ponto, ficou mais triste por causa do meu analfabetismo. Mas o que houve, os problemas estavam com todos os pontos, exceto o primeiro.
Reempacoto o aplicativo sem alterar o código
O Google imediatamente me disse que existe uma excelente ferramenta apktool que permite literalmente dois comandos analisar o apk em arquivos .smali e montá-los novamente. Eu decidi usá-lo. Descompactei o apk com o seguinte comando (a seguir, apesar de, às vezes, fazer parte do trabalho no linux, darei todos os comandos para o windows):
apktool.bat d .\sourceApk\1cem-x86.apk -o .\build\unpacked
e conseguiu o diretório descompactado no qual havia um monte de arquivos, para trabalhar com eles no futuro. Apk embalado de volta com o comando
apktool.bat b .\build\unpacked -o .\build\1cem-x86.apk
Tentando instalá-lo em um emulador
adb install .\build\1cem-x86.apk
Eu recebi o seguinte erro:
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]
Em geral, era necessário prestar mais atenção à base. Obviamente, eu sabia que existem assinaturas e que elas são divididas em vendas e lançamentos, mas não sabia que os aplicativos não são instalados sem assinaturas. Decidi usar minha assinatura de débito e assinar a equipe do apk
apksigner.bat sign --ks C:\Users\neikist\.android\debug.keystore .\build\1cem-x86.apk
Eu consegui executar o aplicativo. É verdade que em vez de um mapa com dois pontos, uma tela cinza com uma assinatura do Google abaixo estava esperando por mim. Depois de riscar o nabo, decidi que o fato é que a chave do Google maps do 1s é usada, o que não funciona com a minha assinatura. Portanto, indo ao console do desenvolvedor no site do google, criei um novo projeto para trabalhar com a API do google maps no android, recebi uma chave da api, especificada em res / values / strings.xml na linha google_maps_key, e também adicionei minha chave de compartilhamento às permissões do projeto . Eu reembalei e reescrevi o apk, o iniciei e, finalmente, tudo funcionou novamente.
Eu adiciono meus logs
A próxima coisa que eu queria fazer era automatizar o lançamento do aplicativo e adicionar a configuração 1c à plataforma. Manualmente, após cada edição, isso ainda seria uma bagunça.
No começo, pesquisei no Google e tentei usar os utilitários jadx ou dex2jar, para não incomodar a leitura de smali, mas para ler o código java mais familiar, mas por algum motivo eles não funcionaram (mais tarde, o jadx conseguiu entrar em algum tipo de xamanismo). Eu tive que desmontar o smali, pois acabou não sendo tão terrível quanto eu tinha medo.
Para entender como a plataforma móvel recebe um comando da plataforma de desktop para iniciar a configuração ao se conectar via adb, decidi examinar os pontos de entrada do aplicativo e adicionar saída aos logs de intenções e outras informações úteis. Decidi começar com o aplicativo ( com.e1c.mobile.E1cApplication
) e a atividade com android.intent.action.MAIN
no filtro de intenção ( com.e1c.mobile.App
). Também estava interessado em com.e1c.mobile.Starter
com um filtro de intenção interessante em com.e1c.mobile.START_TEMPLATE
e com.e1c.mobile.START_CMD
. É muito semelhante ao fato de ele aceitar intenções com os comandos 1s, e é ele quem inicia a configuração a partir do modelo.
Infelizmente, não encontrei nada de interessante no E1cApplication
, tudo o que acontece é instalar o manipulador em caches.
Mas nas outras duas classes, Starter
e App
, havia muito mais informações e isso acabou sendo bastante útil. O App.onCreate(Landroid/os/Bundle;)V
é bastante grande, por isso não o darei por inteiro, apenas as partes que me interessam.
Fatia do método onCreate do aplicativo 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
Pode ser visto na seção de código acima que o aplicativo recebe a intenção, coloca um link para ele no registro p1
, anexa a StringBuilder
ao nome e método da classe via StringBuilder
e passa V(Ljava/lang/String;)V
da classe Utils
para o método estático. Aparentemente, algum tipo de seu próprio logger. Depois disso, a intenção é verificada para obter dados adicionais e, se disponível, Uri
obtido a partir dela, a partir da qual, por sua vez, uma sequência é obtida pelo método getQuery()
e passada para a classe Starter
. Não vi nenhum estudo adicional sobre o onCreate
, dei uma olhada e verifiquei se as ações eram bastante padrão. A menos que a exibição pareça ser criada programaticamente por uma classe completamente diferente, em vez de usar o LayoutInflater
, mas eu não me aprofundava muito, então tudo é possível e não como eu pensava. A próxima aula para onde eu fui é Starter
. Felizmente, ele foi mencionado no ativismo e me interessou no manifesto.
Infelizmente, o método que foi chamado a partir da atividade acabou sendo nativo ( .method static native startCmd(Ljava/lang/String;)V
), portanto prestei atenção no método onRecieve
(no qual o aplicativo aceita os eventos nos quais está inscrito). Não vou dar o código, estava interessado nele que a intenção também é registrada usando o método V(Ljava/lang/String;)V
da classe Utils
. Acontece que basta adicionar um pouco de código smali a esse método - e eu receberei meus logs nos locais nos quais os desenvolvedores da plataforma pretendiam. Indo para a classe Utils, vi imediatamente 2 métodos vazios, V e W. Aparentemente, eles estão vazios porque, ao compilar a versão de lançamento, eles foram cortados. Acabei de adicionar um registro da sequência passada ao android.utils.Log
padrão. Para fazer isso, altere o número de registros locais necessários de 0 para 1 (para a linha de tag), coloque essa sequência no registro v0
e também registrou a chamada nos métodos Log
Implementação dos métodos W e 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
Lancei o celular a partir do 1s 1s desktop e recebi os seguintes logs
Resultados do Lançamento 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 você pode ver, há todas as informações necessárias para automatizar o lançamento do aplicativo por meio do adb. É verdade que, nesse ponto, peguei um rosto palpado duplo. Primeiramente, finalmente peguei as chaves com as quais o jadx dominava a tradução em java (é claro que eu teria que escrever em smali de qualquer maneira, mas ainda assim). E o segundo facespalm acabou sendo o fato de que eu percebi que estava atormentando a plataforma do desenvolvedor em vão (necessária apenas para o desenvolvimento e a depuração de configurações), seria mais correto reverter a compilação do release dos aplicativos 1s e existem projetos gradle semi-prontos para montagem, há um arquivo com uma lista de dependências e outros pães. Fiquei um pouco triste com isso - e ainda decidi terminar o que comecei. Enfim, pelo bem do fã, estou fazendo tudo isso, e não pelo benefício prático.
Decidi automatizar a instalação e iniciar o aplicativo adb usando gradle. De qualquer forma, naquela época, eu já tinha uma pequena lista de shuffles gravados (descompactando, copiando recursos modificados e arquivos smali para o diretório com o aplicativo descompactado, empacotamento, assinatura, de fato, todas as tarefas são apenas corredores para os comandos do console). Nas tarefas existentes, foram adicionados os seguintes 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
Pela sequência de comandos acima, instalamos o apk, concedemos ao aplicativo instalado permissão para ler o disco, copia a configuração 1s para o dispositivo e comandos para iniciar 1s e carregar a configuração. Isso não funciona a propósito em todas as versões do Android, mas eu testei na API 28 e, portanto, essa sequência faz tudo corretamente. Nas versões mais novas, pode haver problemas com a emissão de direitos.
Estou envolvido no processamento de dados de 1s e modifico tags
Aqui eu tinha várias opções para o desenvolvimento futuro de eventos.
- Eu domino diretamente o MapImpl.smali, que prometeu ser uma tarefa bastante difícil, considerando que a biblioteca para trabalhar com o Google Maps é minimamente seriamente reduzida, e eu teria que escrever tudo na sintaxe smali.
- Uso o arquivo MapImpl.java obtido graças ao jadx, faço alterações nele, uso as classes que utilizo para substituí-lo por stubs, destilar em smali, substituir o arquivo e compactá-lo. A opção me pareceu tensa, pois realmente doía ter muitos plugues, além de alguns problemas que eu não queria escolher.
- Combino as abordagens 1 e 2, insiro as chamadas de uma classe de extensão especial MapImplExtenstion.java no código smali que estende uma parte da lógica - como resultado, escolhi essa opção, simplesmente porque é mais simples que a primeira e a segunda opções e me pareceu um pouco mais complicada do que a opção 4, mas menos trabalhoso (sim, sonho, ingênuo).
- Em geral, substitua o Google Maps por outra coisa, por exemplo, Yandex. Para fazer todo o trabalho em outro projeto, simplesmente repetindo as assinaturas MapImpl, minimize a descompactação e solte os arquivos smali finalizados antes de compactar o aplicativo nos locais apropriados. Não há problemas com bibliotecas minificadas, mas eu precisaria me registrar para obter uma chave para cartões, criar um projeto separado, me preocupar com a conexão de recursos e muito mais.
- Igual à opção 4, mas substitua o Google por .... Mas aqui eu suspeitava que seria possível reduzir 1 em 1 mapas sdk, e não havia desejo de experimentar.
Após a terceira opção, criei 2 subdiretórios no src: java, para MapImplExtenstion.java e stubJava para arquivos necessários apenas para compilação. Desde que decidi ajustar um pouco os rótulos, fiquei interessado nos dois campos a seguir:
Campos Xj: Ljava / util / ArrayList e 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 em java, encontrei no código descompilado um método responsável por preenchê-los
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])); } } } }
Assim, a classe MapImplExtension adicionou o método ArrayList [] kN (String [] títulos, coordenadas duplas []) que, no primeiro elemento da matriz, retornará uma lista que precisará ser colocada em Xj e, no segundo, uma lista para 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; } }
Compilado com os seguintes comandos, primeiro na classe, depois no dex, depois descompilado no smali para empacotar junto com o restante dos arquivos
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
Adicionado o campo de extensão do tipo de nossa nova classe ao MapImpl.smali e sua inicialização
Adicionando um 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;
E também substituiu o processamento na classe MapImpl de dados de 1s pelo processamento na classe MapImplExtension
Alterações no 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;
Tendo empacotado e lançado o aplicativo, vi com alegria que tudo estava funcionando. Mas, no momento, não fiz nada que pudesse expandir os recursos da plataforma móvel. Apenas alterei os cabeçalhos das marcas no mapa. Mas, querendo mudar a imagem das tags, interrompi bastante e enterrei bastante tempo nas fontes e na documentação.
Adicionando nova funcionalidade
Primeiro, uma pequena explicação de como o marcador do rótulo está definido. A classe MarkerOptions
possui um public MarkerOptions icon (BitmapDescriptor iconDescriptor)
método public MarkerOptions icon (BitmapDescriptor iconDescriptor)
para o qual o objeto criado por um dos métodos estáticos da classe BitmapDescriptorFactory
é BitmapDescriptorFactory
.
E aqui, de fato, uma chatice estava me esperando. Como 1c não usa esse negócio, as classes correspondentes foram simplesmente cortadas durante a minificação. Eu tive que restaurá-los, e foi doloroso e longo. Mais precisamente, as classes estavam lá, mas foram renomeadas e não continham os métodos necessários. Ao escolher o tipo de biblioteca completa para trabalhar com mapas - por assinaturas e constantes, descobri a correspondência das classes da biblioteca com as classes de aplicativos, adicionei os métodos e os campos necessários, reconstruí-a e fiquei feliz por essa missão finalmente terminar, já que finalmente funcionou, embora não desde a primeira tentativa (várias vezes cortada com assinaturas e classes minificadas confusas).
O resultado (exteriormente não é muito impressionante). Dois pontos com coordenadas (45; 45) e (46; 46) Como o volume do código alterado é bastante grande - não o incluirei no artigo, darei apenas as alterações de assinatura. Se alguém estiver interessado em todas as edições que fiz nesta etapa - você pode procurar neste 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) {
Resumindo - a experiência foi bastante interessante. Pelo menos para mim. Ajudei a descobrir algumas ferramentas, aprendi um pouco melhor sobre o trabalho do andróide e o formato de bytecode para dalvik / art, a experiência de escolher código minificado também seria útil (já havia um caso em que, na versão de lançamento do R8, cortei os campos de classe que eram realmente usados, naquela época apenas pelo método Eu descobri, agora não causaria problemas).
Se alguém está interessado em repetir tudo sozinho e, possivelmente, buscá-lo ainda mais - eu postei no github todas as fontes, juntamente com um script gradle torto que se baseia no apk original modificado.