一、相关结构体

struct msre_engine{apr_pool_t *mp;apr_table_t *variables;apr_table_t *operators;apr_table_t *actions;apr_table_t *tfns;apr_table_t *reqbody_processors;
};
//在msre_engine_variable_register()函数中初始化metadata使用的结构体
struct msre_var_metadata {const char *name;unsigned int type;   /* VAR_TYPE_ constants*/unsigned int argc_min;unsigned int argc_max;fn_var_validate_t validate;fn_var_generate_t generate;unsigned int is_cacheable;/* 0-no,1-yes*/unsigned int availability;/*when does this variable become available?*/
};
struct msc_string { char *name;unsigned int name_len;cahr *value;unsigned int value_len;
};
//运算符元数据结构体
struct msre_op_metadata {const char   *name;fn_op_param_init_t param_init;fn_op_execute_t execute;
};
//转换函数结构体
struct msre_tfn_metadata {const char *name;fn_tfn_execute_t execute;
}
//引擎动作结构体
struct msre_action_metadata {const char *name;unsigned int type;unsigned int argc_min;unsigned int argc_max;unsigned int allow_param_plusminus;unsigned int cardinality;unsigned int cardinality_group;fn_action_validate_t validate;fn_action_init_t init;fn_action_execute_t execute;
};
//在modsecurity_tx_init()函数出现的modsec_rec结构体
struct modsec_rec {apr_pool_t *mp;msc_engine  *modsecurity;request_rec *r_early;request_rec *r;directory_config *dcfg1;directory_config *dcfg2;directory_config *usercfg;directory_config *txcfg;unsigned int reqbody_should_exist;unsigned int reqbody_chunked;unsigned int phase;unsigned int phase_request_headers_complete;unsigned int phase_request_body_complete;apr_bucket_brigade *if_brigade;unsigned int if_seen_eos;unsigned int if_status;unsigned int if_started_forwarding;apr_size_t reqbody_length;apr_bucket_brigade *of_brigade;unsigned int of_status;unsigned int of_done_reading;unsigned int of_skipping;unsigned int of_partial;unsigned int of_is_error;unsigned int resbody_status;apr_size_t resbody_length;char *resbody_data;unsigned int resbody_contains_html;apr_size_t stream_input_length;char *stream_input_data;apr_size_t stream_output_length;char *stream_output_data;unsigned int of_stream_changed;unsigned int if_stream_changed;apr_array_header_t *error_messages;apr_array_header_t *alerts;const char *txid;const char *sessionid;const char *userid;const char *server_software;const char *local_addr;unsigned int local_port;const char *local_user;/*client*/const char *remote_addr;unsigned int remote_port;const char *remote_user;/*useragent*/const char *useragent_ip;/*request*/const char *request_line;const char *request_method;const char *request_uri;const char *query_string;const char *request_protocol;const char *hostname;apr_table_t *request_headers;apr_off_t request_content_length;const char *request_content_type;apr_table_t *arguments;apr_table_t *arguments_to_sanitize;apr_table_t *request_headers_to_sanitize;apr_table_t *response_headers_to_sanitize;apr_table_t *request_cookies;apr_table_t *pattern_to_sanitize;unsigned int urlencoded_error;unsigned int inbound_error;unsigned int outbound_error;unsigned int is_relevant;apr_table_t *tx_vars;apr_table_t *geo_vars;/*response*/unsigned int response_status;const char *status_line;const char *response_protocol;apr_table_t *response_headers;unsigned int response_headers_sent;apr_off_t bytes_sent;/* modsecurity request body processing stuff*/unsigned int msc_reqbody_storage;  /*on disk or in memory*/unsigned int msc_reqbody_spilltodisk;unsigned int msc_reqbody_read;apr_pool_t *msc_reqbody_mp;apr_array_header_t *msc_reqbody_chunks;unsigned int msc_reqbody_length;int msc_reqbody_chunk_position;unsigned int msc_reqbody_chunk_offset;msc_data_chunk *msc_reqbody_chunk_current;char *msc_reqbody_buffer;const char *msc_reqbody_filename;int msc_reqbody_fd;msc_data_chunk *msc_reqbody_disk_chunk;const char *msc_reqbody_processor;int msc_reqbody_error;cosnt char *msc_reqbody_error_msg;apr_size_t msc_reqbody_no_files_length;char *msc_full_request_buffer;int msc_full_request_length;char *multipart_filename;char *multipart_name;multipart_data *mpd;xml_data *xml;
#ifdef WITH_YAJLjson_data *json
#endif/* audit logging */char *new_auditlog_boundary;char *new_auditlog_filename;apr_file_t *new_auditlog_fd;unsigned int new_auditlog_size;apr_md5_ctx_t new_auditlog_md5ctx;unsigned int was_intercepted;unsigned int rule_was_intercepted;unsigned int intercept_phase;msre_actionset *intercept_actionset;const char *intercept_message;/*performance measurement*/apr_time_t request_time;apr_time_t time_phase1;apr_time_t time_phase2;apr_time_t time_phase3;apr_time_t time_phase4;apr_time_t time_phase5;apr_time_t time_storage_read;apr_time_t time_storage_write;apr_time_t time_logging;apr_time_t time_gc;apr_table_t *perf_rules;apr_array_header_t *matched_rules;msc_string *matched_var;int highest_severity;/* upload */int upload_extract_files;int upload_remove_files;int upload_files_count;/* other*/apr_table_t *collections_original;apr_table_t *collections;apr_table_t *collections_dirty;/* rule processing temp pool */apr_pool_t *msc_rule_mptmp;/* content injection*/const char *content_prepend;apr_off_t content_prepend_len;const char *content_append;apr_off_t content_append_len;/*data cache*/apr_hash_t *tcache;apr_size_t tcache_items;/*removed rules*/apr_array_header_t *removed_rules;apr_arrya_header_t *removed_rules_tag;apr_array_header_t *removed_rules_msg;/*removed targets*/apr_table_t *removed_targets;unsigned int allow_scope;/*matched vars*/apr_table_t *matched_vars;void *reqbody_processor_ctx;htmlDocPtr crypto_html_tree;
#if defined(WITH_LUA)#ifdef CACHE_LUAlua_State *L;#endif
#endifint msc_sdbm_delete_error;
};
//在parse_arguments()函数中出现的msc_arg结构体
struct msc_arg {const char *name;unsigned int name_len; unsigned int name_origin_offset;unsigned int name_origin_len;const char *value;unsigned int value_len;unsigned int value_origin_offset;unsigned int value_origin_len;const char *origin;
};
//出现在msc_regexec()函数中的msc_regex_t结构体
struct msc_regex_t {void *re;void *pe;const char *pattern;
};
//出现在msre_ruleset_process_phase()函数中的msre_ruleset结构体
struct msre_ruleset {apr_pool_t *mp;msre_engine *engine;apr_array_header_t *phase_request_headers;apr_array_header_t *phase_request_body;apr_array_header_t *phase_response_headers;apr_array_header_t *phase_response_body;apr_array_header_t *phase_logging;
};
//出现在modsecurity_process_phase()函数中的msre_cache_rec结构体
struct msre_cache_rec {int hits;int changed;int num;const char *path;const char *val;apr_size_t val_len;
};

