簡介
基本概念
APK結構
Dex結構
APK打包過程
APK加載過程
Android JNI機制
常用破解手段
靜态破解
反編譯
靜态分析
動态破解
二次打包
本地資料竊取
通信抓取
加強途徑
防靜态破解
防反編譯
防靜态分析
資源檔案保護
防二次打包保護
防動态破解
SO檔案保護
本地存儲資料保護
ART模式下的加強
常用第三方加強平台
騰訊雲-樂固
簡介
加強項目
DEX檔案加強
資源檔案保護
防二次打包保護
防調試器保護
記憶體防dump保護
進階記憶體保護
so檔案保護
使用客戶
收費情況
加強過程
加強APK反編譯測試
SO
DEX反編譯
安全鍵盤
接入效果
安全資料庫
相容性測試
加強平台開發
簡介
随着Android應用破解的愈發猖獗,Android應用安全越來越受到重視, Apk加強作為一種防止Apk被反編譯的一種強力手段,越來越受到apk開發者的青睐。
說道APK加強,需要和混淆有所差別,由于Java代碼在APK沒有做過混淆處理時,很容易通過反編譯得到源碼的類,變量,函數名,資料結構等資訊。Android應用的混淆是通過将Java代碼中的類,變量,函數名用a,b,c等無意義的字母代替。這樣逆向得到的代碼變得不可讀。從一定程度上保護源碼
基本概念
APK結構
apk檔案實際是一個壓縮包,使用解壓軟體即可解壓縮,解壓後的檔案一般如下表所示:
路徑 | 作用 |
assets目錄 | 存放一些配置檔案,原assets目錄 |
lib目錄 | armeabi子目錄下存放so庫 |
META-INF目錄 | 簽名資訊和證書 |
res目錄 | 資源檔案 |
AndroidManifest | 應用配置檔案 |
classes.dex (classes2.dex) | dex執行檔案(dalvik vm executes) |
resources.arsc | 記錄資源檔案和資源ID之間的映射關系 |
Dex結構
Dex檔案結構如下:
其中header是dex檔案的頭部,包含了dex檔案的檢查簽名,和資料結構偏移位址結構如下
typedef struct DexHeader {
u1 magic[8];
u4 checksum;
u1 signature[kSHA1DigestLen];
u4 fileSize;
u4 headerSize;
u4 endianTag;
u4 linkSize;
u4 linkOff;
u4 mapOff;
u4 stringIdsSize;
u4 stringIdsOff;
u4 typeIdsSize;
u4 typeIdsOff;
u4 protoIdsSize;
u4 protoIdsOff;
u4 fieldIdsSize;
u4 fieldIdsOff;
u4 methodIdsSize;
u4 methodIdsOff;
u4 classDefsSize;
u4 classDefsOff;
u4 dataSize;
u4 dataOff;
} DexHeader;
String table -Class Def table是偏移索引的區域。
Data區域是真正的資料儲存位置。
這裡面需要特殊說明的是Header的前幾個量:
- magic value是一個常量,用來識别dex檔案,其值為:"dex\n035\0"
- checksum 檔案校驗碼 ,使用 alder32 算法校驗檔案除去 maigc ,checksum 外餘下的所有檔案區域 ,用于檢查檔案錯誤 。
- signature , 使用 SHA-1 算法 hash 除去 magic ,checksum 和 signature 外餘下的所有檔案區域 ,用于唯一識别本檔案 。
- fileSize 檔案大小
之後說的dex加殼之後重新打包的時候就需要将這幾個值修改了。
APK打包過程
dex檔案是供dalvik虛拟機運作的可執行檔案,在編譯的時候将java源碼編譯成标準的java位元組碼檔案,然後通過DX工具轉為dex檔案,最後通過aapt(Android Asset Packaging Tool)工具将dex和其他資源檔案打包為apk檔案。
APK加載過程
Android系統zygote程序,通過fork建立一個Dalvik虛拟機執行個體,來執行apk,Dalvik虛拟機運作流程如下:
Android JNI機制
和其他Java虛拟機一樣,Android也可以通過JNI機制調用native層代碼,用來達到提高效率,複用代碼,提高安全性等目的。
JNI代碼的實作有兩種方式:
- 傳統的javah工具,生成一組帶簽名的函數,然後再native層實作這些函數。
- 在native的JNI_OnLoad方法中通過RegisterNatives動态注冊native方法。
通過第二種方法,可以實作運作時動态調整本地函數和Java函數直接的映射關系。
常用破解手段
靜态破解
反編譯
通過反編譯工具将dex檔案反編譯為smali代碼和java源碼,然後通過閱讀源碼擷取原應用的實作。
常用工具:
- apktool GOOGLE提供的APK編譯工具,能夠反編譯及回編譯apk
- Dex2jar 将dex檔案反編譯為java源碼,生成的jar包通過jd-gui檢視
- Smali2Java 将smali代碼反編譯成java代碼的工具
- smali/backsmali 也是實作dex和smali直接的互相轉化工具
- AXMLPrinter2 批量反編譯xml檔案
靜态分析
靜态分析就是直接對反編譯的到的smali代碼和java源碼進行分析。
當然so庫也可以進行反編譯和彙編分析,不過其難度要比java層的分析要困難的多
動态破解
通過挂在debug調試器到運作的apk,來擷取運作時的資料,
使用的工具比如:zjdroid,IDA,smalidea,Andbug
通過使用IDA,甚至可以在apk加載到記憶體後,從dump出記憶體中的dex檔案
畢竟不管上層怎麼加強加密,最終加載到記憶體的dex肯定不是加強的,通過IDA來動态調試libdvm.so中的dvmDexFileOpenPartial函數就擷取記憶體中的dex内容。而這個dex檔案就是完全沒有加殼的源程式dex。
二次打包
修改APK中的資源檔案,修改smali中的代碼,插入自己的惡意代碼或者導流代碼,重新打包後釋出。
本地資料竊取
竊取APK中的SharePreference和緩存檔案,資料庫資料。
通信抓取
通過代理抓包等方式,抓取apk運作過程中的通信資料
加強途徑
防靜态破解
防反編譯
防靜态反編譯一般是根據反編譯工具的流程找起漏洞
常用方式其中一種就是在使用花指令,比如利用jd-gui反編譯時的原理,添加一些特殊代碼,讓反編譯工具出錯。
在類中加入如下字段:
private static final char[] wJ = "0123456789abcdef".toCharArray();
public static String imsi = "204046330839890";
public static String p = "0";
public static String keyword = "電話";public static String tranlateKeyword = "%E7%94%B5%E8%AF%9D";
就會讓jd-gui反編譯出錯(最新版jd-gui已修複該問題)
另外一種方式就是對DEX加殼保護,讓反編譯得到的源碼隻是一堆無用的代碼,真實的代碼是加密的。
加殼原理:
加強過程中涉及3個對象:
- 需要加密的Apk(源Apk)
- 殼程式Apk(負責解密Apk)
- 加密工具(将源Apk進行加密和殼Dex合并成新的Dex)--加密工具可以用任意語言實作
流程:
- 通過加密工具,對源apk的dex進行加密,得到加密後的無意義内容
- 将加密後的内容append到殼程式apk的後面,并修改殼程式apk的頭檔案内容。得到最終apk。
- 殼程式中原有的dex内容是用來對源apk的加密内容進行解密。在運作時,通過DexClassLoader動态加載解密後的源apk,運作源apk内容。

