參考: http://www.itdadao.com/articles/c15a190757p0.htmlhtml
一. 爲何要調試init_arrayjava
init_array的用途android
1. 一些全局變量的初始化 (我這裏試過, 一些全局變量的初始化,會統一用一個init_array表項來完成初始化)數組
2. 經過__attribute__ ((constructor)) 聲明的函數 (能夠定義n個)ionic
經過so加載流程來看,init_array是咱們程序代碼能夠控制的最先的時機了, 其次才加載Jni_onload函數
因此有些樣本會在init_array作一些反調試和相關環境檢測的活, 因此咱們須要在init_array中和對方兵戎相見
spa
固然網上已有不少教咱們如何在init_array下斷的函數, 可是卻都只教了方法, 沒有細說原理, 最後咱們可能只學會了幾個快捷鍵, 空有招式卻無內功, 知其然殊不知其因此然, 下面咱們就姿式和知識這2方面來進行討論操作系統
二. 斷init_array的姿式3d
1. 定位調試進程中linker的dlopen函數地址指針
把調試機器中的linker拷貝出來, 路徑爲/system/bin/linker, 而後開一個IDA分析
在Shift+F12在字符串窗口中查找"dlopen", 跟蹤引用到一個函數, 以下圖
獲得其文件偏移爲0xF30
附加上調試器後, 咱們獲得linker加載到內存的起始地址爲400BD000
因此咱們在代碼窗口Go過去看看400BD000 + F30 = 400BDF30
發現所有是DCB形式的代碼(代碼沒有解析出來), 這個時候咱們須要對linker進行分析, 操做以下: 右鍵->Analyze Module
go過去咱們發現和靜態分析中的同樣, 在函數頭部下一個斷點
2. 定位到calling相關代碼
一樣在拷貝出來的ida搜索字符串calling
一樣定位到代碼,獲得文件偏移 2720
那麼咱們內存中的地址就是 400BD000 + 2720 = 400BF720
一樣在調試的ida中下好斷點, 第2個斷點就是調用.init_array數組的代碼
而後按F9,注意觀察寄存器窗口, 當有顯示調試的是你想要斷的so的時候開始注意
當斷點斷在BLX R4的時候,下一步就是調用init_array數組了, 因此F7跟進去
在直接把咱們想要分析的so拖到ida分析進行驗證, 代碼同樣, 說明咱們成功的斷點在了init_array數組
二. 斷init_array的知識
經過上面的操做咱們學會了招式, 內功心法卻不見修習, 下面咱們經過Android的系統源碼來一探究竟
環境介紹
源碼環境: Android 6.0.1
沒有下載源碼的同窗能夠去androidxref在線看源碼也很方面
1. 回到源頭看問題
咱們都知道咱們要在apk中要加載一個so咱們能夠經過
System.loadLibrary("libname");
System.load("lib_path");
這2者區別以下:
(1). System.load參數必須爲庫文件的絕對路徑,能夠是任意路徑;
(2). System.loadLibrary參數爲庫文件名,不包含庫文件的擴展名,必須是在JVM屬性Java.library.path所指向的路徑中,路徑能夠經過System.getProperty('java.library.path')
2. java層到native層的過程
咱們把android_source\libcore\luni部分的源碼做爲單獨的部分丟進Source Insight進行分析
定位到android_source\libcore\luni\src\main\java\java\lang\System.java, 搜索loadLibrary, 就能夠開始分析了
java層代碼主要是一些路徑, 和標記值的初始化
最後比較關鍵的函數是JavaVMExt.LoadNativeLibrary, 該函數主要完成以下事情
1. 調用linker的dlopen完成加載
2. 調用dlsym獲取目標so的JniOnload地址並調用
3. 初始化SharedLibrary對象並添加到表中, 下次加載相同的so則不在重複加載
linker以前的函數調用流程圖以下:
3. linker的dlopen簡易分析
android系統經過調用linker的dlopen來完成so的轉載
把aosp\bionic目錄添加到source insight中進行分析
配合AndroidXref站點咱們找到, dlopen定義在dlfcn.cpp中
dlopen函數定義以下, 只是簡單的調用了dlopen_ext
do_dlopen簡單的判斷了一下參數, 而後調用find_library進行轉載連接so文件
加載成功後,返回soinfo對象指針,同時調用soinfo的成員函數call_constructors來調用so中的init_array
call_constructors先完成其餘模塊的加載,而後調用call_array()來調用init_array數組的調用
因爲篇幅問題,大體介紹下linker的調用流程, 函數調用流程以下:
1. 在do_dlopen中經過find_library進行加載so
在加載完so後經過call_constructors完成init_array的加載
2. find_library最後調用load_libray完成so的轉載
3. 最後經過load_library的elf_reader.load完成so的裝載
四.總結
因爲android是開源的操做系統, android中的不少問題咱們均可以經過分析源碼來了解細節, 解決問題, 並知其因此然
同時咱們還能夠經過編譯源碼來定製咱們想要的功能, 達到咱們想要的目的