Slimu 发表于 2023-10-28 22:18

【Android逆向】记录一次某某虚拟机的逆向

本帖最后由 Slimu 于 2023-10-28 22:24 编辑

# 导语
学了一段时间的XPosed,发现XPosed真的好强,只要技术强,什么操作都能实现...
这次主要记录一下我对这款应用的逆向思路

## apk检查
1) 使用(https://mt2.cn/)检查apk的加壳情况

2) 发现是某数字的免费版本
3) 直接使用(https://github.com/hluwa/frida-dexdump)
4) 脱下来后备用
## 应用分析
1) 进入应用之后会发现里边含有`登录` `会员`等模块
2) 我们先不管登录的部分,先检查`会员`的使用场景,一般在`会员`的使用场景或者显示场景中都会有检查是否是`VIP`的业务逻辑,根据这个来加载显示不同的资源
###`会员`分析
1) 通过对应用的检查发现在添加虚拟机设备的时候用到了`会员`权限

2) 同时弹出一个对话框,应用也贴心的告诉我们`VIP`的使用场景

3) 打开[算法助手](https://github.com/Xposed-Modules-Repo/com.junge.algorithmaide),算法助手对应用进行了常见功能的Hook,最重要的是**支持免费加固**的Hook
4) 将这3项勾选

5) 重复1、2步,在日志中检查OnClick和弹窗是否有用的信息
6) 很幸运,在这一步就获取有关的逻辑

7) 且函数名称并没有被混淆,能够从调用堆栈读出以下逻辑:点击下载按钮->检查是否VIP->用户没有登录->显示购买VIP的弹窗
8) 打开(https://github.com/skylot/jadx)将脱壳后的dex文件载入,搜索`checkVip`

9) 选择第一个函数,发现无有用信息,继续进入`checkVip`函数

10) 在此函数中发现**getUserConf**,即获取用户的配置

11) 通过对此函数的阅读,得出此函数的返回值为`UserBean`

12) 然后检查`UserBean`

13) 很明显,有关VIP的数据都在此,使用XPosed来修改这些成员变量,即可达到对显示UI的修改,即使服务器对这些数据有校验也不影响,至少在UI层面已经成功了
### 抓包分析
1) 使用抓包工具检查网络请求,当点击底部的导航栏的时候,应用会发送网络请求


2) 通过检查`getInfo`,获取一个Post请求的链接

3) 对此链接进行引用查询,发现有关用户的逻辑

4) 阅读此函数,网络请求库可能为(https://github.com/square/retrofit),当请求成功的时候会将用户的信息保存起来并移除广告?同样也能得到`UserBean`,这个关键的信息


## 编写插件
### 思路
1) 通过对应用的分析可以得出一个关键的信息`getUserConf`
2) `getUserConf`函数右键->复制为XPosed片段

``` Java
XposedHelpers.findAndHookMethod("com.vmos.pro.account.AccountHelper", classLoader, "getUserConf", new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
      super.beforeHookedMethod(param);
    }
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
      super.afterHookedMethod(param);
    }
});
```
3) 可以看到(https://github.com/skylot/jadx)为我们一键生成了有关的Hook代码,但是这样就行了吗?我可以告诉你,不行。别忘了,这是一个**加壳**的应用,即使它是一款**免费**的**加壳**
### 加壳应用Hook
通过对网上公开资料的查询,发现即使应用加固也需要在运行时进行还原修复,使用(https://github.com/skylot/jadx)打开加固的apk文件,找到**attachBaseContext**

``` Java
XposedHelpers.findAndHookMethod("com.stub.StubApp", param.classLoader, "attachBaseContext", android.content.Context.class, new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
      super.afterHookedMethod(param);
      Context context = (Context) param.args;//获取运行时的Context
      ClassLoader classLoader = context.getClassLoader()//获取真正的ClassLoader
      //在此添加Hook VIP等操作,使用classLoader
    }
});
```
### 继续编写
1) 获取到正确的ClassLoader后,对`getUserConf`函数的返回值进行遍历
``` Java
XposedHelpers.findAndHookMethod("com.vmos.pro.account.AccountHelper", classLoader, "getUserConf", new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
      Object UserBean = param.getResult();
      for (Field field : UserBean.getClass().getDeclaredFields()) {
            //设置可访问, 极其重要, 不然会崩溃
            field.setAccessible(true);
            //使用反射来获取运行时的数据
            var name = field.getName();
            var type = field.getType();
            var value = field.get(UserBean);
            Log.i("HookTag", name + ": " + value);
      }
      super.afterHookedMethod(param);
    }
});
```
2) Hook完成后,能够发现`nickName`是正确的,能够对应上UI的显示


