Mini Shell

Direktori : /proc/self/root/opt/imh-scan/
Upload File :
Current File : //proc/self/root/opt/imh-scan/mkdef

#!/opt/imh-python/bin/python3
"""Module/Script for creating yara
based ClamAV definitions"""
import re
import sys
import os
import time
import shutil
import getpass
import argparse


class MkDef:
    """Main class for definition maker"""

    def __init__(self, args):
        """sets user's database file name based on executing user"""
        self.args = args
        if self.args.all:
            self.condition = 'any of them'
        else:
            self.condition = 'PHP and any of them'
        # writes to tmp which should clear on boot, allowing instant definitions
        self.def_file = '/opt/imh-scan/sigs/new/new.yara'
        self.def_dir = os.path.split(self.def_file)[0]
        self.running_user = getpass.getuser()
        if self.running_user != 'root':
            print('mkdef only can be ran as root')
            sys.exit(1)
        self.prefix = f'$new_{self.running_user}'
        self.time = int(time.time())

    def check_dir(self):
        """Makes NDB file if it doesnt exist"""
        if not os.path.exists(self.def_dir):
            os.makedirs(self.def_dir)

    def definition_exists(self, definition):
        """Checks if the definition already exists in any of the dbs"""
        if not os.path.exists(self.def_file):
            print('making new.yara file')
            return None
        definition = re.escape(definition)
        with open(self.def_file, encoding='utf-8') as file:
            searcher = (
                fr'''=\s*("|'){definition}("|')'''
                fr'.*condition:\s*{self.condition}'
            )
            # searcher = 'strings.*condition'
            file_data = file.read().replace('\n', '')
            if re.search(searcher, file_data):
                print('Definition already in database, skipping')
                return 1
        return None

    def make_definition(self):
        """The function that actually writes the definition"""
        e_sig = self.args.sig.replace('\n', r'\n')
        if self.definition_exists(e_sig):
            return
        sig_line = f'        $new_imh_{self.time} = "{e_sig}"\n'
        default = [
            f'rule imh_new_{self.time}\n',
            '{\n',
            '    strings:\n',
            f'{sig_line}\n',
            '    condition:\n',
            f'        {self.condition}\n',
            '}',
        ]
        def_dir = os.path.split(self.def_file)[0]
        if not os.path.exists(def_dir):
            os.makedirs(def_dir)
        if not os.path.exists(self.def_file):
            with open(self.def_file, 'w', encoding='utf-8') as file:
                print(''.join(default), file=file)
                return
        insert_index, insert_legit, index = 0, 0, 0
        tmp_list = []
        with open(self.def_file, encoding='utf-8') as file:
            lines = file.readlines()
            for line in lines:
                if re.search(r'\s* strings:\s*\n', line) and not insert_legit:
                    tmp_list += [line, sig_line]
                    index += 1
                    insert_index = index
                    continue
                if (
                    insert_index
                    and not insert_legit
                    and re.search(r'\s* condition:\s*\n', line)
                ):
                    cond_index = index + 1
                    print(lines[cond_index])
                    if self.condition != lines[cond_index].strip():
                        print('not the right condition, deleting from tmp_list')
                        del tmp_list[insert_index]
                    else:
                        print('cond is good, writing')
                        insert_legit = 1
                tmp_list.append(line)
                index += 1
        if not insert_legit:
            print(f'adding default block to new.yara:\n{default}')
            tmp_list += default
        else:
            print(
                f'inserting line to existing cond: '
                f'{self.condition}\n{sig_line}'
            )
        tmp_fn = f'/tmp/mkdef_{self.time}'
        tmp_str = ''.join(tmp_list)
        with open(tmp_fn, 'w', encoding='utf-8') as file:
            print(tmp_str, file=file)
        shutil.move(tmp_fn, self.def_file)


def parse_args():
    """argparse function"""
    parser = argparse.ArgumentParser()
    # fmt: off
    parser.add_argument(
        '-a', '--all', action='store_true', default=False,
        help='allows the definition to match non php',
    )
    parser.add_argument(
        'sig', type=str, nargs=1, help='signature to make a definition for'
    )
    # fmt: on
    args = parser.parse_args()
    args.sig = ''.join(args.sig)
    return args


def main():
    """Main function executions"""
    args = parse_args()
    mkdef = MkDef(args)
    mkdef.check_dir()
    mkdef.make_definition()


if __name__ == '__main__':
    main()

Zerion Mini Shell 1.0