def rules_to_highlighted_string(rules: List[Rule]): yb = YaraBuilder() for rule in rules: if rule.name not in yb.yara_rules.keys(): rule.add_to_yarabuilder(yb) # As there is no yara lexer available in pygments, we're usign python here. return Syntax(yb.build_rules(), "python", background_color="default")
def export(name: str, tag: str, single: bool, path: str): c, ec = Console(), Console(stderr=True, style="bold red") session = get_session() rules, count = filter_rules_by_name_and_tag(name, tag, session) if count == 0: ec.print(f"Found no matching rules.") exit(-1) if single and os.path.isdir(path): ec.print(f"Given path ({path}) is a directory.") exit(-1) yb = YaraBuilder() for rule in rules: rule.add_to_yarabuilder(yb) if not single: with io.open(os.path.join(path, rule.name + ".yar"), "w") as fh: fh.write(yb.build_rule(rule.name)) if single: with io.open(path, "w") as fh: fh.write(yb.build_rules()) c.print(f"Wrote {len(rules)} rules.")
def get(raw: bool, identifier: str): c = Console() session = get_session() ruleset = get_ruleset_by_identifier(identifier, session) if not ruleset: c.print("Ruleset not found.") exit(-1) if not raw: c.print(rules_to_table(ruleset.rules)) else: yb = YaraBuilder() for rule in ruleset.rules: rule.add_to_yarabuilder(yb) c.print(yb.build_rules())
def edit(identifier: Union[int, str]): c, ec = Console(), Console(file=stderr, style="bold red") session = get_session() rule = get_rule_by_identifier(identifier, session) if len(rule) > 1: ec.print(f"Found more than one rule.") exit(-1) rule = rule[0] yb = YaraBuilder() rule.add_to_yarabuilder(yb) path, _ = write_ruleset_to_tmp_file(yb) hash = get_md5(path) open_file(path, f"{rule.name} opened in external editor...") edit_hash = get_md5(path) if hash == edit_hash: c.print(f"No change detected...") else: c.print(f"Change detected, updating rule...") edited_rule = read_rulefile(path) if not 0 < len(edited_rule) < 2: ec.print("Edited rule file must contain exactly one yara rule.") exit(-1) rule.name = edited_rule[0].get("rule_name", "Unnamed rule") rule.meta = plyara_object_to_meta(edited_rule[0]) rule.imports = plyara_object_to_imports(edited_rule[0]) rule.strings = plyara_object_to_strings(edited_rule[0]) rule.tags = plyara_object_to_tags(edited_rule[0], session) rule.condition = plyara_object_to_condition(edited_rule[0]) rule.ruleset = plyara_object_to_ruleset(edited_rule[0], session) session.commit() os.remove(path)
def get(identifier, output): c = Console() rules = get_rule_by_identifier(identifier) if len(rules) == 0: c.print("Query returned empty list of Rules.") exit(-1) yb = YaraBuilder() _ = [rule.add_to_yarabuilder(yb) for rule in rules] if output and len(output) > 0: with io.open(output, "w") as fh: fh.write(yb.build_rules()) exit(0) # Simple print because rich.Console adjusts to terminal size and might cut something or mess with the format for # readability print(yb.build_rules())
def scan(tag: Tuple[str], exclude_tag: Tuple[str], name: str, timeout: int, no_progress: bool, csv: bool, recursive: bool, paths: List[str]): c, ec = Console(), Console(style="bold red", stderr=True) if len(paths) == 0: with click.Context(scan) as ctx: c.print(scan.get_help(ctx)) exit(-1) session = get_session() rules, count = filter_rules_by_name_and_tag(name, tag, exclude_tag, session) if count == 0: ec.print("No rules matching your criteria.") exit(-1) yb = YaraBuilder() for rule in rules: rule.add_to_yarabuilder(yb) ruleset_path, _ = write_ruleset_to_tmp_file(yb) # Initialize external parameter filename as empty string. This will be filled during matching files. ruleset_compiled = yara.compile(ruleset_path, externals={ "filename": "" }) if not no_progress: c.print(f"Using ruleset {ruleset_path} for scanning. Check the rule file in case any error shows up.") if recursive: list_of_files = [] for path in paths: list_of_files.extend(get_dir_recursive(path)) paths = list_of_files with Progress() if not no_progress else c as prog: if isinstance(prog, Progress): t1 = prog.add_task("Scanning...", total=len(paths)) for path in paths: if isinstance(prog, Progress): prog.update(t1, advance=1, description=f"Scanning {path}...") if os.path.isdir(path): continue try: # Check if file matches. This also adds the basename as filename parameter. matches = ruleset_compiled.match(path, timeout=timeout, externals={ "filename": os.path.basename(path) }) except yara.TimeoutError: prog.print(Text("Timed out!", style="bold red")) if isinstance(prog, Progress): prog.update(t1, description="Timed out!") exit(-1) for match in matches: if csv: prog.print(f'"{match.rule}","{",".join(match.tags)}","{path}"') else: prog.print(f"{match.rule} ({', '.join(match.tags)}): {path}", highlight=not no_progress) if isinstance(prog, Progress): prog.update(t1, description="Finished scanning!") os.remove(ruleset_path)
def write_ruleset_to_file(yb: YaraBuilder, file: Union[int, str]) -> int: """Write a ruleset defined by yarabuilder to a given filedescriptor or filepath.""" text = yb.build_rules() if isinstance(file, int): with os.fdopen(file, "w") as fh: b = fh.write(text) else: with io.open(file, "w") as fh: b = fh.write(text) if b <= 0: ec = Console(file=stderr) ec.print( f"ERR: Number of bytes written should be greater 0 but was {b}.") return b
def string(raw, query_string): c = Console() session = get_session() strings = session.query(String).filter(String.type == "text").filter( String.value.like(query_string)).all() if not raw: c.print(f"Found {len(strings)} strings.") t = Table() t.add_column("String") t.add_column("ID") t.add_column("Rule") t.add_column("Tags") for string in strings: t.add_row(string.value, str(string.rule.id), string.rule.name, ", ".join([tag.name for tag in string.rule.tags])) c.print(t) else: yb = YaraBuilder() for string in strings: if string.rule.name not in yb.yara_rules.keys(): string.rule.add_to_yarabuilder(yb) syntax = Syntax(yb.build_rules(), "python", background_color="default") c.print(syntax)
def scan(tag: str, name: str, timeout: int, no_progress: bool, csv: bool, file: List[str]): c, ec = Console(), Console(stderr=True) if len(file) == 0: with click.Context(scan) as ctx: c.print(scan.get_help(ctx)) exit(-1) session = get_session() rules, count = filter_rules_by_name_and_tag(name, tag, session) if count == 0: ec.print("No rules matching your criteria.") exit(-1) yb = YaraBuilder() for rule in rules: rule.add_to_yarabuilder(yb) ruleset_path, _ = write_ruleset_to_tmp_file(yb) ruleset_compiled = yara.compile(ruleset_path) if not no_progress: c.print( f"Using ruleset {ruleset_path} for scanning. Check the rule file in case any error shows up." ) with Progress() if not no_progress else c as prog: if isinstance(prog, Progress): t1 = prog.add_task("Scanning...", total=len(file)) for path in file: if isinstance(prog, Progress): prog.update(t1, advance=1, description=f"Scanning {path}...") if os.path.isdir(path): continue try: matches = ruleset_compiled.match(path, timeout=timeout) except yara.TimeoutError: prog.print(Text("Timed out!", style="bold red")) if isinstance(prog, Progress): prog.update(t1, description="Timed out!") exit(-1) for match in matches: if csv: prog.print( f'"{match.rule}","{",".join(match.tags)}","{path}"') else: prog.print( f"{match.rule} ({', '.join(match.tags)}): {path}", highlight=not no_progress) if isinstance(prog, Progress): prog.update(t1, description="Finished scanning!") os.remove(ruleset_path)
def print_rule(rules: Union[Dict, List]) -> str: yb = YaraBuilder() if isinstance(rules, dict): rules = [rules] for rule in rules: rn = rule["rule_name"] yb.create_rule(rn) for mdata in rule.get("metadata", []): for k, v in mdata.items(): yb.add_meta(rn, k, v) for tag in rule["tags"]: yb.add_tag(rn, tag) for yara_string in rule["strings"]: s_type = yara_string["type"] s_name = yara_string["name"][1:] s_val = yara_string["value"] s_mod = yara_string.get("modifiers", []) if s_type == "text": yb.add_text_string(rn, s_val, s_name, s_mod) elif s_type == "regex": yb.add_regex_string(rn, s_val, s_name, s_mod) elif s_type == "byte": yb.add_hex_string(rn, s_val[1:-1].strip(), s_name) yb.add_condition(rn, " ".join(rule["condition_terms"])) return yb.build_rules()
def add_to_yarabuilder(self, yb: YaraBuilder) -> None: """Add the rule object to a given YaraBuilder instance >>> rule = Rule(...) >>> yb = YaraBuilder() >>> rule.add_to_yarabuilder(yb) >>> print(yb.build_rules()) """ yb.create_rule(self.name) key = load_config().get("ruleset_meta_key", "ruleset") if self.ruleset: yb.add_meta(self.name, key, self.ruleset.name) for meta in self.meta: yb.add_meta(self.name, meta.key, meta.value) for string in self.strings: s_name = string.name[1:] if string.type == "text": yb.add_text_string(self.name, string.value, s_name, string.modifier_list) elif string.type == "byte": yb.add_hex_string( self.name, string.value, s_name, string. modifier_list # Todo: Check hex string modifiers - this list should always be empty? ) elif string.type == "regex": yb.add_regex_string(self.name, string.value, s_name, string.modifier_list) else: print(f"ERROR: Unknown string type \"{string.type}\".", file=stderr) for tag in self.tags: yb.add_tag(self.name, tag.name) for imp in self.import_list: yb.add_import(self.name, imp) yb.add_condition(self.name, self.condition.strip())
def __str__(self): yb = YaraBuilder() self.add_to_yarabuilder(yb) return yb.build_rule(self.name)