0%

安卓逆向的那些事第三集

前言:纸上得来终觉浅,绝知此事要躬行。继续实战学习安卓逆向

课程目标

1、了解安卓四大组件、Activity生命周期

2、弹窗定、去更新

3、广告分析与布局优化

工具

1、教程demo

2、MT管理器/NP管理器

3、算法助手

4、雷电模拟器

5、jadx-gui

6、开发助手

1、广告类型

手机上的广告主要分为三种:

  • 启动广告、弹窗:一般在软件启动的时候出现
  • 更新广告、弹窗:有些软件会弹出强制更新的广告,不更新就会自动退出
  • 横幅广告:出现在一个页面的中间或者顶部、底部

2、安卓的四大组件

组件 描述
Activity(活动) 在应用中的一个Activity可以用来表示一个界面,意思是可以理解为”活动”,即一个活动的开始,代表Activity组件启动。活动结束,代表一个Activity的生命周期结束,一个Android应用必须通过Activity来运行和启动,Activity的生命周期交给系统统一管理
Service(服务) Service它可以在后台执行长时间运行操作而没有用户界面的应用组件,不依赖任何用户界面,例如后台播放音乐,后台下载文件等
Broadcast Receiver(广播接收器) 一个用于接收广播信息,并作出对应处理的组件。比如我们常见的系统广播:通知时区改变、电量低、用户改变了语言选项等
Content Provider(内容提供者) 作为应用程序之间唯一的共享数据的途径,Content Provider主要的功能就是存储并检索数据以及向其他应用程序提供访问数据的接口。Android内置的许多数据是使用Content Provider形式,供开发者调用(如视频、音频、图片、通讯录等)

启动广告流程

启动Activity -> 广告Activity -> 主页Activity

3、修改方法:

  1. 修改加载时间
  2. Activity切换定位,修改Intent的Activity类名
方法一-修改加载时间
  1. 首先打开MT管理器,选择Activity记录,启动服务。

    image-20241027150555134
  2. 这时候打开wuaipojie的第三关,发现左上角的Activity的记录出现了很多信息,回到MT管理器,停止服务,找到Adactivity,这个就是一打开关卡时出现的广告Activity,复制一下

    image-20241027152636463
  3. 打开MT管理器中的安装包提取,覆盖之前的安装包,定位安装包,点击查看,查看classes.dex文件,选择用Dex编辑器++的方式,在搜索框内粘贴之前复制的内容,将搜索类型改为类名

    image-20241027153058244
  4. 选择第一个Activity,转为Java格式,发现要会员…那就只能用NP管理器

  5. 打开Np管理器,找到apk文件中的classes.dex文件,用Dex编辑Plus的方式打开,同样搜索Activity类名,选择第一个Activity,转为Java代码。找到onCreate方法,这是刚才界面首先会执行的一个方法,我们在Activity的生命周期再做讲解

    image-20241027154614901

    在这个方法中首先进行了窗口的绑定,再加载Ad,loadAd方法中,作者编写了一个线程:等待3000毫秒钟(其实就是3秒),之后会实现一个新方法,这种新方法就会启动第三关这个类

  6. 记住loadAd这个方法,回到smali语法界面,找到第153行:

    1
    const-wide/16 v2, 0xbb8  //0xbb8转为10十进制就是3000,对应的3000毫秒
  7. 原先的登陆界面的广告有3秒钟,所以我们将数值修改为0x0即可(在Java中也显示此时的时间为0秒,说明修改成功了),实操是出现了非预期…事实上,这个Activity并没有被关闭,只是时间非常的短,使用Activity记录依旧可以检测到这个Activity的出现