1.1 位于apache2/mod_securitty2.c文件中,有个模块的入口点,这是挂载到apache主程序的入口:

/* Module entry points 模块的入口点*/
module AP_MODULE_DECLARE_DATA security2_module = {STANDARD20_MODULE_STUFF,create_directory_config, //amerge_directory_configs, //bNULL,    /* create_server_config */NULL,    /* merge_server_configs */module_directives,   //cregister_hooks        //d
};

上述代码块中的a是创建一个directory_config结构体变量,然后赋初值,并return到这个结构体地址。

我们来看看这个结构体内容:

struct directory_config {apr_pool_t *mp;msre_ruleset *ruleset;int is_enabled;int reqbody_access;int reqintercept_oe;int reqbody_buffering;long int reqbody_inmemory_limit;long int reqbody_limit;long int reqbody_no_files_limit;int resbody_access;long int of_limit;apr_table_t *of_mime_types;int of_mime_types_cleared;int of_limit_action;int if_limit_action;const char *debuglog_name;int debuglog_level;apr_file_t *debuglog_fd;int cookie_format;int argument_separator;const char *cookiev0_separator;int rule_inheritance;apr_array_header_t *rule_exceptions;/* -- Audit log -- *//* Max rule time */int max_rule_time;/* Whether audit log should be enabled in the context or not */int auditlog_flag;/* AUDITLOG_SERIAL (single file) or AUDITLOG_CONCURRENT (multiple files) */int auditlog_type;
#ifdef WITH_YAJL/* AUDITLOGFORMAT_NATIVE or AUDITLOGFORMAT_JSON */int auditlog_format;
#endif/* Mode for audit log directories and files */apr_fileperms_t auditlog_dirperms;apr_fileperms_t auditlog_fileperms;char *auditlog_name;char *auditlog2_name;/* The file descriptors for the files above */apr_file_t *auditlog_fd;apr_file_t *auditlog2_fd;/* For the new-style audit log only, the path where audit log entries will be stored */char *auditlog_storage_dir;char *auditlog_parts;/* A regular expression that determines if a response status is treated as relevant */msc_regex_t *auditlog_relevant_regex;/* Upload */const char *tmp_dir;const char *upload_dir;int upload_keep_files;int upload_validates_files;int upload_filemode; /* int only so NOT_SET works */int upload_file_limit;/* Used only in the configuration phase */msre_rule *tmp_chain_starter;msre_actionset *tmp_default_actionset;apr_table_t *tmp_rule_placeholders;/* Misc */const char *data_dir;const char *webappid;const char *sensor_id;const char *httpBlkey;/* Content injection*/int content_injection_enabled;/* Stream Inspection */int stream_inbody_inspection;int stream_outbody_inspection;/* Geo Lookup */geo_db *geo;/* Gsb Lookup */gsb_db *gsb;/* Unicode map*/unicode_map *u_map;/*Cache */int cache_trans;int cache_trans_incremental;apr_size_t cache_trans_min;apr_size_t cache_trans_max;apr_size_t cache_trans_maxitems;apr_array_header_t *component_signatures;/* Request character encoding */const char *request_encoding;int disable_backend_compression;/* Collection timeout */int col_timeout;/*hash of ids*/apr_hash_t *rule_id_htab;/* Hash */apr_array_header_t *hash_method;const char *crypto_key;int crypto_key_len;const char *crypto_param_name;int hash_is_enabled;int hash_enforcement;int crypto_key_add;int crypto_hash_href_rx;int crypto_hash_faction_rx;int crypto_hash_location_rx;int crypto_hash_iframesrc_rx;int crypto_hash_framesrc_rx;int crypto_hash_href_pm;int crypto_hash_faction_pm;int crypto_hash_location_pm;int crypto_hash_iframesrc_pm;int crypto_hash_framesrc_pm;/* xml */int xml_external_entity;
};

