反編譯
反編譯代碼
-
dex2jar 這個工具用於將dex文件轉換成jar文件
-
jd-gui 這個工具用於將jar文件轉換成java代碼 ,使用jd-gui工具打開classes-dex2jar.jar這個文件就能查看Java代碼
反編譯資源
-
apktool 這個工具用於最大幅度地還原APK文件中的9-patch圖片、佈局、字符串等等一系列的資源。
沒有反編譯資源之前,AndroidManifest.xml和activity_main.xml這樣的資源文件都是非明文的,無法閱讀。
重新打包
smali文件夾的目錄結構和我們源碼中src的目錄結構是幾乎一樣的,主要的區別就是所有的java文件都變成了smali文件。smali文件其實也是真正的源代碼,只不過它的語法和java完全不同,它有點類似於彙編的語法,是Android虛擬機所使用的寄存器語言。
使用smali語法,修改代碼,就能重新編譯自己的apk。但是apk還不能安全,因爲還沒簽名。
因爲無法獲得原來正版的簽名,可以使用Android Studio生成自己的簽名,進行打包,生成自己的apk。
參考:
-
https://blog.csdn.net/guolin_blog/article/details/49738023
混淆
混淆的好處:
-
令 APK 難以被逆向工程,增加反編譯的成本。
-
打包時移除無用資源,減少 APK 體積。
啓動混淆
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
-
minifyEnabled 設置爲 true 來開啓混淆
-
設置 shrinkResources 爲 true 來開啓資源的壓縮。
-
一般在打 release 包時才啓用混淆,因爲混淆會增加額外的編譯時間,所以不建議在 debug 模式下啓用。
-
需要注意的是:只有在啓用混淆的前提下開啓資源壓縮纔會有效!
-
以上代碼中的 proguard-android.txt 表示 Android 系統爲我們提供的默認混淆規則文件,而 proguard-rules.pro 則是我們想要自定義的混淆規則,
不能被混淆的元素
枚舉
枚舉類內部存在 values 方法,混淆後該方法會被重新命名,並拋出 NoSuchMethodException。Android 系統默認的混淆規則中已經添加了對於枚舉類的處理,我們無需再去做額外工作。
被反射的元素
原因在於:代碼混淆過程中,被反射使用的元素會被重命名,然而反射依舊是按照先前的名稱去尋找元素,所以會經常發生 NoSuchMethodException 和 NoSuchFiledException 問題。
實體類
實體類即我們常說的"數據類",當然經常伴隨着序列化與反序列化操作。很多人也應該都想到了,混淆是將原本有特定含義的"元素"轉變爲無意義的名稱,所以,經過混淆的"洗禮"之後,序列化之後的 value 對應的 key 已然變爲沒有意義的字段,這肯定是我們不希望的。
反序列化的過程創建對象從根本上來說還是藉助於反射,混淆之後 key 會被改變,所以也會違揹我們預期的效果。
四大組件
Android 中的四大組件同樣不應該被混淆。原因在於:
-
四大組件使用前都需要在 AndroidManifest.xml 文件中進行註冊聲明,然而混淆處理之後,四大組件的類名就會被篡改,實際使用的類與 manifest 中註冊的類並不匹配,故而出錯。
-
其他應用程序訪問組件時可能會用到類的包名加類名,如果經過混淆,可能會無法找到對應組件或者產生異常。
JNI 調用的Java 方法
當 JNI 調用的 Java 方法被混淆後,方法名會變成無意義的名稱,這就與 C++ 中原本的 Java 方法名不匹配,因而會無法找到所調用的方法。
其他不應該被混淆的
-
自定義控件不需要被混淆
-
JavaScript 調用 Java 的方法不應混淆
-
Java 的 native 方法不應該被混淆
-
項目中引用的第三方庫也不建議混淆
參考
-
郭霖:https://blog.csdn.net/guolin_blog/article/details/50451259
-
https://juejin.im/post/5d1717996fb9a07eeb13bc95#heading-0
WebView
問題:WebView明文存儲密碼帶來的安全漏洞
WebView組件默認開啓了密碼保存功能,會提示用戶是否保存密碼,當用戶選擇保存在WebView中輸入的用戶名和密碼,則會被明文保存到應用數據目錄的databases/webview.db中。攻擊者可能通過root的方式訪問該應用的WebView數據庫,從而竊取本地明文存儲的用戶名和密碼。
解決方案:
開發者調用 WebView.getSettings().setSavePassword(false),顯示調用API設置爲false,讓WebView不存儲密碼
四大組件
問題:動態註冊Receiver風險
使用BroadcastReceiver組件需要動態註冊或者靜態註冊,如果動態註冊廣播,即在代碼中使用registerReceiver()方法註冊BroadcastReceiver,只有當registerReceiver()的代碼執行到了才進行註冊,取消時則調用unregisterReceiver()方法。但registerReceiver()方法註冊的BroadcastReceiver是全局的並且默認可導出的,如果沒有限制訪問權限,可以被任意外部APP訪問,向其傳遞Intent來執行特定的功能。因此,動態註冊的BroadcastReceive可能會導致拒絕服務攻擊、APP數據泄漏或是越權調用等安全風險
解決方案:
1:在 AndroidManifest.xml 文件中使用靜態註冊 BroadcastReceiver,同時設置 exported="false",不被外部應用調用。
2:必須動態註冊 BroadcastReceiver時,使用registerReceiver(BroadcastReceiver,IntentFilter,broadcastPermission,android.os.Handle)函數註冊。
3:Android8.0新特性想要支持靜態廣播、需要添加intent.setComponent(new ComponentName()),詳情可以自行查閱
問題:公共組件配置風險
Activity、Service、Provider、Receiver四大組件若配置爲android:exported =”true”,將可以被外部應用調用,這樣存在安全隱患的風險。
解決方案:
在應用的AndroidManifest.xml文件中,設置組件的android:exported 屬性爲false或者通過設置自定義權限來限制對這些組件的訪問。值得一提的是,若部分功能使用前提是配置必須使用exported爲true,這種情況開發者應該根據實際情況來進行集成
問題:數據越權備份風險
Android 2.1以上的系統可以爲APP提供應用程序數據的備份和恢復功能,該功能由AndroidMainfest.xml文件中的allowBackup 屬性值控制,其默認值爲true。當該屬性沒有顯式設置爲false時,攻擊者可通過adb backup和adb restore對APP的應用數據進行備份和恢復,從而可能獲取明文存儲的用戶敏感信息,如用戶的密碼、證件號、手機號、交易密碼、身份令牌、服務器通信記錄等。利用此類信息攻擊者可僞造用戶身份,盜取用戶賬戶資產,或者直接對服務器發起攻擊。
解決方案:
將AndroidMainfest.xml文件中的allowBackup屬性值設置爲false來關閉應用程序的備份和恢復功能;也可以使用專業安全加固方案的本地數據保護功能,避免本地數據泄露。
數據存儲安全:加密和JNI寫入Native層
1、祕鑰及敏感信息
此類配置應當妥善存放,不要在類中硬編碼敏感信息,可以使用JNI將敏感信息寫到Native層。
2、SharePreferences
首先不應當使用SharePreferences來存放敏感信息,sharedpreferces存儲的xml文件數據可能被反編譯拿到。存儲一些配置信息時也要配置好訪問權限,如私有的訪問權限 MODE_PRIVATE(Activity.MODE_PRIVATE,//默認操作模式,代表該文件是私有數據,只能被應用本身訪問,在該模式下,寫入的內容會覆蓋原文件的內容),避免配置信息被篡改。
3、SQLite數據庫文件的安全性
– 描述:敏感信息是否明文存儲
– 檢測:檢測數據庫裏面的重要信息,比如賬號密碼之類的是否明文存儲
– 建議:重要信息進行加密存儲
日誌數據泄露:Log控制,正式環境不打印
問題:日誌數據泄露風險
調試信息函數可能輸出重要的調試信息,常見的就是Log日誌類其中包含的信息可能會導致用戶信息泄露,泄露核心代碼邏輯等,爲發起攻擊提供便利,例如:Activity的組件名;通信交互的日誌;跟蹤的變量值等
解決方案:
應用內使用統一的Log控制基類,可以靈活的控制Log的輸出打印。(測試環境允許打印日誌、正式環境不打印);或者使用第三方的日誌框架
參考:
-
https://www.jianshu.com/p/79b30238b994
-
https://zhuanlan.zhihu.com/p/35100057
網絡安全:HTTPS、使用簽名和數據加密加密
1.1 無處不在的安全隱患
因爲http協議是明文傳輸的,可以採取MD5值傳輸和存儲,近幾年MD5破解能力提高,所以現在通常生成MD5值時都需要加鹽,例如MD5(name+pwd)作爲密碼存儲,同樣的密碼生成的值是不一樣的,在一定程度上提高了安全性。
攻擊不一定需要用戶名密碼,或許可以直接打token和uid的主意。,一旦用戶登錄後,服務器就通過token來標識身份。如果這個token被黑客劫持了,他就可以冒充你的身份進行攻擊,如果token機制設計不合理,攻擊者甚至可以直接暴力去撞token。大部分網站的機制也類似,服務器通過一個sessionId標識用戶,攻擊者一旦拿到這個sessionId,就可以去冒充一個合法用戶。
1.2 使用https是否就萬事大吉
-
https分單向認證和雙向認證,大部分的應用場景是c/s模式,這時通常都是採用的單向認證的方式,也就是說可以保證客戶端拿到的數據是後臺發送的,攻擊難度很大,除非你能忽悠別人安裝並信任你的證書
-
但是,很多人蹭wifi時或者在網上下載一些資源,系統提示要信任什麼東西,看都不看就點確定。
-
使用抓包工具時,給目標設備安裝並信任裝包工具的自簽名證書,這時候就可以分析https請求
-
杜絕 HTTPS 抓包的原理很簡單,其實就是攔截非法的證書,只通過我們信任的 HTTPS 證書的請求。
-
還有一種比較簡單,直接設置okhttp禁用代理模式,也可以避免一些抓包。但是用戶可能需要代理上網,這麼禁止會引來其他問題
-
參考:https://www.jianshu.com/p/11577eb0ce2d
-
1.3 使用簽名和加密數據
上面可以看到,https並不能阻擋攻擊者分析請求接口併發起惡意請求攻擊,爲了增加攻擊者分析請求的難度,通常可以採用兩種方式:
-
使用簽名。
-
即給你的請求參數添加一個簽名,後臺服務接收到請求時,先驗證簽名,簽名不正確的話,則不予處理。簽名規則五花八門,大致策略就是根據請求參數做一些運算最後生成一個唯一的字符串當做sign
-
微信支付的簽名的規則可以參考:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3
-
-
數據加密。
-
post到服務器和從服務器返回的數據都做加密,這樣的話即使攻擊者拿到你的數據,他不知道你的加密算法就無能爲力了。
-
-
祕鑰使用JNI將敏感信息寫到Native層
參考:
-
https://www.jianshu.com/p/fe0206f8be5b
JNI:儲存敏感信息,核心算法和祕鑰
一些常量是不會/不能被混淆的,這種敏感信息就需要額外保護
核心算法和祕鑰,一般選擇放到native層
native代碼的安全性保證
-
相對於java代碼容易被反編譯,使用NDK開發出來的原生C++代碼編譯後生成的so庫是一個二進制文件,這無疑增加了破解的難度。利用這個特性,可以將客戶端敏感信息寫在C++代碼中,增強應用的安全性。
-
萬一別人將你的so庫直接copy出來拿去用了呢?因此,我們還需要在native層對應用的包名、簽名進行鑑權校驗,如果不是自己的應用,不返回相關信息,或者直接退出應用!
安全風險
-
用ndk開發,將密鑰放在so文件,加密解密操作都在so文件裏,這從一定程度上提高了的安全性,擋住了一些逆向者,但是有經驗的逆向者還是會使用IDA破解的。
-
在so文件中不存儲密鑰,so文件中對密鑰進行加解密操作,將密鑰加密後的密鑰命名爲其他普通文件,存放在assets目錄下或者其他目錄下,接着在so文件裏面添加無關代碼(花指令),雖然可以增加靜態分析難度,但是可以使用動態調式的方法,追蹤加密解密函數,也可以查找到密鑰內容。
參考:
-
https://www.jianshu.com/p/fe0206f8be5b
-
https://zhuanlan.zhihu.com/p/34902225
-
即時通訊安全篇(四):實例分析Android中密鑰硬編碼的風險
加殼與脫殼
無論是編譯java代碼生成的dex文件,還是編譯C/C++代碼生成的so文件,反編譯成本都不是特別的高。
加殼直觀理解就是給程序加一層殼,可以用來對原程序進行資源壓縮、防調試、防注入、防反編譯,也就是說通過一個殼把原來的程序保護了起來。
我們知道一個常規Android程序它的所有代碼都在dex文件中,程序啓動時要先把這個dex文件載入到內存中,所以如果要加殼的話,主要工作就是把原dex文件加密或者隱藏起來,放一個新的殼dex到apk中,程序啓動時運行這個殼dex,然後這個殼dex在運行時再加載原dex,用一張圖表示如下:
-
xposed框架這樣的hook技術,類似於降維打擊,可以繞過加固技術輕鬆獲取到dex文件。目前的樂固、360等大廠加固都可以繞過。
-
加固也避免dex文件直接暴露在apk壓縮文件中,但是加固也有明確的缺點,可能會影響啓動的速度,apk體積增大,無法使用補丁,依然可以脫殼逆向。所以加固已經不再被推薦使用,基本上大公司的apk都不會使用加固技術。
-
補充:hook和xposed
什麼是 Hook
Hook 又叫“鉤子”,它可以在事件傳送的過程中截獲並監控事件的傳輸,將自身的代碼與系統方法進行融入。這樣當這些方法被調用時,也就可以執行我們自己的代碼。
Xposed
-
可以在不修改APK的情況下影響程序運行(修改系統)的框架服務。
-
替換自己的代碼,使得程序加載Xposed的jar包,完成劫持
-
在install的時候需要root權限,但是運行時不需要root權限
通過替換 /system/bin/app_process 程序控制 Zygote 進程,使得 app_process 在啓動過程中會加載 XposedBridge.jar 這個 Jar 包,從而完成對 Zygote 進程及其創建的 Dalvik 虛擬機的劫持。
Xposed 在開機的時候完成對所有的 Hook Function 的劫持,在原 Function 執行的前後加上自定義代碼
Xposed框架介紹以及原理
Xposed是Github上rovo89大佬設計的一個針對Android平臺的動態劫持項目,通過替換/system/bin/app_process程序控制Zygote進程,使得app_process在啓動過程中會加載XposedBridge.jar這個jar包,從而完成對Zygote進程及其創建的Dalvik虛擬機的劫持。
因爲Xposed工作原理是在/system/bin目錄下替換文件,在install的時候需要root權限,但是運行時不需要root權限。
看到這裏很多人會很懵,什麼是Zygote?簡單來說在Android系統中,應用程序進程都是由Zygote進程孵化出來的,而Zygote進程是由Init進程啓動的。Zygote進程在啓動時會創建一個Dalvik虛擬機實例,每當它孵化一個新的應用程序進程時,都會將這個Dalvik虛擬機實例複製到新的應用程序進程裏面去,而一個應用程序進程被Zygote進程孵化出來的時候,不僅會獲得Zygote進程中的Dalvik虛擬機實例拷貝,還會與Zygote一起共享Java運行時庫。這也就是可以將XposedBridge這個jar包加載到每一個Android應用程序中的原因。
XposedBridge有一個私有的Native(JNI)方法hookMethodNative,這個方法也在app_process中使用。這個函數提供一個方法對象利用Java的Reflection機制來對內置方法覆寫。。。。等等這些都會借鑑各路大神的思路和分析,總而言之,就是從底層替換方法,可以讓我們在不修改APK源碼的情況下,通過自己編寫的模塊來影響程序運行的框架服務,實現類似於自動搶紅包、微信消息自動回覆等功能。 其實,從本質上來講,Xposed模塊也是一個Android程序。但與普通程序不同的是,想要讓寫出的Android程序成爲一個``Xposed 模塊,要額外多完成以下四個硬性任務:
1、讓手機上的xposed框架知道我們安裝的這個程序是個xposed模塊。
2、模塊裏要包含有xposed的API的jar包,以實現下一步的hook操作。
3、這個模塊裏面要有對目標程序進行hook操作的方法。
4、要讓手機上的xposed框架知道,我們編寫的xposed模塊中,哪一個方法是實現hook操作的。
參考:
-
https://www.jianshu.com/p/fe0206f8be5b
加密算法
加密主要有對稱加密、非對稱加密,不可逆加密。
對稱加密AES
AES主要是用在數據本身的加密,即使傳輸過程中被截取了,也是加密過後的數據。但AES的弊端的是,客戶端加密的話,密鑰肯定是儲存在app中,如果app被成功破解了,數據也就被暴露了。所以只有app本身程序的安全也解決了,app才能相對安全。
非對稱加密RSA
因爲RSA加密有個長度限制,這就導致了RSA加密不能用於所有的數據交互。但是可以用到一些短數據,比如用戶個人信息之類的,在交易中,一次訂單的數據也不是很大等。
不可逆加密
比如MD5加密、SHA加密等。所謂的不可逆加密就是,只能單向加密,不能反向解密。MD5把數據加密,最後得到固定長度的16進制編碼。這個加密的作用一般是匹配驗證,驗證某個數據是否改變。比如密碼,在向服務器存儲密碼,一般不會存儲明文密碼。安卓本地存儲個標誌也一般不會明文存儲。
Android官方
推出了JetPack Security 。
確保數據安全 - 深入解讀加密與安全開發 | AndroidDevSummit 中文字幕視頻