方法二-修改Activity类名
  1. 首先来认识一下xml文件,用NP管理器打开,选择AndroidMainfest.xml,用编辑的方式打开

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <!---声明实现应用部分可视化界面的 Activity,必须使用 AndroidManifest 中的 <activity> 元素表示所有 Activity。系统不会识别和运行任何未进行声明的Activity。----->
    <activity
    android:label="@string/app_name"
    android:name="com.zj.wuaipojie.ui.MainActivity"
    android:exported="true"> <!--当前Activity是否可以被另一个Application的组件启动:true允许被启动;false不允许被启动-->
    <!---指明这个activity可以以什么样的意图(intent)启动--->
    <intent-filter>
    <!--表示activity作为一个什么动作启动,android.intent.action.MAIN表示作为主activity启动--->
    <action
    android:name="android.intent.action.MAIN" />
    <!--这是action元素的额外类别信息,android.intent.category.LAUNCHER表示这个activity为当前应用程序优先级最高的Activity-->
    <category
    android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>
    <activity
    android:name="com.zj.wuaipojie.ui.ChallengeFirst" />
    <activity
    android:name="com.zj.wuaipojie.ui.ChallengeFifth"
    android:exported="true" />
    <activity
    android:name="com.zj.wuaipojie.ui.ChallengeFourth"
    android:exported="true" />
    <activity
    android:name="com.zj.wuaipojie.ui.ChallengeThird"
    android:exported="false" />
    <activity
    android:name="com.zj.wuaipojie.ui.ChallengeSecond"
    android:exported="false" />
    <activity
    android:name="com.zj.wuaipojie.ui.AdActivity" />
  2. 我们发现,下方代码表明,name这个类是作为这个应用的启动页

    1
    2
    3
    4
    5
    6
    7
    8
    <intent-filter>  
    <!--表示activity作为一个什么动作启动,android.intent.action.MAIN表示作为主activity启动--->
    <action
    android:name="android.intent.action.MAIN" />
    <!--这是action元素的额外类别信息,android.intent.category.LAUNCHER表示这个activity为当前应用程序优先级最高的Activity-->
    <category
    android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    后面的Activity就是一些各个关卡的Activity:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <activity  
    android:name="com.zj.wuaipojie.ui.ChallengeFirst" />
    <activity
    android:name="com.zj.wuaipojie.ui.ChallengeFifth"
    android:exported="true" />
    <activity
    android:name="com.zj.wuaipojie.ui.ChallengeFourth"
    android:exported="true" />
    <activity
    android:name="com.zj.wuaipojie.ui.ChallengeThird" //第三关
    android:exported="false" />
    <activity
    android:name="com.zj.wuaipojie.ui.ChallengeSecond"
    android:exported="false" />
    <activity
    android:name="com.zj.wuaipojie.ui.AdActivity" />

    所以我们可以选择将ui.AdActivity替换成第三关的Activity

  3. 选择用MT管理器打开.xml文件(使用NP管理器可能出现安装包损坏的情况),将ui.ChallengeThird替换ui.AdActivity

    替换重新安装出现闪退的现象,事实上这种方法容易导致闪退、安装包损坏的错误情况(可能是因为在启动Activity的时候一些预先的数据没有加载出来)

方法三-修改Activity类名(采用更准确的定位)
  1. 使用NP管理器,打开classes.dex文件(方式还是Dex编辑++),长按搜索的结果,选择复制最后一条路径(方法的smali语法的路径)

    image-20241027193027211
  2. 搜索该路径,并将类型改为代码

    image-20241027193143528
  3. 此时搜索的结果有多个,我们选择的是从其他地方调用的,因此选择最后一个

    image-20241027193320569
  4. 打开该路径,转为Java语言,调用的方法很明显:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    switch (position) {  
    case 0:
    Intent intent = new Intent();
    intent.setClass(it.getContext(), ChallengeFirst.class);
    it.getContext().startActivity(intent);
    return;
    case 1:
    Intent intent2 = new Intent();
    intent2.setClass(it.getContext(), ChallengeSecond.class);
    it.getContext().startActivity(intent2);
    return;
    case 2:
    Intent intent3 = new Intent(); //new一个Intent,
    intent3.setClass(it.getContext(), AdActivity.class); //传入要切换的Acitivity的类名
    it.getContext().startActivity(intent3); //启动对应的Activity
    return;
    // 修改思路:将传入的Activity类名修改为其他的
    case 3:
    Intent intent4 = new Intent();
    intent4.setClass(it.getContext(), ChallengeFourth.class);
    it.getContext().startActivity(intent4);
    return;
    default:
    return;
    }
  5. 这里太明显了,对于第三关的方法来说,是先传入了启动广告的Activity,之后再调用第三关的Activity,所以我们的修改方式为第三关对应的Activity,回到smali语法的界面,找到之前对应的路径,模仿其他关的方式修改为第三关的路径即可

    image-20241027194657959

