Android破解-滾動(dòng)的天空
前言
最近看到很多學(xué)生在玩滾動(dòng)的天空這個(gè)游戲,這游戲看起來(lái)還挺有趣的,于是我也去下載玩了幾把??墒?,坑吶,這游戲太難了,考驗(yàn)人的快速判斷能力和手指滑動(dòng)速度,玩的很渣。玩游戲技術(shù)不行怎么辦呢?俗話說(shuō),能力不夠金錢來(lái)湊!哈哈哈,游戲世界不都是這樣的嗎?各個(gè)游戲界的大神哪個(gè)不是用金錢堆起來(lái)的。玩游戲只是為了娛樂(lè),能夠用一點(diǎn)金錢來(lái)購(gòu)買的娛樂(lè)有什么關(guān)系呢?反正我的理解是這樣的。于是,能不住想要購(gòu)買道具了。但是,突然又想到,我們學(xué)IT的,玩這個(gè)小游戲還需要花錢?自己破解游戲不就好了嗎?于是,就開(kāi)始動(dòng)工了。從中午休息時(shí)間開(kāi)始就著手去做這件事了,搞到我中午都沒(méi)有休息,下午下班后回到家繼續(xù)搞事情。直到晚上九點(diǎn),終于把這破游戲破解成功了。
使用的技術(shù)和工具
話說(shuō),磨刀不誤砍材工,要想破解一款軟件,至少要懂該軟件的基本編程,所以破解Android軟件是需要懂得Android應(yīng)用的編程才可以的。其中用到的技術(shù)當(dāng)然少不了大名鼎鼎的Java語(yǔ)言,然后是Android開(kāi)發(fā)基礎(chǔ),要懂得Android SDK的功能和使用,會(huì)基本的Smali語(yǔ)法。另外,還需要使用一些工具軟件來(lái)輔助。
- 本次使用的APK使最新版的滾動(dòng)的天空,以后如果出新版可能不適用,這是原版沒(méi)有破解的APK下載地址:https://pan.baidu.com/s/1VuWRP7bhUsHg0kjsCpoNpA
破解后的APK:
https://pan.baidu.com/s/1F0JomQeEKRqE-hUn8k9yUA - 使用Android Killer V1.3.1.0版本的反編譯工具,這個(gè)工具很強(qiáng)大適合初學(xué)者使用,想要深入學(xué)習(xí)的話最好用其他的軟件。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
破解過(guò)程
第一步:反編譯APK
打開(kāi)Android Killer,直接鼠標(biāo)拖動(dòng)安裝包APK文件到窗口,會(huì)自動(dòng)反編譯
如果出現(xiàn)失敗的情況,則需要下載最新的apktool.jar,手動(dòng)添加到Android Killer中
第二步:尋找破解關(guān)鍵點(diǎn)
下面,在道具商城點(diǎn)擊商品的的時(shí)候會(huì)提示:sim卡與支付服務(wù)運(yùn)營(yíng)商不匹配或網(wǎng)絡(luò)未連接,請(qǐng)檢查網(wǎng)或無(wú)法購(gòu)買,未插入sim卡等信息,在Android Killer中可以根據(jù)這個(gè)提示的內(nèi)容搜索到這個(gè)內(nèi)容在項(xiàng)目中出現(xiàn)的位置。
可以看到這個(gè)字符串是在Strings.xml中定義的,代碼通過(guò)這個(gè)字符串的id來(lái)調(diào)用這個(gè)字符串。接著,可以在public.xml中看到這個(gè)字符串的id值
然后,搜索這個(gè)ID值0x7f050028就可以找到在代碼中調(diào)用這個(gè)字符串的位置
可以看到,在a.smali文件中調(diào)用了這個(gè)字符串,下面是調(diào)用這個(gè)字符串的函數(shù)的全部?jī)?nèi)容:
.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
通過(guò)代碼可以看到,前面很多的內(nèi)容都是用來(lái)判斷網(wǎng)絡(luò)狀況、SIM運(yùn)營(yíng)商等信息的,我們可以把它們?nèi)縿h掉,只留下調(diào)用支付功能的核心代碼,改動(dòng)后如下:
.method public static b(Ljava/lang/String;Landroid/content/Context;Lcom/turbochilli/rollingsky/pay/PayAgent;I)V
.locals 2
.prologue
.line 302
#點(diǎn)擊道具商品后,執(zhí)行到這里,去除了判斷網(wǎng)絡(luò)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
#這里調(diào)用支付功能
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
保持文件,點(diǎn)擊編譯,會(huì)提示編譯成功,把安裝包安裝到手機(jī)上,可以看到,點(diǎn)擊道具后直接就彈出了支付方式選擇界面,沒(méi)有再提示網(wǎng)絡(luò)情況和SIM卡情況的現(xiàn)象了。但是,到這里并沒(méi)有破解成功,依舊還是需要支付的。
第三步:找到支付回調(diào)
從上面可以看到PayAgent類是一個(gè)抽象類,下面是PayAgent.smali最前面的內(nèi)容:
.class public abstract Lcom/turbochilli/rollingsky/pay/PayAgent;
.super Ljava/lang/Object;
.source "PayAgent.java"
查看這個(gè)類的內(nèi)容,可以看到里面的pay函數(shù)是一個(gè)抽象函數(shù):
.method public abstract pay(Ljava/lang/String;ILcom/turbochilli/rollingsky/pay/PayCallback;)V
.end method
所以,必須要找到這個(gè)類的實(shí)現(xiàn)類才能定位到真正的支付功能的代碼,查看PayAgent.smali同目錄的其他文件,很容易可看到,pay這個(gè)目錄下面的內(nèi)容都是支付相關(guān)的代碼,查看各個(gè)類內(nèi)容,可以找DianxinPay繼承自PayAgent,所以DianxinPay里面的pay函數(shù)才是真正的支付代碼。后面還有接著,EgamePay才是真正的支付網(wǎng)絡(luò)請(qǐng)求,這里就不詳細(xì)展開(kāi)了。通過(guò)這些追蹤,最后找到DianXinPay$3.smali文件里面有三個(gè)支付結(jié)果回調(diào):
.method public payCancel(Ljava/util/Map;)V
.method public payFailed(Ljava/util/Map;I)V
.method public paySuccess(Ljava/util/Map;)V
把payCancel函數(shù)里面的所有內(nèi)容替換為paySuccess里面的內(nèi)容,這時(shí)候破解完成,當(dāng)在APP中取消支付的時(shí)候,會(huì)實(shí)現(xiàn)支付完成的效果。下面是payCancel函數(shù)的內(nèi)容:
.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
#雷:這里是核心關(guān)鍵的破解位置,在彈出的支付界面直接退出支付界面會(huì)調(diào)用這個(gè)取消支付的函數(shù),把內(nèi)容
#全部替換為支付成功函數(shù)的內(nèi)容就可以了。
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
第四步:打包文件、破解成功
保存文件,點(diǎn)擊編譯
編譯成功后,把APK重新安裝到手機(jī)(需要把原來(lái)的APK卸載才能安裝),終于搞定,可以愉快的玩耍了。
練習(xí)
下面的游戲以類似的方式可以破解,筆者已經(jīng)測(cè)試通過(guò),有興趣的同學(xué)可以試著去玩玩。