初探情况

如图所示,再尝试添加多开版安卓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的用例

执行顺序如下:
- 设置加载状态为 true(显示 loading)
- 构造网络请求参数 map(m27003)
- 发起异步网络请求 u78.m58166().m69811(…)
- 回调接口 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后,可以看到成功突破虚拟机限制
