def p_xor_mod_args(self, p): '''xor_mod_args : LPAREN NUM RPAREN | LPAREN NUM HYPHEN NUM RPAREN | LPAREN HEXNUM RPAREN | LPAREN HEXNUM HYPHEN HEXNUM RPAREN | LPAREN NUM HYPHEN HEXNUM RPAREN | LPAREN HEXNUM HYPHEN NUM RPAREN''' logger.debug('Matched an xor arg: {}'.format(''.join(p[1:]))) mods = [x for x in p if x not in (None, '(', '-', ')')] mod_int_list = [] mod_lineidx = set() for i, x in enumerate(mods): mod_int = int(x, 16) if x.startswith('0x') else int(x) if 0 <= mod_int <= 255: mod_int_list.append(mod_int) mod_lineidx.add(i) else: message = 'String modification value {} not between 0-255 on line {}'.format( x, p.lineno(1 + i)) raise ParseTypeError(message, p.lineno, p.lexpos) if mod_int_list[0] > mod_int_list[-1]: mod_lineno = list({p.lineno(1 + i) for i in mod_lineidx}) mod_lineno.sort() line_no = ' and '.join(str(lno) for lno in mod_lineno) message = 'String modification lower bound exceeds upper bound on line {}'.format( line_no) raise ParseTypeError(message, p.lineno, p.lexpos) else: mod_str_mod = YaraXor(mod_int_list) logger.debug('Matched string modifier(s): {}'.format(mod_str_mod)) self._add_element(ElementTypes.STRINGS_MODIFIER, mod_str_mod)
def _add_string_modifier(self, p): mod_str = p[1] prev_mod_with_args = False if mod_str in self.string_modifiers: message = 'Duplicate string modifier {} on line {}'.format(mod_str, p.lineno(1)) raise ParseTypeError(message, p.lineno, p.lexpos) if mod_str in self.EXCLUSIVE_TEXT_MODIFIERS: prev_mods = {x for x in self.string_modifiers if isinstance(x, str)} excluded_modifiers = prev_mods & ({mod_str} ^ self.EXCLUSIVE_TEXT_MODIFIERS) if excluded_modifiers: prev_mod_str = excluded_modifiers.pop() message = ('Mutually exclusive string modifier use of {} on line {} after {} usage' .format(mod_str, p.lineno(1), prev_mod_str)) raise ParseTypeError(message, p.lineno, p.lexpos) if self.string_modifiers: # Convert previously created modifiers with args to strings if mod_str.startswith('base64') and isinstance(self.string_modifiers[-1], YaraBase64): if mod_str == 'base64wide': self.string_modifiers[-1].modifier_name = 'base64wide' logger.debug('Corrected base64 string modifier to base64wide') self.string_modifiers[-1] = str(self.string_modifiers[-1]) prev_mod_with_args = True elif mod_str == 'xor' and isinstance(self.string_modifiers[-1], YaraXor): self.string_modifiers[-1] = str(self.string_modifiers[-1]) logger.debug('Modified xor string was already added') prev_mod_with_args = True if not prev_mod_with_args: self._add_element(ElementTypes.STRINGS_MODIFIER, mod_str) logger.debug('Matched a string modifier: {}'.format(mod_str))
def _process_string_with_escapes(t, escape_chars=None): if escape_chars is None: escape_chars = [t.value] if t.lexer.escape == 1 and t.value in escape_chars or t.value == '\\': t.lexer.escape ^= 1 if t.value == 'x': t.lexer.hex_escape = 2 elif t.lexer.hex_escape > 0: if t.value.lower() in string.hexdigits: t.lexer.hex_escape -= 1 else: raise ParseTypeError('Invalid hex character: {!r}, at line: {}'.format(t.value, t.lexer.lineno), t.lexer.lineno, t.lexer.lexpos) elif t.lexer.escape == 1: raise ParseTypeError('Invalid escape sequence: \\{}, at line: {}'.format(t.value, t.lexer.lineno), t.lexer.lineno, t.lexer.lexpos)
def t_error(t): """Raise parsing error. Args: t: Token input from lexer. Raises: ParseTypeError """ raise ParseTypeError('Illegal character {!r} at line {}'.format(t.value[0], t.lexer.lineno), t.lexer.lineno, t.lexer.lexpos)
def t_REXSTRING_error(t): """Raise parsing error for illegal rexstring character. Args: t: Token input from lexer. Raises: ParseTypeError """ raise ParseTypeError('Illegal rexstring character : {!r}, at line: {}'.format(t.value[0], t.lexer.lineno), t.lexer.lineno, t.lexer.lexpos)
def p_rule(self, p): '''rule : scopes RULE ID tag_section LBRACE rule_body RBRACE''' logger.info('Matched rule: {}'.format(p[3])) if '.' in p[3]: message = 'Invalid rule name {}, on line {}'.format(p[3], p.lineno(1)) raise ParseTypeError(message, p.lineno, p.lexpos) logger.debug('Rule start: {}, Rule stop: {}'.format(p.lineno(2), p.lineno(7))) while self._rule_comments: comment = self._rule_comments.pop() if p.lexpos(5) < comment.lexpos < p.lexpos(7): self._add_element(getattr(ElementTypes, comment.type), comment.value) element_value = (p[3], int(p.lineno(2)), int(p.lineno(7)), ) self._add_element(ElementTypes.RULE_NAME, element_value)
def _parse_string_kv(self, p, string_type): """Perform parsing for all string types. Args: p: Parser object. string_type: StringTypes enum. """ key = p[1] value = p[3] match = re.match('"(.+)"', value) if match: value = match.group(1) if key != '$' and key in self._stringnames: message = 'Duplicate string name key {} on line {}'.format(key, p.lineno(1)) raise ParseTypeError(message, p.lineno, p.lexpos) self._stringnames.add(key) logger.debug('Matched strings kv: {} equals {}'.format(key, value)) self._add_element(ElementTypes.STRINGS_KEY_VALUE, (key, value, string_type, ))
def p_error(self, p): """Raise syntax errors. Args: p: Data from the parser. Raises: ParseTypeError """ if not p: # This happens when we try to parse an empty string or file, or one with no actual rules. pass elif p.type in ('COMMENT', 'MCOMMENT'): self.parser.errok() # This is a method from PLY to reset the error state from parsing a comment self._rule_comments.append(p) else: message = 'Unknown text {} for token of type {} on line {}'.format(p.value, p.type, p.lineno) raise ParseTypeError(message, p.lineno, p.lexpos)