初探情况

如图所示,再尝试添加多开版安卓5.1时,出现了VIP弹窗

在使用算法助手捕获弹窗后,定位到了vip的检验方法

两个非常简单的方法,用Xposed写个简单的hook

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
if(!loadPackageParam.packageName.equals("com.vmos.pro")){
return;
}
XposedHelpers.findAndHookMethod("com.vmos.pro.bean.UserBean", loadPackageParam.classLoader, "isMember", 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);
Log.d("xsf","hook到isMember方法,当前返回值为:"+param.getResult().toString());
param.setResult(true);
Log.d("xsf","isMember方法当前返回值为:"+param.getResult().toString());
}
});
XposedHelpers.findAndHookMethod("com.vmos.pro.account.AccountHelper", loadPackageParam.classLoader, "notLogin", 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);
super.afterHookedMethod(param);
Log.d("xsf","hook到notLogin方法,当前返回值为:"+param.getResult().toString());
param.setResult(false);
Log.d("xsf","notLogin方法当前返回值为:"+param.getResult().toString());
}
});

然后我们再去app看看

深入探索

发现数据没了,猜测应该是hook不够全面,一般这种数据都是从服务器获取的,来抓包试试

获取服务器列表和发送信息的数据均进行了加密

进jadx搜索域名

发现有两条,都hook过后发现是下面这条

包含了api接口m70092和请求体@Body Map map,查找M70092的用例

执行顺序如下:

  1. 设置加载状态为 true(显示 loading)
  2. 构造网络请求参数 map(m27003)
  3. 发起异步网络请求 u78.m58166().m69811(…)
  4. 回调接口 us2:
    • failure:请求失败处理
    • success:请求成功,解析数据,更新 LiveData

m27003的构造函数如下:

1
2
3
4
5
6
7
8
9
10
Map<String, Object> m27003 = fw2.m27003(
dl2.m22383(
f34.m25537(
xw7.m65279(c90.f4211, String.valueOf(el5.m24441())),
xw7.m65279(c90.f4212, String.valueOf(y41.m65576())),
xw7.m65279(c90.f4416, VpiNativeUtils.getFileMD5("MD5"))
)
)
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌────────────────────────────────────┐
│xw7.m65279(key, value)(生成 pair) │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│f34.m25537(...)(组装多个 pair 成 Map │
└────────────────────────────────────┘
↓
┌────────────────────────────────────┐
│dl2.m22383(map) (Gson 转 JSON) │
└────────────────────────────────────┘
↓ JSON 字符串
┌────────────────────────────────────┐
│fw2.m27003(JSON) │
│ → 3DES/CBC 加密 │
│ → Base64 │
│ → 构造 { createTime, msg } │
└────────────────────────────────────┘
↓
最终发给服务器的参数 Map

构造函数的逻辑大概如上

加密逻辑

看看参数都是什么

参数是apk的签名、最低支持的Linux内核、最低支持的安卓版本。理论上这些参数在hook后并不会改变,那为什么get不到虚拟机列表,回头看看抓到的包

(突然发现为什么是post而不是get方法,神秘)已知请求体应该不会变,那么问题应该在请求头上,对比hook前和hook后抓到的包

hook前:

hook后:

发现三个参数发生了改变,分别是s(签名参数)、n(时间戳)、isVip(VIP检验),问题应该就出现在VIP检验这一步

看看调用,随机挑选一个参数查找,这里选择auth_ver,调用比较少

定位到具体方法

跟进mo26027方法

看到是个获取vip状态的方法,hook一下赶返回值是什么

前面我们已经hook过isVip方法,返回值自然为true,可能问题恰恰出现在这,我们通过hook成为了本地vip,但服务器上并没有记录,向服务器发送了一个未知的VIP账户,也就获取不到数据了。

在发送请求头的方法中我们将mo26027方法的返回值hook为”false”,这样一来请求头中发送的isVip参数为“false”

下面是hook过后的包

在回到app看看

已经可以使用了

锦上添花

在添加多个虚拟机后,提示达到上限

上算法助手选择控件文本赋值来定位;定位到后直接下拉看堆栈信息,能够看到checkoutVipCount这个方法

1
2
3
4
5
6
7
8
9
10
11
XposedHelpers.findAndHookMethod("com.vmos.pro.account.AccountHelper", loadPackageParam.classLoader, "getIsAnnualCard", 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);
param.setResult(1);
}
});

hook将返回值改为1后,可以看到成功突破虚拟机限制