# 超级音乐编辑器_2.7.9.apk 逆向思路 # 超级音乐编辑器_2.7.9.apk 逆向思路 ## 一、分析思路 image-20240527235251737 可以看到功能还是蛮多的 随便测试一个会员功能: image-20240527235657232 点击保存后跳转到购买会员的界面: image-20240527235740328 到这里就要想一想了,为什么点击保存按钮不是执行`保存`相关的代码,而是执行跳转到`升级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 代码,再去点击 保存 按钮: image-20240528002113560 当跳转到 升级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) 结束