# 超级音乐编辑器_2.7.9.apk 逆向思路
# 超级音乐编辑器_2.7.9.apk 逆向思路
## 一、分析思路
可以看到功能还是蛮多的
随便测试一个会员功能:
点击保存后跳转到购买会员的界面:
到这里就要想一想了,为什么点击保存按钮不是执行`保存`相关的代码,而是执行跳转到`升级Pro`界面的代码
应该是当点击了`保存`按钮之后,触发一个判断`是不是会员`、或是`能否使用该功能`的方法。如果是会员,去执行保存相关的代码;如果不是会员,则执行跳转到升级Pro界面的代码
我们接下来要做的就是定位到这个方法,修改成不管如何都判断我们是会员
## 二、定位/调试关键方法
1. 将 apk 拖进 jadx 反编译(MT管理器的 smali 转 Java 功能就是用的 jadx )
2. 启动 frida hook 环境(我这里编写 frida hook 代码用的是 js)
我们刚才看到,点击了 保存 按钮就从当前界面跳转到 升级Pro 的界面。在 java 原生开发中,跳转页面(activity)时执行的代码类似这种:
```java
// 创建Intent以启动SecondActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
// 跳转界面
startActivity(intent);
```
当执行 startActivity 方法时页面就跳转过去,所以我们从当前页面跳转到 升级Pro 的界面应该也执行了 startActivity 方法,可以编写 hook 脚本来钩住 startActivity 方法,当触发 startActivity 方法打印堆栈,看看 startActivity 方法的调用链
当前 hook 代码:
```javascript
Java.perform(function () {
//调用堆栈的方法
function showStacks() {
console.log(
Java.use("android.util.Log")
.getStackTraceString(
Java.use("java.lang.Throwable").$new()
)
);
}
// 获取Activity类
var Activity = Java.use("android.app.Activity");
// Hook startActivity方法
Activity.startActivity.overload('android.content.Intent').implementation = function (intent) {
console.log("startActivity 方法被调用");
// 打印堆栈
showStacks();
// 调用原始的startActivity方法
this.startActivity(intent);
};
})
```
运行 hook 代码,再去点击 保存 按钮:
当跳转到 升级Pro 界面时:
![image-20240528002230600](http://cdn.wutongliran.top/img/image-20240528002230600.png)
可以看到 startActivity 方法被调用并打印出堆栈信息,堆栈里的方法是自下而上调用的,我们可以看到
```
at com.tianxingjian.supersound.TTSActivity.onClick(TTSActivity.java:10)
```
TTSActivity 类下的 onClick 方法调用了 startActivity 方法,为什么要看 onClick 方法?因为 onClick 方法是按钮被点击后执行的方法
既然定位到点击 保存 按钮后执行的 onClick 方法,我们在 jadx 里静态分析一下:
![image-20240528002736904](http://cdn.wutongliran.top/img/image-20240528002736904.png)
搜索类名,点进去,再搜索方法名:
![image-20240528002855945](http://cdn.wutongliran.top/img/image-20240528002855945.png)
找到这个 onClick 方法,TTSActivity 类下有很多方法里还有 onClick 方法,我们要找的是不在任何方法里的 onClick 方法
接下来 hook 这个方法,看看传进来的参数是什么,顺便验证一下当点击 保存 按钮时调用的是不是这个 onClick 方法
当前 hook 代码:
```javascript
Java.perform(function () {
/*//调用堆栈的方法
function showStacks() {
console.log(
Java.use("android.util.Log")
.getStackTraceString(
Java.use("java.lang.Throwable").$new()
)
);
}
// 获取Activity类
var Activity = Java.use("android.app.Activity");
// Hook startActivity方法
Activity.startActivity.overload('android.content.Intent').implementation = function (intent) {
console.log("startActivity 方法被调用");
// 打印堆栈
showStacks();
// 调用原始的startActivity方法
this.startActivity(intent);
};*/
let TTSActivity = Java.use("com.tianxingjian.supersound.TTSActivity");
TTSActivity["onClick"].implementation = function (view) {
console.log(`TTSActivity.onClick 方法被调用: view=${view}`);
this["onClick"](view);
};
})
```
运行后点击 保存 按钮测试:
![image-20240528003415596](http://cdn.wutongliran.top/img/image-20240528003415596.png)
可以看到确实是调用了这个 onClick 方法,并且能看到传进来的按钮 id 值为 tv_sure
我们来看下这段代码:
![image-20240528004125269](http://cdn.wutongliran.top/img/image-20240528004125269.png)
到这可以判断出 w 方法是判断`是不是会员`、或是`能否使用该功能`的方法,但我们不知道 `if (App.f30946l.w())` 里面的代码:
```java
t1();
TextToSpeech textToSpeech = this.f31306n;
if (textToSpeech != null) {
textToSpeech.stop();
}
if (this.f31307o != null && new File(this.f31307o).exists()) {
o1();
return;
}
this.f31314v = true;
S0();
return;
```
是执行 保存 功能的,还是 if 外面的 `ProfessionalActivity.I0(this, "tts");`
这时候就可以 hook 一下 w 方法,看我们不是会员的时候返回的值是 true 还是 false
当前 hook 代码:
```java
Java.perform(function () {
/*//调用堆栈的方法
function showStacks() {
console.log(
Java.use("android.util.Log")
.getStackTraceString(
Java.use("java.lang.Throwable").$new()
)
);
}
// 获取Activity类
var Activity = Java.use("android.app.Activity");
// Hook startActivity方法
Activity.startActivity.overload('android.content.Intent').implementation = function (intent) {
console.log("startActivity 方法被调用");
// 打印堆栈
showStacks();
// 调用原始的startActivity方法
this.startActivity(intent);
};
let TTSActivity = Java.use("com.tianxingjian.supersound.TTSActivity");
TTSActivity["onClick"].implementation = function (view) {
console.log(`TTSActivity.onClick 方法被调用: view=${view}`);
this["onClick"](view);
};*/
let App = Java.use("com.tianxingjian.supersound.App");
App["w"].implementation = function () {
console.log(`App.w 方法被调用`);
let result = this["w"]();
console.log(`App.w 方法的返回值=${result}`);
return result;
};
})
```
运行后点击 保存 按钮:
![image-20240528004740568](http://cdn.wutongliran.top/img/image-20240528004740568.png)
当我们不是会员的时候返回 false ,执行 ProfessionalActivity.I0(this, "tts");
这句代码最终会使界面跳转到充值界面,我们不想跳过去很简单,把 w 方法的返回值强行改成 ture,这样就一定会判定我们是会员
最终的 hook 代码:
```javascript
Java.perform(function () {
/*//调用堆栈的方法
function showStacks() {
console.log(
Java.use("android.util.Log")
.getStackTraceString(
Java.use("java.lang.Throwable").$new()
)
);
}
// 获取Activity类
var Activity = Java.use("android.app.Activity");
// Hook startActivity方法
Activity.startActivity.overload('android.content.Intent').implementation = function (intent) {
console.log("startActivity 方法被调用");
// 打印堆栈
showStacks();
// 调用原始的startActivity方法
this.startActivity(intent);
};
let TTSActivity = Java.use("com.tianxingjian.supersound.TTSActivity");
TTSActivity["onClick"].implementation = function (view) {
console.log(`TTSActivity.onClick 方法被调用: view=${view}`);
this["onClick"](view);
};*/
let App = Java.use("com.tianxingjian.supersound.App");
App["w"].implementation = function () {
console.log(`App.w 方法被调用`);
let result = this["w"]();
console.log(`原本 App.w 方法的返回值=${result}`);
result = true;//强行让结果是 true
console.log(`将 App.w 方法的返回值强行改成=${result}`);
return result;
};
})
```
运行后点击 保存 按钮:
![image-20240528005233573](http://cdn.wutongliran.top/img/image-20240528005233573.png)
这样就破解成功了,接下来是修改 apk
## 三、修改 apk
![image-20240528005425145](http://cdn.wutongliran.top/img/image-20240528005425145.png)
![image-20240528005438211](http://cdn.wutongliran.top/img/image-20240528005438211.png)
![image-20240528005500839](http://cdn.wutongliran.top/img/image-20240528005500839.png)
![image-20240528005518788](http://cdn.wutongliran.top/img/image-20240528005518788.png)
![image-20240528005543843](http://cdn.wutongliran.top/img/image-20240528005543843.png)
![image-20240528005557794](http://cdn.wutongliran.top/img/image-20240528005557794.png)
搜索类:com.tianxingjian.supersound.App
![image-20240528005841296](http://cdn.wutongliran.top/img/image-20240528005841296.png)
点这里:
![image-20240528005916819](http://cdn.wutongliran.top/img/image-20240528005916819.png)
点击 w 方法:
![image-20240528005958606](http://cdn.wutongliran.top/img/image-20240528005958606.png)
在这里添上 const v0,0x1,然后一路退出保存
![image-20240528010116880](http://cdn.wutongliran.top/img/image-20240528010116880.png)
结束