snort 源码分析之规则结构分析(四)

解析完一条规则后,会调用FinishPortListRule函数将该规则进行归类。归类的原则是

   Finish adding the rule to the port tables
  
   1) find the table this rule should belong to (src/dst/any-any tcp,udp,icmp,ip or nocontent)
   2) find an index for the sid:gid pair
   3) add all no content rules to a single no content port object, the ports are irrelevant so
      make it a any-any port object.
   4) if it's an any-any rule with content, add to an any-any port object
   5) find if we have a port object with these ports defined, if so get it, otherwise create it.
      a)do this for src and dst port
      b)add the rule index/id to the portobject(s)
      c)if the rule is bidir add the rule and port-object to both src and dst tables

完成将规则添加到端口表(每条规则都有规则头,还有协议,根据他们进行归类)
  
    1)找到此规则应属于的表(src / dst / any-any tcp,udp,icmp,ip或nocontent)
    2)找到sid:gid键值对的索引
    3)将所有无content选项规则添加到单个无content选项端口对象,端口无关紧要,因此将其放入any-any port object。
    4)如果它是包含content的any-any(源和目的端口都是any)规则,则添加到any-any port object
    5)查找我们是否有定义了这些端口的端口对象,如果是这样,则获取它,否则创建它。
       a)为src和dst端口执行此操作
       b)将规则索引/ id添加到portobject(s)
       c)如果规则是双向的,则将规则和端口对象添加到src和dst表中

数据结构

/* 端口对象: 二者的前三个成员一样, 这样设计,可以重用部分处理函数 PortObjectAppend,还有其他的处理也比较方便*/
typedef struct { 
	char           * name;      /* user name - always use strdup or malloc for this*/
	int              id;        /* internal tracking - compiling sets this value */
    SF_LIST        * item_list; /* list of port and port-range items */
    SF_LIST        * rule_list; /* list of rules  */
    void           * data;      /* user data, PORT_GROUP based on rule_list - only used by any-any ports */
    void           (*data_free)(void *);
}PortObject;

typedef struct  {
	char           * name;      /* user name - always use strdup or malloc for this*/
	int              id;        /* internal tracking - compiling sets this value */
    SF_LIST        * item_list; /* list of port and port-range items */
    SFGHASH        * rule_hash; /* hash of rule (rule-indexes) in use */
    int              port_cnt;  /* count of ports using this object */
    BITOP          * bitop;     /* for collecting ports that use this object */
    void           * data;      /* user data, PORT_GROUP based on rule_hash  */
    void           (*data_free)(void *);
}PortObject2;

/*端口表 : 将规则按端口进行归类*/
typedef struct _PortTable_s {

    /* turns on group optimization, better speed-but more memory 
     * otherwise a single merged rule group is used.
     */
    int pt_optimize;

    /* save the users input port objects in this list 
     * rules may be added after creation of a port object
     * but the ports are not modified.
     */
    SF_LIST * pt_polist;
    int       pt_poid;

    /*
    * Array of lists of PortObject pointers to unique PortObjects, 
    * the associated rule lists are stored in Data elements in rh,
    * the keys are the address of the PortObjects
    */
    SF_LIST * pt_port_lists[SFPO_MAX_PORTS];

    /* Compiled / merged port object hash table */
    SFGHASH * pt_mpo_hash;
    SFGHASH * pt_mpxo_hash;

    SF_LIST * pt_plx_list;

    /*  a single rule list with all rules merged together */
    SF_LIST * pt_merged_rule_list; 

    /* 
    * Final Port/Rule Groupings, one port object per port, or null
    */
    PortObject2 * pt_port_object[SFPO_MAX_PORTS];
        
    int pt_lrc; /* large rule count, this many rules is a large group */

    /* Stats */
    int single_merges; /* single PortObject on a port */
    int small_merges;  /* small port objects merged into a bigger object */
    int large_single_merges; /* 1 large + some small objects */
    int large_multi_merges; /* >1 large object merged + some small objects */
    int non_opt_merges;

}PortTable;


/* 用于管理所有端口表的数据结构*/
typedef struct {

    PortTable * tcp_src, * tcp_dst;/* */
    PortTable * udp_src, * udp_dst;
    PortTable * icmp_src,* icmp_dst;
    PortTable * ip_src,  * ip_dst;
    
    PortObject * tcp_anyany;
    PortObject * udp_anyany;
    PortObject * icmp_anyany;
    PortObject * ip_anyany;
    
    PortObject * tcp_nocontent; 
    PortObject * udp_nocontent; 
    PortObject * icmp_nocontent; 
    PortObject * ip_nocontent; 

}rule_port_tables_t;

上面的函数会进行去重处理、合并规则所属的类别等,处理完之后,调用fpCreateFastPacketDetection函数进行生成检测引擎。

内部调用fpCreatePortGroups,再在里面调用fpCreatePortTablePortGroups进行创建检测引擎需要的数据结构,并将规则选项中的模式串加入检测引擎,编译规则生成引擎。

