Sun的逆向之路(一)——元气骑士的内购破解
最近代码码多了于是打算翻翻有什么游戏可玩,无意之中翻到了之前玩的元气骑士,想到还有许多人物都没有解锁,但是又不想攒金币因为太慢了,况且还有一些角色是要RMB才能买的,通过PKiP发现无加固壳,于是一怒之下打算破解其内购。
0x01 找寻突破口
打开元气骑士进入购买人物的场景,点击购买,因为钻石不够所以点进去发现跳转到了购买钻石的页面。1块钱可以兑换800钻石,于是点购买跳转到了爱贝支付。返回取消购买回到了游戏入口界面,提示购买失败。
0x02 初步尝试破解
于是用MT2拿到apk的dex文件,搜索“失败”,发现iapppay下的IAppPayOrderChecker和MainActivit$3$1格外瞩目。进入IAppPayOrderChecker发现没有相关内容,于是转到MainActivity$3$1中。在OnPayResult函数中发现如下代码块:
.line 113
iget-object v1, p0, Lcom/chillyroomsdk/iapppay/MainActivity$3$1;->this$1:Lcom/chillyroomsdk/iapppay/MainActivity$3;
iget-object v2, p0, Lcom/chillyroomsdk/iapppay/MainActivity$3$1;->val$orderId:Ljava/lang/String;
iget-object v3, p0, Lcom/chillyroomsdk/iapppay/MainActivity$3$1;->val$extra:Ljava/lang/String;
invoke-virtual {v1, v2, v3}, Lcom/chillyroomsdk/iapppay/MainActivity$3;->onPayFail(Ljava/lang/String;Ljava/lang/String;)V
.line 116
:goto_18
const-string v1, "Unity"
其中的OnPayFail值得注意,既然有OnPayFail那么就应该有OnPaySuccess,我们继续往下翻的确找到了OnPaySuccess,同时在OnPaySuccess上方看到 :pswitch_45,将其记录下来。翻到底部找到了一个pswitch结构:
.line 96
:pswitch_data_86
.packed-switch 0x0
:pswitch_45
:pswitch_4
:pswitch_7c
.end packed-switch
注意到:pswitch_45下方还有:pswitch_4和:pswitch_7c,我们找到pswitch_4所在的位置发现正好跳转到了OnPayFail所在的位置。这时我们大概清楚了代码逻辑,即不同的情况分别跳转到不同的:pswitch_代码块。我们将另外两个pswitch的跳转编号全部修改为45。到这里,购买成功的破解就完成了。
0x03 深入订单校验
但是进入游戏发现仍然购买失败。提示的是:支付成功但验签失败。
看来元气骑士的内购验证还是比较严密的。回到dex文件,发现该字符串仍旧位于MainActivity$3$1,由:cond_6e跳转而来,发现原来在OnPaySuccess的上一步还做了一个支付判断(if-eqz v0, :cond_6e),如果订单状态校验不吻合就跳转到:cond_6e。将这条代码注释掉,重新进入游戏购买,发现还是失败了,提示订单支付失败。若发生丢单请勿重复支付,并联系客服。
这时冷静下来,猜想除了 MainActivity 内的 OnPayResult 判断外,应该还有其他的订单检验方法,重新审视了一下com类,不出所料,在最开始被忽视的 IAppPayOrderChecker 中发现了对应字符串,代码块对应 :cond_f5,往上一翻发现多处if-eqz跳转到了此处!二话不说对所有转到 :cond_f5 的判断语句进行注释,中途还发现一个 :cond_133 跳转到验证失败于是也一并注释了。 至此不出意外应该已经成功完成了内购破解。
0x04 破除apk签名验证
有意思的是,虽然内购破解应该已经顺利完成了,但是重编译并自己手动打上签名安装后发现直接无法进入游戏了,提示我们请下载正版游戏并跳转到了元气骑士的官方网站。猜想是联网验证于是断掉无线局域网重进游戏,发现依旧无法进入游戏并直接闪退了,看来游戏做了联网+本地的双重验证。 回到dex++,最终在 UnityExtendAcitivity 中的起始位置发现了名为 GetKeyHash 函数,代码片段如下:
.method public static GetKeyHash()Ljava/lang/String;
.registers 7
.prologue
const/4 v3, 0x0
.line 26
:try_start_1
sget-object v4, Lcom/unity3d/player/UnityPlayer;->currentActivity:Landroid/app/Activity;
invoke-virtual {v4}, Landroid/app/Activity;->getPackageManager()Landroid/content/pm/PackageManager;
move-result-object v4
在.prologue与const/4 v3, 0x0之间直接插入return-void拦截此函数的执行,最后保存修改过的dex文件,更新apk包并打上应用签名,重新安装apk文件,没有出现异常,可以进入游戏了,随便购买一个人物,进入订单页面,返回取消订单,可以看到支付成功,除了人物之外,钻石也可以随意购买了。
0x05 附件与声明
破解后的游戏下载地址:https://pan.baidu.com/s/1pL1JzsR 密码: 7dvq
本文及附件内容仅限于技术学习,同时希望相应公司尽快修复相关漏洞。
- 感谢你赐予我前进的力量