一 加入审计回调函数
// 全局声明
typedef void (*Egd_save_action_record)(void *, void *, int);
Egd_save_action_record save_action_record = NULL;
// 初始化赋值
save_action_record = _dpd.icsSaveActionRecord;
// 调用
(*save_action_record)(p, &savemdb, ENUM_ACTION_EGD);
1.审计回调函数三个参数:
(1)void *p:指向snort解码后的网络分组包SFSnortPacket *p
(2)void *mdb:IcsSaveMdb对象指针,保存协议深度解析后的结果
(3)int type:协议的cid,标识协议的枚举值
2.审计回调函数作用:
开发snort.c里ICSSaveActionMDB和SaveActionMDB函数,审计回调函数最终会调用snort.c里的ICSSaveActionMDB和SaveActionMDB函数,分别用于将mdb内容拷贝到工控审计和传统审计的共享内存块,由审计业务进程消费后执行各种业务处理。
二 全局对象_dpd
dpd是一个非常重要的全局对象,它被所有插件引用,其定义在sf dynamic_preproc_lib,c中:
DynamicPreprocessorData _dpd;
PREPROC_LINKAGE int InitializePreprocessor(DynamicPreprocessorData *dpd) // 初始化_dpd
{
...
_dpd = *dpd; // 结构体拷贝
DYNAMIC_PREPROC_SETUP();// 调用插件的SetUp函数
}
(1)DynamicPreprocessorData数据结构:
定义在sf_dynamic_preprocessor.h中
typedef struct _ DynamicPreprocessorData {
...
Save_action_record saveActionRecord;
Save_action_record icsSaveActionRecord;
...
}DynamicPreprocessorData;
(2)_dpd赋值
saveActionRecord和icsSaveActionRecord分别是保存传统审计和工控审计的回调函数,它们在sf_dynamic_plugins.c的InitDynamicPreprocessors被赋值:
void InitDynamicPreprocessors(void)
{
DynamicPreprocessorData preprocData;
...
preprocData.icsSaveActionRecord = ICSSaveActionMDB; // ICSSaveActionMDB定义在snort.cc
...
return InitDynamicPreprocessorPlugins(&preprocData);
}
int InitDynamicPreprocessorPlugins(DynamicPreprocessorData *info)
{
DynamicPreprocessorPlugin *plugin; // 指向插件链表(双向链表)
plugin = loadedPreprocessorPlugins; // 插件链表头结点指针
while (plugin)
{
int i = plugin->initFunc(info); // 调用InitializePreprocessor 将info赋给全局对象_dpd
...
}
}
(3)插件初始化_dpd
snort本身是单进程处理业务,当snort接收到数据包时,所有插件都会被遍历一遍,根据snort.conf配置从而进行相应处理。
(4)插件引用_dpd
dpd在sf_dynamic_preprocessor.h中声明为extern DynamicPreprocessorData _dpd;而每个插件头文件又包含了sf_dynamic_preprocessor.h,因此每个插件都可以引用到_dpd。
三 插件Setup函数
所有插件都有一个Setup函数,如SetupEgd,形如:
void SetupEgd(void)
{
...
_dpd.registerPreproc("egd", EgdInit);
}
SetupEgd定义在spp_egd.c:
#define SetupEgd DYNAMIC_PREPROC_SETUP
这样InitializePreprocessor就可以Setup各个插件了。Setup函数又会调用pluginbase.c的RegisterPreprocessors,对插件进行初始化:
void RegisterPreprocessors(const char *keyword, PreprocConfigFunc pp_config_func)
{
PreprocConfigFuncNode *node = (PreprocConfigFuncNode *)SnortAlloc(sizeof(PreprocConfigFuncNode));
...
else
{
...
last->next = node; // 将node插入链表最后
}
...
}
RegisterPreprocessors会将插件的Init函数插入PreprocConfigFuncNode类型链表的末尾。
函数调用链:SetupEgd=》registerPreproc=》RegisterPreprocessors
四 插件处理函数
所有插件都有一个处理函数,如ProcessEgd,形如:
static void ProcessEgd(void *packetPtr, void *context)
{
EgdSessionData *egdSessionData = NULL;
// 以下过程是对egd报文进行解析,并保存解析结果
...
}
而ProcessEgd是由_dpd的addPreproc调用:
_dpd.addPreproc(sc, ProcessEgd, PROIRTY_APPLICATION, PP_EGD, PROTO_BIT_TCP | PROTO_BIT_UDP);
// addPreproc是指向sf_dynamic_plugins.c的AddPreprocessor
void AddPreprocessor(struct _SnortConfig *sc, void (*pp_func)(void *, void *), uint16_t priority, uint32_t preproc_id, uint32_t proto_mask) {
...
return (void *)AddFuncToPreprocList(sc, preprocessorFunc, priority, preproc_id, proto_mask);
}
// AddFuncToPreprocList是定义在pluginbase.c中
PreprocEvalFuncNode *AddFuncToPreprocList(SnortConfig *sc, PreprocEvalFunc pp_eval_func, uint16_t priority, uint32_t preproc_id, uint32_t proto_mask)
{
...
tSfPolicyId policy_id = getParserPolicy(sc); // 从传入的snort配置读取策略id
...
p = sc->targeted_policies[policy_id]; // 获取具体策略指针
...
else
{
PreprocEvalFuncNode *tmp = p->preproc_eval_funcs;
// 从该策略处理函数链表中查找一个不同preproc_id并且优先级高结点,将pp_func插入该结点前
}
...
p->num_preprocs++; // 累加该策略对应的处理函数数量
...
return node; // 返回链表中该结点
}
即pluginbase将策略对应的插件处理函数组织成链表,当插件调用addPreproc时都会向该链表插入一个新结点,并且链表是按优先级从高到低排序,越往头结点位置的结点优先级越高,也优先被snort调用。