Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

sp_byte_check.c

Go to the documentation of this file.
00001 /* $Id$ */
00002 /* Copyright (C) 2002 Sourcefire Inc. */
00003 /* Author: Martin Roesch*/
00004 
00005 /* sp_byte_check 
00006  * 
00007  * Purpose:
00008  *      Test a byte field against a specific value (with operator).  Capable
00009  *      of testing binary values or converting represenative byte strings
00010  *      to their binary equivalent and testing them.
00011  *
00012  *
00013  * Arguments:
00014  *      Required:
00015  *      <bytes_to_convert>: number of bytes to pick up from the packet
00016  *      <operator>: operation to perform to test the value (<,>,=,!)
00017  *      <value>: value to test the converted value against
00018  *      <offset>: number of bytes into the payload to start processing
00019  *      Optional:
00020  *      ["relative"]: offset relative to last pattern match
00021  *      ["big"]: process data as big endian (default)
00022  *      ["little"]: process data as little endian
00023  *      ["string"]: converted bytes represented as a string needing conversion
00024  *      ["hex"]: converted string data is represented in hexidecimal
00025  *      ["dec"]: converted string data is represented in decimal
00026  *      ["oct"]: converted string data is represented in octal
00027  *   
00028  *   sample rules:
00029  *   alert udp $EXTERNAL_NET any -> $HOME_NET any \
00030  *      (msg:"AMD procedure 7 plog overflow "; \
00031  *      content: "|00 04 93 F3|"; \
00032  *      content: "|00 00 00 07|"; distance: 4; within: 4; \
00033  *      byte_test: 4,>, 1000, 20, relative;)
00034  *
00035  *   alert tcp $EXTERNAL_NET any -> $HOME_NET any \
00036  *      (msg:"AMD procedure 7 plog overflow "; \
00037  *      content: "|00 04 93 F3|"; \
00038  *      content: "|00 00 00 07|"; distance: 4; within: 4; \
00039  *      byte_test: 4, >,1000, 20, relative;)
00040  *
00041  * alert udp any any -> any 1234 \
00042  *      (byte_test: 4, =, 1234, 0, string, dec; \
00043  *      msg: "got 1234!";)
00044  *
00045  * alert udp any any -> any 1235 \
00046  *      (byte_test: 3, =, 123, 0, string, dec; \
00047  *      msg: "got 123!";)
00048  *
00049  * alert udp any any -> any 1236 \
00050  *      (byte_test: 2, =, 12, 0, string, dec; \
00051  *      msg: "got 12!";)
00052  *
00053  * alert udp any any -> any 1237 \
00054  *      (byte_test: 10, =, 1234567890, 0, string, dec; \
00055  *      msg: "got 1234567890!";)
00056  *
00057  * alert udp any any -> any 1238 \
00058  *      (byte_test: 8, =, 0xdeadbeef, 0, string, hex; \
00059  *      msg: "got DEADBEEF!";)
00060  *
00061  * Effect:
00062  *
00063  *      Reads in the indicated bytes, converts them to an numeric 
00064  *      representation and then performs the indicated operation/test on
00065  *      the data using the value field.  Returns 1 if the operation is true,
00066  *      0 if it is not.
00067  *
00068  * Comments:
00069  *
00070  * Any comments?
00071  *
00072  */
00073 
00074 #ifdef HAVE_CONFIG_H
00075 #include "config.h"
00076 #endif
00077 
00078 #include <sys/types.h>
00079 #include <stdlib.h>
00080 #include <ctype.h>
00081 #ifdef HAVE_STRINGS_H
00082 #include <strings.h>
00083 #endif
00084 #include <errno.h>
00085 
00086 #include "bounds.h"
00087 #include "byte_extract.h"
00088 #include "rules.h"
00089 #include "decode.h"
00090 #include "plugbase.h"
00091 #include "parser.h"
00092 #include "debug.h"
00093 #include "util.h"
00094 #include "plugin_enum.h"
00095 #include "mstring.h"
00096 
00097 #define BT_LESS_THAN 1
00098 #define BT_EQUALS    2
00099 #define BT_GREATER_THAN 3
00100 #define BT_AND 4
00101 #define BT_OR 5
00102 
00103 #define BIG    0
00104 #define LITTLE 1
00105 #define PARSELEN 10
00106 #define TEXTLEN  (PARSELEN + 2)
00107 
00108 extern u_int8_t *doe_ptr;
00109 
00110 typedef struct _ByteTestData
00111 {
00112     u_int32_t bytes_to_compare; /* number of bytes to compare */
00113     u_int32_t cmp_value;
00114     u_int32_t operator;
00115     int32_t offset;
00116     u_int8_t not_flag;
00117     u_int8_t relative_flag;
00118     u_int8_t data_string_convert_flag;
00119     u_int8_t endianess;
00120     u_int32_t base;
00121 } ByteTestData;
00122 
00123 extern u_int8_t DecodeBuffer[DECODE_BLEN];
00124 
00125 void ByteTestInit(char *, OptTreeNode *, int);
00126 void ByteTestParse(char *, ByteTestData *, OptTreeNode *);
00127 int ByteTest(Packet *, struct _OptTreeNode *, OptFpList *);
00128 
00129 /****************************************************************************
00130  * 
00131  * Function: SetupByteTest()
00132  *
00133  * Purpose: Load 'er up
00134  *
00135  * Arguments: None.
00136  *
00137  * Returns: void function
00138  *
00139  ****************************************************************************/
00140 void SetupByteTest(void)
00141 {
00142     /* map the keyword to an initialization/processing function */
00143     RegisterPlugin("byte_test", ByteTestInit);
00144 
00145     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: ByteTest Setup\n"););
00146 }
00147 
00148 
00149 /****************************************************************************
00150  * 
00151  * Function: ByteTestInit(char *, OptTreeNode *)
00152  *
00153  * Purpose: Generic rule configuration function.  Handles parsing the rule 
00154  *          information and attaching the associated detection function to
00155  *          the OTN.
00156  *
00157  * Arguments: data => rule arguments/data
00158  *            otn => pointer to the current rule option list node
00159  *            protocol => protocol the rule is on (we don't care in this case)
00160  *
00161  * Returns: void function
00162  *
00163  ****************************************************************************/
00164 void ByteTestInit(char *data, OptTreeNode *otn, int protocol)
00165 {
00166     ByteTestData *idx;
00167     OptFpList *fpl;
00168 
00169     /* allocate the data structure and attach it to the
00170        rule's data struct list */
00171     idx = (ByteTestData *) calloc(sizeof(ByteTestData), sizeof(char));
00172 
00173     if(idx == NULL)
00174     {
00175         FatalError("%s(%d): Unable to allocate byte_test data node\n", 
00176                 file_name, file_line);
00177     }
00178 
00179     /* this is where the keyword arguments are processed and placed into the 
00180        rule option's data structure */
00181     ByteTestParse(data, idx, otn);
00182 
00183     fpl = AddOptFuncToList(ByteTest, otn);
00184     
00185     /* attach it to the context node so that we can call each instance
00186      * individually
00187      */
00188     fpl->context = (void *) idx;
00189 }
00190 
00191 
00192 
00193 /****************************************************************************
00194  * 
00195  * Function: ByteTestParse(char *, ByteTestData *, OptTreeNode *)
00196  *
00197  * Purpose: This is the function that is used to process the option keyword's
00198  *          arguments and attach them to the rule's data structures.
00199  *
00200  * Arguments: data => argument data
00201  *            idx => pointer to the processed argument storage
00202  *            otn => pointer to the current rule's OTN
00203  *
00204  * Returns: void function
00205  *
00206  ****************************************************************************/
00207 void ByteTestParse(char *data, ByteTestData *idx, OptTreeNode *otn)
00208 {
00209     char **toks;
00210     char *endp;
00211     int num_toks;
00212     char *cptr;
00213     int i =0;
00214 
00215     toks = mSplit(data, ",", 12, &num_toks, 0);
00216 
00217     if(num_toks < 4)
00218         FatalError("ERROR %s (%d): Bad arguments to byte_test: %s\n", file_name,
00219                 file_line, data);
00220 
00221     /* set how many bytes to process from the packet */
00222     idx->bytes_to_compare = strtol(toks[0], &endp, 10);
00223 
00224     if(toks[0] == endp)
00225     {
00226         FatalError("%s(%d): Unable to parse as byte value %s\n",
00227                    file_name, file_line, toks[0]);
00228     }
00229 
00230     if(idx->bytes_to_compare > PARSELEN || idx->bytes_to_compare == 0)
00231     {
00232         FatalError("%s(%d): byte_test can't process more than "
00233                 "10 bytes!\n", file_name, file_line);
00234     }
00235 
00236     cptr = toks[1];
00237 
00238     while(isspace((int)*cptr)) {cptr++;}
00239 
00240     if(*cptr == '!')
00241     {
00242         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, 
00243                     "enabling not flag\n"););
00244        idx->not_flag = 1;
00245        cptr++;
00246     }
00247    
00248     if (idx->not_flag && strlen(cptr) == 0) 
00249     {
00250         idx->operator = BT_EQUALS;
00251     }
00252     else
00253     {
00254         /* set the operator */
00255         switch(*cptr)
00256         {
00257             case '<': idx->operator = BT_LESS_THAN;
00258                       break;
00259 
00260             case '=': idx->operator = BT_EQUALS;
00261                       break;
00262 
00263             case '>': idx->operator = BT_GREATER_THAN;
00264                       break;
00265 
00266             case '&': idx->operator = BT_AND;
00267                       break;
00268 
00269             case '^': idx->operator = BT_OR;
00270                       break;
00271 
00272             default: FatalError("ERROR %s(%d): byte_test unknown "
00273                              "operator ('%c, %s')\n", file_name, file_line, 
00274                              *cptr, toks[1]);
00275         }
00276     }
00277 
00278     errno = 0;
00279 
00280     /* set the value to test against */
00281     idx->cmp_value = strtoul(toks[2], &endp, 0);
00282 
00283     if(toks[2] == endp)
00284     {
00285         FatalError("%s(%d): Unable to parse as comparison value %s\n",
00286                    file_name, file_line, toks[2]);
00287     }
00288 
00289     if(errno == ERANGE)
00290     {
00291         printf("Bad range: %s\n", toks[2]);
00292     }
00293 
00294     /* set offset */
00295     idx->offset = strtol(toks[3], &endp, 10);
00296 
00297     
00298     if(toks[3] == endp)
00299     {
00300         FatalError("%s(%d): Unable to parse as offset value %s\n",
00301                    file_name, file_line, toks[3]);
00302     }
00303 
00304     
00305     i = 4;
00306 
00307     /* is it a relative offset? */
00308     if(num_toks > 4)
00309     {
00310         while(i < num_toks)
00311         {
00312             cptr = toks[i];
00313 
00314             while(isspace((int)*cptr)) {cptr++;}
00315 
00316             if(!strcasecmp(cptr, "relative"))
00317             {
00318                 /* the offset is relative to the last pattern match */
00319                 idx->relative_flag = 1;
00320             }
00321             else if(!strcasecmp(cptr, "string"))
00322             {
00323                 /* the data will be represented as a string that needs 
00324                  * to be converted to an int, binary is assumed otherwise
00325                  */
00326                 idx->data_string_convert_flag = 1;
00327             }
00328             else if(!strcasecmp(cptr, "little"))
00329             {
00330                 idx->endianess = LITTLE;
00331             }
00332             else if(!strcasecmp(cptr, "big"))
00333             {
00334                 /* this is the default */
00335                 idx->endianess = BIG;
00336             }
00337             else if(!strcasecmp(cptr, "hex"))
00338             {
00339                 idx->base = 16;
00340             }
00341             else if(!strcasecmp(cptr, "dec"))
00342             {
00343                 idx->base = 10;
00344             }
00345             else if(!strcasecmp(cptr, "oct"))
00346             {
00347                 idx->base = 8;
00348             }
00349             else
00350             {
00351                 FatalError("%s(%d): unknown modifier \"%s\"\n", 
00352                         file_name, file_line, cptr);
00353             }
00354 
00355             i++;
00356         }
00357     }
00358 
00359     /* idx->base is only set if the parameter is specified */
00360     if(!idx->data_string_convert_flag && idx->base)
00361     {
00362         FatalError("%s(%d): hex, dec and oct modifiers must be used in conjunction \n"
00363                    "        with the 'string' modifier\n", file_name,file_line);
00364     }
00365     
00366     mSplitFree(&toks, num_toks);
00367 }
00368 
00369 
00370 /****************************************************************************
00371  * 
00372  * Function: ByteTest(char *, OptTreeNode *, OptFpList *)
00373  *
00374  * Purpose: Use this function to perform the particular detection routine
00375  *          that this rule keyword is supposed to encompass.
00376  *
00377  * Arguments: p => pointer to the decoded packet
00378  *            otn => pointer to the current rule's OTN
00379  *            fp_list => pointer to the function pointer list
00380  *
00381  * Returns: If the detection test fails, this function *must* return a zero!
00382  *          On success, it calls the next function in the detection list 
00383  *
00384  ****************************************************************************/
00385 int ByteTest(Packet *p, struct _OptTreeNode *otn, OptFpList *fp_list)
00386 {
00387     ByteTestData *btd;
00388     u_int32_t value = 0;
00389     int success = 0;
00390     int use_alt_buffer = p->packet_flags & PKT_ALT_DECODE;
00391     int dsize;
00392     char *base_ptr, *end_ptr, *start_ptr;
00393 
00394     if(use_alt_buffer)
00395     {
00396         dsize = p->alt_dsize;
00397         start_ptr = (char *)DecodeBuffer;
00398         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, 
00399                     "Using Alternative Decode buffer!\n"););
00400     }
00401     else
00402     {
00403         dsize = p->dsize;
00404         start_ptr = (char *) p->data;
00405     }
00406 
00407     base_ptr = start_ptr;
00408     end_ptr = start_ptr + dsize;
00409     
00410     DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00411                 "[*] byte test firing...\npayload starts at %p\n", start_ptr););
00412 
00413     if(doe_ptr)
00414     {
00415         /* @todo: possibly degrade to use the other buffer, seems non-intuitive*/        
00416         if(!inBounds(start_ptr, end_ptr, doe_ptr))
00417         {
00418             DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00419                                     "[*] byte test bounds check failed..\n"););
00420             return 0;
00421         }
00422     }
00423 
00424     btd = (ByteTestData *) fp_list->context;
00425 
00426     if(btd->relative_flag && doe_ptr)
00427     {
00428         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00429                                 "Checking relative offset!\n"););
00430         base_ptr = doe_ptr + btd->offset;
00431     }
00432     else
00433     {
00434         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00435                                 "checking absolute offset %d\n", btd->offset););
00436         base_ptr = start_ptr + btd->offset;
00437     }
00438 
00439 
00440     /* both of these functions below perform their own bounds checking within
00441      * byte_extract.c
00442      */
00443        
00444     if(!btd->data_string_convert_flag)
00445     {
00446         if(byte_extract(btd->endianess, btd->bytes_to_compare,
00447                         base_ptr, start_ptr, end_ptr, &value))
00448         {
00449             DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00450                                     "Byte Extraction Failed\n"););
00451 
00452             return 0;
00453         }
00454     }
00455     else
00456     {
00457         if(string_extract(btd->bytes_to_compare, btd->base,
00458                           base_ptr, start_ptr, end_ptr, &value))
00459         {
00460             DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00461                                     "String Extraction Failed\n"););
00462 
00463             return 0;
00464         }
00465 
00466     }
00467 
00468     DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, 
00469                 "Grabbed %d bytes at offset %d, value = 0x%08X(%d)\n",
00470                 btd->bytes_to_compare, btd->offset, value, value); );
00471 
00472     switch(btd->operator)
00473     {
00474         case BT_LESS_THAN: if(value < btd->cmp_value)
00475                                success = 1;
00476                            break;
00477 
00478         case BT_EQUALS: if(value == btd->cmp_value)
00479                             success = 1;
00480                         break;
00481 
00482         case BT_GREATER_THAN: if(value > btd->cmp_value)
00483                                   success = 1;
00484                               break;
00485 
00486         case BT_AND: if ((value & btd->cmp_value) > 0)
00487                          success = 1;
00488                      break;
00489 
00490         case BT_OR: if ((value ^ btd->cmp_value) > 0)
00491                         success = 1;
00492                     break;
00493     }
00494 
00495     if (btd->not_flag)
00496     {
00497         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, 
00498                     "checking for not success...flag\n"););
00499         if (!success)
00500         {
00501             return fp_list->next->OptTestFunc(p, otn, fp_list->next);
00502         }
00503     }
00504     else if (success)
00505     {
00506         return fp_list->next->OptTestFunc(p, otn, fp_list->next);
00507     }
00508 
00509     /* if the test isn't successful, this function *must* return 0 */
00510     return 0;
00511 }

Generated on Sun May 14 14:51:12 2006 by  doxygen 1.4.2