免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2183 | 回复: 0
打印 上一主题 下一主题

[Web] Apache Hook 结构分析(一) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-03-18 17:13 |只看该作者 |倒序浏览
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.aspxhttp://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 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP