歡迎您光臨本站 註冊首頁

Apache Hook 結構分析(一)

←手機掃碼閱讀     火星人 @ 2014-03-04 , reply:0

Apache Hook 結構分析(一)

Apache Hook 結構分析(一)
                                          楊學剛
                                          2009-3-18
                                          xuegangyang@eyou.com


一、簡介        1
二、Hook array結構分析        2
三、Hook 函數分析        3
3.1 hook函數實現        3
3.2 Run函數實現        4
3.2.1 FIRST處理流程對應的run函數定義        4
3.2.2 ALL處理流程對應的run函數定義        5
3.2.3 VOID處理流程對應的run函數定義        5
3.3 get_hook函數實現        6
四、HOOK宏分析        6
4.1 HOOK_STRUCT宏分析        6
4.2 EXTERNAL_HOOK宏分析        7
4.3 VOID宏分析        7
4.4 RUN_ALL宏分析        8
4.5 RUN_FIRST宏分析        9
4.6 使用宏定義HOOK        10
五、HOOK定義擴展        10
六、小結和思考        11

一、簡介
     為了讓第三方開發的Module可以擴展伺服器的默認處理,Apache使用了Hook機制。本文在源碼基礎上對Hook實現原理和結構進行分析,結合http://blog.csdn.net/ConeZXY/archive/2007/11/22/1898000.aspx和http://www.loveopensource.com/?p=18兩篇文章將會更容易理解Hook機制實現原理。
Apache啟動和運行被分成許多階段,如果某些階段允許第三方開發的module進行擴展,在此階段便會實現一些Hook,通過該Hook便可以掛載用戶自定義的Module。計算機程序是數據結構和演算法的結合體,Hook核心數據結構是一個array,每一個array項存儲一個要執行的函數指針,而hook演算法由三個函數來實現:hook, run和hook_get(註:這三個名稱是對hook函數的簡稱,在下文將看到,不同hook對應的函數名不相同)。假設我們要實現一個名為example的hook,以下內容將以example為例來探索hook原理。
二、Hook array結構分析
每一個Hook需要一個array存儲要執行的函數指針,apache中,每個hook都會定義一個變數:_hooks,以example hook為例,以下是該變數的定義:
static struct {apr_array_header_t * link_example} _hooks;
其中apr_array_header_t  是apache的apr中定義的一個array管理結構,其定義如下:
struct apr_array_header_t {
    /** The pool the array is allocated out of */
    apr_pool_t *pool;
    /** The amount of memory allocated for each element of the array */
    int elt_size;
    /** The number of active elements in the array */
    int nelts;
    /** The number of elements allocated in the array */
    int nalloc;
    /** The elements in the array */
    char *elts;
};
apr_array_header_t 結構實現array結構並對其進行管理,array的每一項稱為element,其中pool記錄該array是由哪個pool分配的空間,elt_size記錄每一個element的size,nelts表示array中有效的element數,nalloc表示系統已經給該array分配的element數,elts指針指向array的第一個element地址。Apache採用比較靈活的方法管理array,在我們向array中添加一個element時,它會判斷array中是否有空閑的位置(即array中已經分配的element數大於有效的element數,nalloc>nelts),如果沒有空閑位置(即nelts == nalloc),則array會重新申請大小為:2 * nalloc的空間。
        Hook array的每個element如何存儲函數指針,這涉及到hook定義的另外一個LINK數據結構,同樣假設我們要定義的Hook名字為:example,則對應的LINK結構定義如下:
typedef struct ap_LINK_example_t
{
ap_HOOK_example * pFunc;
const char* const aszPredecessor;
const char* const aszSuccessors;
int nOrder;
}ap_LINK_example;
定義中的pFunc就是要執行的函數指針,Hook array中每一個element都是ap_LINK_example結構,apr_array_header_t 結構中的elt_size將會等於sizeof(ap_LINK_example),而elts指向第一個ap_LINK_example地址。
通過以上對Hook array的分析,我們便可以畫出Hook array的結構圖:

