我们在android下反转手机1。 如何添加一些功能并放弃一些晚上

我必须马上说,尽管有人可能会想起这篇文章的标题很大声-根本没有任何直接的硬核。 而且,尽管事实是倒退通常与信息安全相关,但是再也不会出现这样的情况,因为不需要绕过任何保护措施(小巧的例外)。 没有一个。 Mobile 1s是免费产品,其中没有内置任何检查,保护和付费功能。


因此,我吸引那些希望看到使用去混淆器,使用Frida / Xposed和其他棘手软件的人-您自己不会发现任何有趣的东西。 在这里,我们将只使用apktool,baksmali,apksigner,adb,jadx,控制台,文本编辑器,javac,d8等。


我还想提前让那些等待对该平台进行深入分析或其强大修改的人感到失望。 仅对几个文件进行小的编辑,实际上,即使是我没有彻底弄清楚的这几个文件,都位于顶部。


一切如何开始


我会告诉您一些有关为什么我突然想到以某种方式进入手机的原因。 目前,我从事android的本机开发已经有一年了,但是在此之前,我以程序员的身份工作了4年,而在过去的一年半中,我们经常专门针对移动平台进行开发。 尽管她满足了基本需求,但她也有很多缺点(除了编程语言之外)。 特别是,至少通过常规手段并利用我们当时的知识存储,不可能人工嵌入任何外部库。 例如,在地图上显示标签的功能还使一切都感到非常难过。 其配置的全部可能性是在您点击标签时为标签指定文本。 那时,解决此问题的唯一方法是使用特殊对象“ HTML Document Field”,但存在问题。 在使用1s时,我在android本机开发中的所有知识都在HelloWorld配对中,所以我什至没有想到将手机改成1s,或者我们没有解决客户关于1s非标准扩展的各种问题,或者我们看到了非常简单的本机与它相邻并歪斜/倾斜地与1s集成的应用程序(并且1s许可协议似乎禁止在平台本身中进行编辑)。


因此,进行1c逆转的第一个原因是我变得很感兴趣,并且知道如何使用当前的知识库。 我必须马上说,本地开发的经验并不是说需要什么,在日常工作中几乎没有发现下面将要描述的内容。 因此,原则上,大概几天或几周的普通1snik都可以弄清楚这一切。


第二个原因是我只想尝试选择其他人的apk,因为在此之前我缺少了相当广泛的知识,而且由于我必须从某个地方开始,所以我只需要1s。


第一步


我做的第一件事是,即使倒转1的想法只是在脑海中徘徊,我也只是用鼠标将应用程序的apk文件拖到AndroidStudio窗口上。 我想马上说一下,我感到有些难过,因为大多数1c代码都是用C ++编写的,并且位于.so库中,而将它们反转的难度更大,这并不有趣。 但是classes.dex文件完全吸引了我的注意,特别是因为它的大小适中,使我有机会假设它很容易反转。 总的来说,结果就是这样。 顺便说一下,C ++中的大多数代码这一事实的有趣结果是,许多方法和类都避免了使用ProGuard进行处理。 用最小的代码进行互操作是困难的;)。


这是我在Studio窗口中看到的内容(分解后的x86版本可用于仿真器而不是实际设备)


如您在上面的屏幕快照中所见,该应用程序几乎没有缩小(就重命名类和方法而言)。 另外,您可以看到Java中几乎没有代码,因此库几乎占据了所有空间。


推送了一段时间的类列表后,我看到了一个奇怪的MapImpl类,令人怀疑是由他来负责这种自定义显示标签的情况。


MapImpl结构


方法列表及其签名启发了人们希望一切都非常简单,因此我决定研究一下smali代码,此后我变得非常紧张,然后阅读了smali命令列表以及如何读取/编写它。 但是,正是在这一点上,我才决定这件事将是简单而短暂的,因此,在这里,是一个进行逆转的机会。 决定自己花几个晚上来解决这个问题(我多么残酷地误会了),我带着镇定的灵魂上床睡觉。


