1. 程式人生 > >一種攔截Linux動態庫API的方法及裝置

一種攔截Linux動態庫API的方法及裝置

描述
   攔截Linux動態庫API的常規方法,是基於動態符號連結覆蓋技術實現的,基本步驟是
    1. 重新命名要攔截的目標動態庫。
    2. 建立新的同名動態庫,定義要攔截的同名API,在API內部呼叫原動態庫對應的API。這裡的同名是指與重新命名前動態庫前的名稱相同。
   顯而易見,如果要攔截多個不同動態庫中的API,那麼必須建立多個對應的同名動態庫,這樣一來不僅繁瑣低效,還必須被優先連結到客戶二進位制程式中(根據動態庫連結原理,對重複ABI符號的處理是選擇優先連結的那個動態庫)。 另外在鉤子函式的實現中,若某呼叫鏈呼叫到了原API,則會引起死迴圈而崩潰。本方法通過直接修改ELF檔案中的動態庫API入口表項,解決了常規方法的上述問題。

特點

   1. 不依賴於動態庫連結順序。
   2. 能攔截多個不同動態庫中的多個API。
   3. 支援執行時動態連結的攔截。
   4. 鉤子函式內的實現體,若呼叫到原API,則不會死迴圈。

實現
   攔截對映表
      為了支援特點2和3,建立了一個攔截對映表,這個對映表有2級。第1級為ELF檔案到它的API鉤子對映表,鍵為ELF檔案控制代碼,值為API鉤子對映表;第2級為API到它的鉤子函式對映表,鍵為API名稱,值為包含最老原函式地址和最新鉤子函式地址的結構體,如下圖
      當最先開啟ELF檔案成功時,會在第1級對映表中插入記錄;反之當最後關閉同一ELF檔案時,就會從中移除對應的記錄。當第一次掛鉤動態庫API時,就會在第2級對映表插入記錄;反之卸鉤同一API時,就會從中刪除對應的記錄。

   計算ELF檔案的映像基地址

      計算映像基地址是為了得到ELF中動態符號表和重定位連結過程表的內容,因為這些表的位置都是相對於基地址的偏移量,該演算法在開啟ELF檔案時執行,如下圖
      EXE檔案為可執行檔案,DYN檔案為動態庫。對於可執行檔案,對映基地址為可執行裝載段的虛擬地址;對於動態庫,可通過任一API的地址減去它的偏移量得到,任一API的地址可通過呼叫libdl.so庫API dlsym得到,偏移量通過查詢動態連結符號表得到。

   開啟ELF檔案
      為了支援特點2即攔截不同動態庫的多個API,節省每次掛鉤API前要開啟並讀檔案的開銷,獨立提供了開啟ELF檔案的介面操作,流程如下圖
      若輸入ELF檔名為空,則表示開啟當前程序的可執行檔案,此時要從偽檔案系統/proc/self/exe讀取檔案路徑名,以正確呼叫系統呼叫open。當同一ELF檔案被多次開啟時,只須遞增結構elf的引用計數。

   掛鉤API
      當開啟ELF檔案後,就可掛鉤API了,流程如下圖
      當第一次掛鉤時,需要儲存原函式以供後面卸鉤;第二次以後繼續掛鉤同一API時,更新鉤子函式,但原函式不變。   
   
   卸鉤API
      當開啟ELF檔案後,就可卸鉤API了,流程如下圖

   關閉ELF檔案
      因為提供了開啟ELF檔案的介面操作,所以得配有關閉ELF檔案的介面操作。當不需要掛鉤API的時候,就可以關閉ELF檔案了,流程如下圖

執行時動態攔截裝置
   在初始化模組中開啟當前可執行檔案,掛鉤libdl.so庫的API dlopen和dlsym;在轉換模組中,按動態庫控制代碼和API名稱在攔截對映表中查詢鉤子函式,若找到則返回鉤子函式,否則返回呼叫dlsym的結果;在銷燬模組中,卸鉤dlopen和dlsym。
當動態庫被程序載入的時候,會呼叫初始化模組;當被程序解除安裝或程序退出的時候,會呼叫銷燬模組;當通過dlsym呼叫API時,則會在dlsym的鉤子函式中呼叫轉換模組。通過環境變數LD_PRELOAD將動態庫libhookapi.so設為預載入庫,這樣就能攔截到所有程序對dlopen及dlsym的呼叫,進而攔截到已掛鉤動態庫API的呼叫。 posted on 2016-08-25 11:10 春秋十二月 閱讀(1140) 評論(0)  編輯 收藏 引用 所屬分類: System