Skip to content

Writing Modifying Visitors

Tadeas Kucera edited this page Jan 30, 2021 · 3 revisions

Writing Modifying Visitors

Modifying Metas

In yaramod, we are able to modify existing yara-rules through many methods. For example, we can add new metas with method Rule::addMeta(const std::string& name, const Literal& value) which inserts a new meta into the TokenStream. This method can be used through python as add_meta as follows:

yara_file = yaramod.Yaramod().parse_string(
'''
rule rule_with_added_metas {
	condition:
		true
}'''
)

rule = yara_file.rules[0]
rule.add_meta('int_meta', yaramod.Literal(42))
rule.add_meta('bool_meta', yaramod.Literal(False))

This will add both meta and : tokens and create two metas and the formatted text will look as follows:

rule rule_with_added_metas
{
	meta:
		int_meta = 42
		bool_meta = false
	condition:
		true
}

We can also modify existing metas using python bindings Rule::get_meta_with_name and Meta::value. The following code will change the integer value 42 of int_meta into string forty two:

meta = rule.get_meta_with_name('int_meta')
meta.value = yaramod.Literal('forty two')

With the following result:

rule rule_with_added_metas
{
	meta:
		int_meta = "forty two"
		bool_meta = false
	condition:
		true
}

The Conditions in Yaramod

To demonstrate how to alter conditions of yara-rules we first need to make sure we understand their structure.

Conditions in yaramod are tree structures where each node is a derived class of the Expression class. Based on the arity of each expression is the number of nodes under it. The leaves of the tree correspond to the expressions of arity 0 such as EntrypointExpression or StringExpression. The expressions like NotExpression or other derived classes of UnaryOpExpression always have one other expression under them. And then we also have BinaryOpExpression with arity 2 or even more, because the ForExpression has another 3 expressions referenced under it.

Visiting Expressions

Yaramod provides two kinds of Visitors enabling the user to observe or modify existing Expressions. This page is about more interesting modifying Visitors:

The ModifyinVisitor class defines a visit method for each derived Expression class as a parameter. These methods are pre-defined not to change anything in the expression they are called with. The only thing these pre-defined methods do is they trigger visiting of all subexpressions. This means, that after calling a visit on an expression, then visit methods are recursively called upon each of the nodes in the expression tree structure. Until we modify the visit methods, each such call performs no changes on the nodes. But this shall change in the following paragraph.

Let now assume that we need to modify each EqExpression in the expression syntax tree. We can do it by writing our own derived class of ModifyingVisitor. The new class will simply override the visit(EqExpression* expr) method.

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
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