对oppo健康研究app中的睡眠数据API的逆向研究

为什么弄

因为它本身oppo健康那个app里面的数据同步机制很奇怪,BLE也没有数据,抓包也没有。所以我在乱翻的时候看到了它可以授权给一健康研究的应用,里面有睡眠数据。

为此,我准备了该应用的.apk安装包和一个通过Fiddler抓取的.saz网络请求样本文件。

模拟器不要用WSA,这种应用会闪退,而且WSA已经是微软的弃子的

这里抓包我是先用的Mumu模拟器,然后Fidder抓。证书只需要PC安装了就行。然后你抓到的东西右键可以导出saz格式。

如上。我导出了.saz文件,然后用bandzip进行了解压。

它其实本身就是一个压缩包,里面有一个raw目录。

raw目录包含了HTTP/HTTPS请求和响应。


通过在这些文本文件中搜索关键词sleep,我找到了一个API端点:

POST https://health-researchkit-cn.heytapmobi.com/researchkit_api/api/sleep/v2/getStageByDate

但是,请求和响应的Content-Type均为application/encrypted-json,其内容主体(Body)是一串乱码,显然是经过加密的。

所以,反编译吧

我用Jadx对.apk文件进行反编译,得到了混淆后的Java源代码。

我在所有反编译出的代码中全局搜索 X-Protocol 得到了一个关键类:com.platform.usercenter.network.interceptor.SecurityRequestInterceptor

这个类负责处理请求的加密和响应的解密。

并且这是一个动态的、基于RSA和AES的混合加密方案。

以下是简述

  1. AES密钥生成:在每次API请求前,客户端在内存中随机生成一个16字节的AES密钥和16字节的AES初始化向量(IV)。这对密钥和IV仅用于本次请求。

  2. RSA公钥加密:客户端代码中硬编码了一个RSA公钥。这个公钥用于加密上一步生成的AES密钥。

    • RsaCoder.java类中有这个公钥的Base64字符串:
    public static String getKey() {
        return "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpgSW5VkZ6/xvh+wMXezrOokNdiupuvuMj4RVJy44byWDupl4H37z907A26RVdFzMeyLUQB4rsDIaXdxCODlljWW+/K96uF5MsDtOFUBw7VlOclIjcYTv/YDQEul8JoXoOuy1Yf3b5sbTpTuVTcl97tAuLJ8PoGe2K7N3B1eUQqQIDAQAB";
    }
    
  3. 构造X-Protocol:这个请求头的值是一个经过URL编码的JSON字符串,其结构如下:

    {
      "key": "Base64编码的、被RSA加密后的AES密钥",
      "iv": "Base64编码的、明文的AES IV",
      "sessionTicket": "" 
    }
    
  4. 请求体加密:原始的JSON请求体(例如:{"date":"YYYY-MM-DD", "projectId":"..."})使用步骤1中生成的随机AES密钥和IV,通过AES/CBC/PKCS5Padding算法进行加密。

  5. 响应体解密:服务器使用相同的AES密钥和IV加密响应数据。客户端在收到响应后,用内存中同一份AES密钥和IV进行解密。

嗯。很有信心,开测吧。

掌握了加密协议后,那就是测试了。

但是不太妙。

在测试中遇到了decrypt client secret key error的错误。这个错误意味着虽然我的加密逻辑和请求格式是正确的(服务器返回了200 OK),但服务器无法用它的私钥解密X-Protocol头。

所以我又去找 Auth Token和 Project ID。

得到的结果是Auth Token是动态的。

然后Project ID是通过一个回调接口拿的。

我就不继续了,放弃了。因为再往下太复杂了。

可以说oppo还是挺...我是该夸他很安全吗?