安卓逆向案例之全局负一屏高级版绕过
概述
什么是全局负一屏?
这是一个可以在任何应用界面打开的负一屏工具,你可以添加常用的应用、快捷方式、网址、图片、联系人以及小工具。只需轻轻一滑,一切尽在眼前。
什么是绕过?
解锁全局负一屏的高级版功能,包括自由布局和高级版标识。
高级版功能有哪些?
- 支持自由布局
- 界面显示为高级版
前置知识
在开始之前,建议了解以下内容:
- ARM 架构的汇编指令集
- 使用 IDA 直接给 so 文件打补丁
- 安卓 APK 的回编译和签名验证绕过
- 使用 Android Studio 动态调试 APK
- 使用 Android Studio 查看 App 运行日志
- MT 管理器的使用
- Smali 语法
Tip
磨刀不误砍柴工,多学学总归没有坏处。
实战步骤
环境准备
这个 App 没有进行加壳,估计也是对安全不是非常重视,整个过程还是比较顺利的,适合新手练手。
Part 1:显示成高级版
目标
让 App 在"关于"页面显示红色"高级版"字样变为绿色(购买成功的颜色)。
步骤 1:定位页面 Activity
- 打开 App,在"关于"页面可以看到"高级版"字样是红色
- 使用 MT 管理器查看当前页面的 Activity
步骤 2:分析 Activity 结构
找到的 Activity 路径为:com.example.xf.negativeonescreen.pro.activity.about.AboutActivity
Note
有的小伙伴可能遇到过这种情况:不管你划到哪个页面,都只能捕获到同一个 Activity。这是因为这个应用采用了单一 Activity 架构,内容部分是由 Fragment 来渲染实现。这个问题不在本次讨论范畴之内,了解即可。
步骤 3:查找"高级版"文本来源
- 使用 JEB 打开 APK,找到 AboutActivity 的实现
- 没有找到"高级版"字样,说明是写死到布局文件里面了
- Activity 的布局文件渲染正常会在
onCreate阶段,重点跟这一块
查看布局文件,可以发现"高级版"文本。不过这种写法不符合安卓多语言规范,按照规范,这边应该是 @string/advance 之类的来指代高级版。
步骤 4:定位 TextView 组件
记一下资源 ID,资源 ID 在安卓中是唯一标记组件的办法。回到 AboutActivity 页面,可以看到这个组件对应了 textview2。
此时再去跟这个 textview2,会发现整个 class 中没有任何调用。可以推测,这个 Activity 被作为一个对象传入了其他地方调用。即便不推测,继续往下看代码,也能发现端倪:ActivationPresenter,单词拆分一下,就是"激活演示"的意思,很容易就能跟到这里。
步骤 5:分析激活逻辑
对混淆的字符做了语义化修改后,可以看到熟悉的 textview2,很显然我们需要显示成高级版的功能就在这里。
只要让 Ooo00OO0.isActived() 返回 true,就能达成显示高级版的目的。
继续跟这个函数,发现它只判断存储于 SharedPreferences 的 sign 是不是 null 就判定是否激活。
步骤 6:修改 Smali 代码
这里的验证不使用 Frida 或 Xposed 来实现,通过直接修改 Smali 的方式搞定。
由于 class 混淆过,直接去找这个类比较麻烦,用搜索功能搜索其中的字符串找到这个类。
对比转换过的 Java,分析 Smali 代码逻辑:
# 关键逻辑:判断 SharedPreferences 中的 sign 是否为 null# 修改方法:让判断直接返回 true由于 AndroidKiller 用着不顺手,下面都换成 MT 管理器。MT 中修改了这部分之后,返回,选择自动签名。
步骤 7:验证结果
安装看看,直接就能打开了,看来没有做应用完整性校验,问题不大。
我们的 Part 1 目标达成:App 现在显示为高级版。
Part 2:解锁自由布局功能
目标
解锁自由布局功能,可以在负一屏自由拖动和调整组件位置。
步骤 1:定位布局相关代码
老办法,先定位 Activity。发现 Activity 是空的,这是因为它的负一屏采用的是悬浮窗方案实现,没有 Activity 这一说。
步骤 2:搜索关键文本
先用 Jadx 找一下(JEB 的查找功能较弱),这个页面会在哪个文件中出现。使用 Jadx 搜索"自由布局",找到 name,根据 name 查找引用。
步骤 3:分析点击事件
直接就定位到了要找的部分。回到 JEB 找到这个类 com.example.xf.negativeonescreen.pro.Oo00O0oo。JEB 中没有将 array 解密,所以看到的就是加密后的字符串。
这是 onClick 事件中的一块,v0 是一个 Tag,标记当前点击的是第几个选项。跟一下这个函数,因为这个弹出的列表触发的事件全是点击,只要关注 onClick 即可。
步骤 4:反推优化代码
反推优化下代码,可以看到接口类的方法在刚才的类中必须要有对应的实现。
找到可疑之处,跟一下这个 native 函数。
步骤 5:分析 SO 文件
在 libbase.so 文件里面,先看看它的导出表,很明显有两个函数:checkSign 和 isActivated。不过这个 checkSign 似乎并没有发挥作用。
反编译这个函数看下,它的校验返回放在最后。这种校验方式最好搞定:只要整个函数中,v3 返回 1 就是已激活状态。
步骤 6:修改 SO 文件
在所有给 R7 赋值前的 R0 全部赋值为 #1,即可让最终 R7 的值为 1。
用 MT 把修改后的文件合并进来。
步骤 7:验证结果
简单测试下,Part 2 目标达成:自由布局功能已解锁。
总结
本次逆向案例展示了两种常见的绕过方式:
- Smali 修改:适用于逻辑简单、未加壳的 App,直接修改判断逻辑
- SO 文件补丁:适用于关键校验在 native 层的场景,通过 IDA 修改汇编代码
Warning
本案例仅用于学习研究,请勿用于非法用途。