大家周末好,現在的app框架是越來越多了,uniapp->flutter->webview與java交互...
目前來說的話,flutter app的逆向一直是個比較困難的問題,在app的lib目錄下會有個libapp.so文件,裡面存放的即是app運行的核心代碼,用ida來看看
很明顯,都是些字節,並不能很直觀的看見彙編代碼,到這裡,就可以難到90%的逆向人員了,我們可以在看看導出函數
what???這些都是什麼玩意
百度問下度娘,查詢結果如下:
_kDartVmSnapshotData』:代表 isolate 之間共享的 Dart 堆 (heap) 的初始狀態。有助於更快地啟動 Dart isolate,但不包含任何 isolate 專屬的信息。
『_kDartVmSnapshotInstructions』:包含 VM 中所有 Dart isolate 之間共享的通用例程的 AOT 指令。這種快照的體積通常非常小,並且大多會包含程序樁 (stub)。---> dart函數代碼存放
『_kDartIsolateSnapshotData』:代表 Dart 堆的初始狀態,並包含 isolate 專屬的信息。
『_kDartIsolateSnapshotInstructions』:包含由 Dart isolate 執行的 AOT 代碼。
Data和Instructions,Vm和isolate又是什麼,為什麼使用Snapshot(中文意思快照)命名函數,可以參考https://mrale.ph/dartvm/中的解釋,這裡我們不深究的就記住Data和Instructions,Vm和isolate是兩對組合就行。
Data和Instructions,Flutter編譯運行在app上分為JIT和AOT模式(編譯模式)
JIT模式:即Just-in-time,動態(即時)編譯,邊運行邊編譯;安卓在2.2版本引入此技術,主要是用來提高程序的執行效率的。對於會多次執行到的代碼(可以理解為多次調用到的函數),JIT會將其編譯成機器碼。下次再執行此代碼時就直接調用機器碼,提升執行效率。而對於不經常調用到的函數,都會在執行運行期間進行編譯,編譯時又需要時間,在程序運行期間花時間編譯一般不會執行到的代碼,這不是一個好的選擇。所以,JIT只編譯常用代碼,對於不常用的代碼仍舊在用到時用JVM(解釋器)去解釋成機器碼然後執行就可以了。JIT編譯而來的機器碼是存儲到內存中的,不是在硬碟上。所以,在應用重新啟動時,所有的常用代碼也都需要使用JIT重新編譯成機器碼。(每啟動一個app,都會相應的啟動一個dalvik虛擬機,啟動時便會創建JIT線程,一直在後臺運行。當某段代碼被調用時,虛擬機會判斷它是否需要編譯成機器碼,如果需要,就做一個標記,JIT線程不斷判斷此標記,如果發現經常被調用,就編譯成機器碼,並將其機器碼地址及相關消息放入entry table中,下次執行時就跳轉到機器碼段執行,而不在解釋執行,從而提高速度)
JIT是Dalvik虛擬機下的編譯模式,隨著ART在4.4版本之後替代Dalvik,編譯模式也有JIT變為了AOT。Davilk虛擬機從android誕生時就存在,4.4版本後被ART虛擬機代替。
AOT模式:ART採取了AOT(Ahead-Of-Time)技術, APP安裝時就將字節碼(原始碼就是你編寫的文本文件(.java文件)字節碼是編譯好的中間文件(.class),運行在java虛擬機裡的就是這個文件,機器碼和本地代碼實際上是一回事,指機器可以直接識別運行的代碼,也就是機器指令字節碼是不能直接運行的,需要通過jvm解釋或編譯成機器碼才能運行,例如字節碼經過aot編譯成為機器碼,為什麼叫字節碼?因為這種類型的代碼以一個字節8bit為最小單位儲存)編譯成了機器碼然後保存在本地(可以理解為硬碟)。這樣app運行時就可以直接從本地取到機器碼然後執行,大大提高代碼的執行效率。在APK安裝的時候就會先做預編譯,編譯好的文件是OAT文件,該文件本質上是一個ELF文件,這裡與dex(odex)文件最大的區別是OAT文件不再是字節碼文件,而是一個可執行文件,這樣就可以更底層的與硬體接觸,運行時也省去了預編譯和轉譯的時間。AOT作為默認的編譯方式,優點是省去了程序運行期間的編譯時間,提高了代碼的執行效率,缺點是延長了應用的安裝時間,增大了本地存儲空間的佔用。(AOT作為默認編譯模式,只存在於android5.x,6.x版本)
來源網址:https://blog.csdn.net/weixin_34550722/article/details/117349977
https://zhuanlan.zhihu.com/p/44657693
安卓7.0以後的編譯模式是怎樣的?
7.0版本以後的編譯模式採取hybirb模式,也就是JIT+AOT+解釋器的結合。app安裝時不在編譯,直接安裝。代碼執行時採用JIT的方式,對於常用代碼進行編譯並產生profile文件,但這種JIT產生的編譯並不是持久的。當手機進入休閒狀態,系統隔一段時間就會掃描一下app的profile文件,基於此profile文件制定的常用代碼進行編譯並進行持久化處理。待應用再次運行時,如果本地有相關機器碼,就直接運行本地的機器碼而不再進行JIT編譯。
此處的AOT,不再指「Ahead Of Time」,而是指「All Of the Time」。
這種混合模式的好處是,因為不在進行安裝時編譯,安裝會變得很快。在手機進入休閒狀態時,進行了編譯,編譯過的常用代碼不會在進行重複編譯,執行效率也會提升。缺點是,應用前幾次代碼執行,效率可能一般,用戶操作應用的此處越多,這種代碼執行的效率越明顯。
Dalvik使用JIT模式編譯.dex字節碼,是針對Android設備優化後的DVM所使用的運行時編譯字節碼。
.odex是對dex的優化,deodex在系統第一次開機時會提取所有apk內的dex文件,odex優化將dex提前提取出,加快了開機的速度和程序運行的速度
ART虛擬機AOT編譯方式
在安裝apk時會進行預編譯,生成OAT文件,仍以.odex保存,但是與Dalvik下不同,這個文件是可執行文件
dex、odex均可通過dex2oat生成oat文件,以實現兼容性
在大型應用安裝時需要更多的時間和空間
不過,flutter線上只能使用AOT模式,也就是Flutter引入的DartVM包含了執行AOT產物的功能。為了與JIT模式兼容,DartVM採用了所謂快照的方式,即JIT運行時編譯後的基本結構與AOT編譯的基本結構相同。將類信息、全局變量、函數指令直接以序列化的方式存在磁碟中,稱為Snapshot(快照)。
那我們該如何解析這個快照呢?拿出裡面的函數消息呢
這裡,github上的大神給出了一個腳本
https://github.com/rscloura/Doldrums
這裡,我寫了個flutter app,採用aes加密的例子
採用腳本對libapp.so進行解析,得到如圖的消息
函數偏移都出來了,那可以利用frida進行勾取參數了
完美,aes/ecb加密模式的key出來了
結束。