1. 程式人生 > >Android Hook框架adbi原始碼淺析(二)

Android Hook框架adbi原始碼淺析(二)

二、libbase

其實上面載入完SO庫後,hook的功能我們完全可以自己在動態庫中實現。而adbi作者為了方便我們使用,編寫了一個通用的hook框架工具即libbase庫。
libbase依然在解決兩個問題:1.獲取要hook的目標函式地址;2.給函式打二進位制補丁即inline hook。

關於獲取hook函式地址的方法這裡不再贅述。直接看inline hook部分,這部分功能在base\hook.c的hook()函式中實現,先看hook_t結構體:

struct hook_t {
    unsigned int jump[3]; //跳轉指令(ARM)
    unsigned int store[3]; //原指令(ARM)
    unsigned char jumpt[20]; //跳轉指令(Thumb)
    unsigned char storet[20]; //原指令(Thumb)
    unsigned int orig; //被hook函式地址
    unsigned int patch; //補丁地址
    unsigned char thumb; //補丁程式碼指令集,1為Thumb,2為ARM
    unsigned char name[128]; //被hook函式名
    void *data;
};

hook_t是一個標準inline hook結構體,儲存了跳轉指令/跳轉地址/指令集/hook函式名等資訊。因為ARM使用了ARM和Thumb兩種指令集,所以程式碼中需進行區分:

if (addr % 4 == 0) {
    /* ARM指令集 */
} else { 
    /* Thumb指令集 */
}

這樣進行判斷的依據,是編譯器在使用Thumb指令集編譯一個函式時,會自動將真正對映地址的最後一位置’1’賦給符號地址,這樣可以實現無縫的Thumb指令集函式與Arm指令集程式碼混編。
接下來看一下ARM指令集分支的處理流程,這是該問題解決的核心部分:

if (addr % 4 == 0) {
    log("ARM using 0x%lx\n", (unsigned long)hook_arm)
    h->thumb = 0;
    h->patch = (unsigned int)hook_arm;
    h->orig = addr;
    h->jump[0] = 0xe59ff000; // LDR pc, [pc, #0]
    h->jump[1] = h->patch;
    h->jump[2] = h->patch;
    for (i = 0; i < 3; i++)                                  
                h->store[i] = ((int*)h->orig)[i];
    for (i = 0; i < 3; i++)                                  
                    ((int*)h->orig)[i] = h->jump[i];
}

首先填充hook_t結構體,第一個for迴圈儲存原地址處3條指令共12位元組。第二個for迴圈用新的跳轉指令進行覆寫,關鍵的三條指令分別儲存在jump[0]-[2]中:

jump[0]賦值0xe59ff000,翻譯成ARM彙編為ldr pc,[pc,#0],由於pc暫存器讀出的值是當前指令地址加8,因此這條指令實際是將jump[2]的值載入到pc暫存器。
jump[2]儲存的是hook函式地址。jump[1]僅用來4位元組佔位。Thumb分支原理與ARM分支一致,不再分析。

接下來我們注意到,函式最後呼叫了一處hook_cacheflush()函式:

hook_cacheflush((unsigned int)h->orig, (unsigned int)h->orig+sizeof(h->jumpt));

我們知道,現代處理器都有指令快取,用來提高執行效率。前面我們修改的是記憶體中的指令,為防止快取的存在,使我們修改的指令執行不到,需進行快取的重新整理:

void inline hook_cacheflush(unsigned int begin, unsigned int end)
{
    const int syscall = 0xf0002;
    __asm __volatile (
        "mov     r0, %0\n"
        "mov     r1, %1\n"
        "mov     r7, %2\n"
        "mov     r2, #0x0\n"
        "svc     0x00000000\n"
        :
        :   "r" (begin), "r" (end), "r" (syscall)
        :   "r0", "r1", "r7"
        );
}

參考資料

[1].adbi原始碼 https://github.com/crmulliner/adbi
[2].minghuasweblog,ARM Cache Flush on mmap’d Buffers with __clear_cache(),March 29, 2013