# 木鱼_1.2.18.apk 逆向思路 # 木鱼_1.2.18.apk 逆向思路 ## 一、分析 所用工具: 1. 静态分析:jadx(将apk反编译成java代码,MT管理器的smali转java功能正是用jadx) 2. 动态分析:frida(hook框架) ​ ### 1.1 分析绕过登录 随便点击一个皮肤,皮肤图标右上角有个“LOCK”,意思是被锁住了(因为我们不是会员) : image-20240530192006348 点击之后会跳转到登陆界面: image-20240530192229193 在Android开发中,页面切换和跳转通常会使用Intent,但并不是所有的页面切换和跳转都必须使用Intent 以下是一个Activity之间的跳转示例(并不唯一): ```java Intent intent = new Intent(CurrentActivity.this, TargetActivity.class); startActivity(intent); ``` 可以尝试hook Intent或者startActivity(我这里hook Intent方法,因为hook startActivity方法没效果不知道什么情况,我太菜了),在Intent方法被调用时打印当前堆栈信息来定位上层方法 编写hook Intent所有重载代码,这里用的是js代码来编写: ```javascript Java.perform(function () { //打印堆栈信息的方法 function showStacks() { console.log( Java.use("android.util.Log") .getStackTraceString( Java.use("java.lang.Throwable").$new() ) ); } var Intent = Java.use("android.content.Intent"); Intent.$init.overload('android.content.Context', 'java.lang.Class').implementation = function(context, cls) { console.log("Intent(context, cls) called with context: " + context + " and class: " + cls); showStacks(); return this.$init(context, cls); }; Intent.$init.overload('java.lang.String').implementation = function(action) { console.log("Intent(action) called with action: " + action); showStacks(); return this.$init(action); }; Intent.$init.overload('java.lang.String', 'android.net.Uri').implementation = function(action, uri) { console.log("Intent(action, uri) called with action: " + action + " and uri: " + uri); showStacks(); return this.$init(action, uri); }; console.log("Hook 成功!"); }); ``` 运行后点击一个皮肤: ![image-20240530194613603](http://cdn.wutongliran.top/img/image-20240530194613603.png) 打印出堆栈,由下而上调用链 搜索`com.ahzy.fish.vm.SharedViewModel.goToPayOrLogIn`方法 ![image-20240530195904027](http://cdn.wutongliran.top/img/image-20240530195904027.png) `MuYuSpUtil.INSTANCE.isLogin()`判断是否登录,已登录返回true、未登录返回false hook 该方法,将返回值改成固定为true可绕过登录: ```javascript let MuYuSpUtil = Java.use("com.ahzy.fish.utils.MuYuSpUtil"); MuYuSpUtil["isLogin"].implementation = function () { console.log(`MuYuSpUtil.isLogin is called`); return true; }; ``` ​ ### 1.2 绕过皮肤限制 运行后点击一个皮肤: ![image-20240530201847285](http://cdn.wutongliran.top/img/image-20240530201847285.png) 这次没有跳转到登录界面,而是跳转到购买会员界面,来看下堆栈信息: `goToPayOrLogIn`方法顾名思义,应该是“去购买界面或登录界面”,多半是因为判断后发现我们不是会员,该功能被锁住而调用这个方法 搜索类名:`com.ahzy.fish.act.SettingAct$bindSkin$1$2`,找到invoke方法: ![image-20240530203116571](http://cdn.wutongliran.top/img/image-20240530203116571.png) `muYuBgBean.isLock()`判断该皮肤是否被锁住,锁住返回true,没锁住返回false hook 该方法,将返回值改成固定为false可绕过皮肤限制: ```javascript let MuYuBgBean = Java.use("com.ahzy.fish.bean.MuYuBgBean"); MuYuBgBean["isLock"].implementation = function () { console.log(`MuYuBgBean.isLock is called`); return false; }; ``` 运行后重新点进这个设置界面,皮肤的“LOCK”已经不见了: image-20240530204226501 ​ ### 1.3 绕过音色限制 在这个界面定位到上一个invoke方法: ![image-20240530205609049](http://cdn.wutongliran.top/img/image-20240530205609049.png) `muYuBean.isLock()`判断该音色是否被锁住,锁住返回true,没锁住返回false hook 该方法,将返回值改成固定为false可绕过音色限制: ```javascript let MuYuBean = Java.use("com.ahzy.fish.bean.MuYuBean"); MuYuBean["isLock"].implementation = function () { console.log(`MuYuBean.isLock is called`); return false; }; ``` 运行后重进设置界面: image-20240530205849006 音色已解锁 ​ ### 1.4 绕过其他的功能限制 刚刚看到两个功能限制都是通过不同类下的`isLock()`方法来判断,所以大胆猜测其他的功能限制也是用对应类下的`isLock()`方法来判断(当然不放心可以像前面那样一步步定位到其他功能限制的方法,我已经验证过其他的功能限制方法也是lsLock) 直接搜索方法:isLock() ![image-20240530210307279](http://cdn.wutongliran.top/img/image-20240530210307279.png) 前两已经hook绕过了,把剩下的也全部hook上,将返回值设置为false: ```javascript let BeadThemeBean = Java.use("com.ahzy.fish.dto.BeadThemeBean"); BeadThemeBean["isLock"].implementation = function () { console.log(`BeadThemeBean.isLock is called`); return false; }; let DocxDTO = Java.use("com.ahzy.fish.dto.DocxDTO"); DocxDTO["isLock"].implementation = function () { console.log(`DocxDTO.isLock is called`); return false; }; let MusicDTO = Java.use("com.ahzy.fish.dto.MusicDTO"); MusicDTO["isLock"].implementation = function () { console.log(`MusicDTO.isLock is called`); return false; }; ``` 运行hook代码: 解锁佛珠样式限制: image-20240530210733542 image-20240530210810105 解锁佛经禅音限制: image-20240530211211415 `DocxDTO.isLock`不知道是关于哪个功能的,这里的般若文海就算没修改也没有看到限制 > 还有一个关键函数:isPay 忘说了,使用“在桌面敲木鱼”功能时会调用,赋值true ​ ## 二、修改smali代码 ### 2.0 去除签名校验 不用加强版也行: image-20240531005852679 ​ ### 2.1绕过登录 搜索方法:com.ahzy.fish.utils.MuYuSpUtil.isLogin image-20240530231614482 image-20240530231641450 如图在箭头指向的位置添加代码 const v0,0x1 image-20240530231724905 ​ ### 2.2 解锁限制 搜索方法:com.ahzy.fish.bean.MuYuBean.isLock image-20240530231103309 image-20240530231130874 如图在箭头指向的位置添加代码 const v0,0x0 image-20240530231217761 以此类推修改以下isLock方法: - com.ahzy.fish.bean.MuYuBgBean.isLock - com.ahzy.fish.dto.BeadThemeBean.isLock - com.ahzy.fish.dto.DocxDTO.isLock - com.ahzy.fish.dto.MusicDTO.isLock 搜索方法:isPay bcff878eae514ec34e8d4249536fbaf3_720 如图位置将0改成1 1c6ce94572382e699f6596739a7cb5e8 ​ ### 2.3 去除开屏广告 (关于广告植入这方面我还未了解,只好去网上找现成的方法,也不知道怎么分析出来的) 用LibChecker查看下广告 image-20240530232453806 快手广告和腾讯广告 搜索方法名:com.qq.e.comm.adevent.ADEvent.getType a21cac779221fd21eb6133bc07969ef4_720 如图在箭头指向的位置添加代码 const/16 v0,0x65 669b836e0d5f159122728169882c600f 搜索方法名:com.qq.e.comm.managers.b.d 如图添加代码 const v0,0x0 bc9c1f2a42815feea12a117f981005bb 搜索方法名:isResultOk 搜到三个结果如图修改: 9b87dbc0234a2b7be0ff90f1870dd524_720 d0e19d35297864d9275abcbf00bbe9b1_720 e8a413bf7015cd3bd059b5fb2eb425c1_720 点击常量,点击替换: 0797f3edb2f888cd594a0486c81b4b63_720 将qq.e替换为#: 938960f122a15d12cdbf16eec1cc3f58_720 点击应用修改: 2587119e864518f898f9b5625202ce4e_720 再替换:com.kwad. 9090693d171c0efae8f8ad47269254d0_720 记得应用修改 来到assets目录下: f30c275c91ade634f3e668910ed1b0b1_720 删除gdt_plugin目录: 63888a99f26ff2f5004fa0ab6f36f617_720 反编译AndroidManifest.xml: ![image-20240530234209505](http://cdn.wutongliran.top/img/image-20240530234209505.png) 搜索: ![image-20240530234417174](http://cdn.wutongliran.top/img/image-20240530234417174.png) 把包含qq.e的activity全删了: image-20240530234439654 结束