公司強推一款設備登記APP,要求每個人進行安裝并每日使用APP進行登記。如果名下只有一兩臺設備操作起來不算麻煩,但對于測試相關工作而言,無論是考慮系統版本的兼容性測試,還是需要依賴各種系統環境的安全測試。

相關測試人員名下都有多臺測試設備,這就需要我們每天手動對所有設備進行開機、啟動APP、登記、關機等一系列的操作。苦不堪言,所以我只能選擇搞TA。

首先對這款設備登記APP進行簡單的介紹,這款APP比較簡單主要包含三部分功能:掃碼登陸、設備登記和記錄查詢。

其中,掃碼登陸主要是采集與用戶身份相關的信息,比如用戶名、用戶ID及所在部室等信息;設備登記則是將用戶信息、設備信息(資源編號、設備IMEI、設備ID等)、地理位置信息等組裝成報文后解密上傳至服務器;記錄查詢則是查詢當前用戶所有設備的登記記錄。

根據上圖設備登記APP的首頁,大致可確定設備登記網絡請求報文所涉及的字段內容,看到首頁我的腦海中也立馬想到了3種實現方案:

1、遍歷設備列表依次修改對應EditText組件的文本內容,主動觸發“提交”按鈕的點擊事件;

2、二次打包設備登記APP,添加設備列表維護、批量設備登記等功能相關代碼;

3、逆向分析設備登記APP,將核心邏輯抽取后自實現一個新的APP用于批量自動化設備登記。

簡單分析下上述3種實現方案的利弊:

對于方案一而言,實現起來相對比較簡單,甚至無需逆向分析目標APP,對類似需求的通用性也比較高,但是比較難管理,一方面涉及UI組件較多,另一方面也很難確定某一具體設備是否登記成功(至于如何快速定位界面重的UI組件以及主動觸發組件的點擊、長按等事件,后續可能會通過其他文章介紹一個好玩的案例);

對于方案二而言,無需解釋,理論可行但是限制性因素太多,比如目標APP可能會存在簽名校驗等防篡改策略,當然本人倒不是因為這個,由于太久沒接觸smali,實現成本就變得比較高了;

對于方案三而言,可能唯一的弊端就是需要實現一個新的APP,但是這種實現所帶來的收益也是巨大的,比如高度定制化,我們可以有更多、更靈活的玩法,本文后面也會介紹兩種趣味破解方式。

抓包是移動APP逆向的常規手段,與大家一樣我也是先嘗試抓包分析一下報文,但讓我感到“驚喜”的是這種內部使用的APP竟然還用上了證書鋼釘。

目前已經公開的破解證書鋼釘的方案有很多,所以呢也不慌,甚至我還悠閑的打開了Android Studio,沒想到卻發現一個新的驚喜。

客戶端日志泄露是一個很常見但極容易被忽視的安全問題,對于這種內部使用,又在“趕鴨子上架”的情況下開發的APP,極容易因為開發者的大意而出現,而對于泄露的日志只要靜下心來分析,總會找到一些有用的信息。

從日志信息基本可確定目標APP會將用戶ID、用戶名及設備相關信息等組裝成JSON字符串并加密后進行網絡請求,因此也沒太大必要破解證書鋼釘進行抓包分析了。

需要注意的是該APP分別用原生開發和flutter進行Android和IOS端的開發,逆向flutter的成本無疑要比Android逆向麻煩一下,所以本人選擇從Android端下手。

后續的逆向分析過程大致包含6步:

1、導出應用安裝包

使用“adb shell pm path 包名”定位apk所在路徑后,直接用“adb pull”命令將apk文件導出到電腦本地。

2、應用脫殼及反編譯

拿到apk文件后一股腦安裝到脫殼機進行脫殼,之后使用jadx進行反編譯。

3、定位當前Activity

使用“adb shell dumpsys activity top”可查詢當前界面對應的Activity類,可確定首頁對應“MainActivity”,使用jadx搜索類名定位并雙擊跳轉。


4、點擊事件邏輯分析

分析點擊事件,需要先定位對應的組件。這里簡單分享幾種定位思路:

一種思路是從當前Activity對應的布局文件xml中進行定位后,根據組件的id,根據View的findViewById函數定位;

第二種思路是根據setOnClickListener函數搜索,然后判斷處理邏輯是否符合預期進行定位;

第三種思路就是肉眼觀察當前Activity類定義的屬性,根據字段名進行判斷,但是這種思路可能因為混淆而失效。

很幸運目標APP在MainActivity中定義了一個“btnConmit”屬性,從名字看應該就是對應的“提交”按鈕,后面就可以快速找到對應的事件處理邏輯相關代碼,部分核心邏輯如下圖所示:

5、加密邏輯分析

目標APP的加密邏輯還是比較簡單的,與加密相關的代碼就三張,如下圖所示:

其中涉及的密碼算法包括AES和RSA兩種,其實有經驗的朋友看到這倆密碼算法,大致就可以猜出加密邏輯了。

AES是一種對稱密鑰加密算法,而RSA是一種非對稱密鑰加密算法,對稱密鑰加密算法最大的優勢是加密速度快,適合對大數據進行加密,但是密鑰管理困難,而非對稱密鑰使用成對的密鑰進行加解密,安全性較高一些,但是加解密速度要比對稱密鑰算法慢得多,因此二者經常被組合起來使用。

現在再分析目標APP的加密邏輯就比較簡單了,大致就是如下三步:

第一步,調用AesUtils類的getAESSecureKey函數,動態生成一個AES密鑰aESSecureKey。

第二步,使用AES密鑰對網絡請求內容(日志泄露中的JSON串)進行加密,生成密文encrypt。

第三步,使用RSA的公鑰對AES密鑰進行加密保護,生成加密后的ASE密鑰encryptByPublicKey。

6、網絡請求分析

繼續分析代碼可確定目標APP使用了OkHttp框架實現網絡通信,并且封裝了一個請求函數如下圖所示:

其中三個參數分別對應網絡請求的url、使用AES密鑰加密后的密文、使用RSA公鑰加密后的AES密鑰,繼續跟蹤相關參數是如何被使用的。

從上圖可看出最終是生成一個FormBody,而message和key則分別對應使用AES密鑰加密后的密文和使用RSA公鑰加密后的AES密鑰。當服務端接收到網絡請求后,只需要取出key并使用RSA的私鑰進行解密獲取到明文的AES密鑰,然后使用AES密鑰對message進行解密即可獲取到明文的請求內容。

現在目標APP網絡請求所包含的信息(用戶信息、設備信息、地理位置信息等)、加密算法、網絡請求等核心邏輯均已分析完成,我們便可以通過偽造網絡請求數據來實現設備登記了,接下來分享兩種好玩的破解方式。

所謂借雞下蛋,其實就是通過Hook技術將目標APP變成一個加密機,由其完成請求內容的加密,我們只需要自實現一個新的APP來進行網絡請求即可。

根據前面的網絡請求分析可確定,我們只需要取到任一設備對應的message和key即可實現設備登記,接下來便借助目標APP自身來為我們提供需要的數據:

第一步,定位Hook函數。理論上只要AesUtils和RSAUtils兩個類被加載了,我們可以Hook任何APP啟動過程中會執行的函數,在這我們可以對Activity類的onResume函數進行Hook。

第二步,基于反射機制實現主動調用。之前在分析加密算法邏輯的時候提及到目標APP僅需要三行代碼即可生成message及key,那么我們僅需要依賴反射機制對這三行代碼進行翻譯即可,相關代碼如下圖:

接下來只需要批量將測試設備對應的明文請求內容進行加密,將每臺設備對應的message和key進行記錄。

