因一紙設計稿,我把競品APP扒得褲衩不剩(中)

嚴正聲明css

  • 一、相關破解技術僅限於技術研究使用,不得用於非法目的,不然後果自負。
  • 二、筆者僅出於對技術的好奇,無惡意破壞APP,尊重原開發者的勞動成果,未用於商業用途。

0x一、無形之刃,最爲致命 => 碎碎念

上一篇文章《因一紙設計稿,我把競品APP扒得褲衩不剩(上)》是一篇比較簡單的:html

  • jsw => 技師文,呸,
  • jsw => 記述文,呸呸,
  • jsw => 技術文,呸呸呸,這什麼垃圾輸入法!

技術文,可是這評論區的風氣,貌似有點不對???java

冤枉啊,小弟真沒去過這種地方,也沒體驗過這種「服務」,只是道聽途說,可能:python

我描述得「繪聲繪色」,加之各位看官「浮想聯翩」,纔會以爲「煞有介事」。android

em…那個,能夠扶下我起來麼,那個…跪久了腿有點麻git

順帶恭喜下:FPX 3-0 G2喜提S9總冠軍FPX牛逼!!!破音!!!github

亞索的快樂你不懂~web

哈哈,回到本文shell

  • 發現 => 不少童鞋對APP逆向很感興趣;
  • 可是 => 網上關於APP逆向文章的比較零散;
  • 不知 => 如何入手,畢竟逆向的水可深了;
  • 筆者 => 也只是個小白玩家,興趣使然玩玩而已;
  • 分享 => 目前會的一些「Android APP基礎逆向姿式」;
  • 還望 => 各位真丶逆向大佬輕噴;
  • 若有 => 有更好的工具或者方法安利,歡迎評論區指出,謝謝~

順帶分享幾個筆者常逛的逆向論壇安全

貼心提醒

此文內容較多,可能會有些枯燥,建議先點贊收藏,茶餘飯後再慢慢品嚐~


0x二、提莫隊長,正在待命 => 硬件準備


在開始折騰Android APP逆向前,你須要:

一、一臺「具備完整Root權限」的Android手機,注意是「完整Root」權限!!!

好比「魅族手機」在設置->安全->Root權限,中能夠開啓Root權限,可是倒是「閹割的Root權限」,安裝SuperSu重啓後就一直卡氣球。

二、怎麼Root根據本身的手機機型百度和逛各類搞機論壇吧(不要問我!)通常的常見的流程:

解BL鎖(BootLoader) -> 刷第三方Recovery(如TWRP) -> 卡刷MagliskSuperSU(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藍疊」等。


0x三、一點寒芒先到,隨後槍出如龍 => 概念與名詞


在開始折騰APP逆向前,先來了解一些概念與名詞~


① APK文件裏都有什麼?


獲取APK的渠道:酷安應用寶豌豆莢等應用市場下載,有些還提供「應用歷史版本」下載。
APK本質上是一個「壓縮包」,把「.apk後綴」改成「.zip後綴後解壓,能夠看到以下目錄結構 (可能還有其餘文件):

簡單介紹下


② 編譯APK和反編譯APK

所謂的「編譯」,就是把「源碼、資源文件等」按照必定的「規則」打包成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)五代加固技術發展歷程及優缺點比較》,不過圖不怎麼清晰,筆者從新排版了一下,有興趣的讀者能夠看看:

④ 混淆與反混淆

混淆」能夠類比爲上面「萬惡的馬賽克」,阻礙人類進步的絆腳石。而混淆則是增長了反編譯的難度,同理,「反混淆」則對應「去除馬賽克」,試圖還原它原來的樣子。


0x四、發動機已啓動,隨時能夠出發 => 得到APP源碼


加固雖然能在必定程度上「防止反編譯和二次打包」,但加固後的APP可能會帶來一些問題:

體積增大,啓動速度變慢,兼容問題等

網上「免費加固」方案有不少,脫殼教程也是爛大街,並且有些噁心的第三方加固還會給你加點料(360加固鎖屏廣告),而使用「企業級的加固」,則須要支付不菲的費用,因此不少APP直接選擇了「裸奔」。先來說解一下未加固的怎麼獲取源碼吧~


① 未加固(筆者使用的工具:apktool + jadx)

  • 使用apktool:獲取「素材資源,AndroidManifest.xml以及smail代碼
  • 使用jadx:把「classes.dex」轉換爲「.java」代碼

使用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的內容。

② 反混淆(simplefy、Deguard)


代碼是拿到了,可是打開代碼,「一堆的abcd」,跟到眼花,能夠試下「反混淆」,方案有兩類,一種是經過「代碼逆推」出名字,另外一種是經過「統計逆推」出名字。

第一種方案的工具備不少(Jeb2simplify等),前者付費需破解,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排除,可是,編譯挺耗時的,並且個人電腦風扇呼呼呼地響。

第二種是經過統計的方法,利用統計推斷出名字:DEGUARDapk-deguard.com/,打開官網:

選擇須要反混淆的APK後,Upload上傳,接着等待處理完成,!!!別關頁面!!!

通常需等待1-10分鐘,處理完成後,點擊output.apk,把APK下載到本地,一樣執行批處理腳本反編譯一波,和simplefy反混淆後的代碼對比下:

大同小異,另外,反混淆並不能100%還原,並且還可能有些小錯誤,好比下面的代碼:

雖然說反編譯後的可讀性有所提升,但建議仍是搭配着混淆的源碼看。


③ 脫殼(FDex2,反射助手,dumpDex)


終於來到不少同窗期待的脫殼環節,先說明下,筆者只是「工具黨」水平,不會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插件重啓後,按以下步驟脫:

  • Step 1:FDex2選中待脫殼應用:
  • Step 2:打開待脫殼應用,接着來到上圖的dex輸出目錄:
  • Step 3:把整個目錄拉到電腦上,這裏直接用adb命令拉取:
adb root
adb pull /data/user/0/包名 電腦文件夾
複製代碼
  • Step 4:「剔除加固相關的dex」,用jadx-gui依次打開,看到下面這種,直接把dex刪掉
  • Step 5:使用jadx命令反編譯dex,順帶更名,命名規則:按照文件大小降序,示例以下:
# 按照文件從大到小排序!!!
jadx aaa.dex -d classes
jadx bbb.dex -d classes1
jadx ccc.dex -d classes2
複製代碼
  • Step 6:刪掉沒脫殼前反編譯項目裏的classes,把這幾個複製到其中:

行吧,脫殼成功,這裏其實還能夠還原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脫殼Githubgithub.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文件。


0x?、To be continue


因內容較多(超掘金2W字限制),剩下內容拆解到下一篇講解(動態調試技法,二次打包等,敬請期待~)
一樣,意思意思送「一本本身寫的Python爬蟲入門書」吧,評論區留言抽,包郵,下週五抽~


Tips:本節用到的東西,都有給出比較官方的下載連接!!!
你也能夠到公號「摳腚男孩」輸入000,回覆對應序號下載,謝謝~


參考文獻

相關文章
相關標籤/搜索