起因今天一个偶然的机会发现自己以前装的hookvip居然打不开了。尝试了各种方法,无果。上网一搜,居然是作者刻意埋的时间炸弹,每次在发布后连个月后爆炸,而我手机里的版本是6月的,炸了两个月我居然才发现。而时间炸弹没有触发的新版本砍掉了微信模块。于是只好从旧版入手。
时间炸弹的拆除首先就是时间炸弹问题。 lsp日志有一点调用栈的痕迹:
1 2 10 -27 19 :02 :10 .129 13930 13930 E AndroidRuntime: Caused by: java.lang.Throwable: 已失效10 -27 19 :02 :10 .129 13930 13930 E AndroidRuntime: at Hook.JiuWu.Xp.ui.MContext.<init>(Unknown Source:28 )
mt打开它的apk,好在它只是加了很多混淆,而没有用什么更加艰深的加固手段。
首先定位到Hook.JiuWu.Xp.ui.MContext.<init>
这种混淆了一堆的代码还是转java看吧,这个尤其是,因为里面加了很多死的switch case代码。电脑可以用jadx等免费工具,我人在学校偷摸玩手机就只好用mt的vip功能了。
不难发现里面有一对可疑的Date,一个是写死的时间,而另一个是当前时间。随便转换一下,发现这个已知的时间戳在2024-08-07 23:59:56
,正好跟时间炸弹吻合。
然而,如果在这里直接改跳转指令忽略掉它的话,你会发现根本不管用。因为这里面到处都埋了类似的时间炸弹,随便搜一下竟然有756处之多,一处处改是根本不可能的。而且作者很聪明,这些时间戳都有一些上下的浮动,让你不好直接替换。这时候我们有一个思路就是写一个正则表达式把这些时间戳全替换成一个很远的时间,比如两百年后,那时候这个炸弹是肯定不会再困扰我们了。
但是,如果事先使用了反混淆处理掉这些恶心的特殊字符字段,我们不难发现这些炸弹好像都被传入了同一个函数进行计算。事实上这个函数接收了756处里的754处,而剩下两处经过试错都可以从报错的调用栈里找到,他们均直接调用了未经包装的Date.after()
。可以很轻易地通过搜索Ljava/util/Date;->after(Ljava/util/Date;)Z
找到它们:
如果使用正则替换法,可以使用0x1912d.*{6}L
来匹配这些炸弹,然后再批量替换成2224年,经过测试,可以正常使用,但是改动太大,感觉不够优雅。
我们接着来看刚刚提到的那个函数,不难发现这个是比较时间用的。实际上如果我们深挖if里作条件的这个函数,会发现它是0x3ce^0x373
是一定>=0的,这也是这个东西不好分析的原因,因为这些混淆视听的死分支到处都有。这里一定会进行时间的比较。我们可以选择把这里的if-ltz
改成if-gtz
,这样就不进行时间的比较直接返回false。也可以把这个函数重写,让他直接返回false。
直接返回false的写法也很简单:
而另外两个特例,我们可以通过修改调用处Date.after()
返回值为false达到我们的效果,如下:
编译打包,发现已经可以进入主页面了。
去除微信模块的弹窗然而,微信模块仍然无法使用,作者在微信里放了个怎么点都退出的弹窗。
在排除了时间炸弹的可能性之后,注意到这个弹窗在模块重新安装之后的十几秒内不会出现,说明应该不是硬编码进去的,而是通过网络之类下发到用户。随后我关闭网络,重新安装模块,发现这个弹窗果真怎么等都不会出现。
在确定了是网络下发后,我使用小黄鸟来同时抓微信和hookvip的请求。不难发现它给https://flowus.cn/api/docs/
发了几个可疑的请求:
而它的响应也相当可疑,比如"微信"、“倒卖”、“停止服务”、"测试弹窗"等字眼也很扎眼,跟弹窗内容有点相似。有一项和微信相关的字段引起了我的注意:
1 { "title" : "微信" , "name" : "com.tencent.mm" , "url" : "https://flowus.cn/api/docs/7e583976-ff84-45bc-8429-7a2189a00d09" , "type" : 0 , "desc" : "免费模块 倒卖泛滥 停止服务" , "version" : "全版本通杀" , "build" : "release" }
这个里面的url很快就被客户端拉取了,并且是它产生的最后一个请求。
浏览器打开,可以看到含有一大堆加过密的value,还有"title":"onCloseWeChat"
,而且长度跟弹窗十分相近,可以基本确定这就是这个弹窗的真身了。本以为只有一层base64,没想到居然没这么简单。掏出SimpleHookR对微信挂钩,记录各种加解密操作,重装模块打开记录,很快就抓到了解密操作。
如下是解密结果:
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import Hook.JiuWu.Xp.tools.Utils.XAppInfo;import android.view.View;import android.os.Handler;import android.os.Looper;import Hook.JiuWu.Xp.tools.Utils.XDialog;import Hook.JiuWu.Xp.tools.HostInfo;import Hook.JiuWu.Xp.plugin.Mods.WeiXin.tools.InfoUtils;import Hook.JiuWu.Xp.tools.Utils.Xflowus;new Xflowus ("https://flowus.cn/api/docs/19529dc5-2aa2-426b-a0ba-0aa63f1780d9" ).getDatalist(new Xflowus .XflowusCallback() { public void onSuccess (ArrayList list) throws Throwable { if (list.contains(InfoUtils.getWxid()))return ; new Handler (Looper.getMainLooper()).postDelayed(new Runnable () { public void run () { XDialog.showText(HostInfo.getActivity(), XDialog.getTextHash("来自模块开发者的通知" , "HookVip为免费模块\n微信功能以及其他功能均是免费的\n\n如果你是付费购买的本插件/定制v/功能v\n那么恭喜你被骗了 请联系售卖者退款\n若售卖者拒不退款 请联系邮箱[email protected] \n邮箱内容请附带售卖者的微信号 以及聊天截图和付款记录\n\n我们会协助你追回钱财!" , "收到" , "取消" , new View .OnClickListener() { public void onClick (View v) { XAppInfo.Kill(); System.exit(0 ); } }, new View .OnClickListener() { public void onClick (View v) { XAppInfo.Kill(); System.exit(0 ); } })); } }, 1500 ); new Handler (Looper.getMainLooper()).postDelayed(new Runnable () { public void run () { XAppInfo.Kill(); System.exit(0 ); } }, 30000 ); }public void onError (Throwable e) { new Handler (Looper.getMainLooper()).postDelayed(new Runnable () { public void run () { XDialog.showText(HostInfo.getActivity(), XDialog.getTextHash("来自模块开发者的通知" , "HookVip为免费模块\n微信功能以及其他功能均是免费的\n\n如果你是付费购买的本插件/定制v/功能v\n那么恭喜你被骗了 请联系售卖者退款\n若售卖者拒不退款 请联系邮箱[email protected] \n邮箱内容请附带售卖者的微信号 以及聊天截图和付款记录\n\n我们会协助你追回钱财!" , "收到" , "取消" , new View .OnClickListener() { public void onClick (View v) { XAppInfo.Kill(); System.exit(0 ); } }, new View .OnClickListener() { public void onClick (View v) { XAppInfo.Kill(); System.exit(0 ); } })); } }, 1500 ); new Handler (Looper.getMainLooper()).postDelayed(new Runnable () { public void run () { XAppInfo.Kill(); System.exit(0 ); } }, 30000 ); } });
可以看出来,这实际上是一套白名单逻辑。
再看看解密的调用栈:
被那些屎一样的死代码恶心坏了之后,我决定放弃追溯完整的调用栈。。。但是我看到了这个函数:Hook.JiuWu.Xp.tools.Utils.JavaLoader -->EvalFlowUs
,这看起来像是专门执行下发代码的一个函数;于是我试着改写了该函数的逻辑,让它直接退出从而打断这条链:
手术很成功,重新安装之后没有这个弹窗了,所有功能正常使用。