4、Activity的生命周期

重要的函数名称:

函数名称 描述
onCreat() 一个Activity启动后第一个被调用的函数,常用来在此方法中进行Activity的一些初始化操作,例如创建View、绑定数据、注册监听、加载参数
onStart() 当Activity显示在屏幕上时,此方法被调用但此时还无法进行与用户
onResume() 这个方法在onStart()之后调用,也就是说在Activity准备好与用户交互的时候调用,此时的Activity一定位于Activity栈顶,处于运行状态
onPause() 这个方法是在系统准备去启动或者恢复另外一个Activity的时候调用,通常在这个方法中执行一些释放资源的方法,以及保存一些关键数据
onStop() 这个方法是在Activity完全不可见的时候调用
onDestroy() 这个方法在Activity销毁之前调用,之后Activity的状态为销毁状态
onRestart() 当Activity从停止stop状态恢复到进入start状态时调用状态
image-20240719152542010

5、弹窗定位&堆栈分析

修改方法:

  • 修改xml中的versioncode
  • Hook弹窗(推荐算法助手开启弹窗定位)
  • 修改dex弹窗代码
  • 抓包修改响应体(也可以路由器阻拦)

方法一-修改xml中的versioncoode

这个主要是应对更新弹窗的出现,用MT管理器打开xml文件,修改versioncode为2

image-20241028091908076

这里更新完之后会出现闪退的现象,可能是因为对方服务器关闭,无法自动检查,就没有更新弹窗。

方法二-hook修改

有些广告弹窗可能会劫持你的返回键,只能回到主页面的那种,那么这种就属于hook弹窗

  1. 安装算法助手,同时启用模块,勾选wuaipojie

  2. 打开算法助手,勾选wuaipojie,点击wuaipojie,下拉勾选弹窗定位(返回键可取消),选择右上角的启动键

    QQ图片20241028162913

    此时发现就可以去掉弹窗了

    {7AF377A3-C11D-41D7-B76A-16DC09B1B9C0}

  3. 还可以勾选下方的屏蔽关键词弹窗,输入关键词广告,结果发现两个弹窗一样被hook

方法三-修改dex弹窗代码

  1. 关闭弹窗定位回到主页面,打开日志,选择最新的日志,点进去

    image-20241028170810909

  2. 再调用堆栈中找到com.zj.wuaipojie.ui.ChallengeThird.onCreate,onCreate之前有提到是在一个Activity被启动之后会调用,复制该内容,打开MT管理器,跟之前同样的方式打开classes.dex文件,搜索复制的内容,将类型修改为方法名,选择最后一个

    image-20241028172907219

  3. 向下寻找找到这个部分代码:

    1
    const-string v0, "一号广告弹窗已就位"

    再往下寻找到带有show()的方法,这大概就是一号弹窗的显示

    1
    2
    .line 53
    invoke-virtual {p1}, Lcom/zj/wuaipojie/util/CommonDialog;->show()V
  4. 我们可以删除这段的代码或者注释这段的代码达到目的

  5. 二号弹窗同样的方式:找到对应的shaow(),将其删除或者注释掉

QQ图片20241028195324

方法四-抓包修改响应体(也可以路由器阻拦)

后续会补充

对横幅广告的去除

  1. 安装开发助手,打开无障碍权限。使用其布局查看功能,再第三关的界面下点击放大镜的图标,点击横幅广告,其中的ad_image就是广告信息

    @B%KG@VWJOIUEMVWN_TJP0D

  2. 点击信息,找Hex的值,复制

    image-20241028200908763
  3. ​ 回到MT中wuaipojie的路径下,在右上角选择XML搜索,内容为十六进制数,类型为资源ID,勾选十六进制,结果为jJ.xml

  4. 反编译该文件,在右上角搜索十六进制数来定位到该文件(省略0x)。这里一共有两种修改方式:

    1. 将宽度和高度修改为0

    2. 在高度下方添加一串代码:

      1
      android:visibility='gone'