// #if 0 // Don't compile the detection functions if they're not used
int rule18063eval(void *p) {
    const uint8_t *cursor_normal = 0, *OfficeArtSPContainer = 0, *OfficeArtSpgrContainer = 0, *OfficeArtFSP = 0, *OfficeArtFSPGR = 0;
    const uint8_t *beg_of_payload = 0, *end_of_payload = 0, *tmp_payload = 0;
    uint32_t OfficeArtSpgrContainer_size = 0, OfficeArtSPContainer_size = 0;
    SFSnortPacket *sp = (SFSnortPacket *) p;

    if(sp == NULL)
        return RULE_NOMATCH;

    if(sp->payload == NULL)
        return RULE_NOMATCH;
    
    // flow:established, to_client;
    if (checkFlow(p, rule18063options[0]->option_u.flowFlags) <= 0 ) {
       return RULE_NOMATCH;
    }
    // flowbits:isset "file.doc";
    if (processFlowbits(p, rule18063options[1]->option_u.flowBit) <= 0) {
       return RULE_NOMATCH;
    }
    if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0) {
      return RULE_NOMATCH;
    }

    // Start by finding inner container
    // content:"|0F 00 04 F0|", depth 0, fast_pattern;
    if (contentMatch(p, rule18063options[3]->option_u.content, &OfficeArtSPContainer) <= 0) {
       return RULE_NOMATCH;
    }

    DEBUG_SO(fprintf(stderr, "OfficeArtSPContainer found: %p\n", OfficeArtSPContainer);)
    
    // Find the outer container - This may or may not exist
    // content:"|0F 00 03 F0|", depth 0; 
    if(contentMatch(p, rule18063options[2]->option_u.content, &OfficeArtSpgrContainer) <= 0) {
/* detection functions */
int rule13897eval(void *p) {
   const u_int8_t *cursor_normal = 0;
   SFSnortPacket *sp = (SFSnortPacket *) p;

   const u_int8_t *beg_of_payload, *end_of_payload;
   u_int32_t atom_size;
   u_int16_t region_size;
      
   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;
    
   // flow:established, to_client;
   if(checkFlow(p, rule13897options[0]->option_u.flowFlags) > 0 ) {
      // flowbits:isset "file.quicktime";
      if(processFlowbits(p, rule13897options[1]->option_u.flowBit) > 0) {
        // content:"crgn";
        if(contentMatch(p, rule13897options[2]->option_u.content, &cursor_normal) > 0) {
           if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
              return RULE_NOMATCH;
                
           if(cursor_normal + 2 >= end_of_payload)
              return RULE_NOMATCH;
   
           region_size = (*cursor_normal++) << 8;
           region_size |= *cursor_normal++;
   
           DEBUG_WRAP(printf("REGION SIZE: %d\n", region_size));
      
           cursor_normal = cursor_normal - 10;

           if(cursor_normal < beg_of_payload)
              return RULE_NOMATCH;
      
           atom_size = (*cursor_normal++) << 24;   
           atom_size |= (*cursor_normal++) << 16;
           atom_size |= (*cursor_normal++) << 8;
           atom_size |= *cursor_normal++;
   
           DEBUG_WRAP(printf("ATOM SIZE: %d\n", atom_size));
  
           // Changed from region_size > (atom_size - 8) to avoid
           // integer underflow from (atom_size - 8)
           if(((u_int32_t)region_size + 8) > atom_size) 
              return RULE_MATCH;
         }
      }
   }

   return RULE_NOMATCH;
}
/* detection functions */
int rule13469eval(void *p) {
    const uint8_t *cursor_normal = 0;
    const uint8_t *beg_of_payload, *end_of_payload;
    uint32_t length;

    SFSnortPacket *sp = (SFSnortPacket *) p;

    // flow:established, to_client;
    if (checkFlow(p, rule13469options[0]->option_u.flowFlags) > 0 ) {
        // flowbits:isset "file.doc";
        if (processFlowbits(p, rule13469options[1]->option_u.flowBit) > 0) {

            // content:"bjbj";
            if (contentMatch(p, rule13469options[2]->option_u.content, &cursor_normal) > 0) {
                // content:"|00 00 00 00|", offset 62, depth 4, relative;
                if (!(contentMatch(p, rule13469options[3]->option_u.content, &cursor_normal) > 0)) {

                if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
                    return RULE_NOMATCH;

                // At this point, cursor_normal is still right after bjbj because negative
                // content matches do not move the pointer
                // Start custom detection here
                if((cursor_normal + 572) > end_of_payload)
                   return RULE_NOMATCH;

                cursor_normal += 568;
                length = *cursor_normal++;
                length |= *cursor_normal++ << 8;
                length |= *cursor_normal++ << 16;
                length |= *cursor_normal++ << 24;

                if((length - 4) % 0x1a)
                   return RULE_MATCH;
                }
            }
        }
    }
    return RULE_NOMATCH;
}
/* detection functions */
int rule13666eval(void *p) {
    const uint8_t *cursor_normal = 0, *cursor_offBmi;
    SFSnortPacket *sp = (SFSnortPacket *) p;

    const uint8_t *record_size_ptr, *beg_of_payload, *end_of_payload;

    uint32_t recordType, offBmi, bcSize;
    uint16_t bcWidth,bcHeight,bcPlanes,bcBitCount;
    uint64_t Value;  // For storing exploit calculations

    if(sp == NULL)
        return RULE_NOMATCH;

    if(sp->payload == NULL)
        return RULE_NOMATCH;
    
    // flow:established, to_client;
    if (checkFlow(p, rule13666options[0]->option_u.flowFlags) <= 0 )
        return RULE_NOMATCH;
    
    // flowbits:isset "file.emf";
    if (processFlowbits(p, rule13666options[1]->option_u.flowBit) <= 0)
        return RULE_NOMATCH;
    
    // content:" EMF|00 00 01 00|";
    if (contentMatch(p, rule13666options[2]->option_u.content, &cursor_normal) > 0) {
        // content:"|01 00 00 00|", offset -48, depth 4, relative;
        if (contentMatch(p, rule13666options[3]->option_u.content, &cursor_normal) > 0) {
            // byte_jump:size 4, relative, endian little;
            if (byteJump(p, rule13666options[4]->option_u.byte, &cursor_normal) > 0) {
                
                if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
                    return RULE_NOMATCH;

                // Now we're at the start of records.  Find the interesting ones.
                while(cursor_normal < end_of_payload) {
                     
                    // We point to record size so we're at the right state for the relative jump
                    // if it turns out the record doesn't have a BITMAPCOREHEADER. 
                    record_size_ptr = cursor_normal - 4;                    

                    // content:"|5E 00 00 00|", offset -8, depth 4, relative;  XXX /* EMR_CREATEDIBPATTERNBRUSHPT */
                    if (contentMatch(p, rule13666options[5]->option_u.content, &cursor_normal) > 0
                        // content:"|51 00 00 00|", offset -8, depth 4, relative; XXX /* EMR_STRETCHDIBITS */
                        || (contentMatch(p, rule13666options[6]->option_u.content, &cursor_normal) > 0))
                    {
                        recordType = *(cursor_normal - 4);
                        switch(recordType)
                        {
                            case EMR_CREATEDIBPATTERNBRUSHPT:
                                DEBUG_SO(printf("matched EMR_CREATEDIBPATTERNBRUSHPT\n");)
                                    
                                if (cursor_normal + 28 > end_of_payload) // Make sure there's enough room
                                    return RULE_NOMATCH;

                                // extract offset to BITMAPCOREHEADER.  can't use byte_jump because it could put us
                                // past end of packet since the "jump" actually starts from an earlier offset
                                cursor_offBmi = cursor_normal + 12;
                                offBmi = read_little_32(cursor_offBmi);
                                break;
                                    
                            case EMR_STRETCHDIBITS: 
                                DEBUG_SO(printf("matched EMR_STRETCHDIBITS\n");)

                                if (cursor_normal + 60 > end_of_payload) 
                                    return RULE_NOMATCH;
                        
                                // read offBmiSrc field 
                                cursor_offBmi = cursor_normal + 44;
                                offBmi = read_little_32(cursor_offBmi);
                                break;
                                
                            default:
                                DEBUG_SO(printf("This case must not happen\n");)
                                return RULE_NOMATCH;                                    
                        }
                                    
                        if (offBmi + cursor_normal - 4 < cursor_normal)  // check integer overflow
                            return RULE_NOMATCH;

                        // move cursor to the beginning of a device independent bitmap (DIB)
                        DEBUG_SO(printf("offBmi=0x%08x\n", offBmi);)  
                        cursor_normal += offBmi - 4; 
                                  
                        if(cursor_normal + 12 > end_of_payload)
                           return RULE_NOMATCH;
                                  
                        // match size of BITMAPCOREHEADER to determine if it's the proper structure
                        bcSize = read_little_32(cursor_normal);
                        cursor_normal += 4;
                        DEBUG_SO(printf("bcSize=0x%08x\n", bcSize);)
        
                        if (bcSize == 12) // checks BITMAPCOREHEADER
/* detection functions */
int rule15117eval(void *p) {
   const uint8_t *cursor_normal = 0;
   SFSnortPacket *sp = (SFSnortPacket *) p;
   const uint8_t *end_of_payload, *end_of_record;
   const uint8_t *cursor_nextloop;

   uint16_t obj_record_len;
   uint16_t ft;
   uint16_t cb;

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;
    
   // flow:established, to_client;
   if(checkFlow(p, rule15117options[0]->option_u.flowFlags) <= 0 )
      return RULE_NOMATCH;

   // flowbits:isset "file.xls";
   if(processFlowbits(p, rule15117options[1]->option_u.flowBit) <= 0)
      return RULE_NOMATCH;

   if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_payload) <= 0)
      return RULE_NOMATCH;

   // content:"|5D 00|";
   while(contentMatch(p, rule15117options[2]->option_u.content, &cursor_normal) > 0) {
      // If we need to search again, we want to start here in case we matched on data
      cursor_nextloop = cursor_normal; 

      // The first sub-record is 22 bytes plus the 2-byte obj_record_len
      if(cursor_normal + 24 >= end_of_payload)
         return RULE_NOMATCH;

      // Verify the first sub-record is type 0x0015 to reduce false positives.
      // This also adds false negatives, but otherwise the FP rate will be
      // astronomical.
      if((*(cursor_normal + 2) != 0x15) || (*(cursor_normal + 3) != 0x00))
         continue;

      // Verify the second sub-record is type 0x0012 to reduce false positives.
      if((*(cursor_normal + 4) != 0x12) || (*(cursor_normal + 5) != 0x00))
         continue;

      // Verify the last 3 bytes in the sub-record are null
      if(*(cursor_normal + 12) != 0)
         continue;
      if(*((uint16_t*)(cursor_normal + 13)) !=0)  // byte order doesn't matter here
         continue;

      obj_record_len = *cursor_normal++;
      obj_record_len |= *cursor_normal++ << 8;

      DEBUG_WRAP(printf("obj_record_len=0x%04x\n", obj_record_len));

      if(obj_record_len == 0) // if 0, not a valid record
         return RULE_NOMATCH;

      obj_record_len -= 4; // minus 2 for tag and 2 for size to properly find end_of_record

      //the record with no variable length data will not exceed 160 ish bytes. Use this check to eliminate FPs
      if(obj_record_len > 1000)
         continue;

      // Stop at the end of the record or when we run out of data
      end_of_record = (cursor_normal + obj_record_len >= end_of_payload) ? end_of_payload : cursor_normal + obj_record_len;

      // Skip the first sub-record, it's always 22 bytes. Bounds check was prior to this
      cursor_normal += 22;

      while(cursor_normal + 2 < end_of_record) {

         ft = *cursor_normal++;
         ft |= *cursor_normal++ << 8;
                 
         DEBUG_WRAP(printf("FT: 0x%02x\n", ft));
                    
         if(ft > 0x15)
            return RULE_MATCH;

         if(cursor_normal + 2 > end_of_record)
            break; // Get out of the loop, look for another OBJ record

         cb = *cursor_normal++;
         cb |= *cursor_normal++ << 8;

         DEBUG_WRAP(printf("  cb: 0x%02x\n", cb));

         cursor_normal += cb;
      }

      DEBUG_WRAP(printf("Searching for new OBJ record (|5D 00|)\n"));

      // Start our next search immediately after the last "|5D 00|"
      cursor_normal = cursor_nextloop;
   }

   return RULE_NOMATCH;
}
/* detection functions */
int rule17700eval(void *p) {
    const u_int8_t *cursor_normal = 0, *beg_of_payload, *end_of_payload;
    SFSnortPacket *sp = (SFSnortPacket *) p;

    const u_int8_t *cursor_detect;

    u_int32_t fourCC;
    u_int32_t listsize;
    u_int32_t chunksize;
    u_int32_t stringsize;

    if(sp == NULL)
        return RULE_NOMATCH;

    if(sp->payload == NULL)
        return RULE_NOMATCH;

    // flow:established, to_client;
    if (checkFlow(p, rule17700options[0]->option_u.flowFlags) > 0 ) {
        // flowbits:isset "wav_file.request";
        if (processFlowbits(p, rule17700options[1]->option_u.flowBit) > 0) {
            // content:"LIST", payload raw, depth 0, fast_pattern;
            if (contentMatch(p, rule17700options[2]->option_u.content, &cursor_normal) > 0) {
                // content:"INFO", payload raw, offset 4, depth 4, relative;
                if (contentMatch(p, rule17700options[3]->option_u.content, &cursor_normal) > 0) {
                    
                    if (getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
                        return RULE_NOMATCH;

                    //Extract size of list chunk size
                    listsize = *(cursor_normal-8);
                    listsize |= *(cursor_normal-7) << 8;
                    listsize |= *(cursor_normal-6) << 16;
                    listsize |= *(cursor_normal-5) << 24;

                    //Make sure we have 8 bytes to extact 4CC and chunk size
                    while(cursor_normal + 8 < end_of_payload) {
                        //Extract chunk size
                        chunksize = *(cursor_normal+4);
                        chunksize |= *(cursor_normal+5) << 8;
                        chunksize |= *(cursor_normal+6) << 16;
                        chunksize |= *(cursor_normal+7) << 24;

                        //In a loop, check the 4CC tag to see if it's any of the vulnerable tags
                        fourCC = *cursor_normal << 24;
                        fourCC |= *(cursor_normal + 1) << 16;
                        fourCC |= *(cursor_normal + 2) << 8;
                        fourCC |= *(cursor_normal + 3);

                        if((fourCC == 0x49415254) || // IART
                           (fourCC == 0x494e414d) || // INAM
                           (fourCC == 0x49434f50)) { // ICOP

                            //If it is one of the vulnerable tags, jump to the subchunk data
                            //and treat it as a string. Count the actual size of the string
                            //in another loop.
                            stringsize = 0;
    
                            cursor_detect = cursor_normal + 8;
    
                            //Match if stringsize > either chunksize or listsize
                            //otherwise keep counting characters or break if we
                            //encounter a null character or end_of_payload
                            while(cursor_detect < end_of_payload) {
                               if(!*cursor_detect++)
                                  break;  // stop when we find a null
                            }

                            stringsize = cursor_detect - cursor_normal - 8;

                            //printf("stringsize=%d, chunksize=%d, listsize=%d\n");
                            if (stringsize > chunksize || stringsize > listsize)
                                return RULE_MATCH;
                        }
                        
                        //We need to do a check before skipping to the next chunk to make sure that
                        //addition won't cause an integer overflow on cursor_normal
                        if (cursor_normal + chunksize + 8 <= cursor_normal) // <= to also avoid infinite loops
                            return RULE_NOMATCH;

                        // Jump to the start of the next chunk
                        cursor_normal += chunksize + 8; // fourCC, 4 bytes size, chunk size
                    } 
                }
            }
        }
    }
    return RULE_NOMATCH;
}
// #if 0 // Don't compile the detection functions if they're not used
int rule24666eval(void *p) {
   const u_int8_t *cursor_normal = 0;
   const u_int8_t *beg_of_payload = 0;
   const u_int8_t *end_of_payload = 0;
   const u_int8_t *cursor_detect = 0;
   const u_int8_t *cursor_tmp = 0;

   u_int16_t cref = 0; // number of 8 byte refs
   u_int16_t rgbNameL = 0; // length of rgbName field
   u_int16_t cFieldData = 0; // number of Feat11FieldDataItems
   u_int16_t entryIDL = 0; // length of entryID field
   u_int16_t strFieldNameL = 0; // length of strFieldName
   u_int16_t strCaptionL = 0; // length of strCaption
   u_int32_t cbdxfHdrDiskL = 0; // length of cbdxfHdrDisk field
   u_int32_t cbFmtInsertRowL = 0; // length of cbFmtInsertRowL
   int i = 0; // iterator for for() loop
   SFSnortPacket *sp = (SFSnortPacket *) p;

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;

   // flow:established, to_client;
   if (checkFlow(p, rule24666options[0]->option_u.flowFlags) <= 0 )
   return RULE_NOMATCH;

   // flowbits:isset "file.xls";
   if (processFlowbits(p, rule24666options[1]->option_u.flowBit) <= 0)
   return RULE_NOMATCH;

   if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
   return RULE_NOMATCH;

   cursor_normal = beg_of_payload;

   // content:"x|08|", depth 0; // this is 0x7808, header for feature12
   while(contentMatch(p, rule24666options[2]->option_u.content, &cursor_normal) > 0) {
   
      cursor_detect = cursor_normal;
   
      // jump to "x|08|" offset 2, depth 2
      // there is a two byte length field and the 0x7808 header again
      cursor_detect += 2;
   
         // check if we can read two bytes
      if((cursor_detect + 2) > end_of_payload)
         return RULE_NOMATCH;
   
      // are those bytes 0x0878 (little endian)
      if((*cursor_detect != 0x78) || (*(cursor_detect+1) != 0x08))
         continue;
   
      // jump to reserved fields
      cursor_detect += 14;
   
      // Two fields labeled reserved1 and reserved2
      // total of 5 null bytes, plus 2 bytes for cref
      if((cursor_detect + 8) > end_of_payload)
         return RULE_NOMATCH;
   
      // make sure reserved fields are null
      // Normally we can't just typecase our cursor, but because we
      // are checking for NULL here, we can
      if(*((u_int32_t*)cursor_detect) != 0)
         continue;
   
      // jump to cref, after reserved fields
      cursor_detect += 5;
   
      // read the cref value, we checked for 7 bytes before reserved fields
      // so we don't need to check again here
      cref = READ_LITTLE_16(cursor_detect);
   
      // jump cref*8+8, this jumps over all the ref structures
      // must check for integer overflows
      cursor_tmp = cursor_detect + 16 + (cref * 8);
         
      if(cursor_tmp < cursor_detect)
            return RULE_NOMATCH;
   
      cursor_detect = cursor_tmp;
   
   
      // this jump puts us at the beginning of the rgbFeatData
      // make sure we can read crwHeader
      if((cursor_detect + 4) >= end_of_payload)
         return RULE_NOMATCH;
   
      // make sure crwHeader==0
      if(*((u_int32_t*)cursor_detect) != 0)
         continue;
   
      // jump to rgbName, it is an XLUnicodeString, the first 2-bytes = length
      cursor_detect += 56;
   
      // make sure we can read rgbNameL
      if((cursor_detect + 2) > end_of_payload)
         return RULE_NOMATCH;
   
      // store rgbNameL
      rgbNameL = READ_LITTLE_16(cursor_detect);
   
      // jump over rgbName
      cursor_detect += rgbNameL + 3;
   
      // make sure we can read cFieldData and entryID
      if((cursor_detect + 4) > end_of_payload)
         return RULE_NOMATCH;
   
      // store cFieldData
      cFieldData = READ_LITTLE_16(cursor_detect);
   
      // jump over cFieldData
      cursor_detect += 2;
   
      // read entryIDL
      entryIDL = READ_LITTLE_16(cursor_detect);
   
      // jump over entryID
      cursor_detect += 3 + entryIDL;
      
      // max 10 iterations through Feat11FieldDataItem structures
      if(cFieldData > 10) cFieldData = 10;
      
      // loop through the Feat11FieldDataItem fields
      for(i = 0; i < cFieldData; i++) {
   
         // jump to cbFmtInsertRowL
         cursor_detect += 28;
   
         // make sure we can read length of cbFmtInsertRowL and strFieldNameL
         if((cursor_detect + 16) > end_of_payload)
            return RULE_NOMATCH;
   
         // read the cbFmtInsertRowL
         cbFmtInsertRowL =  READ_LITTLE_32(cursor_detect);
   
         // jump to strFieldName
         cursor_detect += 8;
   
         // read stFieldNameL
         strFieldNameL = READ_LITTLE_32(cursor_detect);
   
         // jump over strFieldName, with overflow check
         cursor_tmp = cursor_detect + strFieldNameL + 3;
   
         if(cursor_tmp < cursor_detect)
            return RULE_NOMATCH;
   
         cursor_detect = cursor_tmp;
   
         // make sure we can read strCaptionL
         if((cursor_detect + 2) > end_of_payload)
            return RULE_NOMATCH;
   
         // read strCaptionL
         strCaptionL = READ_LITTLE_16(cursor_detect);
   
         // jump over strCaptionL and dxFmtInsertRow
         cursor_tmp = cursor_detect + strCaptionL + cbFmtInsertRowL + 3;
   
         if(cursor_tmp < cursor_detect) 
            return RULE_NOMATCH;
   
         cursor_detect = cursor_tmp;
   
         // make sure we can read cbdxfHdrDiskL
         if((cursor_detect + 4) >  end_of_payload)
            return RULE_NOMATCH;
   
         // read the cbdxfHdrDiskL
         cbdxfHdrDiskL = READ_LITTLE_32(cursor_detect);
   
         //DEBUG_SO(fprintf(stderr, "cbdxfHdrDiskL: %d\n", cbdxfHdrDiskL);)
         if(cbdxfHdrDiskL > 0x2020)
            return RULE_MATCH; // cbdxfHdrDiskL > 0x2020 is the vulnerable condition
   
         // if cbdxfHdrDiskL is <= 0x2020 than it's the length
         // used for the last field and we jump
         cursor_tmp = cursor_detect + cbdxfHdrDiskL + 4;
         if(cursor_tmp < cursor_detect)
            return RULE_NOMATCH;
   
         cursor_detect = cursor_tmp;
      }
      
      return RULE_NOMATCH;
   }
   return RULE_NOMATCH;
}
/* detection functions */
int rule16343eval(void *p) {
    const u_int8_t *cursor_normal = 0;
    const u_int8_t *end_of_payload = 0;
    int ascii_flag;
    int hex_flag;
    unsigned long escape_char;
    char escape_buf[3];
    SFSnortPacket *sp = (SFSnortPacket *) p;

    if(sp == NULL)
        return RULE_NOMATCH;

    if(sp->payload == NULL)
        return RULE_NOMATCH;
    // flow:established, to_client;
    if (checkFlow(p, rule16343options[0]->option_u.flowFlags) > 0 ) {

        // flowbits:isset "http.pdf"; || content:"|25|PDF-";
        if (   processFlowbits(p, rule16343options[1]->option_u.flowBit) > 0 ||
               contentMatch(p, rule16343options[5]->option_u.content, &cursor_normal) > 0) {

            // If we're in a Partial Content packet, get out to avoid false positives.
            // There are some really stupid packets / multiparts out there and I can't
            // think of a good way to avoid false negatives while avoiding the false
            // positives, so may as well go for broke. 
            // content:"HTTP/1.1 206", depth 12, offset 0; 
            if(contentMatch(p, rule16343options[4]->option_u.content, &cursor_normal) > 0)
               return RULE_NOMATCH;

            if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_payload) <= 0)
               return RULE_NOMATCH;

            escape_buf[2] = 0x00;  // No need to set this every time as long as it's preset

            // content:"obj", depth 0, nocase, fast_pattern;
            while (contentMatch(p, rule16343options[2]->option_u.content, &cursor_normal) > 0) {

                if (contentMatch(p, rule16343options[3]->option_u.content, &cursor_normal) <= 0) {
                    continue;
                }

                hex_flag = 0;
                ascii_flag = 0;

                while (cursor_normal < end_of_payload) {

                    // Check for end of object header.  If found, search for new "obj<<" tag
                    if ((*cursor_normal == '>') && 
                              ((cursor_normal + 1 < end_of_payload) && (*(cursor_normal + 1) == '>'))) {
                        break;
                    }

                    if (*cursor_normal == '#') {

                        cursor_normal++; // Skip the #
                        // Make sure we've got the bytes to read
                        if ((cursor_normal + 2 > end_of_payload)) {
                            return RULE_NOMATCH;
                        }
                        escape_buf[0] = *cursor_normal++;
                        escape_buf[1] = *cursor_normal; // The pointer increment is below
                        //escape_buf[2] = 0x00;  No need to set this every time as long as it's preset
                        escape_char = strtoul(escape_buf, NULL, 16);

                        // We only care if the resultant value is alphanumeric
                        if(!isalnum(escape_char))
                           continue;

                        if (ascii_flag == 1) 
                            return RULE_MATCH;

                        hex_flag = 1;

                    } else if (*cursor_normal == '(') { // binary data in object tag

                        cursor_normal++;

                        // Run through to the end of the binary data
                        while(cursor_normal < end_of_payload) {
                           // If we come across a close parens, see if it's escaped.
                           // If not, we're at the end of the binary data.
                           if(*cursor_normal != ')')
                              cursor_normal++;
                           else if(*(cursor_normal - 1) == '\\')
                              cursor_normal++;
                           else
                              break;
                        }

                        // We'll step over the ')' at the end of the while() loop

                    } else {

                        if (isalnum(*cursor_normal)) {

                            if (hex_flag == 1)
                                return RULE_MATCH;

                            ascii_flag = 1;
                        }

                        // Fall through advances the pointer

                    }

                    cursor_normal++; // Ensure we will always advance the pointer
                }
            }
        }
    }
    return RULE_NOMATCH;
}
/* detection functions */
int rule16370eval(void *p) {
    const uint8_t *cursor_normal = 0;
    SFSnortPacket *sp = (SFSnortPacket *) p;

    const uint8_t *beg_of_payload, *end_of_payload;
    uint16_t Csiz;
    uint16_t atomlen;
    uint16_t Crgn;

    if(sp == NULL)
        return RULE_NOMATCH;

    if(sp->payload == NULL)
        return RULE_NOMATCH;
    
    // flow:established, to_client;
    if (checkFlow(p, rule16370options[0]->option_u.flowFlags) > 0 ) {
        // flowbits:isset "file.pdf";
        if (processFlowbits(p, rule16370options[1]->option_u.flowBit) > 0) {
                // content:"jP  ", depth 0, fast_pattern;
            if (contentMatch(p, rule16370options[2]->option_u.content, &cursor_normal) > 0) {
                // content:"|FF|O|FF|Q", depth 0;
                if (contentMatch(p, rule16370options[3]->option_u.content, &cursor_normal) > 0) {

                    if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
                        return RULE_NOMATCH;

                    //Make sure at least the entire atom is present, as well as
                    //the atom signature and length of the following atom
                    if(cursor_normal + 47 + 4 > end_of_payload)
                        return RULE_NOMATCH;

                    // Now make sure this atomlen is 47
                    if(*cursor_normal != 0 || *(cursor_normal + 1) != 47)
                       return RULE_NOMATCH;

                    //Extract the value of Csiz
                    Csiz = read_big_16(cursor_normal + 36);

                    //Jump to the next atom, just before the signature
                    cursor_normal += 47; //atomlen;

                    //Check to see if the following byte is FF (eg:the first byte of a signature)
                    //and that we haven't ended up in no man's land
                    while((cursor_normal + 4 < end_of_payload) && (*cursor_normal == 255)) {
                  
                        //Extract the length of next atom
                        atomlen = read_big_16(cursor_normal+2);

                        //Make sure the whole atom is present. Atom length is atomlen + file signature (2 bytes)
                        if ((cursor_normal + atomlen + 2) > end_of_payload)
                            return RULE_NOMATCH;

                        //Check second byte of atom signature
                        //A FF5E is the Region-of-interest atom
                         if (*(cursor_normal+1) == '\x5e') {
                            
                            //This atom can only be either 5 or 6 bytes long, but no other value.
                            //Crgn will be either 1 or 2 bytes depending on this length.
                            //Exit if you some unexpected value.
                            if (atomlen == 5)
                                Crgn = *(cursor_normal+4);
                            else if (atomlen == 6) {
                                Crgn = read_big_16(cursor_normal+4);
                            }
                            else
                                return RULE_NOMATCH;

                            //Fire if Crgn >= Csiz
                            if (Crgn >= Csiz)
                                return RULE_MATCH;
                        }

                        //Skip to next atom
                        cursor_normal += atomlen + 2;

                    }
                }
            }
        }
    }
    return RULE_NOMATCH;
}
Example #10
0
/* 
 *  ruleMatch
 * 
 *          p: packet data structure, same as the one found in snort.
 *    options: NULL terminated list of rule options
 *
 * Returns: 
 *    > 0 : match found
 *    = 0 : no match found
 *
 * Predefined constants: 
 *    (see sf_snort_plugin_api.h for more values)
 *    RULE_MATCH   -  if asn1 specifier is found within buffer 
 *    RULE_NOMATCH -  if asn1 specifier is not found within buffer 
 * 
 */
int ruleMatchInternal(SFSnortPacket *p, Rule* rule, u_int32_t optIndex, u_int8_t **cursor)
{
    u_int8_t *thisCursor = NULL, *startCursor = NULL;
    u_int8_t *tmpCursor = NULL;
    int retVal = RULE_NOMATCH;
    u_int32_t notFlag = 0;
    int thisType;
    ContentInfo *thisContentInfo = NULL;
    int startAdjust = 0;
    u_int32_t origFlags = 0;
    int32_t origOffset = 0;
    u_int32_t origDepth = 0;
    int continueLoop = 1;
    PCREInfo *thisPCREInfo = NULL;

    if (cursor)
        startCursor = thisCursor = *cursor;

    if (optIndex >= rule->numOptions || !rule->options[optIndex] )
        return RULE_NOMATCH;

    thisType = rule->options[optIndex]->optionType;

    /* Do some saving off of some info for recursion purposes */
    switch (thisType)
    {
        case OPTION_TYPE_CONTENT:
            thisContentInfo = rule->options[optIndex]->option_u.content;
            origFlags = thisContentInfo->flags;
            origDepth = thisContentInfo->depth;
            origOffset = thisContentInfo->offset;
            break;
        case OPTION_TYPE_PCRE:
            thisPCREInfo = rule->options[optIndex]->option_u.pcre;
            origFlags = thisPCREInfo->flags;
            break;
        default:
            /* Other checks should not need to check again like
             * PCRE & Content do.
             */
            break;
    }

    do
    {
        switch (thisType)
        {
        case OPTION_TYPE_CONTENT:
            retVal = contentMatch(p, rule->options[optIndex]->option_u.content, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.content->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_PCRE:
            retVal = pcreMatch(p, rule->options[optIndex]->option_u.pcre, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.pcre->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_FLOWBIT:
            retVal = processFlowbits(p, rule->options[optIndex]->option_u.flowBit);
            notFlag = rule->options[optIndex]->option_u.flowBit->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_BYTE_TEST:
            retVal = byteTest(p, rule->options[optIndex]->option_u.byte, thisCursor);
            notFlag = rule->options[optIndex]->option_u.byte->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_BYTE_JUMP:
            retVal = byteJump(p, rule->options[optIndex]->option_u.byte, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.byte->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_FLOWFLAGS:
            retVal = checkFlow(p, rule->options[optIndex]->option_u.flowFlags);
            notFlag = rule->options[optIndex]->option_u.flowFlags->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_ASN1:
            retVal = detectAsn1(p, rule->options[optIndex]->option_u.asn1, thisCursor);
            notFlag = rule->options[optIndex]->option_u.asn1->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_CURSOR:
            retVal = checkCursor(p, rule->options[optIndex]->option_u.cursor, thisCursor);
            notFlag = rule->options[optIndex]->option_u.cursor->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_SET_CURSOR:
            retVal = setCursor(p, rule->options[optIndex]->option_u.cursor, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.cursor->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_HDR_CHECK:
            retVal = checkHdrOpt(p, rule->options[optIndex]->option_u.hdrData);
            notFlag = rule->options[optIndex]->option_u.hdrData->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_BYTE_EXTRACT:
            retVal = extractValue(p, rule->options[optIndex]->option_u.byteExtract, thisCursor);
            notFlag = rule->options[optIndex]->option_u.byteExtract->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_LOOP:
            retVal = loopEval(p, rule->options[optIndex]->option_u.loop, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.loop->flags & NOT_FLAG;
            break;
        case OPTION_TYPE_PREPROCESSOR:
            retVal = preprocOptionEval(p, rule->options[optIndex]->option_u.preprocOpt, &thisCursor);
            notFlag = rule->options[optIndex]->option_u.preprocOpt->flags & NOT_FLAG;
            break;
        }

        if ( notFlag )
        {
            if ((retVal <= RULE_NOMATCH))
            {
                /* Set this as a positive match -- a ! was specified. */
                retVal = RULE_MATCH;
            }
            else  /* Match */
            {
                retVal = RULE_NOMATCH;
            }
        }

        if (retVal > RULE_NOMATCH)
        {
            /* This one matched.  Depending on type, check the next one
             * either in a loop, or not, saving cursor temporarily.
             */
            if (optIndex < rule->numOptions -1) /* hehe, optIndex is 0 based */
            {
                int nestedRetVal;
                /* Here's where it gets tricky... */
                if (thisType == OPTION_TYPE_CONTENT)
                {
                    /* If this is a content option, we've found a match.
                     * Save off the end-point of the current match.
                     * Less the length of current match plus 1.
                     *
                     * This gives us the starting point to check for this
                     * content again if subsequent options fail.  That starting
                     * point is the byte AFTER the beginning of the current
                     * match.
                     */
                    if ((origFlags & CONTENT_RELATIVE) && startCursor)
                    {
                        /* relative content.
                         * need to adjust offset/depth as well
                         */
                        tmpCursor = thisCursor -
                            thisContentInfo->patternByteFormLength + thisContentInfo->incrementLength;

                        /* Start Adjust is the difference between the old
                         * starting point and the 'next' starting point. */
                        startAdjust = tmpCursor - startCursor;
                    }
                    else
                    {
                        /* non-relative content */
                        tmpCursor = thisCursor -
                            thisContentInfo->patternByteFormLength + thisContentInfo->incrementLength;
                    }
                }
                else if (thisType == OPTION_TYPE_PCRE)
                {
                    /* Start next search at end of current pattern */
                    /* XXX: Could miss something here if part of pattern
                     * repeats but not easy to tell with PCRE.
                     */
                    tmpCursor = thisCursor;
                }

                nestedRetVal = ruleMatchInternal(p, rule, optIndex+1, &thisCursor);
                if (nestedRetVal == RULE_MATCH)
                {
                    /* Cool, everyone after us matched, we're done with a match */
                    if (cursor)
                        *cursor = thisCursor;
                    break;
                }

                /* If Content or PCRE, look farther into the packet
                 * for another match. */
                if (((thisType == OPTION_TYPE_CONTENT) ||
                     (thisType == OPTION_TYPE_PCRE))
                     && !notFlag)
                {
                    /* Only try to find this content again if it is a
                     * positive match.
                     */

                    /* And only if the next option is relative */
                    if (!isRelativeOption(rule->options[optIndex+1]))
                    {
                        /* Match failed, next option is not relative. 
                         * We're done. */
                        retVal = nestedRetVal;
                        break;
                    }

                    switch (thisType)
                    {
                    case OPTION_TYPE_CONTENT:
                        if (origFlags & CONTENT_RELATIVE)
                        {
                            if ((int32_t)(origDepth - startAdjust) < (int32_t)thisContentInfo->patternByteFormLength)
                            {
                                /* Adjusted depth would be less than the content we're searching for?
                                 * we're done. */
                                retVal = nestedRetVal;
                                continueLoop = 0;
                            }
                            else
                            {
                                /* For contents that were already relative, adjust the offset & depth fields
                                 * from their original values.  Makes it easy to determine when we'll be out
                                 * of the original bounds, relative to the original cursor. */
                                thisContentInfo->offset = origOffset + startAdjust;
                                thisContentInfo->depth = origDepth - startAdjust;

                                /* And use the original cursor that was passed in */
                                thisCursor = startCursor;
                            }
                        }
                        else
                        {
                            thisContentInfo->flags |= CONTENT_RELATIVE;
                            /* For contents that were not already relative, we simply use the adjusted
                             * cursor.  Set thisCursor to tmpCursor as calculated above */
                            thisCursor = tmpCursor;
                        }
                        break;
                    case OPTION_TYPE_PCRE:
                        /* Doesn't matter if it was already relative,
                         * just make it relative anyway.
                         */
                        thisPCREInfo->flags |= CONTENT_RELATIVE;
                        /* For PCREs that were not already relative, we use the cursor
                         * that was returned at the end of the pattern to start searching
                         * again. */
                        thisCursor = tmpCursor;
                        break;
                    }
                    continue;
                }


                /* Only need to search again when this is a
                 * content option.  If its not, we're done. */
                if (nestedRetVal <= RULE_NOMATCH)
                {
                    /* Handle the case when an error is propigated
                     * via nestedRetVal.
                     */
                    retVal = RULE_NOMATCH;
                }
                break;
            }
            else
            {
                /* Cool, nobody after us, we're done with a match */
                if (cursor)
                    *cursor = thisCursor;
                break;
            }
        }
        else
        {
            /* No match, get outta dodge */
            break;
        }
    } while (continueLoop);
    /* Keep looping until we break or serialized content checks returns no match. */

    /* Reset the flags for this content in case we added the
     * relative flag above.
     */
    if (thisContentInfo)
    {
        thisContentInfo->flags = origFlags;
        thisContentInfo->offset = origOffset;
        thisContentInfo->depth = origDepth;
    }
    if (thisPCREInfo)
    {
        thisPCREInfo->flags = origFlags;
    }

    return retVal;
}
/* detection functions */
int rule13969eval(void *p) {
    const uint8_t *cursor_normal = 0;
    SFSnortPacket *sp = (SFSnortPacket *) p;
    const uint8_t *end_of_payload, *beg_of_payload;
    uint8_t value=0, rec_instance;
    uint16_t word=0, pid, fcomplex;
    uint32_t op, rec_len;
    int i;

    if(sp == NULL)
        return RULE_NOMATCH;

    if(sp->payload == NULL)
        return RULE_NOMATCH;

    // flow:established, to_client;
    if (checkFlow(p, rule13969options[0]->option_u.flowFlags) > 0 ) {
        // flowbits:isset "file.ppt";
        if (processFlowbits(p, rule13969options[1]->option_u.flowBit) > 0) {
            // content:"|0B F0|";
            if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
                return RULE_NOMATCH;

            cursor_normal = beg_of_payload;

            while (contentMatch(p, rule13969options[2]->option_u.content, &cursor_normal) > 0) {
                if((cursor_normal + 4 >= end_of_payload) || (cursor_normal - 2 < beg_of_payload))
                    return RULE_NOMATCH;

                rec_len = *cursor_normal++;
                rec_len |= (*cursor_normal++) << 8;
                rec_len |= (*cursor_normal++) << 16;
                rec_len |= (*cursor_normal++) << 24;

                if(   (cursor_normal + rec_len - 4 >= end_of_payload) ||
                        (cursor_normal + rec_len < cursor_normal))
                    return RULE_NOMATCH;

                // MSO Drawing Property Table Property Table Record Instance
                // cursor_normal = cursor_normal -8;
                value = *(cursor_normal-8);

                DEBUG_WRAP(printf("WORD: %d\n", value));

                rec_instance=(value>>4) & 0x0f;

                DEBUG_WRAP(printf("RECORD INSTANCE: %d\n", rec_instance));

                // Loop through all FOPTE objects
                for(i=0; i < rec_instance; i++) {
                    if(cursor_normal + 6 >= end_of_payload)
                        return RULE_NOMATCH;

                    word = *cursor_normal++;
                    word |= *cursor_normal++ << 8;
                    pid = word & 0x3fff;
                    fcomplex = word & 0x8000;

                    op = *cursor_normal++;
                    op |= *cursor_normal++ << 8;
                    op |= *cursor_normal++ << 16;
                    op |= *cursor_normal++ << 24;

                    DEBUG_WRAP(printf("PID: %d\n", pid));
                    DEBUG_WRAP(printf("FCOMPLEX: %d\n", fcomplex));

                    if ((fcomplex || pid==272) && (op >= 0xFFFFFFFC)) {
                        return RULE_MATCH;
                    }
                }
            }
        }
    }
    return RULE_NOMATCH;
}