Android破解-滚动的天空
前言
最近看到很多学生在玩滚动的天空这个游戏,这游戏看起来还挺有趣的,于是我也去下载玩了几把。可是,坑呐,这游戏太难了,考验人的快速判断能力和手指滑动速度,玩的很渣。玩游戏技术不行怎么办呢?俗话说,能力不够金钱来凑!哈哈哈,游戏世界不都是这样的吗?各个游戏界的大神哪个不是用金钱堆起来的。玩游戏只是为了娱乐,能够用一点金钱来购买的娱乐有什么关系呢?反正我的理解是这样的。于是,能不住想要购买道具了。但是,突然又想到,我们学IT的,玩这个小游戏还需要花钱?自己破解游戏不就好了吗?于是,就开始动工了。从中午休息时间开始就着手去做这件事了,搞到我中午都没有休息,下午下班后回到家继续搞事情。直到晚上九点,终于把这破游戏破解成功了。
使用的技术和工具
话说,磨刀不误砍材工,要想破解一款软件,至少要懂该软件的基本编程,所以破解Android软件是需要懂得Android应用的编程才可以的。其中用到的技术当然少不了大名鼎鼎的Java语言,然后是Android开发基础,要懂得Android SDK的功能和使用,会基本的Smali语法。另外,还需要使用一些工具软件来辅助。
- 本次使用的APK使最新版的滚动的天空,以后如果出新版可能不适用,这是原版没有破解的APK下载地址:https://pan.baidu.com/s/1VuWRP7bhUsHg0kjsCpoNpA
破解后的APK:
https://pan.baidu.com/s/1F0JomQeEKRqE-hUn8k9yUA - 使用Android Killer V1.3.1.0版本的反编译工具,这个工具很强大适合初学者使用,想要深入学习的话最好用其他的软件。http://www.ouyaoxiazai.com/soft/yyrj/158/38785.html#dizhi
- APKTool V2.3.4,大名鼎鼎的反编译工具
https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.3.4.jar
破解过程
第一步:反编译APK
打开Android Killer,直接鼠标拖动安装包APK文件到窗口,会自动反编译
如果出现失败的情况,则需要下载最新的apktool.jar,手动添加到Android Killer中
第二步:寻找破解关键点
下面,在道具商城点击商品的的时候会提示:sim卡与支付服务运营商不匹配或网络未连接,请检查网或无法购买,未插入sim卡等信息,在Android Killer中可以根据这个提示的内容搜索到这个内容在项目中出现的位置。
可以看到这个字符串是在Strings.xml中定义的,代码通过这个字符串的id来调用这个字符串。接着,可以在public.xml中看到这个字符串的id值
然后,搜索这个ID值0x7f050028就可以找到在代码中调用这个字符串的位置
可以看到,在a.smali文件中调用了这个字符串,下面是调用这个字符串的函数的全部内容:
.method public static b(Ljava/lang/String;Landroid/content/Context;Lcom/turbochilli/rollingsky/pay/PayAgent;I)V
.locals 2
.prologue
.line 302
invoke-static {p1}, Lcom/turbochilli/rollingsky/util/NetUtil;->isNetworkAvailable(Landroid/content/Context;)Z
move-result v0
if-nez v0, :cond_0
.line 303
const v0, 0x7f050022
invoke-virtual {p1, v0}, Landroid/content/Context;->getString(I)Ljava/lang/String;
move-result-object v0
invoke-static {p1, v0}, Lcom/turbochilli/rollingsky/util/CommonUtil;->showToast(Landroid/content/Context;Ljava/lang/String;)V
.line 322
:goto_0
return-void
.line 306
:cond_0
invoke-static {}, Lcom/turbochilli/rollingsky/util/CommonUtil;->isFlavorTelecom()Z
move-result v0
if-eqz v0, :cond_2
.line 307
invoke-static {}, Lcom/turbochilli/rollingsky/util/CommonUtil;->getSimOperator()Ljava/lang/String;
move-result-object v0
.line 308
invoke-static {v0}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z
move-result v1
if-eqz v1, :cond_1
.line 309
const v0, 0x7f050023
invoke-virtual {p1, v0}, Landroid/content/Context;->getString(I)Ljava/lang/String;
move-result-object v0
invoke-static {p1, v0}, Lcom/turbochilli/rollingsky/util/CommonUtil;->showToast(Landroid/content/Context;Ljava/lang/String;)V
goto :goto_0
.line 311
:cond_1
invoke-static {}, Lcom/turbochilli/rollingsky/util/CommonUtil;->getCurrentFLAVOR()Ljava/lang/String;
move-result-object v1
invoke-static {v1, v0}, Landroid/text/TextUtils;->equals(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Z
move-result v0
if-nez v0, :cond_2
.line 312
const v0, 0x7f050028
invoke-virtual {p1, v0}, Landroid/content/Context;->getString(I)Ljava/lang/String;
move-result-object v0
invoke-static {p1, v0}, Lcom/turbochilli/rollingsky/util/CommonUtil;->showToast(Landroid/content/Context;Ljava/lang/String;)V
goto :goto_0
.line 316
:cond_2
if-eqz p2, :cond_3
.line 317
const/4 v0, 0x0
new-instance v1, Lcom/turbochilli/rollingsky/b/a;
invoke-direct {v1, p0}, Lcom/turbochilli/rollingsky/b/a;-><init>(Ljava/lang/String;)V
invoke-virtual {p2, p0, v0, v1}, Lcom/turbochilli/rollingsky/pay/PayAgent;->pay(Ljava/lang/String;ILcom/turbochilli/rollingsky/pay/PayCallback;)V
goto :goto_0
.line 319
:cond_3
const v0, 0x7f050021
invoke-virtual {p1, v0}, Landroid/content/Context;->getString(I)Ljava/lang/String;
move-result-object v0
invoke-static {p1, v0}, Lcom/turbochilli/rollingsky/util/CommonUtil;->showToast(Landroid/content/Context;Ljava/lang/String;)V
goto :goto_0
.end method
通过代码可以看到,前面很多的内容都是用来判断网络状况、SIM运营商等信息的,我们可以把它们全部删掉,只留下调用支付功能的核心代码,改动后如下:
.method public static b(Ljava/lang/String;Landroid/content/Context;Lcom/turbochilli/rollingsky/pay/PayAgent;I)V
.locals 2
.prologue
.line 302
#点击道具商品后,执行到这里,去除了判断网络SIM卡等代码
const/4 v0, 0x0
new-instance v1, Lcom/turbochilli/rollingsky/b/a;
invoke-direct {v1, p0}, Lcom/turbochilli/rollingsky/b/a;-><init>(Ljava/lang/String;)V
#这里调用支付功能
invoke-virtual {p2, p0, v0, v1}, Lcom/turbochilli/rollingsky/pay/PayAgent;->pay(Ljava/lang/String;ILcom/turbochilli/rollingsky/pay/PayCallback;)V
goto :goto_0
:goto_0
return-void
.end method
保持文件,点击编译,会提示编译成功,把安装包安装到手机上,可以看到,点击道具后直接就弹出了支付方式选择界面,没有再提示网络情况和SIM卡情况的现象了。但是,到这里并没有破解成功,依旧还是需要支付的。
第三步:找到支付回调
从上面可以看到PayAgent类是一个抽象类,下面是PayAgent.smali最前面的内容:
.class public abstract Lcom/turbochilli/rollingsky/pay/PayAgent;
.super Ljava/lang/Object;
.source "PayAgent.java"
查看这个类的内容,可以看到里面的pay函数是一个抽象函数:
.method public abstract pay(Ljava/lang/String;ILcom/turbochilli/rollingsky/pay/PayCallback;)V
.end method
所以,必须要找到这个类的实现类才能定位到真正的支付功能的代码,查看PayAgent.smali同目录的其他文件,很容易可看到,pay这个目录下面的内容都是支付相关的代码,查看各个类内容,可以找DianxinPay继承自PayAgent,所以DianxinPay里面的pay函数才是真正的支付代码。后面还有接着,EgamePay才是真正的支付网络请求,这里就不详细展开了。通过这些追踪,最后找到DianXinPay$3.smali文件里面有三个支付结果回调:
.method public payCancel(Ljava/util/Map;)V
.method public payFailed(Ljava/util/Map;I)V
.method public paySuccess(Ljava/util/Map;)V
把payCancel函数里面的所有内容替换为paySuccess里面的内容,这时候破解完成,当在APP中取消支付的时候,会实现支付完成的效果。下面是payCancel函数的内容:
.method public payCancel(Ljava/util/Map;)V
.locals 4
.annotation system Ldalvik/annotation/Signature;
value = {
"(",
"Ljava/util/Map",
"<",
"Ljava/lang/String;",
"Ljava/lang/String;",
">;)V"
}
.end annotation
.prologue
.line 187
#雷:这里是核心关键的破解位置,在弹出的支付界面直接退出支付界面会调用这个取消支付的函数,把内容
#全部替换为支付成功函数的内容就可以了。
const-string v0, "DianXinPay"
const-string v1, "paySuccess"
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 171
iget-object v0, p0, Lcom/turbochilli/rollingsky/pay/DianXinPay$3;->this$0:Lcom/turbochilli/rollingsky/pay/DianXinPay;
invoke-static {v0}, Lcom/turbochilli/rollingsky/pay/DianXinPay;->access$300(Lcom/turbochilli/rollingsky/pay/DianXinPay;)Lcom/turbochilli/rollingsky/pay/PayCallback;
move-result-object v0
if-eqz v0, :cond_0
.line 172
invoke-static {}, Lcom/turbochilli/rollingsky/c;->a()Lcom/turbochilli/rollingsky/c;
move-result-object v0
invoke-virtual {v0}, Lcom/turbochilli/rollingsky/c;->h()Lcom/turbochilli/rollingsky/c$b;
move-result-object v0
iget-object v1, p0, Lcom/turbochilli/rollingsky/pay/DianXinPay$3;->val$orderId:Ljava/lang/String;
invoke-interface {v0, v1}, Lcom/turbochilli/rollingsky/c$b;->b(Ljava/lang/String;)V
.line 173
iget-object v0, p0, Lcom/turbochilli/rollingsky/pay/DianXinPay$3;->this$0:Lcom/turbochilli/rollingsky/pay/DianXinPay;
invoke-static {v0}, Lcom/turbochilli/rollingsky/pay/DianXinPay;->access$300(Lcom/turbochilli/rollingsky/pay/DianXinPay;)Lcom/turbochilli/rollingsky/pay/PayCallback;
move-result-object v0
iget-object v1, p0, Lcom/turbochilli/rollingsky/pay/DianXinPay$3;->val$product:Lcom/turbochilli/rollingsky/pay/IProduct;
const/16 v2, 0xb
invoke-interface {v0, v1, v2}, Lcom/turbochilli/rollingsky/pay/PayCallback;->onSendOrderInfo(Lcom/turbochilli/rollingsky/pay/IProduct;I)V
.line 175
:cond_0
return-void
.end method
第四步:打包文件、破解成功
保存文件,点击编译
编译成功后,把APK重新安装到手机(需要把原来的APK卸载才能安装),终于搞定,可以愉快的玩耍了。
练习
下面的游戏以类似的方式可以破解,笔者已经测试通过,有兴趣的同学可以试着去玩玩。