3) 接下来只需要对循环里的数据进行判断赋值,然后返回即可
``` Java
//修改名称,其他自行测试
for (Field field : UserBean.getClass().getDeclaredFields()) {
    ......
    if (name.equals("nickName")) {
      field.set(UserBean, "测试文字");
    } else if(......) {
      ......
    }
}
param.setResult(UserBean);//设置返回值,替换掉param.getResult()获取的
```
4) UI显示和下载功能


## 插件下载破解
获取到`VIP`后,发现还有一个插件下载的逻辑没有效果
### 下载逻辑分析
1) 当点击Root或者XPosed的时候,会提示加载失败

2) 但是点击谷歌服务的时候却有效果,猜测是网络请求
3) 打开抓包工具,通过对两者的对比,发现是其中少了一些数据,所以才会加载失败


4) 在jadx中搜索`getPluginUrl`,通过阅读此函数发现有2个匿名函数,`failure`和`success`


5) 使用jadx默认给我们的参数Hook不太行,这时候需要使用其他函数来获取`vu`
``` Java
//vu<jo4>.class 无法获取
//使用loadClass来获取,在参数中填写vu即可
Class<?> vu = classLoader.loadClass("vu");
XposedHelpers.findAndHookMethod("com.vmos.pro.activities.main.fragments.PluginHelper$getPluginDownloadBean$2$1", classLoader, "success", vu, new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
      Log.i("HookTag", "success: " + Arrays.toString(param.args));
      super.beforeHookedMethod(param);
    }
});
//或者使用XposedBridge.hookAllMethods
```
6) 通过对这两者的Hook,当点击`Root`插件按钮时会进入`success`且参数为:
```
谷歌服务按钮:success:
Root按钮:success:
```
7) 可以明显发现其中确实是少了一些数据,接下来只需要补齐下载链接即可,但是如何获取这个链接呢?
* ~~充值VIP获取其中的链接~~
* ~~扫描网站链接?或者找到一个函数获取?~~
* 猜测
8) 上面2点显然不是我能够解决的,2333,那就通过对链接的猜测吧,根据能够下载的谷歌服务链接来看,root和xposed可能为:
* `http://xxx/xxx/plugin/android71root_plugin-64bit.zip'`
* `http://xxx/xxx/plugin/android71xposed_plugin-64bit.zip'`
* 通过一系列猜测,得出来正确的下载链接,MD5的话只需要在终端输入`md5 file`即可得到
9) 阅读函数,检查参数发现具体的链接在`vu`中的`data`,但是返回类型是**T**,这就比较麻烦了



10) 这里采用一种比较麻烦的方法来修改:
* Hook`vu`类的`m49633`函数获取返回结果
* 遍历返回结果的`Fields`
* 找到含有`systemPluginResult`的field
* 使用`field.getType()`获取到`Class<?> jo4`
* 使用`jo4.newInstance()`创建一个实例`ret`
* 再次遍历`ret.getDeclaredFields()`
* 根据`pluginMd5`和`pluginUrl`分别赋值到`ret`中
* 最后使用`field.set(data, ret);`赋值即可
* 具体代码截图,`pluginMd5`和`pluginUrl`是我获取的正确链接,就不公布了

### 效果图


FengYAo 发表于 2023-11-13 09:20

楼主好,我在微信公众号上个看到了一模一样的文章,也没有说明是从哪搬运的,怀疑是倒运了咱得原文
一个公众号是:Web安全工具库
一个公众号是:看雪
不知道是不是楼主本人

jacktvt 发表于 2023-11-2 10:55

大佬大佬,之前用过这个软件,不过之前是拿的现成的,现在正好可以跟着练练,另外最近也碰到过一个某数字加固的软件,但是还没有学过去壳这方面的知识,大佬能出个教程吗{:1_893:}

a33463 发表于 2023-10-30 18:13

感谢分享 学习一下

1188 发表于 2023-10-30 18:23

360加固最新版的,还好脱吗?

lin1918 发表于 2023-10-30 18:45

小白看着这个有点迷糊

b71001339 发表于 2023-10-30 19:02

有点像看天书,感觉你们都好厉害呀

gl1433223 发表于 2023-10-30 19:26

感谢分享 学习一下 思路很清晰

icevoit 发表于 2023-10-30 20:07

以跟着做一做了

wasm2023 发表于 2023-10-30 20:42

厉害了楼主

CSAPPYYDS 发表于 2023-10-30 20:53

慢慢看了……{:1_909:}

rhol 发表于 2023-10-30 22:56

学习了,没想到还能这么用算法助手
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 【Android逆向】记录一次某某虚拟机的逆向