上述代码块中的b作用是:合并两个目录配置,参数2和参数3分别是_parent和_child,说明是合并两个父子目录。

上述代码块中的c中结构体叫做module_directives,这里面跟apache中的module的参数写法一样,调用的函数分别是AP_INIT_TAKE1、AP_INIT_TAKE12等,主要是指令名和参数的个数区别。

上述代码块中的d是注册Apache的模块钩子。在此钩子中,相继调用了多个函数,比如初始化函数等。

下面分析register_hooks()中所做的事情:

1.1.1 注册可选函数

#if (!defined(NO_MODSEC_API))/*导出可选的函数在模块register_hooks函数内注册可选函数,将可选函数添加到apache内核维护的全局可选函数哈希表中,Optional Function将可选函数注册到apache内核的全局可选函数哈希表中*/APR_REGISTER_OPTIONAL_FN(modsec_register_tfn);APR_REGISTER_OPTIONAL_FN(modsec_register_operator);API_REGISTER_OPTIONAL_FN(modsec_register_variable);APR_REGISTER_OPTIONAL_FN(modsec_register_reqbody_processor);
#endif

1.1.2 主要的钩子函数

1.1.2.1

ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_FIRST); 

上面函数中目的是预配置的初始化,在hook_pre_config()函数中初始化创建ModSecuritty引擎,其中modsecurity是全局变量。然后有条件的注册了一个modsec_var_log_handler(),看起来是用于log作用的,这里先不分析这个log函数。

hook_pre_config()|->modsecurity=modsecurity_create(mp,MODSEC_ONLINE)|->msre_engine_create(msce->mp);|->apr_pool_create()|->engine=apr_pcalloc()|->engine->tfns=apr_table_make()|->msre_engine_register_default_variables(msce->msre);//在此函数中使用msre_engine_variable_register()函数向引擎中注册很多个默认变量|->msre_engine_variable_register(); |->msre_var_metadata *metadata = ap_pcalloc();|->赋值(包括变量名,回调函数等,回调函数放到后面举例介绍)然后apr_table_setn(engine->variables, name,(void *)metadata);|->msre_engine_register_default_operators(msce->msre);//注册了很多运算符|->msre_engine_op_register()|->msre_op_metadata *metadata = apr_pcalloc()|->赋值(包括变量名,回调函数等,回调函数放到后面举例介绍)然后apr_table_setn();|->msre_engine_register_default_tfns(msce->msre);|->msre_engine_tfn_register()|->msre_tfn_metadata *metadata = apr_pcalloc()|->赋值(包括变量名,回调函数等,回调函数放到后面举例介绍)然后apr_table_setn()|->msre_engine_register_default_actions(msce->msre);|->msre_engine_action_register()|->msre_action_metadata *metadata = apr_pcalloc()|->赋值(包括变量名,回调函数等,回调函数放到后面举例介绍)然后apr_table_setn()

通过举例来说明msre_engine_register_default_variables()函数的需要完成的任务:

/*ARGS_POST*/
msre_engine_variable_register(engine,"ARGS_POST",VAR_LIST,0, 1,var_generic_list_validate,var_args_post_generate,VAR_CACHE,PHASE_REQUEST_BODY
);
其中,var_generic_list_validate()函数中主要判断了参数是否是一个正则表达式
var_args_post_generate()函数:|->for(i=0;i<arr->nelts;i++) if(strcmp("BODY",arg->origin)!=0) continue;if(var->param==NULL)match=1;elseif(var->param_data!=NULL) //正则表达式msc_regexec((msc_regex_t *)var->param_data,...)elseif(strcasecmp(arg->name,var->param)==0) match=1//简单的比较if(match)  //如果我们有一个匹配,将这个参数添加到集合中apr_table_addn(vartab,rvar->name,(void *)rvar)

通过举例来说明msre_engine_register_default_operators()函数需要做的工作,此处的例子中还有一个

1./* contains*/
msre_engine_op_register(engine,"contains",NULL,/*init function to flag var substitution*/msre_op_contains_execute
);
其中,msre_op_contains_execute()函数,参考一个SecRule例子:
SecRule REQUEST_LINE "!@contains .php" t:none,deny,status:403
SecRule ARGS:ip "!@contains %{TX.1}"
|->msre_op_contains_execute() |->expand_macros(msr,str,rule,msr->mp)//在给定的变量中扩展宏("%{NAME}"实体 |->for(i=0;i<=i_max;i++) { if(target[i] == match[0]) { if((match_length==1) || (memcmp((match+1),(target+i+1),(match_length-1)) == 0)) return 1;//匹配 |->return 0;//没有匹配
2./* detectSQLi */msre_engine_op_register(engine,"detectSQLi",NULL,msre_op_detectSQLi_execute);
其中msre_op_detectSQLi_execute()函数会使用libinjection/目录下的相关文件的函数,具体的分析看源代码,暂不介绍

通过举例来说明msre_engine_register_default_tfns()函数的需要完成的任务:

/*lowercase*/
msre_engine_tfn_register(engine,"lowercase",msre_fn_lowercase_execute
);
其中,msre_fn_lowercase_execute()函数具有小写化的功能
|->msre_fn_lowercase_execute()|->while(i<input_len) {int x = input[i];input[i]=tolower(x);if(x!=input[i]) changed=1;i++;}

通过举例来说明msre_engine_register_default_actions()函数的需要完成的任务:

/*phase*/
msre_engine_action_register(engine,"phase",ACTION_DISRUPTIVE,1, 1,NO_PLUS_MINUS,ACTION_CARDINALITY_ONE,ACTION_CGROUP_NONE,msre_action_phase_validate,msre_action_phase_init,NULL);
其中,msre_action_phase_validate()函数什么也没做,msre_action_phase_init()函数根据参数名将actionset->phase设置成相应的值
if(strcasecmp(action->param,"request") == 0)actionset->phase = 2;else if(strcasecmp(action->param,"response") == 0)actionset->phase = 4;else if(strcasecmp(action->param,"logging") == 0)actionset->phase = 5;

1.1.2.2

ap_hook_post_config(hook_post_config, postconfig_beforeme_list,postconfig_afterme_list,APR_HOOK_REALLY_LAST);

由于没有找到ap_hook_post_config()函数的定义,所以上面函数中的postconfig_beforeme_list

和postconfig_afterme_list参数暂时不清楚,我们将重点放在hook_post_config()函数上:

//此函数是(后配置)模块初始化
|->hook_post_config()|->apr_pool_userdata_get(&init_flag,...)//通过apr函数获取在当前池中的key的value|->如果init_flag==NULL,调用apr_pool_userdata_set(),否则调用modsecurity_init(modsecurity,mp);//在hook_pre_config()中已经初始化好了modsecurity对象|->modsecurity_init()预置modsecurity引擎,这个函数必须在配置处理完成后被调用,因为Apache需要知道正在运行的用户名|->rc=apr_global_mutex_create(&msce->auditlog_lock,...)|->rc=apr_global_mutex_create(&msce->geo_lock,...)|->rc=apr_global_mutex_create(&msce->dbm_lock,...)|->real_server_signature=apr_pstrdup(mp, apache_get_server_version()) //存储原始服务器签名|->如果real_server_signature不是NULL,则ap_add_version_component()和change_server_signature()//忽略此函数的过程|->#if (!(defined(WIN32) || defined(NETWARE))) 则执行内部一系列chroot功能|->apr_pool_cleanup_register(mp,(void *)s, module_cleanup,apr_pool_cleanup_null);//在主池被销毁时,为稍后的时间安排主要的清理工作

1.1.2.3

ap_hook_child_init(hook_child_init,NULL,NULL,APR_HOOK_MIDDLE);

上面函数中hook_child_init()函数为每个新的子进程执行初始化

|->hook_child_init()|->modsecurity_child_init(modsecurity);|->xmlInitParser();//在任何其他XML调用之前,需要将此过程调用一次|->apr_status_t rc = apr_global_mutex_child_init()//apr_global_mutex_child_init在子进程中重新打开互斥锁|->apr_global_mutex_child_init()|->apr_global_mutex_child_init()

1.1.2.4 连接进程钩子

ap_hook_process_connection(hook_connection_early, NULL, NULL, APR_HOOK_FIRST)

上面函数中hook_connection_early()函数目的是为连接钩子限制繁忙状态的连接数

|->hook_connection_early()|->ap_get_scoreboard_worker(sbh)|->ws_record=ap_get_scoreboard_worker_from_indexes(i,j)|->tree_contains_ip()

1.1.2.5 事务进程钩子

ap_hook_post_read_request(hook_request_early,postread_beforeme_list, postread_afterme_list, APR_HOOK_REALLY_FIRST);

上面函数中的hook_request_early()函数初始请求处理,在Apache接受请求头之后立即执行,该函数将创建事务上下文。 在下面的函数分析中,有几个定义需要了解一下:

#define AUDITLOG_PART_FIRST                 'A'
#define AUDITLOG_PART_HEADER                'A'
#define AUDITLOG_PART_REQUEST_HEADERS       'B'
#define AUDITLOG_PART_REQUEST_BODY          'C'
#define AUDITLOG_PART_RESPONSE_HEADERS      'D'
#define AUDITLOG_PART_RESPONSE_BODY         'E'
#define AUDITLOG_PART_A_RESPONSE_HEADERS    'F'
#define AUDITLOG_PART_A_RESPONSE_BODY       'G'
#define AUDITLOG_PART_TRAILER               'H'
#define AUDITLOG_PART_FAKE_REQUEST_BODY     'I'
#define AUDITLOG_PART_UPLOADS               'J'
#define AUDITLOG_PART_MATCHEDRULES          'K'
#define AUDITLOG_PART_LAST                  'K'
#define AUDITLOG_PART_ENDMARKER             'Z'
#define NEXT_CHAIN 1
#define NEXT_RULE  2
#define SKIP_RULES 3
|->hook_request_early()|->msr=create_tx_context(r);//初始化事务上下文并创建初始配置|->msr=apr_pcalloc(r->pool,..)//创建一个新的msr并赋值|->apr_allocator_create(&allocator);//创建一个新的分配器|->apr_allocator_max_free_set(allocator, 1024);//设置当前的阈值,在该阈值中,分配器应该开始向系统返回块|->apr_pool_create_ex(&msr->mp,r->pool,NULL,allocator);//创建新pool,这个函数是线程安全的,因为多个线程可以同时安全地创建同一个父池的子池,类似地,一个线程可以在另一个线程访问父池的同时创建一个子池|->apr_allocator_owner_set(allocator, msr->mp);//设置分配器的所有者|->msr->dcfg1=ap_get_module_config(r->per_dir_config,&security2_module)|->msr->usercfg=create_directory_config()//创建特殊的用户配置,将被用来覆盖默认设置|->msr->txcfg=create_direcotry_config() //创建一个事务上下文并用我们刚从Apache得到的目录配置填充它|->msr->txcfg=merge_directory_configs(msr->mp,msr->txcfg,msr->dcfg1)|->init_directory_config(msr->txcfg);//初始化目录配置|->msr->txid=get_env_var(r, "UNIQUE_ID")//检索指定的环境变量,当mod_unique_id模块注册的时候这个值存在|->apr_table_get(r->notes, name)|->msr->request_uri=r->uri//这里有很多赋值操作,目的是填充tx字段,从r的字段到msr的相关字段的赋值|->msr->request_headers = apr_table_copy(msr->mp,r->headers_in)//创建一个新表,并将另一个表复制到其中|->msr->hostname=ap_get_server_name(r)//从请求中获取当前的服务器名称|->modsecurity_tx_init(msr) //调用引擎以继续初始化,继续给msr的相关字段赋值|->apr_pool_cleanup_register(msr->mp,msr,modsecurity_tx_cleanup,apr_pool_cleanup_null);|->apr_table_get(msr->request_headers,"Content-Length");//这里判断了请求是否有正文,总共两者情况有正文|->apr_table_get(msr->request_headers,"Content-Type")|->parse_arguments()  //解析QUERY_STRING字段值|->urldecode_nonstrict_inplace_ex()  //进行urldecode处理|->add_argument(msr,arguments,arg) //向msr的成员arguments成员中增加key-value对|->apr_table_addn(arguments,log_escape_nq_ex(msr->mp,arg->name,arg->name_len),(void *)arg)|->if(msr->txcfg->cookie_format==COOKIES_V0) parse_cookies_v0(msr,te[i].val, msr->request_cookies,";")|->apr_strtok(cookie_header,delim,&saveptr)|->else parse_cookies_v1(msr, te[i].val,msr->request_cookies)|->store_tx_context(msr,r);  //存储事务上下文,可以在随后的阶段、重定向或子请求中找到它|->apr_table_setn(r->nots,NOTE_MSR,(void *)msr);//apr_table_setn()向表中添加键/值对。如果另一个元素已经具有相同的键,那么覆盖之|->#ifdef REQUEST_EARLY|->if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) //一个事务阶段,由于在modsec_rec结构中已经可用,所以不需要显示地提供阶段号|->modsecurity_process_phase_request_headers(msr); //处理进程请求头(REQUEST_HEADERS)阶段|->rc=msre_ruleset_process_phase(msr->txcfg->ruleset,msr)|->首先确定我们需要使用哪一组规则(包括PHASE_REQUEST_HEADERS,PHASE_REQUEST_BODY等)|->apr_table_clear(msr->matched_vars)//从表中删除所有元素|->for(i=0;i<arr->nelts;i++)//这是一个循环,针对每一个ruleset中的相应成员(阶段)的元素来做处理,一直到整个函数结束|->if(mode==SKIP_RULES) //SKIP_RULES用于跳过所有规则,直到我们用指定的规则ID命中一个占位符,然后在此之后继续执行|->if(rule->placeholder != RULE_PH_NONE)//跳过任何标记为占位符的规则|->if(mode==NEXT_CHAIN) //当链中的一个规则不匹配时,就会使用NEXT_CHAIN,然后我们需要跳过该链中的剩余规则,以获得可以执行的下一个规则|->if((mode == NEXT_RULE)&&(skip>0))//如果我们在这里意味着是NEXT_RULE,如果设置"跳过"参数,则需要跳过|->if(((rule->actionser->id!=NULL) && !apr_is_empty_array(msr->removed_rules)) ||(apr_is_empty_array(msr->removed_rules_tag)==0 ||(apr_is_empty_array(msr->removed_rules_msg)==0)) //检查该规则是否在运行时被删除,此处的逻辑块不分析|->rc=msre_rule_process(rule,msr);//使用一个新的内存子池来处理每个规则|->apr_pool_create(&msr->msc_rule_mptmp,msr->mp)//创建规则处理临时池|->#if defined(WITH_LUA) msre_rule_process_lua(rule,msr)//处理lua脚本,这里直接不介绍|->msre_rule_process_normal(rule,msr) //对给定的事务执行规则|->apr_table_get(rule->actionset->actions, "multiMatch")  //获取multiMatch字段的值|->for(i=0;i<rule->targets->nelts;i++) {list_count=targets[i]->metadata->generate(msr,targets[i],rule,vartab,mptmp)//这里调用之前初始化的回调函数|->for(i=0;i<arr->nelts;i++) //循环一直到函数结尾,循环遍历最终目标列表中的目标,根据需要执行转换,并调用操作符|->if(msr->txcfg->cache_trans != MODSEC_CACHE_DISABLED) //判断这是不是var缓存|->for(k=0;k<tarr->nelts;k++) //构建转换函数的最终列表apr_table_addn(normtab,action->param,(void *)action) //增加t的参数到表中|->if(usecache && !multi_match && (crec != NULL) &&(crec == last_crec)) //如果最后一个缓存的tfn是列表中的最后一个,那么我们可以在这里停止并立即执行该操作|->rc = execute_operator(var,rule,msr,acting_actionset, mptmp)//根据给定值调用规则操作符,例如: SecRule REQUEST_HEADERS:Content-Type "text/xml" ...或者 SecRule REQUEST_HEADERS:User-Agent "@contains SECRET_PASSWORD"|->tarr=apr_table_elts(msr->removed_targets)|->telts=(const apr_table_entry_t*)tarr->elts|->for(i=0;i<tarr->nelts;i++) //循环处理msr的removed_targets成员rc=msre_ruleset_rule_matches_exception(rule,re) //if(rc>0) rc=fetch_target_exception(rule,msr,var,exceptions)|->rc=rule->op_metadata->execute(msr,rule, var, &my_error_msg) //此函数调用了op_metadata的回调执行函数,是最关键的函数之一,另一个是转换metadata的回调执行函数和action_metadata的回调函数|->if(((rc==0)&&(rule->op_negated == 0)) || ((rc==1)&&(rule->op_negated==1)))//返回RULE_NO_MATCH|->else  //匹配if(rc==0) //记录日志*(const msre_rule **)apr_array_push(msr->matched_rules) = rule;if (var!=NULL && msr !=NULL)//保存最后匹配的var数据给msr->matched_var的各个成员赋值,给创建的变量mvar赋值apr_table_addn(msr->matched_vars, mvar->name, (void *)mvar)if((acting_actionser->serverity>0)&&(acting_actionset->serverity<msr->highest_severity)&&!rule->actionset->is_chained)msre_perform_nondisruptive_actions(msr,rule,rule->actionset,mptmp)//执行非破坏性操作|->for(i=0;i<tarr->nelts;i++)action->metadata->execute(msr,mptmp,rule,action)//执行action_metadata的回调执行函数if(rule->actionset->is_chained==0)msre_perform_disruptive_actions(msr,rule,acting_actionset,mptmp,my_error_msg)//执行破坏性操作|->for(i=0;i<tarr->nelts;i++)action->metadata->execute(msr,mptmp,rule,action)|->if(actionset->intercept_action_rec->metadata->type==ACTION_DISRUPTIVE)actionset->intercept_action_rec->metadata->execute(msr,mptmp,rule,actionset->intercept_action_rec)|->if((msr->phase==PHASE_LOGGING)||...)apr_array_push(msr->alerts)=msc_alert_message(msr,actionset,NULL,message)//msc_alert_message()格式化一个警告信息|->msc_alert(msr, log_level, actionset, "Warning", message)|->tarr=apr_table_elts(normtab)//从normtab表中获取元素的,返回整个元素数组的地址|->for(;k<tarr->nelts;k++) if(multi_match && (k==0||tfnchanged)) //在多匹配模式下,我们在开始时执行一次运算符,然后每次变量被转换函数改变一次rc=execute_operator(var,rule,msr,acting_actionset,mptmp)metadata=(msre_tfn_metadata *)action->param_data;tfnchanged=metadata->execute(mptmp,(unsigned char *)var->value,var->value_len,&rval,&rval_length)//调用metadata的回调函数if(usecache) //这里不介绍,忽略|->if(!multi_match || tfnchanged) //如果没有启用多匹配,则执行操作符,或者如果是,我们需要处理最后一个转换的结果rc=execute_opeartor(var,rule,msr,acting_actionset,mptmp)|->if(rc==RULE_NO_MATCH) //如果返回值rc==RULE_NO_MATCH|->else if(rc==RULE_MATCH)//如果返回值rc==RULE_MATCH|->else if(rc<0) //如果返回值rc小于0,表示规则匹配失败|->else   //剩余的情况表示规则匹配失败而且未知的返回码|->modsecurity_process_phase_request_body(msr)|->rc=msre_ruleset_process_phase(msr->txcfg->ruleset,msr)//到这儿,请求体和请求头的处理基本相同|->modsecurity_process_phase_response_headers(msr);|->rc=msre_ruleset_process_phase(msr->txcfg->ruleset,msr);//到这儿,响应头和请求头的处理基本相同|->modsecurity_process_phase_response_body(msr);|->msre_ruleset_process_phase(msr->txcfg->ruleset,msr)//到这儿,响应体和请求头的处理基本相同|->modsecurity_process_phase_logging(msr);|->msre_ruleset_process_phase(msr->txcfg->ruleset,msr)|->modsecurity_persist_data(msr)|->collection_store(msr,col)|->collections_remove_stale(msr,te[i].key)|->if(msr->is_relevant==0) //这个请求是否与日志记录有关?is_response_status_relevant(msr,msr->r->status) //检查状态|->if((msr->txcfg->upload_keep_files==KEEP_FILES_ON)||...)//如果我们向保存这些文件(如果有的话)|->sec_audit_logger(msr) //调用审计日志记录器|->#ifdef WITH_YAJL  sec_audit_logger_json(msr) //这里不介绍|->sec_audit_logger_native(msr) //以本机格式生成审计日志条目|->msr->new_auditlog_boundary=create_auditlog_boundary(msr->r)|->if(msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) //串行日志记录-我们已经有一个打开的文件描述符|->elseapr_md5_init(&msr->new_auditlog_md5ctx)//MD5初始化,开始MD5操作,编写新的上下文msr->new_auditlog_filename=construct_auditlog_filename(msr->mp,msr->txid)//构造一个文件名,用于存储审计日志条目entry_filename=msr->txcfg->auditlog_storage_direntry_basename=file_dirname(msr->mp,entry_filename)apr_dir_make_recursive()//在文件系统上创建一个新目录,但行为类似于“mkdir -p”。根据需要创建中间目录。如果路径已经存在,则不会报告错误。apr_file_open()|->apr_global_mutex_lock(msr->modsecurity->auditlog_lock)|->sec_auditlog_write(msr,text,strlen(text))|->if(strchr(msr->txcfg->auditlog_parts,AUDITLOG_PART_REQUEST_HEADERS)!=NULL) //REQUEST_HEADERS的日志|->if(strchr(msr->txcfg->auditlog_parts,AUDITLOG_PART_REQUEST_BODY)!=NULL) //REQUEST_BODY|->if(strchr(msr->txcfg->auditlog_parts,AUDITLOG_PART_RESPONSE_HEADERS) !=NULL) //RESPONSE_HEADERS|->if(strchr(msr->txcfg->auditlog_parts,AUDITLOG_PART_RESPONSE_BODY) !=NULL) //RESPONSE_BODY|->剩下的if分支在这里不显示|->rc=perform_interception(msr)  //使用结构本身指定的方法拦截事务,必须返回一个HTTP状态码,它将被用来终止事务|->switch(actionset->intercept_action) //确定如何响应和准备日志消息case ACTION_DENY:case ACTION_PROXY:case ACTION_DROP:case ACTION_REDIRECT:expand_macros(msr, var, NULL, msr->mp)case ACTION_ALLOW:case ACTION_PAUSE:case ACTION_ALLOW_PHASE:case ACTION_ALLOW_REQUEST:default:|->msc_alert_message(msr,actionset,NULL,message)|->msc_alert()

