之前做了一段時間的安卓手游破解正版驗證工作射沟,總結(jié)一些基本的方法和知識辉懒,便于分享交流愉耙。破解的目的是讓國內(nèi)一些“被閹割”過(無GooglePlay框架)的安卓手機也能暢快地玩兒上GooglePlay上的游戲蔬蕊,所以文章主要討論海外游戲市場下載的apk包如何去除正版驗證。國內(nèi)的手游大部分還是做了防破解工作的奕枝,加殼棺榔、防反編譯等等(此篇不予討論)。大概海外市場的氛圍較好隘道,所以GooglePlay症歇、Amazon商店的游戲基本都可以反編譯成功,頂多加了代碼混淆谭梗、調(diào)用了GP/Amazon的驗證SDK而已忘晤。
適合誰看?
破解初學(xué)者激捏、懂一些編程知識的童鞋(完全不懂編程也可以设塔,只能破解那些只需要替換一些文件/文本的游戲)、以及對apk破解/安全性維護感興趣的童鞋缩幸。
什么是Smali壹置?
先說Dalvik是google專門為Android操作系統(tǒng)設(shè)計的一個虛擬機,經(jīng)過深度的優(yōu)化表谊。雖然Android上的程序是使用java來開發(fā)的,但是Dalvik和標(biāo)準(zhǔn)的java虛擬機JVM還是兩回事盖喷。Dalvik VM是基于寄存器的爆办,而JVM是基于棧的;Dalvik有專屬的文件執(zhí)行格式dex(dalvik executable)课梳,而JVM則執(zhí)行的是java字節(jié)碼距辆。Dalvik VM比JVM速度更快余佃,占用空間更少。
通過Dalvik的字節(jié)碼我們不能直接看到原來的邏輯代碼跨算,這時需要借助如Apktool或dex2jar+jd-gui工具來幫助查看爆土。但是,注意的是最終我們修改APK需要操作的文件是.smali文件诸蚕,而不是導(dǎo)出來的Java文件重新編譯(況且這基本上不可能)步势。
詳細(xì)的Smali語法學(xué)習(xí)可以參考這篇文章:http://blog.csdn.net/lpohvbe/article/details/7981386,里面詳細(xì)介紹了Smali中的數(shù)據(jù)類型背犯、方法調(diào)用等等坏瘩。
此外,反編譯過程中遇到不懂的關(guān)鍵字漠魏,可參考http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html倔矾,非常實用的文檔。
如何去除正版驗證柱锹?
你需要的工具
工欲善其事哪自,必先利其器,反編譯工具很多禁熏,針對點也不同壤巷,本文主要用的是APKIDE(也叫APK改之理,一個類似APK studio的可視化反編譯軟件匹层,內(nèi)部已經(jīng)集成了dex轉(zhuǎn)smali隙笆、dex轉(zhuǎn)jar、重新編譯升筏、簽名撑柔、搜索等功能,推薦使用)您访,其他工具和功能如下:
apktool铅忿,命令行工具,可以decode和build apk文件灵汪,主要用來解析xml等資源文件檀训。
dex2jar,一系列命令行工具享言,能將文件在dex峻凫、jar和smali之間相互轉(zhuǎn)換,反編譯時可以dex轉(zhuǎn)為smali后修改smali代碼览露,再將smali轉(zhuǎn)回成dex荧琼。
jd_guid,可視化工具,將jar文件反編譯為java代碼命锄,便于閱讀代碼邏輯堰乔,至于直接編輯java代碼什么的,你想多了脐恩。
baksmali镐侯,命令行工具,也是用來轉(zhuǎn)換dex和smali文件驶冒。
apksigner苟翻,命令行簽名工具,反編譯重新打包之后用來重新簽名apk只怎。
apk studio袜瞬,類似APK IDE,英文界面身堡,有時候會反編譯失敗而APK IDE不會邓尤,猜想可能是內(nèi)嵌的baksmali之類的版本不一樣或者編碼問題吧。
以上工具在百度贴谎、谷歌上下載都可搜到下載汞扎。
常用方法
通常不能運行的游戲表現(xiàn)有這幾種:
1. 提示未安裝谷歌框架,不能游戲擅这;或者提示谷歌驗證此游戲為盜版(可能你的apk不是從GooglePlay下載而來)澈魄。
2. 提示當(dāng)前賬號并未購買此游戲(同樣,可能此游戲并非GooglePlay上購買后直接下載的)
你打開游戲可能會卡在這樣的頁面:
第一種情況是因為代碼中調(diào)用了Google官方的驗證SDK仲翎,僅驗證游戲是否來自GooglePlay痹扇,而第二種添加了Google賬號驗證,檢查你是否購買了此游戲溯香,這兩種情況都可以通過繞開Google驗證來破解:
去除GooglePlay驗證(可寫成腳本進(jìn)行自動化破解)
最簡單的思路其實是找到代碼中調(diào)用驗證的地方鲫构,將其注釋掉,直接調(diào)用callback函數(shù)(一般此類SDK都是異步返回玫坛,開發(fā)者實現(xiàn)一個帶callback的Listener)中驗證成功的方法结笨,拷貝復(fù)制即可。至于調(diào)用的地方湿镀,一般都在入口Activity類中炕吸,函數(shù)名一般帶有verify、certificate勉痴、auth之類的字樣赫模。這也的確是個可行的辦法,但用這個辦法破解了幾款游戲后發(fā)現(xiàn)游戲之間差異很大蒸矛,你必須針對每款游戲去找調(diào)用入口嘴瓤,太費時了扫外,有沒有規(guī)律性的莉钙、可自動化的方法呢廓脆?
答案是肯定的,既然是SDK進(jìn)行的驗證磁玉,我們只修改SDK內(nèi)的代碼不就好了嗎停忿?只要驗證的結(jié)果每次都返回成功就OK了。按Google文檔上的教程蚊伞,接入驗證使用的是sdk\extras\google\play_licensing下的sdk工程席赂,引入后就一個包:com.google.android.vending.licensing,用APK IDE打開如下:
其內(nèi)部大致做的事情就是驗證apk簽名时迫,請求Google服務(wù)器檢查驗證是否通過颅停、并得到游戲的擴展數(shù)據(jù)包信息(obb文件名、大小等)掠拳,其中夾雜超時處理癞揉、緩存過期時間等等。
找到了負(fù)責(zé)驗證的sdk包溺欧,修改哪里就可以強制返回成功了呢喊熟?一個是LicenseValidator,負(fù)責(zé)解析Google服務(wù)器返回的responseCode姐刁,另一個是實現(xiàn)Policy接口的類芥牌,負(fù)責(zé)解析從服務(wù)器獲取的額外信息(擴展包文件等)。這里偷了一點懶聂使,沒仔細(xì)研究SDK壁拉,而是找到個市面上比較好用的安卓破解器:幸運破解器(裝在手機上就能直接破解游戲的神器,還能破解內(nèi)購)柏靶,用它破解一款游戲后導(dǎo)出apk弃理,反編譯出smali文件,寫個腳本批量對比前后文件變化宿礁,過濾掉注釋等無用信息得到的就是我們要修改的地方了案铺!總結(jié)后如下:
1. 工程中所有調(diào)用java/security/Signature;->verify的地方,將下一行的
move-result v3
改為
const/4 v3, 0x1
move-result v3的意思是將verify函數(shù)的返回結(jié)果賦值給v3梆靖,我們直接替換成聲明一個值為true的v3變量即可控汉。verify函數(shù)用以驗證簽名,我們在破解后必然要重新簽名返吻,無法保留原有開發(fā)者簽名姑子,所以所有驗證簽名的地方都需要進(jìn)行這一步修改。
2. 實現(xiàn)了Policy接口的類(一般是APKExpansionPolicy和ServerManagedPolicy)中的函數(shù)allowAccess测僵,將函數(shù)開頭的聲明
const/4 v1, 0x0
改為
const/4 v1, 0x1
(其中v1命名不確定街佑,可能叫v0或v2等等)此函數(shù)根據(jù)上次請求結(jié)果和重試次數(shù)判斷是否驗證通過谢翎,v1是默認(rèn)返回值,初始化時為false沐旨,后續(xù)代碼判斷滿足某些條件后將v1賦值為true森逮,函數(shù)退出時必然返回v1。所以我們將v1默認(rèn)值設(shè)為true就相當(dāng)于默認(rèn)驗證通過了磁携。
注意褒侧,這里可能v1默認(rèn)值已經(jīng)是true了,而是在判斷驗證未通過時將v1賦值為false再返回谊迄,因此可以將此函數(shù)所有的變量聲明都改為默認(rèn)值為true闷供。
3.?smali\com\google\android\vending\licensing\LicenseValidator.smali中的函數(shù)verify,將
0x1 -> :sswitch_1
0x2 -> :sswitch_0
改為
0x1 -> :sswitch_0
0x2 -> :sswitch_1
這里是將switch中驗證失敗的情況指向了驗證成功時要執(zhí)行的代碼段统诺。
4. 為了以防萬一歪脏,可以在繼承了Lcom/google/android/vending/licensing/LicenseCheckerCallback接口的類中修改dontAllow函數(shù),在函數(shù)一開始加入:
const/16 v1, 0x100
invoke-interface {p0, v1}, Lcom/google/android/vending/licensing/LicenseCheckerCallback;->allow(I)V
return-void
這里的意思是直接調(diào)用listener的allow函數(shù)粮呢,p0是callback自己婿失,變量v1聲明的初始值為0x100,是代表成功的常量鬼贱,調(diào)用p0的allow函數(shù)移怯,參數(shù)為v1,然后直接返回这难,之后的代碼就不會被執(zhí)行了舟误。這樣就確保必然調(diào)用allow,萬無一失姻乓。
5. 完成以上的步驟就可以通過GooglePlay驗證了嵌溢,但打開游戲后會開始下載游戲擴展包(obb文件),強制忽略手機上已存在的obb文件蹋岩。這是因為正常的sdk在驗證的同時會返回此游戲最新的擴展包名赖草、包大小等信息,方便用戶下載剪个、開發(fā)者更新等秧骑。因此為了可以順暢游戲,還要在實現(xiàn)了Lcom/google/android/vending/licensing/Policy接口的類中修改getExpansionFileName等一系列函數(shù)扣囊,直接將游戲擴展包的信息寫在代碼中直接返回乎折。
完成以上步驟即可順利游戲了,因為這些步驟都是有跡可循侵歇、規(guī)律的骂澄,所以寫個腳本就可以一鍵破解了(腳本正在編寫中)。
至于為什么不用破解后的驗證sdk直接覆蓋替換其他游戲的sdk惕虑,是因為各自游戲開發(fā)時間不同坟冲,采用的sdk版本不同磨镶,直接覆蓋可能會導(dǎo)致有些方法找不到,而以上的這些修改都是在sdk核心邏輯中健提,親自驗證了幾個版本發(fā)現(xiàn)這些核心邏輯并沒有改變琳猫。
可能遇到的特殊處理
有些游戲可能在進(jìn)行完以上步驟會出現(xiàn)閃退、報錯等情況矩桂,這可能是由于該游戲接入的第三方工具在搗亂沸移。以“拔拔曼陀羅”為例,本人完成以上步驟后報錯NullPointer侄榴,遂打開Eclipse查找Logcat(不得不說查Logcat是非常實用的辦法,只要眼疾手復(fù)制出來即可)网沾,發(fā)現(xiàn)的錯誤如下:
1. 無com.android.vending.CHECK_LICENSE權(quán)限錯誤:游戲中有個corona的包癞蚕,搜索后得知這是個打包插件,方便開發(fā)者集成Google辉哥、Amazon等sdk桦山、方便build。在Google驗證處調(diào)用enforceCallingOrSelfPermission時報錯醋旦,但Manifest中已添加此權(quán)限恒水,未找到原因,反正已經(jīng)破解了驗證饲齐,直接注釋掉钉凌,運行通過。
2. 空指針報錯:同樣還是Corona的坑捂人,在獲取谷歌框架服務(wù)時未判斷null御雕,大概corona的開發(fā)者覺得全世界的安卓機都應(yīng)該有谷歌框架吧,呵呵滥搭,注釋后運行成功酸纲。
當(dāng)然需要特殊處理的地方肯定會很多,并不是所有游戲都能自動化破解瑟匆,但有了自動化腳本就已經(jīng)節(jié)省不少工作量了闽坡。
那些難以跨越的坑
1. 代碼混淆
混淆后的代碼幾乎找不到從哪個類下手、從哪個方法下手愁溜,因為放眼望去所有類名疾嗅、方法名全部都是a、b祝谚、c宪迟。海外市場也有不少游戲和應(yīng)用添加混淆,但這也僅僅是“幾乎”而已交惯,并不是完全沒辦法次泽,具體如何破解下一篇再來闡述穿仪。
2. 加殼等防反編譯措施
還有很多種辦法可以防止apk被反編譯,如字符串混淆讓反編譯后的String類型不可讀、用花指令或動態(tài)加載等方式讓反編譯看不到源碼甚至無法反編譯源代碼。所幸目前見到的海外市場游戲較少采取此種措施粥诫,無需對此進(jìn)行破解特幔。