第一次嘗試反向操作繞過 apk 簡單的 jni 簽名校驗
1. 現象
修改了應用的內容之后,搜索 smali 沒發現有做應用的簽名校驗,但重打包之后應用打開直接出現閃退。查看日志,確定是 jni 方法做了簽名校驗。
Caused by: java.lang.UnsatisfiedLinkError: JNI_ERR returned from JNI_OnLoad in "/data/app/pkgpath/lib/arm64/libencryption.so"
錯誤出現在JniUtils
去 load libencryption.so
的過程中,查看 java 代碼,發現這個庫是用來做 aes 加密的,每一個網絡請求都會用到這個方法,把簽名校驗放到這里的JNI_Onload
可以防止直接從java層去掉LoadLibrary
。
打開(下載安裝) IDA 看代碼。
2. 定位
由於第一次下載 IDA ,最開始下的 IDA Free 不支持 arm,又重新去搞 IDA Pro。
第一次用了 ida64 去加載 armeabi-v7a 的文件,才直到需要區分64位,用不同的 exe。
終於成功加載了,直接搜索Signature
,找到了一個checkSignature
方法,然后在右邊看不懂的界面里看到了熟悉的字符串,這個應該就是用來比較的本地簽名緩存了。
因為是簡單的簽名校驗,只要把這個值改成我們重簽名之后的 MD5 值,應該就可以了。最簡單粗暴的方法,直接改數據。。。
在 Hex View 中找到字符串,按 F2 編輯,可以看到直接改16進制的值,就可以生效了。把這一段替換好后,保存重打包,就可以正常運行了。
但這樣改實在是太Low了,也沒有學到任何知識。還是需要找到錯誤的出處,嘗試去繞過校驗,不是簡單的修改簽名
第一次用 IDA,邊查教程邊操作。先用Ctrl
+F5
讓 IDA 反編譯一個 C 的偽代碼出來,IDA-View 里面全是指令,完全看不懂。
200KB 的 lib 直接導出了一個三萬多行的 C 文件,看的讓人頭大。不過總算是能理一理邏輯了,先看出現錯誤的 JNI_OnLoad
方法。可以看到在判斷簽名非空和checkSignature
的結果的地方,return -1
或版本號。
定位到問題,接下來就要解決怎么改了。
3. 修改
作為只能看得懂一點點 smali 的菜雞,對於 IDA 里面顯示的內容沒有一處能看得懂。
找到了對應的方法位置,要是 smali 代碼,真的是隨便就改掉了。這個第一次搞,完全無從下手。
迫於人菜癮還大,不會改if(!checkSignature())
,也不會直接改return 65542
,最終決定從checkSignature
方法的返回值下手,讓他無論如何都return true
就好了。依靠Copy to assembly
,把反編譯后的代碼放到天書里面,至少讓我看到了哪是哪。
可以看到這里調用了strcmp
方法,比較 s1 與 s2 ,直接把這里改成 s1 比較 s1 就好了。
想法是美好的,改這個參數花了好久,最后用key-patch
實現了,直接改這里的調用
修改后重新看反編譯的代碼,實現了繞過簽名校驗的功能。
4. 測試
64和32位的庫都對應修改好之后,重新打包簽名,運行正常。
5. 總結
比較笨方法直接改存儲的簽名信息,后面更多的是嘗試修改 lib 庫的代碼。
理代碼邏輯,修改內容可比繞過簽名校驗本身有意思多了,后面還是要多學習。
6. PS
我自己寫的第一個 jni 的代碼,就是從網上扒來的簽名校驗,實現幾乎和這個一模一樣,在Application:onCreate
中調用 JNI 方法,然后方法內和寫死的簽名比較,錯誤的話直接killProcess
。從安全性上還不如這個放在必須調用的加密庫里面,我當時寫的直接不去調用就可以繞過。
做這個嘗試的時候好幾次完全看不懂想放棄,想着直接用笨方法也能實現需求就算了。
但總想看看,如何能徹底把自己以前寫過的垃圾代碼干掉,就一點點的查和改。
后面可以通過這個去學習別的應用更好的簽名校驗做法。之前遇到過一個有意思的設計,簽名校驗成功之后去初始化一個靜態的單例,然后在其它地方直接調用這個單例的方法。如果簽名校驗失敗,這里的調用只會拋出空指針錯誤導致應用 crash,一開始不會聯想到是因為重簽名導致的應用錯誤。。。
當然一看 traces 就找到了問題,也可能只是設計的時候沒考慮到這里會導致空指針,畢竟沒什么人會無聊到閑着沒事去重簽名別人 apk。