三、Hook 函數分析
每一個Hook的實現對應三個函數:hook,run和get_hook,實現一個名字為example的hook,對應的函數分別為:ap_hook_example,ap_run_example和 ap_hook_get_example,其中hook函數用於向hook array隊列中添加element,run函數則順序讀取hook array的每一個element,並調用element中指向的函數(即調用ap_LINK_example_t結構中pFunc指向的函數),hook_get函數僅僅簡單返回_hooks變數中對應的link_example指針。
3.1 hook函數實現
ap_hook_example函數用於向Hook array中添加一個element,其定義如下:
AP_DECLARE(void) ap_hook_example(ap_HOOK_example_t *pf,const char * const *aszPre, const char * const *aszSucc,int nOrder)
{
    ap_LINK_example_t *pHook;
    if(!_hooks.link_example)
    {
    _hooks.link_example=apr_array_make(apr_hook_global_pool,1,sizeof(ap_LINK_example_t)); \
    apr_hook_sort_register(example,&_hooks.link_example);
    }
    pHook=apr_array_push(_hooks.link_example);
    pHook->pFunc=pf;
    pHook->aszPredecessors=aszPre;
    pHook->aszSuccessors=aszSucc;
    pHook->nOrder=nOrder;
    pHook->szName=apr_hook_debug_current;
    if(apr_hook_debug_enabled)
    apr_hook_debug_show(#name,aszPre,aszSucc);
}
該函數中通過apr_array_push把element添加到了array末尾,apache允許載入用戶自己開發的Module,在用戶自己開發的模塊中,存在一個register_hooks(參見:http://www.loveopensource.com/?p=18)函數,該函數將調用一個HOOK的hook函數把用戶自己編寫的函數注入到系統處理流程中,該例子中的ap_hook_handler(c_handler, NULL, NULL, APR_HOOK_MIDDLE)函數表示把用戶自己開發的函數c_handler注入到handler這個HOOK中,當handler對應的run函數被調用時,將會調用c_handler函數。
3.2 Run函數實現
        Apache中每個Hook會對應FIRST、ALL和VOID三種處理流程中的一個,這三種處理流程都會順序訪問Hook array的每一項,並調用element中pFunc指向的函數,區別僅在於它們的返回條件不一樣。顧名思義,在FIRST流程順序訪問array中,只要碰到一個pFunc指向的函數執行完成(即返回結果不等於DECLINED),FIRST流程將會結束處理並return;ALL流程將會和FIRST一樣順序訪問array,但只有碰到pFunc指向的函數執行異常(即返回結果既不等於DECLINED,也不等於OK)才返回,如果沒碰到異常將會調用所有element對應的函數;而VOID將會忽略pFunc指向的函數執行情況,不論其返回何值,都將把array中所有element都處理一遍。每一個Hook都對應一個run函數,run函數的實現將會對應FIRST,ALL或VOID中的一個,以下以example HOOK為例,演示FIRST,ALL和VOID三種處理流程下run函數的實現。
3.2.1 FIRST處理流程對應的run函數定義
        FIRST流程順序訪問array的每一個element,只要碰到一個element對應的的函數執行完成(即返回結果不等於DECLINED),FIRST流程就結束處理並return,Example HOOK的FIRST處理流程run函數定義如下:
AP_DECLARE(ret) ap_run_example (apr_pool_t *pconf, apr_pool_t *plog,apr_pool_t *ptemp)
{
    ap_LINK_example_t *pHook;
    int n;
    ret rv;

    if(!_hooks.link_example)
    return decline;

    pHook=(ap_LINK_example_t *)_hooks.link_example->elts;
    for(n=0 ; n < _hooks.link_example->nelts ; ++n)
    {
    rv=pHook[n].pFunc (apr_pool_t *pconf, apr_pool_t *plog,apr_pool_t *ptemp);

    if(rv != decline)
        return rv;
    }
    return decline;
}

從以上代碼中可以看到,當rv 不等於 decline時候,將會從該函數中return,不再繼續處理。
3.2.2 ALL處理流程對應的run函數定義
ALL流程順序訪問array的每一個element,如果不碰到element對應的函數執行異常(即返回結果既不等於DECLINED也不等與OK),ALL流程將會調用所有element對應的函數,Example HOOK的ALL 處理流程run函數定義如下:
ap_DECLARE(ret) ap_run_example (request_rec *r)
    {
    ap_LINK_example_t *pHook;
    int n;
    ret rv;

    if(!_hooks.link_example)
    return ok;

    pHook=(ap_LINK_example_t *)_hooks.link_example->elts;
    for(n=0 ; n < _hooks.link_example->nelts ; ++n)
    {
    rv=pHook[n].pFunc (request_rec *r);

    if(rv != ok && rv != decline)
        return rv;
    }
    return ok;
}
在該函數中可以看到,只有當(rv != ok && rv != decline)時才會從該函數中return,否則將會把所有的array都順序處理一遍。
3.2.3 VOID處理流程對應的run函數定義
VOID將會忽略pFunc指向的函數執行情況,不論其返回何值,都將把array中所有element都處理一遍。Example HOOK的VOID 處理流程run函數定義如下:
AP_DECLARE(void) ap_run_example (apr_pool_t *pchild, server_rec *s)
    {
    ap_LINK_example_t *pHook;
    int n;

    if(!_hooks.link_example)
    return;

    pHook=(ap_LINK_example_t *)_hooks.link_example->elts;
    for(n=0 ; n < _hooks.link_example->nelts ; ++n)
    pHook[n].pFunc (apr_pool_t *pchild, server_rec *s);
}
在該函數中可以看到,不論函數執行如何,將會順序處理一遍hook array中的所有element。
以上的ALL、FIRST和ALL對應的函數名字相同,因此一個HOOK可以從FIRST,ALL或VOID中選擇一種處理方式,定義一個HOOK的run 函數。
3.3 get_hook函數實現
get_hook 函數僅簡單返回hook array的apr_array_header_t 指針,example HOOK對應的get_hook函數實現如下:
AP_DECLARE(apr_array_header_t *) ap_hook_get_example(void)
    {
        return _hooks.link_example;
    }

[ 本帖最後由 ftomorrow 於 2009-3-18 17:15 編輯 ]

[火星人 ] Apache Hook 結構分析(一)已經有552次圍觀

http://coctec.com/docs/service/show-post-23838.html