int fpCreateFastPacketDetection(SnortConfig *sc)
{
    rule_port_tables_t *port_tables;
    FastPatternConfig *fp;

    /* This is somewhat necessary because of how the detection option trees
     * are added via a callback from the pattern matcher */
    if(!rule_count || (sc == NULL))
        return 0;

    port_tables = sc->port_tables;
    fp = sc->fast_pattern_config;
...

    if (fpCreatePortGroups(sc, port_tables))
        FatalError("Could not create PortGroup objects for PortObjects\n");

...

    if (fpCreateRuleMaps(sc, port_tables))
        FatalError("Could not create rule maps\n");

...

    return 0;
}
/*
 * 对所有的端口表创建端口组对象
 * 注意 any-any ports使用PortObjects表示,这个函数会将其转化为PortObject2,为后面的创建端口组函数做准备
 */
static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
{
    PortObject2 *po2, *add_any_any = NULL;
    FastPatternConfig *fp = sc->fast_pattern_config;

    if (!rule_count)
        return 0 ;

    /* 进行转换 */
    po2 = PortObject2Dup(p->tcp_anyany);
    if (po2 == NULL)
        FatalError("Could not create a PortObject version 2 for tcp-any-any rules\n!");

...
    if (fpCreatePortTablePortGroups(sc, p->tcp_src, add_any_any))
    {
        LogMessage("fpCreatePorTablePortGroups failed-tcp_src\n");
        return -1;
    }
...
    /* 转换后的po2进行处理,通过po2创建PortGroup。里面会基于content和uricontent构建多模式状态机的第一步*/
    if (fpCreatePortObject2PortGroup(sc, po2, 0))
    {
        LogMessage("fpCreatePorTablePortGroups failed-tcp any-any\n");
        return -1;
    }
...
}
/*
 * 内部遍历p中的pt_mpo_hash哈希表调用fpCreatePortObject2PortGroup函数进行构建多模式状态机
 */
static int fpCreatePortTablePortGroups(SnortConfig *sc, PortTable *p, PortObject2 *poaa)
{
...
   for (node=sfghash_findfirst(p->pt_mpo_hash);  //p->pt_mpxo_hash
        node;
        node=sfghash_findnext(p->pt_mpo_hash) ) //p->pt->mpxo_hash
   {
        PortObject2 * po;

        po = (PortObject2*)node->data;
...
        if (fpCreatePortObject2PortGroup(sc, po, poaa))
        {
            LogMessage("fpCreatePortObject2PortGroup() failed\n");
            return -1;
        }
...
   }
}
static int fpCreatePortObject2PortGroup(SnortConfig *sc, PortObject2 *po, PortObject2 *poaa)
{
    SFGHASH_NODE *node;
    unsigned sid, gid;
    OptTreeNode * otn;
    PORT_GROUP * pg;
    PortObject2 *pox;
    FastPatternConfig *fp = sc->fast_pattern_config;

    /* verify we have a port object */
    if (po == NULL)
        return 0;

    po->data = 0;
...

    /* rule_hash中保存了多有与该端口相关的所有规则选项的索引,通过它们快速获取到对应的规则选项 */
    if (po->rule_hash == NULL)
        return 0;

    /* 初始化:创建port_group */
    pg = (PORT_GROUP *)SnortAlloc(sizeof(PORT_GROUP));

    /* 创建多模式状态机需要的对象,每一种类别都会分配内存*/ 
    if (fpAllocPms(sc, pg, fp) != 0)
    {
        free(pg);
        return -1;
    }


    /*
     * 遍历PortObject中的规则,并将它们添加到PORT_GROUP 多模式状态机和 port group RULE_NODE 
     * 的list中(这个list在某些情况下会用到,比如fpEvalHeader函数中,在遍历规则进行检测的时候会
     * 用到)
     * po 源端口、目的端口:content/uri and nocontent
     * poaa 任意端口:content/uri and nocontent
 * each PG has 
     * 每个PG 都有src or dst contents, generic-contents, and no-contents
     */
    pox = po;

    while (pox != NULL)
    {
        /* 遍历规则索引的哈希表*/
        for (node = sfghash_findfirst(pox->rule_hash);
                node;
                node = sfghash_findnext(pox->rule_hash))
        {
            int *prindex = (int *)node->data;

            /* be safe - no rule index, ignore it */
            if (prindex == NULL)
                continue;

            /* 通过规则索引找到对应的 gid:sid */
            gid = RuleIndexMapGid(ruleIndexMap, *prindex);
            sid = RuleIndexMapSid(ruleIndexMap, *prindex);

            /*使用 gid:sid 定位 otn */
            otn = OtnLookup(sc->otn_map, gid, sid);
            if (otn == NULL)
            {
...
                continue;
            }

...
            /* 1:将otn中的content、uricontent等模式串添加到pg中对应的pg的链表中:Content List、No-Content List、 Uri-Content List
             * 2: 调用mpseAddPatternWithSnortConfig往pg的pgPms数组中对应的模式状态机中放入最长的模式串,为后面编译状态机做准备
             */
            if (fpAddPortGroupRule(sc, pg, otn, fp) != 0)
                continue;
        }
...
        if (pox == poaa)
            break;

        pox = poaa;
    }

    /* 调用mpsePrepPatternsWithSnortConf编译模式状态机 */
    if (fpFinishPortGroup(sc, pg, fp) != 0)
        return 0;

    po->data = pg;
    po->data_free = fpDeletePortGroup;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/guoguangwu/article/details/88808792