1.1.2.6

ap_hook_fixups(hook_request_late, fixups_beforeme_list, NULL,APR_HOOK_REALLY_FIRST)

上述函数中的hook_request_late()函数作为处理程序链中的第一个钩子,该函数执行ModSecurity请求处理的第二阶段

|->hook_request_late()|->msr=retrieve_tx_context(r) //找到事务上下文并确保我们继续进行|->if(msr->phase_request_body_complete) //这个阶段已经完成了吗?|->msr->dcfg2=(directory_config *)ap_get_module_config(r->per_dir_config,&security2_module)//获取第二个配置上下文|->msr->txcfg=create_directory_config(msr->mp,NULL)//创建一个事务上下文|->msr->txcfg=merge_directory_configs(msr->mp,msr->txcfg,msr->dcfg2) |->msr->txcfg=merge_directory_configs(msr->mp,msr->txcfg,msr->usercfg);//使用显示用户设置更新|->init_directory_config(msr->txcfg)|->rc=read_request_body(msr,&my_error_msg) //从客户端读取请求体|->modsecurity_request_body_start(msr, error_msg) |->bb_in=apr_brigade_create()|->do{rc=ap_get_brigade(r->input_filters,...)for(bucket=APR_BRIGADE_FIRST(bb_in);...) //循环遍历brigade中的Bucket,以便提取可用数据的大小rc=apr_bucket_read(bucket,&buf,&buflen,APR_BLOCK_READ)if(buflen!=0)modsecurity_request_body_store(msr,buf,buflen,error_msg)//存储一大块请求体数据if(APR_BUCKET_IS_EOS(bucket))finished_reading=1;msr->if_seen_eos=1;}while(!finished_reading);|->modsecurity_request_body_end(msr,error_msg) //停止接收请求体      