通過上面的流程得到的最終apk由于實際dex已經被加密,反編譯的得到的隻是殼apk 的内容。
不過反編譯之後,他人可以得到殼apk中是如何對源apk進行解密的代碼。是以實際上隻是加大了反編譯難度而已。為了進一步增加反編譯難度。一般的殼程式中都會把解密部分放在native代碼中。
防靜态分析
代碼混淆--代碼混淆是Android常用的一種代碼加密方式,混淆過後的代碼中類名方法名等都會變成abc等無意義的字元。讓閱讀者沒法直接從名稱中直接分析出源碼目的。增加靜态分析難度。
資源檔案保護
把資源放在assets目錄下,必要情況對圖檔等資源的檔案名和内容進行加密。
防二次打包保護
保護應用在被非法二次打包後不能正常運作:
- Java 代碼中加入簽名校驗
- NDK 中加入簽名校驗
- 利用二次打包工具本身的缺陷阻止打包(manifest 欺騙,圖檔修改字尾等等)
- 通過Server認證校驗。
但是實際上不論java層面或者ndk層面的簽名校驗,都可以通過修改smali或者native源碼來繞過。
防動态破解
原理:當檢測到apk被調試器連接配接時,終止apk的運作。
方式有如下幾種:
1、多程序ptrace反調試---建立子程序。在子程序中擷取父程序pid,并通過ptrace() 方法附着父程序。如果附着失敗,說明程序已經被調試
2、擷取/proc/$pid/status中TracerPid行,能夠顯示調試程式的pid, 可以寫一個方法檢查下這個值, 如果!=0就退出程式。參考Android Native反調試,用JNI實作APK的反調試。
3、檢查代碼執行的間隔時間。
4、檢測手機上的一些硬體資訊,判斷是否在模拟器中,如果是模拟器則無法運作。
SO檔案保護
對so檔案進行優化壓縮、源碼加密隐藏、防止調試器逆向分析
某種so加密結構:
應用加載過程:
本地存儲資料保護
本地存儲資料的保護一般是對本地資料内容進行加密,讀取時進行解密,資料庫和SP存儲都有一些開源方案。
資料庫保護:SQLCipher
SharePreference:Secure-Preferences
檔案:對稱加密,或者底層對檔案操作加一層,對所有檔案操作都進行統一加解密
ART模式下的加強
上面提到過的apk加強方案實際是針對dex的,但是在Android4.4之後,Android引入了新的運作模式ART,ART模式相比Dalvik,引入了AOT(ahead of time)編譯,改進了垃圾回收機制,對取樣分析的支援等。在安裝時,ART模式使用dex2oat将apk的dex檔案重新編譯成oat檔案。oat檔案是一種可執行檔案(ELF)。這樣ART模式就可以在應用安裝時,将apk轉為機器碼的可執行檔案儲存在本地,在每次啟動時加快啟動速度。
Android5.0之後,ART模式就是預設的運作模式。其流程
這就導緻了原有的加強方案隻能準對Dalvik虛拟機的運作模式,無法再ART模式下使用。
Art模式下的加強,需要用到Hook技術,所謂hook就是通過一些手段改變一個函數的執行邏輯,比如在函數調用前更改一下參數或者在調用後修改傳回值,甚至直接傳回,不執行原函數。
在ART中類方法的執行要比在Dalvik中要複雜得多,Dalvik如果除去JIT部分,可以了解為是一個解析執行的虛拟機,而ART則同時包含本地指令執行和解析執行兩種模式,同時所生成的oat檔案也包含兩種類型,分别是portable和quick。portable和quick的主要差別是對于方法的加載機制不相同,quick大量使用了Lazy Load機制,是以應用的啟動速度更快,但加載流程更複雜。其中quick是作為預設選項,這裡涉及的技術分析都是基于quick類型的。
由于ART存在本地指令執行和解析執行兩種模式,是以類方法之間并不是能直接跳轉的,而是通過一些預先定義的bridge函數進行狀态和上下文的切換,如下圖:
Hook方法涉及到ART底層的C代碼和彙編代碼,這裡不列舉分析了,其基本原理是:
修改ArtMethod中的entrypoint,通過中轉處理,讓ART中間執行一段我們的自定義代碼,然後再繼續執行原代碼。而自定義代碼部分可以做一些加密解密等操作。
常用第三方加強平台
市面上的第三方加強平台很多,加強用到的手段基本上已經涵蓋了上文提到的所有手段,并且還有一些其他保密的加強方式,畢竟對于破解者來說,如果知道了其加強或者加密的方法,就更加容易找到相應的破解方法。
市面上比較常見的加強平台:
- 愛加密
- 梆梆加強
- 360加強保
- 騰訊雲應用樂固
- 阿裡聚安全
時間關系,暫時我并沒有針對性的使用各個加強平台進行一個對比測試,下面放一個其他人做的幾個平台的對比測試:
加強前 | 360 | 阿裡 | 騰訊 | 梆梆 | |
加強時間 | 1'20" | 35" | 1'1" | 2'14" | |
apk體積 | 16M | 16M | 15M | 17M | 16.5M |
啟動速度 | 2.52s | 1.77s | 5.02 | 3.48 | 5.57 |
相容性 | 100% | 99% | 100% | 100% | 99% |
騰訊雲-樂固
簡介
應用樂固(Cloud Reinforcement)是移動應用一站式安全服務平台,涵蓋應用加強、安全評測、管道監控、安全SDK、适配分析、品質跟蹤等服務。可防止應用被盜版破解、及時發現應用漏洞、監控應用正盜版分發等,有效捍衛應用開發者利益。
加強項目
DEX檔案加強
對DEX檔案進行專業加殼加花保護,防止利用調試器對應用進行逆向破解。
資源檔案保護
資源檔案被非法篡改、删除後,程式将無法正常運作。
防二次打包保護
應用内任意檔案被修改、替換後,都将無法正常運作。
防調試器保護
防止使用各類靜、動态調試工具影響應用運作。
記憶體防dump保護
防止通過動态調試、dump形式擷取應用部分代碼。
進階記憶體保護
可對記憶體資料進行高強度防護,有效防止記憶體調試、記憶體dump等方式竊取源碼。
so檔案保護
可對指定so檔案進行安全防護,防止被逆向工具破解,暴露核心敏感邏輯。
使用客戶
大衆點評,駕考寶典,餓了麼,四維圖新
收費情況
目前樂固線上提供的基礎服務免費,定制化需求需要和客服溝通
加強過程
兩種方式:
1,線上上傳應用加強
上傳目标apk進行加強,加強完成後,隻需要将加強後的apk,使用樂固提供的簽名工具,使用原應用的簽名重新進行一次簽名即可。同時支援多管道打包功能。
2,下載下傳使用PC加強工具
使用加強PC工具能夠一站式的加強應用,本地首先需要配置好簽名資訊。然後選擇應用上傳進行加強。
檔案上傳成功并加強完成後,會自己根據配置中的簽名,進行簽名并輸出一個加強後的包
加強APK反編譯測試
SO
對網遊開發平台中下載下傳的網遊sdk demo進行加強測試,測試前後的apk進行解壓後對比可以發現:
加強後的lib庫中,增加了幾個so檔案
其中libbugly.so應該騰訊的bugly異常上報庫,用來監控應用的crash,anr等錯誤。
liblegudb.so和libshella.so則
其中liblegudb.so看起來是資料庫加密的native庫
生成的資料庫如下圖所示:(注意,該資料庫和應用本身的資料庫并不相同,原應用資料庫内容依舊是明文的)
進入某一個具體的資料表,看到的資料也是加密後的。
libshella.so則是解dex殼的native庫,使用ida反編譯時,ida報錯無法反編譯。
DEX反編譯
加強後的apk的dex檔案相比原dex檔案大小略有增加,應該是增加了殼dex導緻的。
對加強後的dex進行反編譯後得到的隻是殼程式的smali代碼,而沒有原應用dex檔案的内容,這也符合上文中的加殼保護的現象。
大部分的代碼進行了混淆操作,對以上的smali檔案内容進行初步分析可知:
TxAppEntry:繼承了Application類,其實作對樂固架構的初始化,我們從Manifest也可以看到,application已經改成了TxAppEntry。其attachBaseContext中做了如下事情:
protected void attachBaseContext(Context paramContext)
{
super.attachBaseContext(paramContext);
Log.d("SecShell", "attach start");
if (!b(this)) {//将libshella,libshellb,libshellc三個so庫的路徑初始化給靜态全局變量
return;
}
LeguDB.init(this, null, null);//加載liblegudb.so
d(paramContext);//加載nfix和ufix SO庫,修複本地資源檔案和Unity資源檔案
this.h = new Handler(getMainLooper());
paramContext = new CrashReport.UserStrategy(this);
paramContext.setAppVersion("2.10.1");
CrashReport.setSdkExtraData(this, "900015015", "2.10.1");
CrashReport.initCrashReport(this, "900015015", false, paramContext);
new Thread(new b(this)).start();
a(this);
paramContext = new IntentFilter(TxReceiver.TX_RECIEVER);
registerReceiver(new TxReceiver(), paramContext);
Log.d("SecShell", "attach end");
}
LeguModuleInitializer:通用的so加載器
LeguDB:資料庫so加載初始化,調用LeguModuleInitializer進行本地庫加載
正常的一個反編譯流程,應該是獲知shell程式是如何從加殼後的dex檔案中,擷取原始dex的解密流程。然後寫一個小程式用同樣的方法,解析出原dex。
不同于上文“防反編譯”章節中對dex整體進行簡單整體加密,目前很多的加密手段是對原dex進行解析,并針對每個方法都進行不同内容的插入,進而增加解殼部分的複雜度。
同時也會在解殼代碼的加載過程,增加其複雜度,從靜态分析中很難看到其解殼代碼。
由于并不擅長反編譯相關的手段和知識,對樂固加強後的應用和原應用的對比也僅限于此。
安全鍵盤
一般來說,通過Android系統自帶的鍵盤可以通過以下幾種方式竊取使用者資訊:
- 鍵盤記錄--通過讀取dev/input/event1(需要root) 中的資訊擷取到手機螢幕事件,進而擷取輸入的點選,進而得到輸入内容。
- 螢幕錄像--螢幕錄像同樣需要root權限,然後根據錄像中的按鈕點選動作來擷取輸入内容
- 螢幕截屏--類似螢幕錄像
- 第三方輸入法漏洞--利用第三方輸入法的漏洞截取輸入
樂固本身提供了一個安全鍵盤的sdk,該sdk使用如下方式來保證輸入安全:
- 使用随機分布的數字鍵盤,對底層擷取按鍵事件位置的方式進行防護,即使擷取到點選位置,也無法擷取按鍵資訊
- 使用自定義的鍵盤和EditText輸入空間,避免調用系統的鍵盤,這樣可以避免系統鍵盤或者第三方鍵盤有漏洞的影響
- 防螢幕截屏--這個就是在使用者點選輸入的時候,不回顯輸入的資料
- 密碼全程動态加密-對每一次使用者輸入和删除操作都會對密碼動态加密,記憶體中不會出現明文密碼
- 高強度加密算法
- sdk的自身防護---自研保護+so保護機制,防止鍵盤本身被攻擊
接入效果
通過接入該sdk,在需要輸入加密内容的部分使用SafeEditText代替EditText,在輸入内容時,SafeEditText并不會顯示你輸入的内容(連閃一下也沒有),規避截屏擷取密碼的可能。下方的鍵盤是自定義的密碼鍵盤。
不過看起來這個鍵盤有個小bug:調起鍵盤界面時,下面的導航欄變成透明,并且和鍵盤下面的部分按鍵沖突。
安全資料庫
目前樂固還沒有上線安全資料庫的sdk,并且樂固加強過程本身也是不會對原應用的資料庫進行什麼加密操作。是以這裡不存在替換加強方案後,資料庫更新問題。
單就資料庫來說:
由于sqlite資料庫是明文存儲,root使用者很容易拿到資料庫的db檔案,并檢視其中儲存的資料内容:
下圖是檢視樂固加強後的應用的資料庫的資料:
其資料内容和原資料沒有差別,并且都是明文的。
是以本地存儲安全--包括資料庫安全,sharepreerence安全和本地緩存檔案安全需要應用本身去實作。這塊可以使用已有的解決方案如:
資料庫保護:SQLCipher---一方面對資料通過使用者提供的key進行加密,另一方面其加密後的資料庫無法打開無法看到其中存儲的已加密資料
SharePreference:Secure-Preferences---其對儲存的SP資料對,通過AES進行加密,加密的密鑰是一個生成的僞随機數。加密後的資料
當然也可以自己選擇加密算法,在寫入本地存儲前進行加密,讀取時進行解密。
相容性測試
從樂固加強更新到 | 覆寫安裝 | 啟動 | 資料庫讀寫 | SP讀寫 |
原應用 | 正常 | 正常 | 正常 | 正常 |
360加強 | 正常 | 正常 | 正常 | 正常 |
某開源加強方案 | 正常 | 正常 | 正常 | 正常 |
不過覆寫安裝後,原有的樂固加強本身建立的bugly的資料依舊存在,因為更新安裝本身并不會删除原因的data目錄下檔案,隻會新增或者更新。
同樣的,從樂固更新到360加強保。則應用的data目錄下,就會存在兩個平台産生的檔案(360并沒有新的資料庫,但是有新的sp檔案)
覆寫安裝後原有的lib目錄會被删除并覆寫,不會遺留原有的so檔案,但是data下本應用的根目錄還殘留有樂固加強使用的檔案(mix.dex,mix.so,tx_shell目錄)。不過由于更新後的應用使用其他加強平台或者自由加強,這些檔案隻是會增加包體積而已,并不會從代碼層面産生影響。
上表中的開源加強方案,使用ApkToolPlus內建的
。比第三方加強方式要簡單得多。僅為測試加強相容性需要。
綜上可以看出:
各種加強方式基本是針對apk本身做的反編譯,反調試等做的加強。從應用啟動過程來看,覆寫安裝後,啟動過程就由新的應用所控制,原加強手段中在Application中所作的解密,hook等操作都已無效。隻要新覆寫安裝的apk的啟動過程中需要的檔案不存在異常即可。
加強本身并沒有幹涉應用本身内部使用的緩存檔案,資料庫,sp等(實際上如果不植入加強平台的sdk的話,加強平台是沒法幹涉這些代碼層的操作的)。
加強平台開發
安全領域一句比較有名的觀點是---“未知攻焉知防”。
對Android加強來說,為了其加強效果,開發者首先需要對常用的破解工具和破解手段比較熟悉。這也是為何大多數的安全專家本身就具有很高的軟體破解能力的原因。
在對各種破解手段有一定的實踐基礎上,再針對性的對每一種破解手段進行防護,就是一個加強平台需要完成的任務。下表總結了針對市面上正常的破解方式的幾種加強方式及其開發的難點。
項目 | 方式 | 難點 |
dex檔案加強 | 加殼 | ART模式下的相容,各Android版本的适配,解殼算法的隐藏 |
加花指令 | 花指令的擷取收集 | |
資源檔案保護 | 資源檔案加密 | |
資源檔案篡改識别 | ||
反調試 | 防止調試器挂載調試 | 針對各種調試器的反調試實踐 |
防止記憶體dump | 調試器dump時如何觸發應用退出 | |
防二次打包 | java層簽名校驗 | |
native層簽名校驗 | ||
利用二次打包工具缺陷 | ||
伺服器校驗 | ||
so保護 | 針對某so檔案進行防護 | 加密後so檔案的加載時機 |
本地資料保護 | 資料庫保護 | 資料庫整體的防加載 |
緩存檔案保護 | ||
SP内容保護 |