本帖最后由 winding 于 2018-5-25 15:22 编辑
(三)《城市飞车》内购(没啥代表性,纯粹因为是老相识)
论坛https://www.52pojie.cn/thread-710786-1-1.html
偶然看到这个帖子,想起来去年刚来坛子的时候,跟着教我哥们学逆向系列,有一个课后作业就是类似的游戏,折腾好几天也没弄出来,现在再看太简单了,身为小白的我还是有进步的。就拿它做示例吧。
额,跟(一)同样,都是咪咕的,没啥代表性。。。算了,算搭上的吧。
0.运行INJECTLOG工具。
1.看到有太多com.feamber.util.GameView$Renderer.onDrawFrame的LOG刷屏,把这个方法中的那句invoke-static {}, Lcom/hook/testsmali/InjectLog;->PrintFunc()V删掉,不打印这个方法了。
还有com.morgoo.droidplugin包下的一大堆,晃眼。。。百度了一下是实现插件功能的,把这个包下所有的LOG调用都去掉,方法同(一)
2.已经有经验了,到图示这一步,开LOG,点X,停止LOG。
把得到的LOG简单处理一下,把明显无关的用notepad++的正则替换去掉,有240多行:
[Asm] 纯文本查看 复制代码 com.morgoo.helper.MyProxy.isMethodDeclaredThrowable(MyProxy.java)[126]
pkg:com.racergame.cityracing3d , class:com.racergame.cityracing3d.GameActivity
updateFocusedWindowLocked, not mm
updateFocusedWindowLocked, focusApp Inform:START:城市飞车:com.racergame.cityracing3d:com.racergame.cityracing3d.GameActivity
Send cmp info to player :START:城市飞车:com.racergame.cityracing3d:com.racergame.cityracing3d.GameActivity
com.morgoo.helper.MyProxy.isMethodDeclaredThrowable(MyProxy.java)[133]
[bodyEncrypt:1070] -----
[bodyEncrypt:1075] -----
[bodyEncrypt:1079] -----
com.morgoo.helper.MyProxy.isMethodDeclaredThrowable(MyProxy.java)[164]
com.morgoo.helper.MyProxy.isMethodDeclaredThrowable(MyProxy.java)[158]
[bodyEncrypt:1070] -----
[bodyEncrypt:1075] -----
[bodyEncrypt:1079] -----
com.ck.sdk.AndGameSDK$1.onResult(AndGameSDK.java)[1]
com.ck.sdk.utils.LogUtil.iT(LogUtil.java)[1]
com.ck.sdk.adapter.CKSDKAdapter.onCKPayFail(CKSDKAdapter.java)[1]
com.ck.sdk.CKSDK.getInstance(CKSDK.java)[1]
com.ck.sdk.CKSDK.isOnlineGame(CKSDK.java)[1]
com.ck.sdk.CKSDK.getInstance(CKSDK.java)[1]
com.ck.sdk.CKSDK.getContext(CKSDK.java)[1]
com.ck.sdk.utils.files.SPUtil.getInt(SPUtil.java)[1]
com.ck.sdk.PayParams.getPaySdk(PayParams.java)[1]
com.ck.sdk.database.CkEventTool.setPayFail(CkEventTool.java)[1]
com.ck.sdk.database.CkEventTool.getBaseEventBean(CkEventTool.java)[1]
com.ck.sdk.bean.CkEventBean.<init>(CkEventBean.java)[1]
com.ck.sdk.database.CkEventTool.setBaseEventData(CkEventTool.java)[1]
com.ck.sdk.utils.DeviceInfo.getImei(DeviceInfo.java)[1]
com.ck.sdk.utils.DeviceInfo.getIccid(DeviceInfo.java)[1]
com.ck.sdk.utils.DeviceInfo.getAndroid_id(DeviceInfo.java)[1]
com.morgoo.helper.MyProxy.isMethodDeclaredThrowable(MyProxy.java)[161]
com.ck.sdk.CKSDK.getInstance(CKSDK.java)[1]
com.ck.sdk.CKSDK.getCKAppID(CKSDK.java)[1]
com.ck.sdk.SDKParams.contains(SDKParams.java)[1]
com.ck.sdk.SDKParams.getInt(SDKParams.java)[1]
com.ck.sdk.SDKParams.getString(SDKParams.java)[1]
(中间略)
com.feamber.isp.SmsIAPListener.onResult(SmsIAPListener.java)[1]
pay falied code 11msg pay cancel
com.racergame.cityracing3d.GameActivity.dismissProgressDialog(GameActivity.java)[1]
com.ck.sdk.utils.net.SubmitExtraDataUtil.submitOrSaveData(SubmitExtraDataUtil.java)[1]
com.ck.sdk.CKSDK.getInstance(CKSDK.java)[1]
com.ck.sdk.CKSDK.isOnlineGame(CKSDK.java)[1]
com.ck.sdk.CKSDK.getInstance(CKSDK.java)[1]
com.ck.sdk.CKSDK.getContext(CKSDK.java)[1]
com.ck.sdk.utils.files.SPUtil.getInt(SPUtil.java)[1]
com.ck.sdk.plugin.CKAppEvents.getInstance(CKAppEvents.java)[1]
com.ck.sdk.plugin.CKAppEvents.payFail(CKAppEvents.java)[1]
com.ck.sdk.plugin.CKAppEvents.isNullPlugin(CKAppEvents.java)[1]
com.ck.sdk.utils.LogUtil.iT(LogUtil.java)[1]
com.ck.sdk.AndGameSDK.dealPayResult(AndGameSDK.java)[1]
Skipped 82 frames! The application may be doing too much work on its main thread.
(其余略)
简单分析,推断com.ck.sdk.AndGameSDK$1.onResult(AndGameSDK.java)[1]是关键方法,com.ck.sdk.adapter.CKSDKAdapter.onCKPayFail(CKSDKAdapter.java)[1]和com.ck.sdk.database.CkEventTool.setPayFail(CkEventTool.java)[1]是失败的分支逻辑。我们还看到,顺着LOG再往下找,还有com.feamber.isp.SmsIAPListener.onResult(SmsIAPListener.java)[1]等方法,也可以作为破解点尝试。我们选择最早的那个。
3.分析com.ck.sdk.AndGameSDK$1.onResult的JAVA代码。
因为是内部类,有的方法名显示不全。直接看到com.ck.sdk.AndGameSDK中看onResult(JAVA代码界面,内部类的所有方法同时在主类中也有,一样的,有时显示略有不同)
看到有onCKPayFail方法了,查看接口
有接口,而且父类就是CKSDKAdapter,那么支付逻辑是从com.ck.sdk.AndGameSDK$1.onResult走到com.ck.sdk.adapter.CKSDKAdapter.onCKPayFail没错了。 老办法,修改com.ck.sdk.AndGameSDK$1.onResult,让他走到成功的cond_0段就可以了。我加了3个goto,你也可以修改switch和if,随意。测试成功。
Ps:原贴里楼主提出了两个疑问:“为什么删除请求太频繁前面的if判断能达到内购破解的目的,请求频繁这个提示为什么需要删除一些权限后才会出现?”楼主的破解方式,实际上是将程序原带的调试功能打开了。我们照着楼主的思路修改com.ck.sdk.plugin.CKPay$1.run,看下LOG的不同(这里开启LOG的时机就要早一些了)。
[Asm] 纯文本查看 复制代码 不修改:
(前略)
com.ck.sdk.plugin.CKPay$1.run(CKPay.java)[1]
com.ck.sdk.CKSDK.getInstance(CKSDK.java)[1]
com.ck.sdk.CKSDK.getContext(CKSDK.java)[1]
com.ck.sdk.utils.files.SPUtil.getInt(SPUtil.java)[1]
com.ck.sdk.plugin.CKPay.checkHaveUpdate(CKPay.java)[1]
com.ck.sdk.CKSDK.getInstance(CKSDK.java)[1]
com.ck.sdk.CKSDK.getContext(CKSDK.java)[1]
com.ck.sdk.utils.files.SPUtil.getString(SPUtil.java)[1]
(后略)
[Asm] 纯文本查看 复制代码 照楼主修改后:
(前略)
com.ck.sdk.plugin.CKPay$1.run(CKPay.java)[1]
com.ck.sdk.plugin.CKPay.checkPayResultTest(CKPay.java)[1]
com.ck.sdk.CKSDK.getInstance(CKSDK.java)[1]
com.ck.sdk.CKSDK.getContext(CKSDK.java)[1]
com.ck.sdk.PayParams.getProductId(PayParams.java)[1]
com.ck.sdk.PayParams.getPrice(PayParams.java)[1]
com.ck.sdk.PayParams.getProductName(PayParams.java)[1]
(后略)
大体对比一下,可以看到,修改后主要的不同是checkPayResultTest运行了(对于一些复杂的app,插件多、逻辑复杂,只好用人工简单判断的方式;对于简单app,可以把修改前后的LOG分别存档为txt文件,然后使用beyond compare比对)。这个方法可疑,看一下这个方法,根据里面的字符判断这个就是那个支付调试提示框。看一下run方法的smali文件
[Asm] 纯文本查看 复制代码 .method public run()V
.locals 7
.prologue
invoke-static {}, Lcom/hook/testsmali/InjectLog;->PrintFunc()V
const/4 v6, 0x0
.line 92
iget-object v1, p0, Lcom/ck/sdk/plugin/CKPay$1;->this$0:Lcom/ck/sdk/plugin/CKPay;
invoke-static {v1}, Lcom/ck/sdk/plugin/CKPay;->access$0(Lcom/ck/sdk/plugin/CKPay;)Lcom/ck/sdk/IPay;
move-result-object v1
#if-nez v1, :cond_1 ------关建行
.line 93
iget-object v1, p0, Lcom/ck/sdk/plugin/CKPay$1;->this$0:Lcom/ck/sdk/plugin/CKPay;
iget-object v2, p0, Lcom/ck/sdk/plugin/CKPay$1;->val$tempData:Lcom/ck/sdk/PayParams;
invoke-static {v1, v2}, Lcom/ck/sdk/plugin/CKPay;->access$1(Lcom/ck/sdk/plugin/CKPay;Lcom/ck/sdk/PayParams;)V
.line 125
:cond_0
:goto_0
return-void
(后略)
如果不注释if-nez v1, :cond_1这行的话,是要跳过return-void的,v1是取Lcom/ck/sdk/plugin/CKPay;->payPlugin:Lcom/ck/sdk/IPay;的实例,一定是不等于0的(非0即真,真非0),所以这里一定是跳过的。注释掉后,.line 93的3行代码就得以执行,其中access$1就是调用启动了checkPayResultTest方法。程序启动checkPayResultTest后,运行到return-void就退出了,所以后边几个if修改是无用的,起作用的只有第一个。另一问也是同理,略。
(未完,下楼继续) |