博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
snort源码分析之规则结构分析(二)
阅读量:2792 次
发布时间:2019-05-13

本文共 8744 字,大约阅读时间需要 29 分钟。

上篇对照snort的规则简单介绍了一下那些规则字段

接下来看一下规则头和规则选项的数据结构

/* 规则头匹配函数链表*/typedef struct _RuleFpList{    /* context data for this test */    /* 目前规则头没有使用 */    void *context;    /* rule check function pointer */    /* 规则头匹配的函数类型指针,参数:数据包,规则头对象, 下一个需要匹配的对象:这样就可以在函数内部进行遍历链表实现整个规则头的匹配, 标志*/    int (*RuleHeadFunc)(Packet *, struct _RuleTreeNode *, struct _RuleFpList *, int);    /* pointer to the next rule function node */    struct _RuleFpList *next;} RuleFpList;/* * 规则头 */typedef struct _RuleTreeNode{    /*规则头的匹配函数 是一个单链表*/    RuleFpList *rule_func; /* match functions.. (Bidirectional etc.. ) */    /* 规则头计数,会去重*/    int head_node_number;    /* 规则类型  比如上一篇的举例:RULE_TYPE__ALERT*/    RuleType type;    /* 规则头中的 源IP组*/    IpAddrSet *sip;    /* 目的IP组*/    IpAddrSet *dip;    /* 协议类型  : tcp udp */    int proto;    /* 源端口组*/    PortObject * src_portobject;    /* 目的端口则*/    PortObject * dst_portobject;    /* 标记 : 数据包方向, 端口类型:any等等*/    uint32_t flags;     /* control flags */    /* stuff for dynamic rules activation/deactivation */    /* 与 Dynamic规则一起使用*/    int active_flag;    int activation_counter;    int countdown;    ActivateListNode *activate_list;#if 0    struct _RuleTreeNode *right;  /* ptr to the next RTN in the list */    /** list of rule options to associate with this rule node */    OptTreeNode *down;   #endif    /**points to global parent RTN list (Drop/Alert) which contains this      * RTN.     */    /*该规则所属某一类的规则头链表头 alert*/    struct _ListHead *listhead;    /**reference count from otn. Multiple OTNs can reference this RTN with the same     * policy.     */    /* 规则选项的引用计数, 多个规则选项从属于一个规则头的情况非常多*/    unsigned int otnRefCount;} RuleTreeNode;/* 规则链表头 */typedef struct _ListHead{    struct _OutputFuncNode *LogList;    struct _OutputFuncNode *AlertList;    struct _RuleListNode *ruleListNode;} ListHead;/* 规则链表, 将各类规则链表头ListHead链成一个链表, 方便检测规则是否合法,比如name="snort", 没有这类动作的规则,程序报错*/typedef struct _RuleListNode{    ListHead *RuleList;         /* The rule list associated with this node */    RuleType mode;              /* the rule mode */    int rval;                   /* 0 == no detection, 1 == detection event */    int evalIndex;              /* eval index for this rule set */    char *name;                 /* name of this rule list (for debugging)  */    struct _RuleListNode *next; /* the next RuleListNode */} RuleListNode;

前面提到规则头是存在很多冗余的,接下来分析snort是如何去重的。

snort在解析规则头的时候,在ProcessHeadNode函数中,调用findHeadNode进行查询之前是否存在一样的规则头,

如果存在rtn->otnRefCount++, 增加引用计数,否则创建新的规则头对象,设置各类匹配回调函数。

以源码的形式进行分析规则头的解析流程

static void ParseRule(SnortConfig *sc, SnortPolicy *p, char *args,                      RuleType rule_type, ListHead *list){...    /* 如果没有规则头, 使用默认规则头, tcp any any -> any any*/    if (*args == '(')    {        test_rtn.flags |= ANY_DST_PORT;        test_rtn.flags |= ANY_SRC_PORT;        test_rtn.flags |= ANY_DST_IP;        test_rtn.flags |= ANY_SRC_IP;        test_rtn.flags |= BIDIRECTIONAL;        test_rtn.type = rule_type;        protocol = IPPROTO_TCP;        roptions = args;        DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "Preprocessor Rule detected\n"););    }    else    {        /* proto ip port dir ip port r*/        /* 调用工具函数 mpspit进行切分读取的规则内容, 规则头以空格或者'\t'分隔,所以传入" \t",规则头有7项,但是没有了动作例如alert,所以规则头剩余6项,顺带切分规则选项,所以传入7, num_toks返回实际切分的个数, 最后一个参数为转义字符, 有的规则很长,使用'\\'进行换行*/        toks = mSplit(args, " \t", 7, &num_toks, '\\');        /* A rule might not have rule options */        if (num_toks < 6)        {            ParseError("Bad rule in rules file: %s", args);        }        /* 正常处理, toks[6]:规则选项*/        if (num_toks == 7)            roptions = toks[6];        /* 规则动作进入函数前就已经解析了*/        test_rtn.type = rule_type;       ...        /* Set the rule protocol - fatal errors if protocol not found */        /* 前一篇的实例:alert tcp $HOME_NET any -> $EXTERNAL_NET $HTTP_PORTS */        /* 第一项 规则协议 */        protocol = GetRuleProtocol(toks[0]);        test_rtn.proto = protocol;        ...        /* 解析源IP组, 会从snort_config对象中的targeted_policies策略对象中的ip_vartable链表中获取$HOME_NET的值*/        ProcessIP(sc, toks[1], &test_rtn, SRC, 0);        ...        /*解析源IP组, 会从snort_config对象中的targeted_policies策略对象中的portVarTable中的哈希表中查找 PortObject对象*/        if (ParsePortList(&test_rtn, portVarTable, nonamePortVarTable,                          toks[2], protocol, 0 /* =src port */ ))        {            ParseError("Bad source port: '%s'", toks[2]);        }        /* 规则头的数据包方向解析,先判定格式*/        if ((strcmp(toks[3], RULE_DIR_OPT__DIRECTIONAL) != 0) &&            (strcmp(toks[3], RULE_DIR_OPT__BIDIRECTIONAL) != 0))        {            ParseError("Illegal direction specifier: %s", toks[3]);        }        /* New in version 1.3: support for bidirectional rules         * This checks the rule "direction" token and sets the bidirectional         * flag if the token = '<>' */        /* 默认"->", 否则 "<>"*/        if (strcmp(toks[3], RULE_DIR_OPT__BIDIRECTIONAL) == 0)        {            DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Bidirectional rule!\n"););            test_rtn.flags |= BIDIRECTIONAL;        }        ...       /* 设置规则头所属的规则动作的链表头*/       test_rtn.listhead = list;       /* 内部会查询是否已经存在该规则头和设置规则头的匹配回调函数*/       rtn = ProcessHeadNode(sc, &test_rtn, list);    ...    }}

 

规则头回调函数源码分析

static RuleTreeNode * ProcessHeadNode(SnortConfig *sc, RuleTreeNode *test_node,                                      ListHead *list){    /* 查找是否存在冗余*/    RuleTreeNode *rtn = findHeadNode(sc, test_node, getParserPolicy(sc));    /* 不存在, 直接创建,并且深拷贝*/    if (rtn == NULL)    {        DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Building New Chain head node\n"););        rtn = (RuleTreeNode *)SnortAlloc(sizeof(RuleTreeNode));        rtn->otnRefCount++;        /* copy the prototype header info into the new header block */        XferHeader(test_node, rtn);        head_count++;        rtn->head_node_number = head_count;        /* initialize the function list for the new RTN */        /* 设置各类回调函数*/        SetupRTNFuncList(rtn);        /* add link to parent listhead */        rtn->listhead = list;...    }    else    {        /*存在增加引用计数*/        rtn->otnRefCount++;        FreeRuleTreeNode(test_node);    }    return rtn;}

 

static void SetupRTNFuncList(RuleTreeNode * rtn){    /* 如果规则头都是双向的, 直接设置回调函数CheckBidirectional*/    if(rtn->flags & BIDIRECTIONAL)    {        DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"CheckBidirectional->\n"););        AddRuleFuncToList(CheckBidirectional, rtn);    }    /* 根据规则选项逐个设置回调函数*/    else    {                /* Attach the proper port checking function to the function list */        /*         * the in-line "if's" check to see if the "any" or "not" flags have         * been set so the PortToFunc call can determine which port testing         * function to attach to the list         */        PortToFunc(rtn, (rtn->flags & ANY_DST_PORT ? 1 : 0),                   (rtn->flags & EXCEPT_DST_PORT ? 1 : 0), DST);        /* as above */        PortToFunc(rtn, (rtn->flags & ANY_SRC_PORT ? 1 : 0),                   (rtn->flags & EXCEPT_SRC_PORT ? 1 : 0), SRC);        /* link in the proper IP address detection function */        AddrToFunc(rtn, SRC);        /* last verse, same as the first (but for dest IP) ;) */        AddrToFunc(rtn, DST);    }    /* tack the end (success) function to the list */    /* snort中采用链式方式管理回调函数, 每个函数只有返回为1才可能成功, 所以在链表尾部设置一个成功的结束函数让规则匹配结束, 设计巧妙*/    AddRuleFuncToList(RuleListEnd, rtn);}/* 该结束函数啥都没做 直接返回1, 检测规则头是否匹配成功,直接判定返回值是否为1就ok了 */int RuleListEnd(Packet *p, struct _RuleTreeNode *rtn_idx,        RuleFpList *fp_list, int check_ports){    return 1;}/* 将rfunc匹配回调函数insert到链表尾部*/void AddRuleFuncToList(int (*rfunc) (Packet *, struct _RuleTreeNode *, struct _RuleFpList *, int), RuleTreeNode * rtn){    RuleFpList *idx;    DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Adding new rule to list\n"););    idx = rtn->rule_func;    if(idx == NULL)    {        rtn->rule_func = (RuleFpList *)SnortAlloc(sizeof(RuleFpList));        rtn->rule_func->RuleHeadFunc = rfunc;    }    else    {        while(idx->next != NULL)            idx = idx->next;        idx->next = (RuleFpList *)SnortAlloc(sizeof(RuleFpList));        idx = idx->next;        idx->RuleHeadFunc = rfunc;    }}

 

规则头解析的源码剖析就到这里分析完了。

看一眼如何调用回调函数进行匹配的

/* *规则头检测函数 * rtn : 规则头 * p : 数据包 * check_ports : 检测标志  */int fpEvalRTN(RuleTreeNode *rtn, Packet *p, int check_ports){...    /* 判定返回值是否1, 不为1 失败*/    if(!rtn->rule_func->RuleHeadFunc(p, rtn, rtn->rule_func, check_ports))    {        DEBUG_WRAP(DebugMessage(DEBUG_DETECT,                    "   => Header check failed, checking next node\n"););        DEBUG_WRAP(DebugMessage(DEBUG_DETECT,                    "   => returned from next node check\n"););        PREPROC_PROFILE_END(ruleRTNEvalPerfStats);        return 0;    }...    /* 成功返回1*/    return 1;}

 

转载地址:http://hvtmd.baihongyu.com/

你可能感兴趣的文章
WEB前端 -- 读取下拉框中的text、value
查看>>
JAVA -- 获取时间
查看>>
PowerDesigner初用问题解决
查看>>
网络知识 -- TCP连接实例
查看>>
Oracle -- like、instr()、substr()
查看>>
UIWindow
查看>>
GCDAsyncSocket类库,IOS下TCP通讯使用心得
查看>>
block使用小结、使用block、如何防止循环引用
查看>>
程序员高效率工作工具推荐(必备工具)
查看>>
iOS多线程编程之NSThread的使用
查看>>
使用Xcode和Instruments调试解决iOS内存泄露
查看>>
CoreData底层架构实现 概述
查看>>
CocoaPods使用详解和pod install/pod update Generating Pods project 更新慢的问题
查看>>
#import和@class在IOS中的区别
查看>>
a valid provisioning profile for this executable was not found错误
查看>>
使用Cocoapods创建私有podspec
查看>>
OC js 交互
查看>>
用 alias 给常用命令取个别名
查看>>
vivi
查看>>
韦的一些视频
查看>>