新建一個新的APP用于發起網絡請求,直接將目標APP封裝的網絡請求相關函數copy過來就可以,自己封裝一個NetUtil類用于發起網絡請求,如下圖所示:

如何批量登記呢?這就用到了前面所記錄的每臺設備對應的message和key,我么在新APP中通過HashMap對設備列表進行維護:

接下來,只需要封裝一個函數doRegister,該函數負責遍歷設備列表并發起網絡請求,同時對響應報文進行解析,記錄成功登記的設備數量,當所有設備登記完成后整理結果。

目前基本功能已經完成,我們可以通過一個按鈕的點擊來實現doRegister函數的調用,從而完成所有設備的登記。如果能夠自動化進行設備的登記,是不是很香?

我們只需要啟一個定時任務就可以自動進行登記,Android中的定時任務一般有兩種實現方式:

一種是使用Java API提供的Timer類,另一種就是Android的Alarm機制。但是呢,既然我們前面用到了Hook技術,這里就分享一種好玩的方式實現定時任務——基于系統鬧鐘的定時任務。

所謂基于系統鬧鐘的定時任務,本質就是系統鬧鐘到達設定的時間后會啟動一個新的Activity,我們就可以對該Activity進行Hook,從而跳轉至前面自己實現的用于設備登記的APP,之后自動執行doRegister函數完成設備登記,核心Hook代碼如下:

所謂殺雞取卵,其實就是通過逆向分析目標APP,梳理其核心邏輯,將相關的代碼(或者dex、jar等)復制到自己的項目中為己所用,前面提到的破解方式一還需要依賴目標APP自身來獲取所需的部分數據。

借雞下蛋只是將網絡請求相關的代碼進行抽取,而加密相關邏輯還是由目標APP自身來完成。而這種方式則是將加密算法、網絡請求等近乎所有的核心邏輯進行抽取,從而可以獨立于目標APP而實現設備登記。

前面已經對核心邏輯進行分析,只需要將相關代碼復制到新建的APP即可,另外完全可以進行功能增強,比如補充設備列表的維護功能,允許自己手動錄入新的設備。

除此之外,這種破解方式還有不少的優點:可在免ROOT設備運行(理論上可在任意Android設備);易于分發(可分發給其他同事,由其自己維護自己的設備列表);開發更加靈活等等。

這種方式比較暴力,當然也有一定的局限性。首先,自動化實現起來就不像前面直接Hook系統鬧鐘那么簡單,另外在實現過程中我也忽略了一個小問題。

那就是目標APP所用的加密算法高度依賴OkHttp庫的版本,不同版本的實現細節稍有不同,這就導致服務端無法正確解密獲取到明文內容,從而導致設備登記失敗。

針對前面提到的問題,固然可以通過修改版本來解決,但是這比較依賴個人的運氣了,其實我們可以考慮dex的動態加載,動態加載目標APP的dex文件,從而實現需要的功能即可。至此,大功告成。

文章接近尾聲,回顧一下其實文章提及的設備登記APP本身并不復雜,也沒有很強的防護,破解成本并不算太高,但是整個破解過程還是蠻好玩的。目標APP采取了證書鋼釘策略來防止抓包,并且報文還進行了加密,卻忽略了客戶端自身的安全防護。

另外由于客戶端自身防護較弱,可以較低成本梳理清晰加密算法、網絡請求等核心邏輯,從而以多種方式實施破解。為應對攻擊者反編譯、抓包、hook等常規手段,APP至少應該形成一種“加固+環境檢測+防抓包”的體系化防護策略,從而提升攻擊成本。

最后聯想到前段時間爆出來某互聯網巨頭APP利用系統漏洞提權后偽造DAU、竊取用戶信息、攻擊競爭對手APP等行為的事件,還是想補充一下:技術本無罪,但是科技向善應該是每一個人都需要遵守的原則,一個人所掌握的專業知識、技能以及工具等應該用來合情合理合法地改善生活、提升幸福感。