制定计划


早上醒来后,我决定,由于到目前为止,我什至从未参与过替代应用程序中的资源,就像逻辑上的改变一样,我没什么可动摇的以卡代替替换活动的,因此最好以小步骤解决问题。 之后,我以简短的步骤清单列出了自己的步骤,这些步骤应该以能够进行编辑的工作移动平台的形式引导我到达旅程的终点​​。 该列表如下:


  1. 为移动平台准备1c配置,其所有功能都是在地图上显示两个标签。
  2. 解压缩移动平台的apk,打包无更改,确保配置正常。
  3. 对smali文件进行少量更改,这些更改几乎不会更改任何内容,但是可以在日志中看到这些更改或更改工作逻辑,进行汇编并确保其正常工作。
  4. 要使用Java中的映射来覆盖smali活动,或使用相同的接口编写活动,请在不更改功能的情况下替换应用程序中的活动。 或者,如果第一个太懒或太复杂,则编写一个类,该类将为MapImpl做一部分工作,并从smali MapImpl添加对其方法的调用。
  5. 写这篇文章(我一直想写点东西,但是其他想法还在我脑海中酝酿,但是看来这终于是一个非常值得的话题)

好吧,好吧,至少在第一段中,我没有问题。 我下载了该平台的免费教育版,下载了移动平台,并将其全部部署在我的计算机上(1C,您是否已经有Linux的培训版,我只是想离开Windows,现在我必须在其中做一些工作)。 配置非常简单,在启动时调用ShowOnMap()函数,该函数在地图上传输两个点。 仅此而已。


但是第二点,由于我的文盲而变得更加悲惨。 但是在那里,除了第一点,其他所有问题都存在。


我重新打包应用程序而不更改代码


谷歌立即告诉我,有一个如此出色的apktool工具,它实际上允许两个命令将apk解析为.smali文件,并将它们重新组合。 我决定使用它。 我用以下命令解压缩了apk(以下内容尽管有时我在Linux上做了部分工作,但我会提供所有用于Windows的命令):


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

并获得了一个解压后的目录,其中放置了一堆文件,以后可以使用它们。 用命令打包apk回来


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

尝试将其安装在模拟器上


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

我收到以下错误:


 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] 

总的来说,有必要更加注意这个基础。 当然,我知道有签名,并且将它们分为销售和发布,但是我不知道没有签名就不会安装应用程序。 我决定使用我的借记签名,并签署APK组


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

我设法运行该应用程序。 没错,下面是带有Google签名的灰屏,而不是两点的地图。 刮了萝卜之后,我决定事实是使用了从1s开始的Google地图密钥,该密钥不适用于我的签名。 因此,转到Google网站上的开发人员控制台,我创建了一个用于在android上使用google maps api的新项目,收到了api键,我在google_maps_key行的res / values / strings.xml中指定了该键,并将共享密钥添加到了该项目的权限中。 我重新打包并重新签名了apk,启动了它,最后一切又恢复了。


我添加我的日志


我想做的下一件事是自动启动应用程序,并将1c配置添加到平台。 手动进行每次编辑后,这样做仍然是一团糟。


最初,我用Google搜索并尝试使用jadx或dex2jar实用程序,以免打扰阅读smali,而是阅读更熟悉的Java代码,但由于某些原因它们不起作用(后来,jadx设法陷入某种萨满教)。 我不得不拆卸smali,因为事实证明它并不像我所担心的那样可怕。


为了了解移动平台如何通过adb连接从台式机平台接收命令以启动配置,我决定查看应用程序的入口点,并将输出添加到意图日志和其他有用信息中。 我决定从应用程序( com.e1c.mobile.E1cApplication )开始,并在意图过滤器( com.e1c.mobile.App )中具有android.intent.action.MAIN活动开始。 我还对接收器com.e1c.mobile.Starter感兴趣,并在com.e1c.mobile.START_TEMPLATEcom.e1c.mobile.START_CMDcom.e1c.mobile.START_TEMPLATE了一个有趣的意图过滤器。 这与他使用1s命令接受意图的事实非常相似,并且是他从模板开始配置。


