def test_get_tokenstream(self): yara_file = yaramod.Yaramod().parse_string(''' rule empty_rule { meta: key = "value" condition: true }''') self.assertEqual(len(yara_file.rules), 1) rule = yara_file.rules[0] rule.metas[0].value = yaramod.Literal('another value') ts = yara_file.tokenstream self.assertFalse(ts.empty) self.assertEqual(ts.front.pure_text, '\n') self.assertEqual(ts.back.pure_text, '}') self.assertEqual(ts.tokens_as_text, [ '\n', 'rule', 'empty_rule', '{', '\n', 'meta', ':', '\n', 'key', '=', 'another value', '\n', 'condition', ':', '\n', 'true', '\n', '}' ]) condition_ts = rule.condition.tokenstream self.assertEqual(condition_ts.tokens_as_text, [ '\n', 'rule', 'empty_rule', '{', '\n', 'meta', ':', '\n', 'key', '=', 'another value', '\n', 'condition', ':', '\n', 'true', '\n', '}' ])
def test_custom_module_interface(self): modules = yaramod.Yaramod(yaramod.Features.AllCurrent, "./tests/python/testing_modules").modules # module module_test self.assertTrue("module_test" in modules) module_symbol = modules["module_test"].structure self.assertEqual("module_test", module_symbol.name) self.assertTrue(module_symbol.is_structure) cuckoo_attributes = module_symbol.attributes self.assertTrue("structure_test" in cuckoo_attributes) structure_symbol = cuckoo_attributes["structure_test"] self.assertTrue(structure_symbol.is_structure) structure_attributes = structure_symbol.attributes self.assertTrue("function_test" in structure_attributes) function_symbol = structure_attributes["function_test"] self.assertTrue(function_symbol.is_function) self.assertEqual(function_symbol.return_type, yaramod.ExpressionType.String) function_overloads = function_symbol.overloads self.assertEqual(len(function_overloads), 2) self.assertEqual(function_overloads[0], [yaramod.ExpressionType.Regexp]) self.assertEqual(function_overloads[1], [yaramod.ExpressionType.Regexp, yaramod.ExpressionType.String]) function_documentations = function_symbol.documentations print(function_documentations) self.assertEqual(len(function_documentations), 2) self.assertEqual(function_documentations[0], "Testing function overload documentation.") self.assertEqual(function_documentations[1], "Testing function cool overload documentation.") self.assertTrue("value_test" in cuckoo_attributes) value_symbol = cuckoo_attributes["value_test"] self.assertTrue(value_symbol.is_value) self.assertEqual(value_symbol.documentation, "Testing value documentation. Example: ```module_test.value_test > 10```")
def test_modifying_visitor_eq_expression(self): class EqModifyer(yaramod.ModifyingVisitor): def add(self, yara_file: yaramod.YaraFile): for rule in yara_file.rules: rule.condition = self.modify(rule.condition) def visit_EqExpression(self, expr: yaramod.Expression): context = yaramod.TokenStreamContext(expr) expr.left_operand.accept(self) expr.right_operand.accept(self) output = (yaramod.YaraExpressionBuilder(expr.right_operand) != yaramod.YaraExpressionBuilder( expr.left_operand)).get() self.cleanUpTokenStreams(context, output) return output yara_file = yaramod.Yaramod().parse_string(r''' rule rule_with_regexp_in_fnc_call { strings: $str1 = "a" $str2222 = "b" condition: !str1 == !str2222 }''') regexp_icase_adder = EqModifyer() regexp_icase_adder.add(yara_file) self.assertEqual(len(yara_file.rules), 1) rule = yara_file.rules[0] cond = rule.condition self.assertTrue(isinstance(cond, yaramod.NeqExpression)) self.assertTrue( isinstance(cond.left_operand, yaramod.StringLengthExpression)) self.assertTrue( isinstance(cond.right_operand, yaramod.StringLengthExpression)) self.assertEqual( r'''rule rule_with_regexp_in_fnc_call { strings: $str1 = "a" $str2222 = "b" condition: !str2222 != !str1 }''', yara_file.text) expected = r''' rule rule_with_regexp_in_fnc_call { strings: $str1 = "a" $str2222 = "b" condition: !str2222 != !str1 } ''' self.assertEqual(expected, yara_file.text_formatted)
def test_simple_modifying_visitor(self): class StringExpressionUpper(yaramod.ModifyingVisitor): def add(self, yara_file: yaramod.YaraFile): for rule in yara_file.rules: self.modify(rule.condition) def visit_StringExpression(self, expr: yaramod.Expression): expr.id = expr.id.upper() yara_file = yaramod.Yaramod().parse_string(r''' import "cuckoo" rule rule_with_regexp_in_fnc_call { strings: $str1 = "s" $str2 = "s" condition: $str1 and $str2 }''') visitor = StringExpressionUpper() visitor.add(yara_file) self.assertEqual(len(yara_file.rules), 1) rule = yara_file.rules[0] cond = rule.condition self.assertTrue(isinstance(cond, yaramod.AndExpression)) self.assertTrue( isinstance(cond.right_operand, yaramod.StringExpression)) self.assertEqual(cond.left_operand.id, "$STR1") self.assertTrue(isinstance(cond.left_operand, yaramod.StringExpression)) self.assertEqual(cond.right_operand.id, "$STR2") self.assertEqual( r'''import "cuckoo" rule rule_with_regexp_in_fnc_call { strings: $STR1 = "s" $STR2 = "s" condition: $STR1 and $STR2 }''', yara_file.text) expected = r''' import "cuckoo" rule rule_with_regexp_in_fnc_call { strings: $STR1 = "s" $STR2 = "s" condition: $STR1 and $STR2 } ''' self.assertEqual(expected, yara_file.text_formatted)
def main(): if len(sys.argv) != 2: sys.exit('Usage: {} YARA_FILE'.format(sys.argv[0])) dumper = Dumper() ymod_parser = yaramod.Yaramod() yara_file = ymod_parser.parse_file(sys.argv[1]) for rule in yara_file.rules: print('==== RULE: {}'.format(rule.name)) dumper.observe(rule.condition)
def test_module_interface(self): modules = yaramod.Yaramod().modules # module cuckoo self.assertTrue("cuckoo" in modules) cuckoo_symbol = modules["cuckoo"].structure self.assertEqual("cuckoo", cuckoo_symbol.name) self.assertTrue(cuckoo_symbol.is_structure) cuckoo_attributes = cuckoo_symbol.attributes self.assertTrue("network" in cuckoo_attributes) network_symbol = cuckoo_attributes["network"] self.assertTrue(network_symbol.is_structure) network_attributes = network_symbol.attributes self.assertTrue("http_get" in network_attributes) http_get_symbol = network_attributes["http_get"] self.assertTrue(http_get_symbol.is_function) self.assertEqual("http_get", http_get_symbol.name) self.assertEqual(http_get_symbol.return_type, yaramod.ExpressionType.Int) http_get_overloads = http_get_symbol.overloads self.assertEqual(len(http_get_overloads), 1) self.assertEqual(http_get_overloads[0], [yaramod.ExpressionType.Regexp]) # module pe self.assertTrue("pe" in modules) pe_symbol = modules["pe"].structure self.assertEqual("pe", pe_symbol.name) self.assertTrue(pe_symbol.is_structure) pe_attributes = pe_symbol.attributes self.assertTrue("MACHINE_UNKNOWN" in pe_attributes) machine_symbol = pe_attributes["MACHINE_UNKNOWN"] self.assertTrue(machine_symbol.is_value) self.assertEqual(machine_symbol.data_type, yaramod.ExpressionType.Int) self.assertTrue("version_info" in pe_attributes) version_info_symbol = pe_attributes["version_info"] self.assertEqual(version_info_symbol.documentation[0:10], "Dictionary") self.assertTrue("sections" in pe_attributes) section_array_symbol = pe_attributes['sections'] self.assertEqual(section_array_symbol.name, 'sections') self.assertTrue(section_array_symbol.is_array) self.assertEqual(section_array_symbol.element_type, yaramod.ExpressionType.Object) self.assertEqual(section_array_symbol.documentation[0:10], 'Individual') section_symbol = section_array_symbol.structure self.assertEqual(section_symbol.name, 'sections') self.assertTrue(section_symbol.is_structure) section_attributes = section_symbol.attributes self.assertTrue("characteristics" in section_attributes)
def test_get_modulepool(self): ymod = yaramod.Yaramod() modules = ymod.modules self.assertTrue("cuckoo" in modules) self.assertTrue("dex" in modules) self.assertTrue("dotnet" in modules) self.assertTrue("elf" in modules) self.assertTrue("hash" in modules) self.assertTrue("macho" in modules) self.assertTrue("magic" in modules) self.assertTrue("math" in modules) self.assertTrue("pe" in modules) self.assertTrue("time" in modules)
def test_modifying_visitor_inpact_on_regexp_expression(self): class RegexpCaseInsesitiveAdder(yaramod.ModifyingVisitor): def add(self, yara_file: yaramod.YaraFile): for rule in yara_file.rules: self.modify(rule.condition) def visit_RegexpExpression(self, expr: yaramod.Expression): output = yaramod.regexp('abc', 'i').get() expr.exchange_tokens(output) return output yara_file = yaramod.Yaramod().parse_string(r''' import "cuckoo" rule rule_with_regexp_in_fnc_call { condition: cuckoo.network.http_request(/http:\/\/someone\.doingevil\.com/) }''') regexp_icase_adder = RegexpCaseInsesitiveAdder() regexp_icase_adder.add(yara_file) self.assertEqual(len(yara_file.rules), 1) rule = yara_file.rules[0] cond = rule.condition self.assertTrue(isinstance(cond, yaramod.FunctionCallExpression)) self.assertEqual(len(cond.arguments), 1) self.assertTrue(isinstance(cond.arguments[0], yaramod.RegexpExpression)) self.assertTrue( isinstance(cond.arguments[0].regexp_string, yaramod.Regexp)) self.assertEqual(cond.arguments[0].regexp_string.text, r'/abc/i') self.assertEqual(cond.arguments[0].regexp_string.pure_text, rb'abc') self.assertEqual( r'''import "cuckoo" rule rule_with_regexp_in_fnc_call { condition: cuckoo.network.http_request(/abc/i) }''', yara_file.text) expected = r''' import "cuckoo" rule rule_with_regexp_in_fnc_call { condition: cuckoo.network.http_request(/abc/i) } ''' self.assertEqual(expected, yara_file.text_formatted)
def test_get_tokenstream_after_syntax_error_1(self): input_text = ''' rule dummy_rule { condition true }''' ymod = yaramod.Yaramod() try: ymod.parse_string(input_text) except: ts = ymod.yara_file.tokenstream self.assertFalse(ts.empty) self.assertEqual(ts.front.pure_text, '\n') self.assertEqual(ts.back.pure_text, 'true')
def main(): if len(sys.argv) != 2: sys.exit('Usage: {} YARA_FILE'.format(sys.argv[0])) simplifier = BoolSimplifier() ymod_parser = yaramod.Yaramod() yara_file = ymod_parser.parse_file(sys.argv[1]) for rule in yara_file.rules: print('==== RULE: {}'.format(rule.name)) print('==== BEFORE') print(rule.text) rule.condition = simplifier.modify(rule.condition, when_deleted=yaramod.bool_val(False).get()) print('==== AFTER') print(rule.text)
def test_get_tokenstream_after_unknown_identifier_error(self): input_text = ''' rule dummy_rule { condition: blah or true }''' ymod = yaramod.Yaramod() try: ymod.parse_string(input_text) except: ts = ymod.yara_file.tokenstream self.assertFalse(ts.empty) self.assertEqual(ts.front.pure_text, '\n') # After 'blah', also 'or' got into TS, because 'blah' is not tested by the grammar, it is semantics issue self.assertEqual(ts.back.pure_text, 'or')
def test_get_tokenstream_after_unknown_module_error(self): input_text = ''' import "unknown" rule dummy_rule { condition: true }''' ymod = yaramod.Yaramod() try: ymod.parse_string(input_text) except: ts = ymod.yara_file.tokenstream self.assertFalse(ts.empty) # After 'unknown', also 'rule' got into TS, because 'unknown' is not tested by the grammar, it is semantics issue self.assertEqual(ts.tokens_as_text, ['\n', 'import', 'unknown', '\n', '\n', 'rule'])
def test_meta_values_interface(self): input_text = """rule test { meta: author = "Name Surname" description = "Test checking the meta value tokens" condition: false } """ ymod = yaramod.Yaramod() yfile = ymod.parse_string(input_text) self.assertEqual(len(yfile.rules[0].metas), 2) meta = yfile.rules[0].metas[0] # author self.assertTrue(hasattr(meta, "token_key")) token = meta.token_key self.assertEqual(token.location.begin.line, 3) self.assertEqual(token.location.begin.column, 9) self.assertEqual(token.location.end.line, 3) self.assertEqual(token.location.end.column, 14) self.assertTrue(hasattr(meta, "token_value")) token = meta.token_value self.assertEqual(token.location.begin.line, 3) self.assertEqual(token.location.begin.column, 18) self.assertEqual(token.location.end.line, 3) self.assertEqual(token.location.end.column, 31) meta = yfile.rules[0].metas[1] # description self.assertTrue(hasattr(meta, "token_key")) token = meta.token_key self.assertEqual(token.location.begin.line, 4) self.assertEqual(token.location.begin.column, 9) self.assertEqual(token.location.end.line, 4) self.assertEqual(token.location.end.column, 19) self.assertTrue(hasattr(meta, "token_value")) token = meta.token_value self.assertEqual(token.location.begin.line, 4) self.assertEqual(token.location.begin.column, 23) self.assertEqual(token.location.end.line, 4) self.assertEqual(token.location.end.column, 59)
def test_change_meta_of_rule(self): yara_file = yaramod.Yaramod().parse_string(''' rule empty_rule { meta: key = "value" condition: true }''') self.assertEqual(len(yara_file.rules), 1) rule = yara_file.rules[0] rule.metas[0].value = yaramod.Literal('another value') expected = ''' rule empty_rule { meta: key = "another value" condition: true } ''' self.assertEqual(expected, yara_file.text_formatted)
def test_rule_inserter(self): class RuleInserter(yaramod.ModifyingVisitor): def insert_rule(self, yara_file): rule_cond = yaramod.conjunction( [yaramod.id('first_file'), yaramod.id('second_file')]) another_rule = yaramod.YaraRuleBuilder() \ .with_modifier(yaramod.RuleModifier.Private) \ .with_name('ANOTHER_RULE') \ .with_condition(rule_cond.get()) \ .get() for rule in yara_file.rules: if not rule.is_private: context = yaramod.TokenStreamContext(rule.condition) output = yaramod.conjunction([ yaramod.id(another_rule.name), yaramod.paren(yaramod.YaraExpressionBuilder( rule.condition), linebreaks=True) ]).get() self.cleanup_tokenstreams(context, output) rule.condition = output yara_file.insert_rule(0, another_rule) yara_file = yaramod.Yaramod().parse_string(r''' rule rule_1 { strings: $str1 = "a" condition: $str1 } ''') visitor = RuleInserter() visitor.insert_rule(yara_file) self.assertEqual(len(yara_file.rules), 2) rule = yara_file.rules[1] cond = rule.condition self.assertEqual(r'''ANOTHER_RULE and ( $str1 )''', cond.text) self.assertEqual( r'''private rule ANOTHER_RULE { condition: first_file and second_file } rule rule_1 { strings: $str1 = "a" condition: ANOTHER_RULE and ( $str1 ) }''', yara_file.text) expected = r''' private rule ANOTHER_RULE { condition: first_file and second_file } rule rule_1 { strings: $str1 = "a" condition: ANOTHER_RULE and ( $str1 ) } ''' self.assertEqual(expected, yara_file.text_formatted)
def test_modifying_visitor_delete_rules(self): class RulesDeleter(yaramod.ModifyingVisitor): def __init__(self): super(RulesDeleter, self).__init__() self.rule_map = {} self.rules_for_remove = set() def remove_disabled_rules(self, yara_file: yaramod.YaraFile) -> None: for rule in yara_file.rules: if rule.name.startswith('delete'): self.rules_for_remove.add(rule.name) yara_file.remove_rules( lambda r: r.name in self.rules_for_remove) for rule in yara_file.rules: rule.condition = self.modify( rule.condition, when_deleted=yaramod.bool_val(False).get()) def visit_IdExpression(self, expr): if expr.symbol.name in self.rules_for_remove: return yaramod.VisitAction.Delete yara_file = yaramod.Yaramod().parse_string(r''' rule delete_rule_1 { strings: $str0 = "a" condition: $str0 } rule rule_2 { strings: $str1 = "b" condition: $str1 or not delete_rule_1 } rule delete_rule_3 { condition: delete_rule_1 } rule rule_4 { condition: not delete_rule_3 and not rule_2 } rule rule_5 { strings: $str1 = "c" condition: not delete_rule_1 and not rule_2 and not delete_rule_3 and $str1 } ''') visitor = RulesDeleter() visitor.remove_disabled_rules(yara_file) self.assertEqual(len(yara_file.rules), 3) self.assertEqual( r'''rule rule_2 { strings: $str1 = "b" condition: $str1 } rule rule_4 { condition: not rule_2 } rule rule_5 { strings: $str1 = "c" condition: not rule_2 and $str1 }''', yara_file.text) expected = r''' rule rule_2 { strings: $str1 = "b" condition: $str1 } rule rule_4 { condition: not rule_2 } rule rule_5 { strings: $str1 = "c" condition: not rule_2 and $str1 } ''' self.assertEqual(expected, yara_file.text_formatted)
def test_pe_iconhash_deleter(self): class PeIconhashDeleter(yaramod.ModifyingVisitor): """Temporary pe.iconhash() remover which removes pe.iconhash() until we get it into the upstream. """ def delete_pe_iconhash(self, yara_file): pe_symbol = yara_file.find_symbol('pe') if not pe_symbol: return for rule in yara_file.rules: rule.condition = self.modify( rule.condition, when_deleted=yaramod.bool_val(False).get()) def visit_AndExpression(self, expr): return self._visit_logical_ops(expr) def visit_OrExpression(self, expr): return self._visit_logical_ops(expr) def visit_LeExpression(self, expr): return self._visit_binary_ops(expr) def visit_LtExpression(self, expr): return self._visit_binary_ops(expr) def visit_GeExpression(self, expr): return self._visit_binary_ops(expr) def visit_GtExpression(self, expr): return self._visit_binary_ops(expr) def visit_EqExpression(self, expr): return self._visit_binary_ops(expr) def visit_NeqExpression(self, expr): return self._visit_binary_ops(expr) def visit_PlusExpression(self, expr): return self._visit_binary_ops(expr) def visit_MinusExpression(self, expr): return self._visit_binary_ops(expr) def visit_MultiplyExpression(self, expr): return self._visit_binary_ops(expr) def visit_DivideExpression(self, expr): return self._visit_binary_ops(expr) def visit_ModuloExpression(self, expr): return self._visit_binary_ops(expr) def visit_BitwiseXorExpression(self, expr): return self._visit_binary_ops(expr) def visit_BitwiseAndExpression(self, expr): return self._visit_binary_ops(expr) def visit_BitwiseOrExpression(self, expr): return self._visit_binary_ops(expr) def visit_ShiftLeftExpression(self, expr): return self._visit_binary_ops(expr) def visit_ShiftRightExpression(self, expr): return self._visit_binary_ops(expr) def visit_ContainsExpression(self, expr): return self._visit_binary_ops(expr) def visit_MatchesExpression(self, expr): return self._visit_binary_ops(expr) def visit_StringOffsetExpression(self, expr): return self._visit_string_manipulation_ops(expr) def visit_StringLengthExpression(self, expr): return self._visit_string_manipulation_ops(expr) def visit_FunctionCallExpression(self, expr): if expr.function.text == 'pe.iconhash': return yaramod.VisitAction.Delete def _visit_logical_ops(self, expr): context = yaramod.TokenStreamContext(expr) left_context = yaramod.TokenStreamContext(expr.left_operand) right_context = yaramod.TokenStreamContext(expr.right_operand) left_result = expr.left_operand.accept(self) right_result = expr.right_operand.accept(self) if left_result == yaramod.VisitAction.Delete or right_result == yaramod.VisitAction.Delete: if left_result == yaramod.VisitAction.Delete: new_operand = yaramod.bool_val(False).get() self.cleanup_tokenstreams(left_context, new_operand) expr.left_operand = new_operand if right_result == yaramod.VisitAction.Delete: new_operand = yaramod.bool_val(False).get() self.cleanup_tokenstreams(right_context, new_operand) expr.right_operand = new_operand else: return self.default_handler(context, expr, left_result, right_result) def _visit_binary_ops(self, expr): context = yaramod.TokenStreamContext(expr) left_result = expr.left_operand.accept(self) right_result = expr.right_operand.accept(self) if left_result == yaramod.VisitAction.Delete or right_result == yaramod.VisitAction.Delete: return yaramod.VisitAction.Delete else: self.default_handler(context, expr, left_result, right_result) def _visit_string_manipulation_ops(self, expr): context = yaramod.TokenStreamContext(expr) index_result = None if expr.index_expr: index_result = expr.index_expr.accept(self) if index_result == yaramod.VisitAction.Delete: return yaramod.VisitAction.Delete return self.default_handler(context, expr, index_result) yara_file = yaramod.Yaramod().parse_string(r''' import "pe" rule rule_1 { strings: $str1 = "a" $str2 = "b" condition: $str1 and ( pe.iconhash() == "9d0bd50f710" or pe.iconhash() != "9d0bd50f711" or "9d0bd50f712" == pe.iconhash() or pe.iconhash() or $str2 or pe.iconhash() == "9d0bd50f714" or pe.iconhash() == "9d0bd50f715" ) } ''') visitor = PeIconhashDeleter() visitor.delete_pe_iconhash(yara_file) self.assertEqual(len(yara_file.rules), 1) self.assertEqual( r'''import "pe" rule rule_1 { strings: $str1 = "a" $str2 = "b" condition: $str1 and (false or false or false or false or $str2 or false or false) }''', yara_file.text) expected = r''' import "pe" rule rule_1 { strings: $str1 = "a" $str2 = "b" condition: $str1 and ( false or false or false or false or $str2 or false or false ) } ''' self.assertEqual(expected, yara_file.text_formatted)
def test_cuckoo_function_replacer(self): class CuckooFunctionReplacer(yaramod.ModifyingVisitor): def __init__(self): super(CuckooFunctionReplacer, self).__init__() self.filesystem_symbol = None self.registry_symbol = None self.FILESYSTEM_REPLACE = set([ 'cuckoo.filesystem.file_write', ]) self.REGISTRY_REPLACE = set([ 'cuckoo.registry.key_write', ]) self.WHITELIST = set([ 'cuckoo.network.http_post', ]) def replace_functions(self, yara_file): cuckoo_symbol = yara_file.find_symbol('cuckoo') if not cuckoo_symbol: return if not self.filesystem_symbol and not self.registry_symbol: self.filesystem_symbol = yara_file.find_symbol( 'cuckoo').get_attribute('filesystem').get_attribute( 'file_access') self.registry_symbol = yara_file.find_symbol( 'cuckoo').get_attribute('registry').get_attribute( 'key_access') for rule in yara_file.rules: rule.condition = self.modify( rule.condition, when_deleted=yaramod.bool_val(False).get()) def visit_AndExpression(self, expr): return self._visit_logical_ops(expr) def visit_OrExpression(self, expr): return self._visit_logical_ops(expr) def visit_LeExpression(self, expr): return self._visit_binary_ops(expr) def visit_LtExpression(self, expr): return self._visit_binary_ops(expr) def visit_GeExpression(self, expr): return self._visit_binary_ops(expr) def visit_GtExpression(self, expr): return self._visit_binary_ops(expr) def visit_EqExpression(self, expr): return self._visit_binary_ops(expr) def visit_NeqExpression(self, expr): return self._visit_binary_ops(expr) def visit_PlusExpression(self, expr): return self._visit_binary_ops(expr) def visit_MinusExpression(self, expr): return self._visit_binary_ops(expr) def visit_MultiplyExpression(self, expr): return self._visit_binary_ops(expr) def visit_DivideExpression(self, expr): return self._visit_binary_ops(expr) def visit_ModuloExpression(self, expr): return self._visit_binary_ops(expr) def visit_BitwiseXorExpression(self, expr): return self._visit_binary_ops(expr) def visit_BitwiseAndExpression(self, expr): return self._visit_binary_ops(expr) def visit_BitwiseOrExpression(self, expr): return self._visit_binary_ops(expr) def visit_ShiftLeftExpression(self, expr): return self._visit_binary_ops(expr) def visit_ShiftRightExpression(self, expr): return self._visit_binary_ops(expr) def visit_StringOffsetExpression(self, expr): return self._visit_string_manipulation_ops(expr) def visit_StringLengthExpression(self, expr): return self._visit_string_manipulation_ops(expr) def _visit_logical_ops(self, expr): context = yaramod.TokenStreamContext(expr) left_context = yaramod.TokenStreamContext(expr.left_operand) right_context = yaramod.TokenStreamContext(expr.right_operand) left_result = expr.left_operand.accept(self) right_result = expr.right_operand.accept(self) if left_result == yaramod.VisitAction.Delete or right_result == yaramod.VisitAction.Delete: if left_result == yaramod.VisitAction.Delete: new_operand = yaramod.bool_val(False).get() self.cleanup_tokenstreams(left_context, new_operand) expr.left_operand = new_operand if right_result == yaramod.VisitAction.Delete: new_operand = yaramod.bool_val(False).get() self.cleanup_tokenstreams(right_context, new_operand) expr.right_operand = new_operand else: return self.default_handler(context, expr, left_result, right_result) def _visit_binary_ops(self, expr): context = yaramod.TokenStreamContext(expr) left_result = expr.left_operand.accept(self) right_result = expr.right_operand.accept(self) if left_result == yaramod.VisitAction.Delete or right_result == yaramod.VisitAction.Delete: return yaramod.VisitAction.Delete else: self.default_handler(context, expr, left_result, right_result) def _visit_string_manipulation_ops(self, expr): context = yaramod.TokenStreamContext(expr) index_result = None if expr.index_expr: index_result = expr.index_expr.accept(self) if index_result == yaramod.VisitAction.Delete: return yaramod.VisitAction.Delete return self.default_handler(context, expr, index_result) def visit_FunctionCallExpression(self, expr): function_name = expr.function.text if function_name.startswith('cuckoo.'): if function_name in self.FILESYSTEM_REPLACE: expr.function.symbol = self.filesystem_symbol elif function_name in self.REGISTRY_REPLACE: expr.function.symbol = self.registry_symbol elif function_name not in self.WHITELIST: return yaramod.VisitAction.Delete yara_file = yaramod.Yaramod().parse_string(r''' import "cuckoo" rule rule_1 { strings: $str1 = "a" condition: $str1 and ( cuckoo.filesystem.file_write(/C:\\Users\\Avastian\\file1.exe/i) or cuckoo.filesystem.file_read(/C:\\Users\\Avastian\\file1.exe/i) or cuckoo.registry.key_write(/\\Microsoft\\Windows NT\\CurrentVersion/i) or cuckoo.network.http_post(/\/.*\/tasks\.php/) or cuckoo.process.executed_command(/(^|\\)a(\.exe|\s)/i) ) } ''') visitor = CuckooFunctionReplacer() visitor.replace_functions(yara_file) self.assertEqual(len(yara_file.rules), 1) rule = yara_file.rules[0] cond = rule.condition print(cond.text) self.assertEqual( r'''$str1 and (cuckoo.filesystem.file_access(/C:\\Users\\Avastian\\file1.exe/i) or false or cuckoo.registry.key_access(/\\Microsoft\\Windows NT\\CurrentVersion/i) or cuckoo.network.http_post(/\/.*\/tasks\.php/) or false)''', cond.text) self.assertEqual( r'''import "cuckoo" rule rule_1 { strings: $str1 = "a" condition: $str1 and (cuckoo.filesystem.file_access(/C:\\Users\\Avastian\\file1.exe/i) or false or cuckoo.registry.key_access(/\\Microsoft\\Windows NT\\CurrentVersion/i) or cuckoo.network.http_post(/\/.*\/tasks\.php/) or false) }''', yara_file.text) expected = r''' import "cuckoo" rule rule_1 { strings: $str1 = "a" condition: $str1 and ( cuckoo.filesystem.file_access(/C:\\Users\\Avastian\\file1.exe/i) or false or cuckoo.registry.key_access(/\\Microsoft\\Windows NT\\CurrentVersion/i) or cuckoo.network.http_post(/\/.*\/tasks\.php/) or false ) } ''' self.assertEqual(expected, yara_file.text_formatted)
def test_custom_module_enhancing_known_module(self): modules = yaramod.Yaramod(yaramod.Features.AllCurrent, "./tests/python/testing_modules").modules # module cuckoo self.assertTrue("cuckoo" in modules) cuckoo_symbol = modules["cuckoo"].structure self.assertEqual("cuckoo", cuckoo_symbol.name) self.assertTrue(cuckoo_symbol.is_structure) cuckoo_attributes = cuckoo_symbol.attributes # module pe - added of an overload self.assertTrue("pe" in modules) pe_symbol = modules["pe"].structure self.assertEqual("pe", pe_symbol.name) pe_attributes = pe_symbol.attributes # other pe json does not delete functions from base pe json: self.assertTrue("MACHINE_AM33" in pe_attributes) machine_symbol = pe_attributes["MACHINE_AM33"] self.assertTrue(machine_symbol.is_value) self.assertEqual(machine_symbol.data_type, yaramod.ExpressionType.Int) # no problem with multiple definitions of the same symbol if those definitions are compatible: self.assertTrue("MACHINE_TEST_VALUE" in pe_attributes) machine_symbol = pe_attributes["MACHINE_TEST_VALUE"] self.assertTrue(machine_symbol.is_value) self.assertEqual(machine_symbol.data_type, yaramod.ExpressionType.Int) self.assertTrue("sections" in pe_attributes) section_array_symbol = pe_attributes['sections'] self.assertEqual(section_array_symbol.name, 'sections') self.assertTrue(section_array_symbol.is_array) self.assertEqual(section_array_symbol.element_type, yaramod.ExpressionType.Object) self.assertEqual(section_array_symbol.documentation[0:10], 'Individual') section_symbol = section_array_symbol.structure self.assertEqual(section_symbol.name, 'sections') self.assertTrue(section_symbol.is_structure) section_attributes = section_symbol.attributes # pe.sections.characteristics still exists: self.assertTrue("virtual_address" in section_attributes) # pe.sections.test_sections_value is added: self.assertTrue("test_sections_value" in section_attributes) test_section_value_symbol = section_attributes['test_sections_value'] self.assertEqual(test_section_value_symbol.name, 'test_sections_value') self.assertTrue(test_section_value_symbol.is_value) self.assertTrue(test_section_value_symbol.data_type, yaramod.ExpressionType.String) self.assertTrue("rich_signature" in pe_attributes) rich_signature_symbol = pe_attributes['rich_signature'] self.assertTrue(rich_signature_symbol.is_structure) rich_signature_attributes = rich_signature_symbol.attributes self.assertTrue("test_value" in rich_signature_attributes) self.assertTrue("version" in rich_signature_attributes) version_symbol = rich_signature_attributes['version'] self.assertTrue(version_symbol.is_function) version_overloads = version_symbol.overloads self.assertEqual(len(version_overloads), 3) self.assertEqual(version_overloads[0], [yaramod.ExpressionType.Int]) self.assertEqual(version_overloads[1], [yaramod.ExpressionType.Int, yaramod.ExpressionType.Int]) self.assertEqual(version_overloads[2], [yaramod.ExpressionType.Int, yaramod.ExpressionType.String]) version_overloads_names = version_symbol.argument_names self.assertEqual(version_overloads_names[0], ["version"]) self.assertEqual(version_overloads_names[1], ["version", "toolid"]) self.assertEqual(version_overloads_names[2], ["version", "test string argument"])