嚴正聲明:css
- 一、相關破解技術僅限於技術研究使用,不得用於非法目的,不然後果自負。
- 二、筆者僅出於對技術的好奇,無惡意破壞APP,尊重原開發者的勞動成果,未用於商業用途。
上一篇文章《因一紙設計稿,我把競品APP扒得褲衩不剩(上)》是一篇比較簡單的:html
- jsw => 技師文,呸,
- jsw => 記述文,呸呸,
- jsw => 技術文,呸呸呸,這什麼垃圾輸入法!
技術文,可是這評論區的風氣,貌似有點不對???java
冤枉啊,小弟真沒去過這種地方,也沒體驗過這種「服務」,只是道聽途說,可能:python
我描述得「繪聲繪色」,加之各位看官「浮想聯翩」,纔會以爲「煞有介事」。android
em…那個,能夠扶下我起來麼,那個…跪久了…腿有點麻…git
順帶恭喜下:FPX 3-0 G2,喜提S9總冠軍,FPX牛逼!!!破音!!!github
亞索的快樂你不懂~web
哈哈,回到本文:shell
- 發現 => 不少童鞋對APP逆向很感興趣;
- 可是 => 網上關於APP逆向文章的比較零散;
- 不知 => 如何入手,畢竟逆向的水可深了;
- 筆者 => 也只是個小白玩家,興趣使然玩玩而已;
- 分享 => 目前會的一些「Android APP基礎逆向姿式」;
- 還望 => 各位真丶逆向大佬輕噴;
- 若有 => 有更好的工具或者方法安利,歡迎評論區指出,謝謝~
順帶分享幾個筆者常逛的逆向論壇:安全
- 看雪論壇:bbs.pediy.com/
- 吾愛破解:www.52pojie.cn/
- 蟻安網:bbs.mayidui.net/
- 逆向大佬姜維:www.520monkey.com/
貼心提醒:
此文內容較多,可能會有些枯燥,建議先點贊收藏,茶餘飯後再慢慢品嚐~
在開始折騰Android APP逆向前,你須要:
一、一臺「具備完整Root權限」的Android手機,注意是「完整Root」權限!!!
好比「魅族手機」在設置->安全->Root權限,中能夠開啓Root權限,可是倒是「閹割的Root權限」,安裝SuperSu重啓後就一直卡氣球。
二、怎麼Root?根據本身的手機機型百度和逛各類搞機論壇吧(不要問我!)通常的常見的流程:
解BL鎖(BootLoader) -> 刷第三方Recovery(如TWRP) -> 卡刷Maglisk 或 SuperSU(Android 8.0之前)
三、不要輕易嘗試使用哪一種「一鍵Root」的軟件(大部分是毒瘤,如KingRoot),固然不是就不能用,能夠過河拆橋,好比個人魅藍E2的Root流程:
- 安裝KingRoot(v5.0)授予Root權限,修復Root權限異常,此時就有完整Root權限了;
- 接着用「移除KingRoot」刪掉KingRoot,此時還有完整Root權限;
- 安裝SuperSu(v2.8.2),常規方式更新二進制文件,重啓,Root完成。
四、推薦些能Root的手機?
Google Pixel親兒子(真原生,香,就是性價比不高),小米,一加 等。
五、沒錢買Android機或者已經有不能Root的手機了,能夠試試「Android模擬器」
AS自帶的AVD模擬器Root能夠參見《搞機:AS自帶模擬器AVD Root 和 Xposed安裝》
也可使用其餘第三方的安卓模擬器,好比「夜遊安卓模擬器、BlueStacks藍疊」等。
在開始折騰APP逆向前,先來了解一些概念與名詞~
獲取APK的渠道:酷安、應用寶,豌豆莢等應用市場下載,有些還提供「應用歷史版本」下載。
APK本質上是一個「壓縮包」,把「.apk後綴」改成「.zip後綴」後解壓,能夠看到以下目錄結構 (可能還有其餘文件):
簡單介紹下:
所謂的「編譯」,就是把「源碼、資源文件等」按照必定的「規則」打包成APK,官網 提供了詳細的編譯構建過程圖:
簡述下大概流程:
Step 1:資源文件處理「AAPT」
- assets會原封不動地打包在APK中;
- res中每個資源會賦予資源ID,以常量形式定義在R.java中,生成一個resource.arsc文件(資源索引表)。
Step 2:aidl文件「aidl」
- 將aidl後綴的文件轉換爲可用於進程通訊的C/S端Java代碼。
Step 3:Source Code「Java Compiler」
- 編譯生成.class文件。
Step 4:代碼混淆「ProGuard」(可選)
- 增長反編譯難度,命名縮短爲1-2個字母的名字,壓縮(移除無效類、屬性、方法等),優化bytecode移除沒用的結構。
Step 5:轉換爲dex「dx.bat」
- 把全部claas文件轉換爲classes.dex文件,class -> Dalvik字節碼,生成常量池,消除冗餘數據等。(方法數超65535會生成多個dex文件)
Step 6:打包「ApkBuilder」
- 把resources.arsc、classes.dex、其餘的資源一塊打包生成未簽名apk。
Step 7:簽名「Jarsigner」
- 對未簽名apk進行debug或release簽名。
Step 8:對齊優化「zipalign」
- 使apk中全部資源文件距離文件起始偏移爲4字節的整數倍,從而在經過內存映射訪問apk文件時會更快。
若是想了解更多編譯構建流程可移步至:《10分鐘瞭解Android項目構建流程》
而「反編譯」則是反過來了,經過一些反編譯工具,提取出源碼,轉換過程以下:
「APK ====> Dex ====> Jar(class文件)/Smali ==> Java源碼」。
APK能夠說是每一個Android開發仔的「心血結晶」,把各類本身以爲「牛逼哄哄的奇淫巧技」封裝其中。但總有些「心懷叵測」想去搞你的APP,經過一些「反編譯工具」獲取你的源碼,而後隨心所欲:
- 加廣告:你應用免費,給你加點廣告,亦或者改爲付費,而後下載量比你的多,氣不氣?
- 破解付費:你應用收費,Hook掉你的檢測方法,發個破解包,還處處傳播,氣不氣?
- 惡意攻擊:逆向得出請求接口規律,批量短信驗證註冊,耗光你的短信池等,氣不氣?
這種「惡劣」的行徑使人「氣憤」像極了某類經典「動做電影」裏的橋段:
- 男子上進,努力工做,妹子賢惠,料理家務;
- 妹子天天作好飯菜,等男子回來,一塊兒吃飯,滿懷憧憬,暢談之後的二人世界;
- 酒飽飯後,溫飽思XX,不可描述一番,卻被「居心叵測」的鄰居給盯上了;
- 和往常同樣,男子出門上班,妹子在家作家務,晾衣服;
- 鄰居 上線,用「謊話」誘騙妹子開門,接而擠門而入;
- 用「暴力和脅迫」,無視妹子的やめて和反抗,違揹我的意願;
- 粗暴地把衣服一件件褪去,僅剩下那「萬惡的馬賽克」;
- 守護着最後的一處「絕對領域」;
- 在幾番不可描述後,把妹子佔爲己有,而後像玩物般戲耍。
看着妹子「因情緒過激而身體抽搐」,哭得「梨花帶雨」,不由讓人「心生憐惜」,像我這種感性的藍孩子:
總會忍不住抽上幾張抽紙,「靜靜抹淚」,擦拭完,頓覺索然無味,一片空明,而後開始反思:
爲何那個鄰居不是我?呸呸呸…
除了「同情女主」和「斥責壞人」外,應該如何避免這種事情的發生呢?
- 一、花點錢,請個「保鏢」看門,壞人想進來要先過保鏢這一關;
- 二、給妹子「加個鎖」,讓壞人沒法不可描述,只能望而興嘆。
能夠把例子中的「妹子」看作是咱們編寫的「APK」,而「請保鏢」和「加鎖的操做」則能夠看作是「APK加固」,另外加固又稱「加殼」,殼的定義:
一段專門負責「保護軟件不被非法修改或反編譯的程序」,通常先於程序運行,拿到控制權,而後完成它們保護軟件的任務。
有「加殼」,天然也有「脫殼」,即去掉這層殼,拿到源碼,也稱爲「砸殼」。
關於加固技術的發展,看雪上有篇:《一張表格看懂:市面上最爲常見的 Android 安裝包(APK)五代加固技術發展歷程及優缺點比較》,不過圖不怎麼清晰,筆者從新排版了一下,有興趣的讀者能夠看看:
「混淆」能夠類比爲上面「萬惡的馬賽克」,阻礙人類進步的絆腳石。而混淆則是增長了反編譯的難度,同理,「反混淆」則對應「去除馬賽克」,試圖還原它原來的樣子。
加固雖然能在必定程度上「防止反編譯和二次打包」,但加固後的APP可能會帶來一些問題:
體積增大,啓動速度變慢,兼容問題等
網上「免費加固」方案有不少,脫殼教程也是爛大街,並且有些噁心的第三方加固還會給你加點料(360加固鎖屏廣告),而使用「企業級的加固」,則須要支付不菲的費用,因此不少APP直接選擇了「裸奔」。先來說解一下未加固的怎麼獲取源碼吧~
使用Jadx的注意事項:
使用jadx-gui可直接打開apk查看源碼,但若是APK比較大(classes.dex有好幾個),會直接卡死(好比微信),筆者的作法是命令行一個個dex文件去反編譯,最後再把反編譯的文件夾整合到同一個目錄下。
這樣的操做繁瑣且重複,最適合批處理了,遂寫了個反編譯的批處理腳本(取需):
"""
自動解壓apk,批量使用jadx進行反編譯,結果代碼彙總
"""
import os
import shutil
import zipfile
from datetime import datetime
apk_file_dict = {} # APK路徑字典
# 遍歷構造APK路徑字典(構造文件路徑列表,過濾apk,拼接)
def init_apk_dict(file_dir):
apk_path_list = list(filter(lambda fp: fp.endswith(".apk"),
list(map(lambda x: os.path.join(file_dir, x), os.listdir(file_dir)))))
index_list = [str(x) for x in range(1, len(apk_path_list) + 1)]
return dict(zip(index_list, apk_path_list))
# 移動文件夾
def move_dir(origin_dir, finally_dir):
shutil.move(origin_dir, finally_dir)
# 若是文件夾存在刪除重建
def deal_dir_existed(path):
if os.path.exists(path):
print("檢測到文件夾【%s】已存在,執行刪除..." % path)
shutil.rmtree(path)
os.makedirs(path)
# 判斷目錄是否存在,不存在則建立
def is_dir_existed(path, mkdir=True):
if mkdir:
if not os.path.exists(path):
os.makedirs(path)
else:
return os.path.exists(path)
# 獲取目錄下的全部文件路徑
def fetch_all_file(file_dir):
return list(map(lambda x: os.path.join(file_dir, x), os.listdir(file_dir)))
# 解壓文件到特定路徑中
def unzip_file(file_name, output_dir):
print("開始解壓文件...")
f = zipfile.ZipFile(file_name, 'r')
for file in f.namelist():
f.extract(file, os.path.join(os.getcwd(), output_dir))
print("文件解壓完畢...")
if __name__ == '__main__':
print("遍歷當前目錄下全部APK...")
apk_file_dict = init_apk_dict(os.getcwd())
print("遍歷完畢...\n\n============ 當前目錄下全部的APK ============\n")
for (k, v) in apk_file_dict.items():
print("%s.%s" % (k, v.split(os.sep)[-1]))
print("\n%s" % ("=" * 45))
choice_pos = input("%s" % "請輸入須要反編譯APK的數字編號:")
print("=" * 45, )
choice_apk = apk_file_dict.get(choice_pos)
apk_name = choice_apk.split(os.sep)[-1][:-4] # APK名字
# 建立相關文件夾
crack_dir = os.path.join(os.getcwd(), apk_name) # 工程根目錄
deal_dir_existed(crack_dir)
crack_apktool_dir = os.path.join(crack_dir, "apktool" + os.sep) # APKTool反編譯目錄
deal_dir_existed(crack_apktool_dir)
crack_jadx_dir = os.path.join(crack_dir, "jadx" + os.sep) # JADX反編譯目錄
deal_dir_existed(crack_jadx_dir)
crack_temp_dir = os.path.join(crack_dir, "temp" + os.sep) # 解壓後文件的臨時存儲路徑
deal_dir_existed(crack_temp_dir)
# 利用APKTool提取資源文件
begin = datetime.now() # 計時
print("APKTool提取資源文件...")
os.system("./apktool d %s -f -o %s" % (choice_apk, crack_apktool_dir))
# 複製一份AndroidManifest.xml、res、assets文件到外部
shutil.copy(os.path.join(crack_apktool_dir, "AndroidManifest.xml"), os.path.join(crack_dir, "AndroidManifest.xml"))
shutil.copytree(os.path.join(crack_apktool_dir, "res" + os.sep), os.path.join(crack_dir, "res" + os.sep))
shutil.copytree(os.path.join(crack_apktool_dir, "assets" + os.sep), os.path.join(crack_dir, "assets" + os.sep))
print("資源文件提取完畢")
# 利用jadx反編譯源碼
print("JADX反編譯提取源碼...")
choice_apk_zip = shutil.copy(choice_apk, choice_apk.replace(".apk", ".zip"))
unzip_file(choice_apk_zip, crack_temp_dir)
print("開始批量反編譯dex文件")
for dex in list(filter(lambda fp: fp.endswith(".dex"), fetch_all_file(crack_temp_dir))):
os.system(
"./jadx -d {0} {1}".format(os.path.join(crack_jadx_dir, dex.split(os.sep)[-1][:-4]), dex))
print("全部dex文件反編譯完畢")
# 將資源文件移入
shutil.move(os.path.join(crack_dir, "AndroidManifest.xml"), os.path.join(crack_jadx_dir, "AndroidManifest.xml"))
shutil.move(os.path.join(crack_dir, "res" + os.sep), os.path.join(crack_jadx_dir, "res" + os.sep))
shutil.move(os.path.join(crack_dir, "assets" + os.sep), os.path.join(crack_jadx_dir, "assets" + os.sep))
# 刪除臨時文件夾,壓縮文件
shutil.rmtree(crack_temp_dir)
os.unlink(choice_apk_zip)
end = datetime.now()
print("收尾操做~~~\n反編譯完成,總耗時:%s秒" % (end - begin).seconds)
複製代碼
執行前,你須要把apktool相關的東西,丟到jadx/build/jadx/bin目錄下,如圖所示:
接着終端鍵入:python3 auto_extract_apk.py,回車後輸入對應編號,回車開始編譯:
靜待片刻後:
Tips:這裏沒有把多個classes文件夾整合到一塊兒,是由於有些APP會出現合併衝突。
打開反編譯後的目錄,有以下兩個文件夾:
按照本身的須要用Android Studio打開其中一個就行了:
- apktool目錄:apktool反編譯後的內容,主要用於smail動態調試。
- jadx目錄:反編譯成Java的內容。
代碼是拿到了,可是打開代碼,「一堆的abcd」,跟到眼花,能夠試下「反混淆」,方案有兩類,一種是經過「代碼逆推」出名字,另外一種是經過「統計逆推」出名字。
第一種方案的工具備不少(Jeb2,simplify等),前者付費需破解,Java版本有限制,Mac配置有點麻煩,故筆者用的是後者:「Simplefy」,Github倉庫:github.com/CalebFenton…,使用方法也很簡單:
打開終端依次鍵入:
# 拉取倉庫代碼
git clone --recursive https://github.com/CalebFenton/simplify.git
# 來到目錄下
cd simplify
# 編譯
./gradlew fatjar
複製代碼
編譯後完,執行下述指令便可反混淆APK:
# 反混淆APK(須要反混淆的APK,反混淆後的APK名)
./gradlew build && cp xxx.apk yyy.apk
複製代碼
靜待反混淆完畢,接着反編譯批處理腳本走一波,打開MapFragment比對下:
相比混淆前,多了一些變量名,固然也不是徹底的,偶爾仍是有abcd,可是可讀性稍微提升了些,好比查找的時候不用在一個個adcd排除,可是,編譯挺耗時的,並且個人電腦風扇呼呼呼地響。
第二種是經過統計的方法,利用統計推斷出名字:DEGUARD:apk-deguard.com/,打開官網:
選擇須要反混淆的APK後,Upload上傳,接着等待處理完成,!!!別關頁面!!!
通常需等待1-10分鐘,處理完成後,點擊output.apk,把APK下載到本地,一樣執行批處理腳本反編譯一波,和simplefy反混淆後的代碼對比下:
大同小異,另外,反混淆並不能100%還原,並且還可能有些小錯誤,好比下面的代碼:
雖然說反編譯後的可讀性有所提升,但建議仍是搭配着混淆的源碼看。
終於來到不少同窗期待的脫殼環節,先說明下,筆者只是「工具黨」水平,不會Native層的,so文件調試!若是本節的工具,你脫不出來,或者脫出來有問題,筆者也是心有餘而力不足。看雪有不少幫人脫殼的大佬,能夠在上面發個帖子求助下~
一、判斷是哪一種加固
解壓apk後在assets目錄下看到so文件,好比360加固寶:libjagu.so和libjiagu_x86.so,百度搜下名字就知道是哪家的加固了,也能夠直接用後面講的「MT文件管理器2.0」直接查看。
二、FDex2脫殼(只適用於Android 7或如下版本,能夠脫市面上大多數免費加固,成功率較高,推薦)
- 有ROOT:安裝「XposedInstaller」和「FDex2」
- 沒ROOT:安裝「VirtualXposed」「FDex2」
好比:這裏有個「360免費版加固的APK」,直接用jadx反編譯後導入AS,可是反編譯後的classes:
只有這麼一丟丟點東西,把「待脫殼應用」安裝到手機上,接着用FDex2來脫殼
已Root玩家:XposedInstall啓用FDex2插件重啓後,按以下步驟脫:
adb root
adb pull /data/user/0/包名 電腦文件夾
複製代碼
# 按照文件從大到小排序!!!
jadx aaa.dex -d classes
jadx bbb.dex -d classes1
jadx ccc.dex -d classes2
複製代碼
行吧,脫殼成功,這裏其實還能夠還原APK的(二次打包),等下再講~
未root玩家,安裝打開VirtualXposed,添加應用:Fdex2和待脫殼應用
若是炮製,只是dex的路徑有些不同。
三、反射大師(和FDex相似,下載地址:www.lanzous.com/b04xxlujg)
注意,一樣只支持Android 7.0及如下,adb安裝後,xposed啓用插件,重啓手機,接着打開反射大師:
Step 1:選中待脫殼APP,彈出對話框選擇打開
Step 2:點擊中間的六芒星,彈出以下對話框,長按「寫出DEX」
Step 3:等待寫出完畢,能夠在/storage/emulated/0中找到導出的dex:
Step 4:pull到電腦上用jadx-gui打開看看:
行吧,脫殼成功,就是咱們想要的dex了,另外一個classes2.dex則是相關的~:
四、dumpDex脫殼(Github:github.com/WrBug/dumpD…)
官方倉庫的README.md中有一句:
能夠的話建議本身編譯,流程也很簡單:
# 一、拉取項目代碼到本地
git clone https://github.com/WrBug/dumpDex.git
# 二、AS中Open項目,等待編譯完成
# 三、刪掉build.gradle裏簽名相關的代碼
# 四、點擊頂部菜單欄Build -> Build APK,或者直接在終端./gradlew clean build
# 五、adb命令直接把編譯生成的apk安裝到手機上
# 六、接着來到以下左圖路徑,把對應的so,經過adb push到目錄下:
adb push lib/armeabi-v7a/libnativeDump.so /data/local/tmp
adb push lib/arm64-v8a/libnativeDump.so /data/local/tmp/libnativeDump64.so
# 修改權限
adb shell
su
chmod 777 /data/local/tmp/libnativeDump.so
chmod 777 /data/local/tmp/libnativeDump64.so
# 臨時關閉SELinux(重啓後會失效,可調用getenforce查詢)
setenfore 0
# 七、打開XposedInstaller看已經啓用DumpDex插件,是的話重啓手機
# 八、開機後,打開想脫殼的應用,不用理閃退,接着打開data/data/包名查看是否有Dump目錄
# 九、進入若是出現下圖所示的多個dex,說明脫殼成功,不然多是脫殼失敗
# (看是否有報錯信息),或者不支持(好比360加固免費版只支持新版,不支持舊版)。
複製代碼
另外,脫出來的dex不必定就可用,好比某個用了「騰訊御安全」的應用:
用jadx-gui打開這的dex,一堆這樣的錯誤:
出現這個的緣由是「指令集被抽取」,打開smail文件你就知道了:
方法指令都被nop(零)替換了,工具黨到這裏就能夠放棄了,要調試so文件。
因內容較多(超掘金2W字限制),剩下內容拆解到下一篇講解(動態調試技法,二次打包等,敬請期待~)
一樣,意思意思送「一本本身寫的Python爬蟲入門書」吧,評論區留言抽,包郵,下週五抽~
Tips:本節用到的東西,都有給出比較官方的下載連接!!!
你也能夠到公號「摳腚男孩」輸入000,回覆對應序號下載,謝謝~
參考文獻: