android

安卓逆向的经验记录

2023 年,博客也开通 10 年了。我都不敢看 10 年前自己写的东西。感觉全是黑历史,不过也记录了自己一点点的成长和心境的变化。今年也将继续写,记录自己技术上的各种折腾。

# 安卓逆向初体验

首先自己知道了一个安卓电视盒子可以看全球的电视频道,我就想着干嘛要买人家的硬件,随便找个安卓手机不也可以使用么?于是找到了电视盒子上的软件,发现人家是有校验的。不是自家硬件初始化都无法通过。 于是我就好奇这个硬件校验是怎么做的,带着这个好奇心,开始了安卓逆向的征程。

# Java 代码脱壳

现在的安卓软件都会使用梆梆加固之类的软件防止别人进行逆向操作,所以第一步就是处理这个。先看到 JAVA 代码才能深入研究相关逻辑。经过同学指点,使用了一个 BlackDex 的工具。可以直接在安卓手机上进行脱壳操作。使用这个工具可以拿到 dex 文件。然后使用 jadx 输出 JAVA 代码。

jadx -d dist_fold ./cookie_xxx.dex
1

# 安卓手机抓包

拿到 JAVA 代码全部都读一遍也不现实,基于前端的逆向经验。我想先抓包看看请求内容。抓包软件使用的是 Charles 一款跨平台的抓包软件,不过是商业软件需要付费。 通过抓包分析到,请求全部是基于 HTTP 的,所以不需要再向安卓手机上安装抓包用的 CA 证书确实省事了不少。但是请求的数据和返回都是加密的。基于请求的字段并结合 JAVA 代码分析出加密使用的是 aes-128-cbc 算法。这个算法需要 key 和 iv 两个参数,那这两个参数是基于什么信息来生成的呢?通过请求带的特殊的 HTTP headers 进行猜测,应该是 utctime mac model 这三个参数。 为什么这么分析呢,因为服务器端需要进行解密,那必须要把需要的参数都告诉服务端才能把密文解密回明文。

# 加密算法逻辑分析

来到这里,最重要的是需要知道如何生成 key 和 iv 参数,因为 aes 是对称加密,我们知道这两个参数就能把密文再解析回明文。那就带着这个疑问来去读 JAVA 代码。

public class EncryptRule {
    private static String getEncrypKey(String str) {
        String substring = MACUtils.getMac().substring(4, 10);
        return MD5Util.getStringMD5_32(substring + "k3k7a3mM" + str).substring(8, 24);
    }

    private static String getDecrypKey() {
        String substring = MACUtils.getMac().substring(4, 10);
        String str = Build.MODEL;
        return MD5Util.getStringMD5_32(substring + "k3k7a3mM" + str).substring(8, 24);
    }

    public static String getEncryptionParams(String str, String str2) {
        String encrypKey = getEncrypKey(str2);
        return new String(Base64.encodeBase64(AES.encrypt(str.getBytes(), encrypKey.getBytes(), encrypKey.getBytes())));
    }

    public static String getSoEncryptionParams(String str, String str2, String str3) {
        return android.util.Base64.encodeToString(MainActivity.getJniApi().encodeAES(str.getBytes(), str3.getBytes(), str2.getBytes()), 2);
    }