1.1.2.7 Logging

ap_hook_error_log(hook_error_log,NULL,NULL,APR_HOOK_MIDDLE)

此函数中的hook_error_log()函数在每次Apache都有要写入的错误日志的东西时调用

|->hook_error_log|->retrieve_tx_context((request_rec *)info->r) //通过查看朱请求和之前的请求来检索之前存储的事务上下文
ap_hook_log_transaction(hook_log_transaction,NULL,transaction_afterme_list,APR_HOOK_MIDDLE)

上述函数中的hook_log_transaction()函数在每个事务结束时调用

|->hook_log_transaction()|->msr=retrieve_tx_context(r)|->msr->response_protocol=get_response_protocol(origr)|->sec_guardian_logger(r,origr,msr)  //Guardian日志记录器用于连接到web服务器保护的外部脚本——httpd_guardian。|->modsecurity_process_phase(msr, PHASE_LOGGING) //调用引擎来完成剩余的工作

1.1.2.8 Filter hooks

ap_hook_insert_filter(hook_insert_filter,NULL,NULL,APR_HOOK_FIRST)

上述函数中的hook_insert_filter()在请求处理开始之前调用,这是我们需要决定是否要连接到输出过滤器链的时候

|->hook_insert_filter()|->msr=retrieve_tx_context(r)//首先发现事务上下文|->ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection) //增加输入过滤器|->ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection)  //增加输出过滤器
ap_hook_insert_error_filter(hook_insert_error_filter,NULL,NULL,APR_HOOK_FIRST)

上述函数中的hook_insert_error_filter()在Apache开始处理错误时调用,这是一个插入到输出过滤器链中的机会。

|->hook_insert_error_filter()|->msr=retrieve_tx_context(r)|->ap_add_output_filter("MODSECURITY_OUT",msr,r,r->connection)//如果输出过滤器已经完成,不要运行此行

