# 木鱼_1.2.18.apk 逆向思路
# 木鱼_1.2.18.apk 逆向思路
## 一、分析
所用工具:
1. 静态分析:jadx(将apk反编译成java代码,MT管理器的smali转java功能正是用jadx)
2. 动态分析:frida(hook框架)
### 1.1 分析绕过登录
随便点击一个皮肤,皮肤图标右上角有个“LOCK”,意思是被锁住了(因为我们不是会员) :
点击之后会跳转到登陆界面:
在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”已经不见了:
### 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;
};
```
运行后重进设置界面:
音色已解锁
### 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代码:
解锁佛珠样式限制:
解锁佛经禅音限制:
`DocxDTO.isLock`不知道是关于哪个功能的,这里的般若文海就算没修改也没有看到限制
> 还有一个关键函数:isPay 忘说了,使用“在桌面敲木鱼”功能时会调用,赋值true
## 二、修改smali代码
### 2.0 去除签名校验
不用加强版也行:
### 2.1绕过登录
搜索方法:com.ahzy.fish.utils.MuYuSpUtil.isLogin
如图在箭头指向的位置添加代码 const v0,0x1
### 2.2 解锁限制
搜索方法:com.ahzy.fish.bean.MuYuBean.isLock
如图在箭头指向的位置添加代码 const v0,0x0
以此类推修改以下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
如图位置将0改成1
### 2.3 去除开屏广告
(关于广告植入这方面我还未了解,只好去网上找现成的方法,也不知道怎么分析出来的)
用LibChecker查看下广告
快手广告和腾讯广告
搜索方法名:com.qq.e.comm.adevent.ADEvent.getType
如图在箭头指向的位置添加代码 const/16 v0,0x65
搜索方法名:com.qq.e.comm.managers.b.d
如图添加代码 const v0,0x0
搜索方法名:isResultOk
搜到三个结果如图修改:
点击常量,点击替换:
将qq.e替换为#:
点击应用修改:
再替换:com.kwad.
记得应用修改
来到assets目录下:
删除gdt_plugin目录:
反编译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全删了:
结束