    public static String getSoDecrypt(String str, String str2) {
        try {
            return new String(MainActivity.getJniApi().decodeAES(str.getBytes(), android.util.Base64.decode(str2.getBytes(), 2)));
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    public static String getDecrypt(String str) {
        return AES.decrypt(str, getDecrypKey(), getDecrypKey(), "utf8");
    }
}
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
32
33
34

通过上面这段 JAVA 代码,我们知道加密的 key 是基于 MAC 地址信息进行生成的,传入的 str 参数其实是 utctime 。而解密 key 是基于 MAC 地址和 MODEL 信息,返回值的解密并没有用到 utctime 。返回信息的时候考虑到网络延迟 utctime 不是一个靠谱的参数。这里的 JAVA 逻辑是非核心请求的算法,我们想知道核心的播放列表信息的时候,发现它使用的是 getSoEncryptionParams 这个方法调用,而这个方法经过分析是调用 C++ 生成的 so 文件。逆向难度又上了一个台阶。顺便说下,经过 JAVA 代码分析 key 和 iv 其实是相同的参数。

# so 文件分析

C++ 编译成 so 文件本质就是一个二进制文件。要想分析这个二进制文件需要很高的汇编功底和 C 语言知识,因为有些工具可以把二进制反编译成可读的 C 语言。经过同学点拨,使用了 IDA ProGhidra 两个工具来对 so 文件进行逆向。so 文件的分析非常复杂,这里就只说结论了,so 里面分析出来的加解密算法和 JAVA 是一样的,但是中间拼接的字符串是不一样的。只要找到了核心的字符串就能完成请求的加解密。关于请求的解密,针对非英文字符,请使用 UTF-8 编码方式,由于我使用的工具默认采用 ASCII 编码,导致中文乱码,花了一段时间才找到原因。

# 总结

出于满足自己的好奇心,在各路同学的指点下,进行了安卓程序的逆向分析。非常有趣并且学到了很多知识,最后拿到了播放列表,但发现是基于一个私有的 tvbus 协议,没法移植到别的 APP 上使用,只能到此作罢。但是这次逆向分析的收获还是不少,在此记录一下。

# 参考

BlackDex (opens new window)

jadx (opens new window)

Charles (opens new window)

CyberChef (opens new window)

Ghidra (opens new window)

IDA Pro (opens new window)

EncryptRule (opens new window)

Archlinux下Galaxy Nexus刷机并root

安卓本来就是 Linux 衍生的,所以必然可以用 Linux 系统进行刷机,而且效果比 Win 还要好.不需要安装驱动,只需要安装 adb 和 fastboot 即可.

# 安装相关依赖

首先通过 AUR 源安装[android-sdk](https://aur.archlinux.org/packages/android-sdk/和android-sdk-platform-tools (opens new window)相关的wiki在 这里 (opens new window)

通过 yaourt -Ql android-sdk-platform-tools 可以发现 adb 和 fastboot 都安装在/opt/android-sdk/platform-tools 目录下,但是 PATH 环境变量没有这个值,所以无法在终端里直接调用必须先设置 PATH 变量 export PATH="${PATH}:/opt/android-sdk/platform-tools"在 .xinitrc 里面添加这句就可以使 X 下的终端 PATH 环境变量被添加.也可以直接执行,当终端关闭时 PATH 变量的设置也失效.

# 配置 ADB

前面提供的 wiki 已经很详细的讲述如何进行ADB 的配置了 (opens new window)

# 测试 FASTBOOT

fastboot 模式(上下音量键和开机键一起按),可以看到一个大大的 Start 画面,这就是进入了 fastboot.或者使用命令adb reboot bootloader。此时把手机用 USB 连入电脑. 在终端里面输入 fastboot devices 如果提示 no permission 即是普通用户没有权限。这时候就需要切换到 root 用户进行执行.如果输入一串数字.则说明系统识别到了你的手机.

# 刷机开始

首先要下载固件 (opens new window)下载下来的 tgz 文件解压即可看到有个 flash-all.sh 的脚本。首先 chmod +x flash-all.sh 赋予脚本执行权限,然后执行这个脚本即可,如果有大量输出则说明刷机正常进行中.

# 后续 ROOT

ROOT 的话首先需要刷 recovery,我们先下载recovery (opens new window)找到自己型号的 recovery 进行下载,我的是 recovery-clockwork-touch-6.0.3.3-maguro.img,touch 代表的是是否支持触摸.否则只能用音量键和开机键进行选择和确定.然后下载 superSU 的 ZIP 压缩包,目前版本是1.41 (opens new window) ,ZIP 格式不要解压.放入 SD 卡中.adb push UPDATE-SuperSU-v1.41.zip /sdcard/将手机关机进入 fastboot 模式,应该先解锁, fastboot oem unlock然后刷入 recovery fastboot flash recovery recovery-clockwork-touch-6.0.3.3-maguro.img按下音量键调至 recovery,按开机键进入,手机会自动重启进入 recovery。choose zip from SDcard,然后依次进入,最终选择 UPDATE-SuperSU-v1.41.zip,确认安装.

注:如果提示是否清除 recovery flash,请选 No,否则下次推送时无法进行 OTA 更新.安装完成后依次返回.这个步骤不会影响 OTA 更新,并且 OTA 更新之后会自动解决 ROOT 的问题,无需再次 ROOT.

# ADB 小技巧

刷机之后需要一大堆 APK 软件安装怎么办 adb 本身只有单个 APK 安装的功能,通过 ls -1 *.apk | xargs -l adb install 这个命令可以批量的把一个文件夹下的所以 APK 安装上去,而且还是静默安装. 不过首先要打开 ADB 调试才行.

# 手机 MTP 连接 Linux 电脑

MTP 是微软发明的东西....当然不好用 目前有两种解决方案一种是 GMTP,速度较慢,因为第一次和之后所有操作之后都会全部索引一遍文件列表.第二种是 gvfs-mtp 结合 thunar 使用,可以自动挂载,如果无法挂载可以使用 lsusb 命令.

Bus 001 Device 117: ID 04e8:6860 Samsung Electronics Co., Ltd GT-I9100 Phone [Galaxy S II], GT-I9300 Phone [Galaxy S III], GT-P7500 [Galaxy Tab 10.1]
1

可以看到我的设备号,然后在地址栏里面输入 mtp://[usb:001,117]/。第一个数字是 Bus 号,第二个数字是设备号.