1.1.2.9 注册一个输入过滤器

ap_register_input_filter("MODSECURITY_IN", input_filter, NULL, AP_FTYPE_CONTENT_SET)

上述函数用于在系统中注册一个输入过滤器,在执行此注册之后,可以使用ap_add_input_filter()将过滤器添加到过滤器链中,并简单地指定名称。其中input_filter()函数会将先前存储的请求体转发到链。

|->input_filter()|->rc=modsecurity_request_body_retrieve_start(msr,&my_error_msg)  //准备转发请求体|->rc=modsecurity_request_body_retrieve(msr,&chunk,(unsigned int)nbytes,&my_error_msg)|->if(rc==0) modsecurity_request_body_retrieve_end(msr)

1.1.2.10 注册输出过滤器

ap_register_output_filter("MODSECURITY_OUT", output_filter, NULL, AP_FTYPE_CONTENT_SET - 3)

确保输出过滤器在其他模块之前运行,这样我们就可以得到一个不被修改的更好的请求

|->output_filter()|->msr->response_protocol=get_response_protocol(r)|->rc=modify_response_header(msr)|->rc=modsecurity_process_phase(msr,PHASE_RESPONSE_HEADERS)|->if(rc>0) perform_interception(msr) //事务需要被中断|->rc=output_filter_init(msr,f,bb_in) //初始化输出过滤器switch(rc)case -2:case -1:case 0:|->for(bucket=APR_BRIGADE_FIRST(bb_in);...) {//循环遍历brigade中的bucket,以便提取可用数据的大小rc=apr_bucket_read(bucket,&buf,&buflen, APR_BLOCK_READ);if(APR_BUCKET_IF_EOS(bucket))bucket_ci=apr_bucket_heap_create(msr->content_append,...)APR_BUCKET_INSERT_BEFORE(bucket,bucket_ci);//在指定的桶前插入一个桶|->ap_save_brigade(f,&msr->of_brigade,&bb_in,msr->mp)|->flatten_response_body(msr) |->rc=modsecurity_process_phase(msr,PHASE_RESPONSE_BODY);//处理阶段RESPONSE_BODY|->if(rc>0) perform_interception(msr)|->perpend_content_to_of_brigade(msr, f)|->rc=send_of_brigade(msr, f)|->if(msr->phase<PHASE_RESPONSE_BODY)flatten_response_body(msr)modsecurity_process_phase(msr,PHASE_RESPONSE_BODY)|->inject_content_to_of_brigade(msr,f)|->prepend_content_to_of_brigade(msr, f)|->rc=send_of_brigade(msr,f)//将数据发送到过滤器流

“ModSecurity2”源码分析相关推荐

  1. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  2. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  3. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  4. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  5. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

  6. ViewGroup的Touch事件分发(源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...

  7. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  8. MyBatis原理分析之四:一次SQL查询的源码分析

    上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的. 准备工作 Mybatis完成一次SQL查询需要使用的代码如下: Java代码   String res ...

  9. [转]slf4j + log4j原理实现及源码分析

    slf4j + log4j原理实现及源码分析 转载于:https://www.cnblogs.com/jasonzeng888/p/6051080.html

最新文章

  1. powershell真香
  2. 分布式架构:并发重复请求和幂等场景技术实现总结
  3. ajax的访问 WebService 的方法
  4. u-boot移植随笔:u-boot启动流程简图
  5. linux 重新安装内核,升级操作系统内核(不重新安装UltraPath)
  6. dom4j的操作(增删改查)
  7. 数据库系统概论第五版_第九章:关系查询处理和查询优化
  8. 服务器机柜设备信息卡,信息机房标识标准V.doc
  9. 北航计算机学院博士开题,【北航毕设开题报告】北航博士开题报告格式.doc
  10. 分享七个超好用的免费工具网站,每一个都是神器!
  11. 堆栈的区别linux C,uClinux堆栈
  12. 牛客网--23803--DongDong认亲戚
  13. 一点处的导数无法确定单调性
  14. 手提计算机10发现不到打印机,笔记本电脑连接打印机的详细步骤_笔记本电脑如何连接打印机-win7之家...
  15. java imageio temp_Java ImageIO.setUseCache方法代碼示例
  16. Java全栈学习day05(面向对象02)
  17. 数字转换成大写人民币
  18. 附件的文件夹超过了服务器,邮件附件太大发不了 这3种方式了解一下
  19. SPRING系列一之 依赖注入
  20. 设置VSCode编辑器、终端字体为微软雅黑Microsoft Yahei,字号大小为11像素

热门文章

  1. 垃圾收集算法与垃圾收集器
  2. 0-1 背包问题的 4 种解决方法算法策略
  3. 【最优解】Leecode 594. 最长和谐子序列——Leecode每日一题系列
  4. 计算机网络 数据段、报文、IP数据报、数据包、MAC帧的区别;应用层、运输层、网络层、数据链路层、物理层的区别与功能;转发器、集线器、网桥、交换机、路由器、网关的功能与区别
  5. 黑盒测试的用例设计方法
  6. Android 自动向上滚动,android – Recyclerview在插入数据时自动向上滚动
  7. mysql数据库基本操作总结与归纳
  8. win service 2003 和 win service2008 区别
  9. Android与服务器端数据交互(基于SOAP协议整合android+webservice)
  10. 接受map_[译] 图解 Map、Reduce 和 Filter 数组方法