不幸的是,我在E1cApplication找不到任何有趣的E1cApplication ,所有事情都是在缓存上安装处理程序。


但是在其他两个类StarterApp ,有更多的信息,而且它非常有用。 App.onCreate(Landroid/os/Bundle;)V方法非常大,因此我不会完整介绍它,而只给出我感兴趣的部分。


App onCreate方法切片
 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 

从上面的代码部分可以看出,应用程序收到了意图,将其链接放在p1寄存器中,通过StringBuilderStringBuilder附加到类名和方法上StringBuilder并将Utils类的V(Ljava/lang/String;)V传递给静态方法。 显然是您自己的某种记录器。 此后,将检查该意图以获取其他数据,如果有可用数据, Uri从中获取Uri ,然后再通过getQuery()方法从中获取一个字符串并将其传递给Starter类。 onCreate没有对onCreate任何进一步的研究,看了一下并确保这些动作非常标准。 除非该视图似乎是由一个完全不同的类以编程方式创建的,否则不要使用LayoutInflater ,但我的挖掘不是很深入,所以一切LayoutInflater可能,并非我想的那样。 我去的下一堂课是Starter 。 幸运的是,他在行动主义者中被提及,并且对清单感兴趣。


不幸的是,从活动中调用的方法原来是本地方法( .method static native startCmd(Ljava/lang/String;)V ),因此我注意了onRecieve方法(在该方法中应用程序接受它所预订的事件)。 我不会给出代码,对此我很感兴趣,它也使用Utils类的V(Ljava/lang/String;)V方法记录了意图。 事实证明,向此方法添加一些smali代码就足够了-而且,我将在平台开发人员预期的相同位置获取日志。 进入Utils类,我立即看到2个空方法V和W。显然它们是空的,因为在编译发行版时,它们被切掉了。 我只是将传递的字符串的记录添加到标准android.utils.Log中。 为此,将所需的本地寄存器的数量从0更改为1(用于标记行),将此字符串放入v0寄存器中,并还注册对Log方法的调用


方法W和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 

我从1s桌面1s启动了mobile,并收到了以下日志


启动结果
  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 

如您所见,这里有所有必要的信息,可以通过adb自动启动应用程序。 是的,在这一点上,我陷入了双重困境。 首先,我最终选择了jadx掌握Java翻译的键(很明显,无论如何我仍然必须用smali编写)。 而第二个facespalm事实是,我意识到我徒劳地折磨了开发人员的平台(仅用于配置的开发和调试),反转1s应用程序的发布版本会更正确,并且有用于组装的半准备好的gradle项目,有一个包含依赖项列表和其他文件的文件头。 我对此感到有些难过-仍然决定完成我的工作。 无论如何,为了支持者,我正在做这一切,而不是为了实际利益。


通过adb启动平台


我决定使用gradle自动安装并通过adb应用程序启动。 无论如何,到那时,我已经编写了一小段洗牌列表(解包,将已修改的资源和smali文件与解包的应用程序,打包,签名一起复制到目录中,实际上所有任务只是控制台命令的执行者)。 向现有任务添加以下命令


  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 

通过以上命令序列,我们安装apk,授予已安装的应用程序读取磁盘的权限,将1s的配置复制到设备,并给出命令以启动1s并加载配置。 这并不是在所有版本的android上都能正常工作,但我在api 28上进行了测试,因此,此序列可以正确执行所有操作。 在较新的版本上,发行权可能存在问题。


我从1s开始参与数据处理并修改标签


在这里,我有一些进一步发展活动的选择。


  1. 我直接统治了MapImpl.smali,因为考虑到使用Google地图的库已经被严重缩减了,所以我保证这将是一个相当艰巨的任务,而我将不得不用smali语法编写所有内容。
  2. 我使用感谢jadx获得的MapImpl.java文件,对其进行更改,使用我用存根替换它的类,蒸馏成smali,替换文件并打包。 这个选项对我来说似乎很紧张,因为拥有很多插头确实很痛,而且其中有些问题我不想摘。
  3. 我结合了方法1和2,在特殊的扩展类MapImplExtenstion.java的调用中插入了扩展逻辑的一部分的smali代码-结果,我选择了此选项,这是因为它比第一个和第二个选项更简单,并且在我看来比选项4还要复杂一些。但省力(是,梦想,天真)。
  4. 通常,用其他东西代替Google地图,例如Yandex。 要完成另一个项目中的所有工作,只需重复MapImpl签名,然后最小化解压缩,然后在将应用程序打包到适当的位置之前,只需删除完成的smali文件即可。 缩小的库没有问题,但是我必须注册才能获得卡的密钥,创建单独的项目,烦恼连接资源等等。
  5. 与选项4相同,但将Google替换为... Google。 但是在这里我怀疑有可能将sdk地图中的1分之一缩小,并且没有实验的欲望。

在第三个选项之后,我在src中创建了2个子目录:java,用于MapImplExtenstion.java和stubJava,用于仅用于编译的文件。 由于我决定稍微调整标签,因此我对以下两个领域感兴趣:


字段Xj:Ljava / util / ArrayList和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 

使用jadx在Java中反编译时,我在反编译代码中发现了一种负责填充它们的方法


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

因此,MapImplExtension类添加了ArrayList [] kN(字符串[]标题,双精度[]坐标)方法,该方法在数组的第一个元素中将返回需要放置在Xj中的列表,在第二个元素中返回用于Xk的列表。


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

使用以下命令编译,首先在类中,然后在dex中,然后在smali中反编译,然后与其余文件一起打包


  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 

在MapImpl.smali中添加了新类类型的扩展字段,并对其进行了初始化


新增栏位
 #    .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; 

并且还用MapImplExtension类中的处理替换了从1s开始的MapImpl类数据中的处理。


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; 

打包并启动该应用程序后,我欣喜地看到一切正常。 但是目前,我还没有做任何能够扩展移动平台功能的事情。 刚刚更改了地图上标记的标题。 但是,由于想更改标签的图像,我断断续续地工作了很长时间,并在源代码和文档中埋了很长时间。


增加新功能


首先,简要说明如何设置标签标记。 MarkerOptions类具有一个public MarkerOptions icon (BitmapDescriptor iconDescriptor)方法public MarkerOptions icon (BitmapDescriptor iconDescriptor) ,由BitmapDescriptorFactory类的一种静态方法创建的对象将public MarkerOptions icon (BitmapDescriptor iconDescriptor)给该方法。


实际上,在这里,一个无赖在等着我。 由于1c不使用此业务,因此在缩小过程中仅剪切了相应的类。 我不得不恢复它们,这是痛苦而漫长的。 更准确地说,它们是类,但是它们被重命名并且不包含必需的方法。 通过选择用于操作地图的完整库的种类-通过签名和常量,我发现了库类与应用程序类的对应关系,添加了必要的方法和字段,对其进行了重新构建,并且很高兴此任务最终得以完成,因为它终于成功了,尽管不是第一次尝试(几次被签名和困惑的缩小类所割伤)。


结果(外观不太好看)。 两点的坐标分别为(45; 45)和(46; 46)


由于更改后的代码量很大-我将不在本文中介绍,因此仅给出签名更改。 如果有人对我在这一步所做的所有编辑都感兴趣,则可以查看此提交


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

总结一下-经验非常有趣。 至少对我来说。 我帮助找出了一些工具,对dalvik / art的android和字节码格式的工作了解得更多,选择精简代码的经验也很有用(在R8的发行版中,我曾经切过实际使用的类字段,当时只有通过这种方法我知道了,现在不会造成问题了)。


如果有人有兴趣自己重复一切,甚至有可能再做更多的话- 我在github上发布了所有源代码以及从原始apk修改而来的弯曲gradle脚本。

Source: https://habr.com/ru/post/zh-CN475266/


All Articles